Page MenuHomeFreeBSD

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/contrib/unbound/.gitignore b/contrib/unbound/.gitignore
deleted file mode 100644
index b25c15b81fae..000000000000
--- a/contrib/unbound/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-*~
diff --git a/contrib/unbound/Makefile.in b/contrib/unbound/Makefile.in
index 0a2e7f9b6f08..22fb75c123bd 100644
--- a/contrib/unbound/Makefile.in
+++ b/contrib/unbound/Makefile.in
@@ -1,1625 +1,1625 @@
# Copyright 2007 NLnet Labs
# See the file LICENSE for the license
SHELL=@SHELL@
VERSION=@PACKAGE_VERSION@
srcdir=@srcdir@
prefix=@prefix@
exec_prefix=@exec_prefix@
bindir=@bindir@
sbindir=@sbindir@
mandir=@mandir@
libdir=@libdir@
# datarootdir is here to please some checkers, use datadir.
datarootdir=@datarootdir@
datadir=@datadir@
includedir=@includedir@
doxygen=@doxygen@
libtool=@libtool@
staticexe=@staticexe@
EXEEXT=@EXEEXT@
configfile=@ub_conf_file@
CHECKLOCK_SRC=testcode/checklocks.c
CHECKLOCK_OBJ=@CHECKLOCK_OBJ@
DNSTAP_SRC=@DNSTAP_SRC@
DNSTAP_OBJ=@DNSTAP_OBJ@
DNSCRYPT_SRC=@DNSCRYPT_SRC@
DNSCRYPT_OBJ=@DNSCRYPT_OBJ@
WITH_DYNLIBMODULE=@WITH_DYNLIBMODULE@
WITH_PYTHONMODULE=@WITH_PYTHONMODULE@
WITH_PYUNBOUND=@WITH_PYUNBOUND@
PY_MAJOR_VERSION=@PY_MAJOR_VERSION@
PYTHON_SITE_PKG=@PYTHON_SITE_PKG@
PYTHONMOD_INSTALL=@PYTHONMOD_INSTALL@
PYTHONMOD_UNINSTALL=@PYTHONMOD_UNINSTALL@
PYUNBOUND_INSTALL=@PYUNBOUND_INSTALL@
PYUNBOUND_UNINSTALL=@PYUNBOUND_UNINSTALL@
UNBOUND_EVENT_INSTALL=@UNBOUND_EVENT_INSTALL@
UNBOUND_EVENT_UNINSTALL=@UNBOUND_EVENT_UNINSTALL@
UNBOUND_VERSION_MAJOR=@UNBOUND_VERSION_MAJOR@
UNBOUND_VERSION_MINOR=@UNBOUND_VERSION_MINOR@
UNBOUND_VERSION_MICRO=@UNBOUND_VERSION_MICRO@
ALLTARGET=@ALLTARGET@
INSTALLTARGET=@INSTALLTARGET@
SSLLIB=@SSLLIB@
# _unbound.la if pyunbound enabled.
PYUNBOUND_TARGET=@PYUNBOUND_TARGET@
# override $U variable which is used by autotools for deansification (for
# K&R C compilers), but causes problems if $U is defined in the env).
U=
PROTOC_C=@PROTOC_C@
SWIG=@SWIG@
YACC=@YACC@
LEX=@LEX@
STRIP=@STRIP@
CC=@CC@
CPPFLAGS=-I. @CPPFLAGS@
PYTHON_CPPFLAGS=-I. -I$(srcdir) @PYTHON_CPPFLAGS@
CFLAGS=-DSRCDIR=$(srcdir) @CFLAGS@
LDFLAGS=@LDFLAGS@
LIBS=@LIBS@
PYTHON_LIBS=@PYTHON_LIBS@
LIBOBJS=@LIBOBJS@
# filter out ctime_r from compat obj.
LIBOBJ_WITHOUT_CTIME=@LIBOBJ_WITHOUT_CTIME@
LIBOBJ_WITHOUT_CTIMEARC4=@LIBOBJ_WITHOUT_CTIMEARC4@
RUNTIME_PATH=@RUNTIME_PATH@
DEPFLAG=@DEPFLAG@
DATE=@CONFIG_DATE@
LIBTOOL=$(libtool)
BUILD=build/
UBSYMS=@UBSYMS@
EXTRALINK=@EXTRALINK@
WINDRES=@WINDRES@
LINT=splint
LINTFLAGS=+quiet -weak -warnposix -unrecog -Din_addr_t=uint32_t -Du_int=unsigned -Du_char=uint8_t -preproc -Drlimit=rlimit64 -D__gnuc_va_list=va_list -formatcode
#-Dglob64=glob -Dglobfree64=globfree
# compat with openssl linux edition.
LINTFLAGS+="-DBN_ULONG=unsigned long" -Dkrb5_int32=int "-Dkrb5_ui_4=unsigned int" -DPQ_64BIT=uint64_t -DRC4_INT=unsigned -fixedformalarray -D"ENGINE=unsigned" -D"RSA=unsigned" -D"DSA=unsigned" -D"EVP_PKEY=unsigned" -D"EVP_MD=unsigned" -D"SSL=unsigned" -D"SSL_CTX=unsigned" -D"X509=unsigned" -D"RC4_KEY=unsigned" -D"EVP_MD_CTX=unsigned" -D"ECDSA_SIG=DSA_SIG"
# compat with NetBSD
LINTFLAGS+=@NETBSD_LINTFLAGS@
# compat with OpenBSD
LINTFLAGS+="-Dsigset_t=long"
# FreeBSD
LINTFLAGS+="-D__uint16_t=uint16_t" "-DEVP_PKEY_ASN1_METHOD=int" "-D_RuneLocale=int" "-D__va_list=va_list" "-D__uint32_t=uint32_t" "-D_Alignof(x)=x" "-D__aligned(x)=" "-D__requires_exclusive(x)=" "-D__requires_unlocked(x)=" "-D__locks_exclusive(x)=" "-D__trylocks_exclusive(x)=" "-D__unlocks(x)=" "-D__locks_shared(x)=" "-D__trylocks_shared(x)="
# GCC Docker
LINTFLAGS+=@GCC_DOCKER_LINTFLAGS@
INSTALL=$(SHELL) $(srcdir)/install-sh
DYNLIBMOD_SRC=dynlibmod/dynlibmod.c
DYNLIBMOD_OBJ=@DYNLIBMOD_OBJ@
DYNLIBMOD_HEADER=@DYNLIBMOD_HEADER@
DYNLIBMOD_EXTRALIBS=@DYNLIBMOD_EXTRALIBS@
#pythonmod.c is not here, it is mentioned by itself in its own rules,
#makedepend fails on missing interface.h otherwise.
PYTHONMOD_SRC=pythonmod/pythonmod_utils.c
# pythonmod.lo pythonmod_utils.lo if python mod enabled.
PYTHONMOD_OBJ=@PYTHONMOD_OBJ@
PYTHONMOD_HEADER=@PYTHONMOD_HEADER@
# libunbound/python/libunbound_wrap.c is dealt with by its own rules.
PYUNBOUND_SRC=
# libunbound_wrap.lo if python libunbound wrapper enabled.
PYUNBOUND_OBJ=@PYUNBOUND_OBJ@
SUBNET_SRC=edns-subnet/edns-subnet.c edns-subnet/subnetmod.c edns-subnet/addrtree.c edns-subnet/subnet-whitelist.c
SUBNET_OBJ=@SUBNET_OBJ@
SUBNET_HEADER=@SUBNET_HEADER@
IPSECMOD_SRC=ipsecmod/ipsecmod.c ipsecmod/ipsecmod-whitelist.c
IPSECMOD_OBJ=@IPSECMOD_OBJ@
IPSECMOD_HEADER=@IPSECMOD_HEADER@
CACHEDB_SRC=@CACHEDB_SRC@
CACHEDB_OBJ=@CACHEDB_OBJ@
COMMON_SRC=services/cache/dns.c services/cache/infra.c services/cache/rrset.c \
util/as112.c util/data/dname.c util/data/msgencode.c util/data/msgparse.c \
util/data/msgreply.c util/data/packed_rrset.c iterator/iterator.c \
iterator/iter_delegpt.c iterator/iter_donotq.c iterator/iter_fwd.c \
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 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/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/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 \
validator/val_nsec3.c validator/val_nsec.c validator/val_secalgo.c \
validator/val_sigcrypt.c validator/val_utils.c dns64/dns64.c \
edns-subnet/edns-subnet.c edns-subnet/subnetmod.c \
edns-subnet/addrtree.c edns-subnet/subnet-whitelist.c \
$(CACHEDB_SRC) respip/respip.c $(CHECKLOCK_SRC) \
$(DNSTAP_SRC) $(DNSCRYPT_SRC) $(IPSECMOD_SRC) $(IPSET_SRC)
COMMON_OBJ_WITHOUT_NETCALL=dns.lo infra.lo rrset.lo dname.lo msgencode.lo \
as112.lo msgparse.lo msgreply.lo packed_rrset.lo iterator.lo iter_delegpt.lo \
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 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 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 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
# set to $COMMON_OBJ or to "" if --enableallsymbols
COMMON_OBJ_ALL_SYMBOLS=@COMMON_OBJ_ALL_SYMBOLS@
COMPAT_SRC=compat/ctime_r.c compat/fake-rfc2553.c compat/gmtime_r.c \
compat/inet_aton.c compat/inet_ntop.c compat/inet_pton.c compat/malloc.c \
compat/memcmp.c compat/memmove.c compat/snprintf.c compat/strlcat.c \
compat/strlcpy.c compat/strptime.c compat/getentropy_freebsd.c compat/getentropy_linux.c \
compat/getentropy_osx.c compat/getentropy_solaris.c compat/getentropy_win.c \
compat/explicit_bzero.c compat/arc4random.c compat/arc4random_uniform.c \
compat/arc4_lock.c compat/sha512.c compat/reallocarray.c compat/isblank.c \
compat/strsep.c
COMPAT_OBJ=$(LIBOBJS:.o=.lo)
COMPAT_OBJ_WITHOUT_CTIME=$(LIBOBJ_WITHOUT_CTIME:.o=.lo)
COMPAT_OBJ_WITHOUT_CTIMEARC4=$(LIBOBJ_WITHOUT_CTIMEARC4:.o=.lo)
SLDNS_SRC=sldns/keyraw.c sldns/sbuffer.c sldns/wire2str.c sldns/parse.c \
sldns/parseutil.c sldns/rrdef.c sldns/str2wire.c
SLDNS_OBJ=keyraw.lo sbuffer.lo wire2str.lo parse.lo parseutil.lo rrdef.lo \
str2wire.lo
SLDNS_ALLOCCHECK_EXTRA_OBJ=@SLDNS_ALLOCCHECK_EXTRA_OBJ@
UNITTEST_SRC=testcode/unitanchor.c testcode/unitdname.c \
testcode/unitlruhash.c testcode/unitmain.c testcode/unitmsgparse.c \
testcode/unitneg.c testcode/unitregional.c testcode/unitslabhash.c \
testcode/unitverify.c testcode/readhex.c testcode/testpkts.c testcode/unitldns.c \
testcode/unitecs.c testcode/unitauth.c testcode/unitzonemd.c \
testcode/unittcpreuse.c
UNITTEST_OBJ=unitanchor.lo unitdname.lo unitlruhash.lo unitmain.lo \
unitmsgparse.lo unitneg.lo unitregional.lo unitslabhash.lo unitverify.lo \
readhex.lo testpkts.lo unitldns.lo unitecs.lo unitauth.lo unitzonemd.lo \
unittcpreuse.lo
UNITTEST_OBJ_LINK=$(UNITTEST_OBJ) worker_cb.lo $(COMMON_OBJ) $(SLDNS_OBJ) \
$(COMPAT_OBJ)
DAEMON_SRC=daemon/acl_list.c daemon/cachedump.c daemon/daemon.c \
daemon/remote.c daemon/stats.c daemon/unbound.c daemon/worker.c @WIN_DAEMON_SRC@
DAEMON_OBJ=acl_list.lo cachedump.lo daemon.lo \
shm_main.lo remote.lo stats.lo unbound.lo \
worker.lo @WIN_DAEMON_OBJ@
DAEMON_OBJ_LINK=$(DAEMON_OBJ) $(COMMON_OBJ_ALL_SYMBOLS) $(SLDNS_OBJ) \
$(COMPAT_OBJ) @WIN_DAEMON_OBJ_LINK@
CHECKCONF_SRC=smallapp/unbound-checkconf.c smallapp/worker_cb.c
CHECKCONF_OBJ=unbound-checkconf.lo worker_cb.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_LINK=$(CONTROL_OBJ) worker_cb.lo $(COMMON_OBJ_ALL_SYMBOLS) \
$(SLDNS_OBJ) $(COMPAT_OBJ) @WIN_CONTROL_OBJ_LINK@
HOST_SRC=smallapp/unbound-host.c
HOST_OBJ=unbound-host.lo
HOST_OBJ_LINK=$(HOST_OBJ) $(SLDNS_OBJ) $(COMPAT_OBJ_WITHOUT_CTIMEARC4) $(SLDNS_ALLOCCHECK_EXTRA_OBJ) @WIN_HOST_OBJ_LINK@
UBANCHOR_SRC=smallapp/unbound-anchor.c
UBANCHOR_OBJ=unbound-anchor.lo
UBANCHOR_OBJ_LINK=$(UBANCHOR_OBJ) parseutil.lo \
$(COMPAT_OBJ_WITHOUT_CTIME) $(SLDNS_ALLOCCHECK_EXTRA_OBJ) @WIN_UBANCHOR_OBJ_LINK@
TESTBOUND_SRC=testcode/testbound.c testcode/testpkts.c \
daemon/worker.c daemon/acl_list.c \
daemon/daemon.c daemon/stats.c \
testcode/replay.c testcode/fake_event.c
TESTBOUND_OBJ=testbound.lo replay.lo fake_event.lo
TESTBOUND_OBJ_LINK=$(TESTBOUND_OBJ) testpkts.lo worker.lo acl_list.lo \
daemon.lo stats.lo shm_main.lo $(COMMON_OBJ_WITHOUT_NETCALL) ub_event.lo $(SLDNS_OBJ) \
$(COMPAT_OBJ)
LOCKVERIFY_SRC=testcode/lock_verify.c
LOCKVERIFY_OBJ=lock_verify.lo
LOCKVERIFY_OBJ_LINK=$(LOCKVERIFY_OBJ) worker_cb.lo $(COMMON_OBJ) $(COMPAT_OBJ) \
$(SLDNS_OBJ)
PETAL_SRC=testcode/petal.c
PETAL_OBJ=petal.lo
PETAL_OBJ_LINK=$(PETAL_OBJ) $(COMPAT_OBJ_WITHOUT_CTIMEARC4)
PKTVIEW_SRC=testcode/pktview.c testcode/readhex.c
PKTVIEW_OBJ=pktview.lo
PKTVIEW_OBJ_LINK=$(PKTVIEW_OBJ) worker_cb.lo readhex.lo $(COMMON_OBJ) \
$(COMPAT_OBJ) $(SLDNS_OBJ)
MEMSTATS_SRC=testcode/memstats.c
MEMSTATS_OBJ=memstats.lo
MEMSTATS_OBJ_LINK=$(MEMSTATS_OBJ) worker_cb.lo $(COMMON_OBJ) $(COMPAT_OBJ) \
$(SLDNS_OBJ)
ASYNCLOOK_SRC=testcode/asynclook.c
ASYNCLOOK_OBJ=asynclook.lo
ASYNCLOOK_OBJ_LINK=$(ASYNCLOOK_OBJ) log.lo locks.lo $(CHECKLOCK_OBJ) $(COMPAT_OBJ) @ASYNCLOOK_ALLOCCHECK_EXTRA_OBJ@
STREAMTCP_SRC=testcode/streamtcp.c
STREAMTCP_OBJ=streamtcp.lo
STREAMTCP_OBJ_LINK=$(STREAMTCP_OBJ) worker_cb.lo $(COMMON_OBJ) $(COMPAT_OBJ) \
$(SLDNS_OBJ)
DOHCLIENT_SRC=testcode/dohclient.c
DOHCLIENT_OBJ=dohclient.lo
DOHCLIENT_OBJ_LINK=$(DOHCLIENT_OBJ) worker_cb.lo $(COMMON_OBJ) $(COMPAT_OBJ) \
$(SLDNS_OBJ)
PERF_SRC=testcode/perf.c
PERF_OBJ=perf.lo
PERF_OBJ_LINK=$(PERF_OBJ) worker_cb.lo $(COMMON_OBJ) $(COMPAT_OBJ) $(SLDNS_OBJ)
DELAYER_SRC=testcode/delayer.c
DELAYER_OBJ=delayer.lo
DELAYER_OBJ_LINK=$(DELAYER_OBJ) worker_cb.lo $(COMMON_OBJ) $(COMPAT_OBJ) \
$(SLDNS_OBJ)
READZONE_SRC=testcode/readzone.c
READZONE_OBJ=readzone.lo
READZONE_OBJ_LINK=$(READZONE_OBJ) worker_cb.lo $(COMMON_OBJ) $(COMPAT_OBJ) $(SLDNS_OBJ)
IPSET_SRC=@IPSET_SRC@
IPSET_OBJ=@IPSET_OBJ@
DNSTAP_SOCKET_SRC=dnstap/unbound-dnstap-socket.c
DNSTAP_SOCKET_OBJ=unbound-dnstap-socket.lo
DNSTAP_SOCKET_OBJ_LINK=$(DNSTAP_SOCKET_OBJ) $(COMMON_OBJ) \
$(COMPAT_OBJ) $(SLDNS_OBJ)
DNSTAP_SOCKET_TESTBIN=@DNSTAP_SOCKET_TESTBIN@
LIBUNBOUND_SRC=libunbound/context.c libunbound/libunbound.c \
libunbound/libworker.c
LIBUNBOUND_OBJ=context.lo libunbound.lo libworker.lo ub_event_pluggable.lo
LIBUNBOUND_OBJ_LINK=$(LIBUNBOUND_OBJ) $(COMMON_OBJ_WITHOUT_UB_EVENT) $(SLDNS_OBJ) $(COMPAT_OBJ)
# win apps or "" if not on windows
WINAPPS=@WINAPPS@
WIN_DAEMON_THE_SRC=winrc/win_svc.c winrc/w_inst.c
SVCINST_SRC=winrc/unbound-service-install.c
SVCINST_OBJ=unbound-service-install.lo
SVCINST_OBJ_LINK=$(SVCINST_OBJ) w_inst.lo rsrc_svcinst.o $(COMPAT_OBJ_WITHOUT_CTIMEARC4)
SVCUNINST_SRC=winrc/unbound-service-remove.c
SVCUNINST_OBJ=unbound-service-remove.lo
SVCUNINST_OBJ_LINK=$(SVCUNINST_OBJ) w_inst.lo rsrc_svcuninst.o \
$(COMPAT_OBJ_WITHOUT_CTIMEARC4)
ANCHORUPD_SRC=winrc/anchor-update.c
ANCHORUPD_OBJ=anchor-update.lo
ANCHORUPD_OBJ_LINK=$(ANCHORUPD_OBJ) rsrc_anchorupd.o $(COMPAT_OBJ_WITHOUT_CTIMEARC4) wire2str.lo str2wire.lo parseutil.lo sbuffer.lo rrdef.lo keyraw.lo parse.lo
RSRC_OBJ=rsrc_svcinst.o rsrc_svcuninst.o rsrc_anchorupd.o rsrc_unbound.o \
rsrc_unbound_host.o rsrc_unbound_anchor.o rsrc_unbound_control.o \
rsrc_unbound_checkconf.o
ALL_SRC=$(COMMON_SRC) $(UNITTEST_SRC) $(DAEMON_SRC) \
$(TESTBOUND_SRC) $(LOCKVERIFY_SRC) $(PKTVIEW_SRC) \
$(MEMSTATS_SRC) $(CHECKCONF_SRC) $(LIBUNBOUND_SRC) $(HOST_SRC) \
$(ASYNCLOOK_SRC) $(STREAMTCP_SRC) $(PERF_SRC) $(DELAYER_SRC) \
$(CONTROL_SRC) $(UBANCHOR_SRC) $(PETAL_SRC) $(DNSTAP_SOCKET_SRC)\
$(PYTHONMOD_SRC) $(PYUNBOUND_SRC) $(WIN_DAEMON_THE_SRC) \
$(SVCINST_SRC) $(SVCUNINST_SRC) $(ANCHORUPD_SRC) $(SLDNS_SRC) \
$(DOHCLIENT_SRC) $(READZONE_SRC)
ALL_OBJ=$(COMMON_OBJ) $(UNITTEST_OBJ) $(DAEMON_OBJ) \
$(TESTBOUND_OBJ) $(LOCKVERIFY_OBJ) $(PKTVIEW_OBJ) \
$(MEMSTATS_OBJ) $(CHECKCONF_OBJ) $(LIBUNBOUND_OBJ) $(HOST_OBJ) \
$(ASYNCLOOK_OBJ) $(STREAMTCP_OBJ) $(PERF_OBJ) $(DELAYER_OBJ) \
$(CONTROL_OBJ) $(UBANCHOR_OBJ) $(PETAL_OBJ) $(DNSTAP_SOCKET_OBJ)\
$(COMPAT_OBJ) $(PYUNBOUND_OBJ) \
$(SVCINST_OBJ) $(SVCUNINST_OBJ) $(ANCHORUPD_OBJ) $(SLDNS_OBJ) \
$(DOHCLIENT_OBJ) $(READZONE_OBJ)
COMPILE=$(LIBTOOL) --tag=CC --mode=compile $(CC) $(CPPFLAGS) $(CFLAGS) @PTHREAD_CFLAGS_ONLY@
LINK=$(LIBTOOL) --tag=CC --mode=link $(CC) $(staticexe) $(RUNTIME_PATH) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS)
LINK_LIB=$(LIBTOOL) --tag=CC --mode=link $(CC) $(RUNTIME_PATH) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -version-info @LIBUNBOUND_CURRENT@:@LIBUNBOUND_REVISION@:@LIBUNBOUND_AGE@ -no-undefined
.PHONY: clean realclean doc lint all install uninstall tests test strip lib longtest longcheck check alltargets
all: $(COMMON_OBJ) $(ALLTARGET)
alltargets: unbound$(EXEEXT) unbound-checkconf$(EXEEXT) lib unbound-host$(EXEEXT) unbound-control$(EXEEXT) unbound-anchor$(EXEEXT) unbound-control-setup $(WINAPPS) $(PYUNBOUND_TARGET)
# compat with BSD make, register suffix, and an implicit rule to actualise it.
.SUFFIXES: .lo
.c.lo:
$(COMPILE) -o $@ -c $<
$(ALL_OBJ):
@@SOURCEDETERMINE@
$(COMPILE) -o $@ -c @SOURCEFILE@
$(RSRC_OBJ):
@@SOURCEDETERMINE@
$(WINDRES) $(CPPFLAGS) @SOURCEFILE@ $@
rsrc_svcinst.o: $(srcdir)/winrc/rsrc_svcinst.rc config.h
rsrc_svcuninst.o: $(srcdir)/winrc/rsrc_svcuninst.rc config.h
rsrc_anchorupd.o: $(srcdir)/winrc/rsrc_anchorupd.rc config.h
rsrc_unbound.o: $(srcdir)/winrc/rsrc_unbound.rc config.h
rsrc_unbound_host.o: $(srcdir)/winrc/rsrc_unbound_host.rc config.h
rsrc_unbound_anchor.o: $(srcdir)/winrc/rsrc_unbound_anchor.rc config.h
rsrc_unbound_control.o: $(srcdir)/winrc/rsrc_unbound_control.rc config.h
rsrc_unbound_checkconf.o: $(srcdir)/winrc/rsrc_unbound_checkconf.rc config.h
TEST_BIN=asynclook$(EXEEXT) delayer$(EXEEXT) \
lock-verify$(EXEEXT) memstats$(EXEEXT) perf$(EXEEXT) \
petal$(EXEEXT) pktview$(EXEEXT) streamtcp$(EXEEXT) \
$(DNSTAP_SOCKET_TESTBIN) dohclient$(EXEEXT) \
testbound$(EXEEXT) unittest$(EXEEXT) readzone$(EXEEXT)
tests: all $(TEST_BIN)
check: test
longcheck: longtest
test: unittest$(EXEEXT) testbound$(EXEEXT)
./unittest$(EXEEXT)
./testbound$(EXEEXT) -s
for x in $(srcdir)/testdata/*.rpl; do \
output=`./testbound$(EXEEXT) -p $$x -o -vvvvv 2>&1`; \
if test $$? -eq 0; then \
printf "%s OK\n" "$$x "; \
else \
printf "%s\n" "$$output "; \
printf "%s failed\n" "$$x "; \
exit 1; \
fi; \
done
@echo test OK
longtest: tests
if test ! $(srcdir)/testdata -ef ./testdata; then rm -rf testcode testdata; mkdir testcode testdata; cp -R $(srcdir)/testdata/*.sh $(srcdir)/testdata/*.tdir $(srcdir)/testdata/*.rpl $(srcdir)/testdata/*.crpl testdata; cp $(srcdir)/testcode/*.sh testcode; if test ! -d util; then mkdir util; fi; cp $(srcdir)/util/iana_ports.inc util; fi
if test -x "`which bash`"; then bash testcode/do-tests.sh; else sh testcode/do-tests.sh; fi
lib: libunbound.la unbound.h
libunbound.la: $(LIBUNBOUND_OBJ_LINK)
$(LINK_LIB) $(UBSYMS) -o $@ $(LIBUNBOUND_OBJ_LINK) -rpath $(libdir) $(SSLLIB) $(LIBS)
unbound$(EXEEXT): $(DAEMON_OBJ_LINK) libunbound.la
$(LINK) -o $@ $(DAEMON_OBJ_LINK) $(EXTRALINK) $(SSLLIB) $(LIBS) $(DYNLIBMOD_EXTRALIBS)
unbound-checkconf$(EXEEXT): $(CHECKCONF_OBJ_LINK) libunbound.la
$(LINK) -o $@ $(CHECKCONF_OBJ_LINK) $(EXTRALINK) $(SSLLIB) $(LIBS)
unbound-control$(EXEEXT): $(CONTROL_OBJ_LINK) libunbound.la
$(LINK) -o $@ $(CONTROL_OBJ_LINK) $(EXTRALINK) $(SSLLIB) $(LIBS)
unbound-host$(EXEEXT): $(HOST_OBJ_LINK) libunbound.la
$(LINK) -o $@ $(HOST_OBJ_LINK) libunbound.la $(SSLLIB) $(LIBS)
unbound-anchor$(EXEEXT): $(UBANCHOR_OBJ_LINK) libunbound.la
$(LINK) -o $@ $(UBANCHOR_OBJ_LINK) libunbound.la -lexpat $(SSLLIB) $(LIBS)
unbound-service-install$(EXEEXT): $(SVCINST_OBJ_LINK)
$(LINK) -o $@ $(SVCINST_OBJ_LINK) $(LIBS)
unbound-service-remove$(EXEEXT): $(SVCUNINST_OBJ_LINK)
$(LINK) -o $@ $(SVCUNINST_OBJ_LINK) $(LIBS)
anchor-update$(EXEEXT): $(ANCHORUPD_OBJ_LINK) libunbound.la
$(LINK) -o $@ $(ANCHORUPD_OBJ_LINK) libunbound.la $(LIBS)
unittest$(EXEEXT): $(UNITTEST_OBJ_LINK)
$(LINK) -o $@ $(UNITTEST_OBJ_LINK) $(SSLLIB) $(LIBS)
testbound$(EXEEXT): $(TESTBOUND_OBJ_LINK)
$(LINK) -o $@ $(TESTBOUND_OBJ_LINK) $(SSLLIB) $(LIBS)
lock-verify$(EXEEXT): $(LOCKVERIFY_OBJ_LINK)
$(LINK) -o $@ $(LOCKVERIFY_OBJ_LINK) $(SSLLIB) $(LIBS)
petal$(EXEEXT): $(PETAL_OBJ_LINK)
$(LINK) -o $@ $(PETAL_OBJ_LINK) $(SSLLIB) $(LIBS)
pktview$(EXEEXT): $(PKTVIEW_OBJ_LINK)
$(LINK) -o $@ $(PKTVIEW_OBJ_LINK) $(SSLLIB) $(LIBS)
memstats$(EXEEXT): $(MEMSTATS_OBJ_LINK)
$(LINK) -o $@ $(MEMSTATS_OBJ_LINK) $(SSLLIB) $(LIBS)
asynclook$(EXEEXT): $(ASYNCLOOK_OBJ_LINK) libunbound.la
$(LINK) -o $@ $(ASYNCLOOK_OBJ_LINK) libunbound.la $(SSLLIB) $(LIBS)
streamtcp$(EXEEXT): $(STREAMTCP_OBJ_LINK)
$(LINK) -o $@ $(STREAMTCP_OBJ_LINK) $(SSLLIB) $(LIBS)
dohclient$(EXEEXT): $(DOHCLIENT_OBJ_LINK)
$(LINK) -o $@ $(DOHCLIENT_OBJ_LINK) $(SSLLIB) $(LIBS)
perf$(EXEEXT): $(PERF_OBJ_LINK)
$(LINK) -o $@ $(PERF_OBJ_LINK) $(SSLLIB) $(LIBS)
delayer$(EXEEXT): $(DELAYER_OBJ_LINK)
$(LINK) -o $@ $(DELAYER_OBJ_LINK) $(SSLLIB) $(LIBS)
readzone$(EXEEXT): $(READZONE_OBJ_LINK)
$(LINK) -o $@ $(READZONE_OBJ_LINK) $(SSLLIB) $(LIBS)
signit$(EXEEXT): testcode/signit.c
$(CC) $(CPPFLAGS) $(CFLAGS) @PTHREAD_CFLAGS_ONLY@ -o $@ testcode/signit.c $(LDFLAGS) -lldns $(SSLLIB) $(LIBS)
unbound.h: $(srcdir)/libunbound/unbound.h
sed -e 's/@''UNBOUND_VERSION_MAJOR@/$(UNBOUND_VERSION_MAJOR)/' -e 's/@''UNBOUND_VERSION_MINOR@/$(UNBOUND_VERSION_MINOR)/' -e 's/@''UNBOUND_VERSION_MICRO@/$(UNBOUND_VERSION_MICRO)/' < $(srcdir)/libunbound/unbound.h > $@
unbound-control-setup: smallapp/unbound-control-setup.sh
cp smallapp/unbound-control-setup.sh $@
-chmod +x $@
# dnstap
dnstap.lo dnstap.o: $(srcdir)/dnstap/dnstap.c config.h dnstap/dnstap_config.h \
dnstap/dnstap.pb-c.c dnstap/dnstap.pb-c.h $(srcdir)/dnstap/dnstap.h \
$(srcdir)/util/config_file.h $(srcdir)/util/log.h \
$(srcdir)/util/netevent.h $(srcdir)/util/net_help.h
dnstap/dnstap.pb-c.c dnstap/dnstap.pb-c.h: $(srcdir)/dnstap/dnstap.proto
@-if test ! -d dnstap; then $(INSTALL) -d dnstap; fi
$(PROTOC_C) --c_out=. --proto_path=$(srcdir) $(srcdir)/dnstap/dnstap.proto
unbound-dnstap-socket$(EXEEXT): $(DNSTAP_SOCKET_OBJ_LINK)
$(LINK) -o $@ $(DNSTAP_SOCKET_OBJ_LINK) $(SSLLIB) $(LIBS)
dnstap.pb-c.lo dnstap.pb-c.o: dnstap/dnstap.pb-c.c dnstap/dnstap.pb-c.h
dtstream.lo dtstream.o: $(srcdir)/dnstap/dtstream.c config.h $(srcdir)/dnstap/dtstream.h
dnstap_fstrm.lo dnstap_fstrm.o: $(srcdir)/dnstap/dnstap_fstrm.c config.h $(srcdir)/dnstap/dnstap_fstrm.h
unbound-dnstap-socket.lo unbound-dnstap-socket.o: $(srcdir)/dnstap/unbound-dnstap-socket.c config.h $(srcdir)/dnstap/dtstream.h
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 \
dnscrypt/dnscrypt_config.h \
$(srcdir)/dnscrypt/dnscrypt.h $(srcdir)/dnscrypt/cert.h \
$(srcdir)/util/config_file.h $(srcdir)/util/log.h \
$(srcdir)/util/netevent.h
# Python Module
pythonmod.lo pythonmod.o: $(srcdir)/pythonmod/pythonmod.c config.h \
pythonmod/interface.h \
$(srcdir)/pythonmod/pythonmod.h $(srcdir)/util/module.h \
$(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h \
$(srcdir)/util/log.h $(srcdir)/util/data/msgreply.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/msgparse.h \
$(srcdir)/services/outbound_list.h $(srcdir)/util/config_file.h \
$(srcdir)/pythonmod/pythonmod_utils.h $(srcdir)/util/netevent.h \
$(srcdir)/util/regional.h $(srcdir)/util/data/dname.h \
$(srcdir)/services/cache/dns.h $(srcdir)/services/mesh.h \
$(srcdir)/util/rbtree.h $(srcdir)/services/modstack.h
pythonmod/interface.h: $(srcdir)/pythonmod/interface.i config.h
@-if test ! -d pythonmod; then $(INSTALL) -d pythonmod; fi
$(SWIG) $(PYTHON_CPPFLAGS) -o $@ -python $(srcdir)/pythonmod/interface.i
libunbound_wrap.lo libunbound_wrap.o: libunbound/python/libunbound_wrap.c \
unbound.h
libunbound/python/libunbound_wrap.c: $(srcdir)/libunbound/python/libunbound.i unbound.h
@-if test ! -d libunbound/python; then $(INSTALL) -d libunbound/python; fi
$(SWIG) -python -o $@ $(PYTHON_CPPFLAGS) -DPY_MAJOR_VERSION=$(PY_MAJOR_VERSION) $(srcdir)/libunbound/python/libunbound.i
# Pyunbound python unbound wrapper
_unbound.la: libunbound_wrap.lo libunbound.la
$(LIBTOOL) --tag=CC --mode=link $(CC) $(RUNTIME_PATH) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -module -avoid-version -no-undefined -shared -o $@ libunbound_wrap.lo -rpath $(PYTHON_SITE_PKG) -L. -L.libs libunbound.la $(PYTHON_LIBS)
util/config_file.c: util/configparser.h
util/configlexer.c: $(srcdir)/util/configlexer.lex util/configparser.h
@-if test ! -d util; then $(INSTALL) -d util; fi
if test "$(LEX)" != ":"; then \
echo "#include \"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 $(INSTALL) -d util; fi
$(YACC) -d -o util/configparser.c $(srcdir)/util/configparser.y
clean:
rm -f *.o *.d *.lo *~ tags
rm -f unbound$(EXEEXT) unbound-checkconf$(EXEEXT) unbound-host$(EXEEXT) unbound-control$(EXEEXT) unbound-anchor$(EXEEXT) unbound-control-setup libunbound.la unbound.h
rm -f $(ALL_SRC:.c=.lint)
rm -f _unbound.la libunbound/python/libunbound_wrap.c libunbound/python/unbound.py pythonmod/interface.h pythonmod/unboundmodule.py
rm -f libunbound.a
rm -rf autom4te.cache .libs build doc/html doc/xml
distclean: clean
rm -f config.status config.log config.h
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
maintainer-clean: distclean
rm -f util/configlexer.c util/configparser.c util/configparser.h
realclean: maintainer-clean
rm -f configure config.h.in config.sub config.guess ltmain.sh aclocal.m4 libtool
.SUFFIXES: .lint
.c.lint:
$(LINT) $(LINTFLAGS) -I. -I$(srcdir) $<
touch $@
util/configparser.lint util/configlexer.lint pythonmod/pythonmod.lint libunbound/python/libunbound_wrap.lint dnstap/dnstap.pb-c.lint:
# skip lint for generated code
touch $@
winrc/win_svc.lint winrc/w_inst.lint winrc/unbound-service-install.lint winrc/unbound-service-remove.lint:
# skip lint for windows types
touch $@
lint: $(ALL_SRC:.c=.lint)
tags: $(srcdir)/*.[ch] $(srcdir)/*/*.[ch]
ctags -f $(srcdir)/tags $(srcdir)/*.[ch] $(srcdir)/*/*.[ch]
doc:
if test -n "$(doxygen)"; then \
$(doxygen) $(srcdir)/doc/unbound.doxygen; fi
if test "$(WITH_PYUNBOUND)" = "yes" -o "$(WITH_PYTHONMODULE)" = "yes"; \
then if test -x "`which sphinx-build-$(PY_MAJOR_VERSION) 2>&1`"; then \
sphinx-build-$(PY_MAJOR_VERSION) -b html pythonmod/doc doc/html/pythonmod; \
sphinx-build-$(PY_MAJOR_VERSION) -b html libunbound/python/doc doc/html/pyunbound;\
fi ;\
fi
strip:
$(STRIP) unbound$(EXEEXT)
$(STRIP) unbound-checkconf$(EXEEXT)
$(STRIP) unbound-control$(EXEEXT)
$(STRIP) unbound-host$(EXEEXT) || $(STRIP) .libs/unbound-host$(EXEEXT)
$(STRIP) unbound-anchor$(EXEEXT) || $(STRIP) .libs/unbound-anchor$(EXEEXT)
pythonmod-install:
$(INSTALL) -m 755 -d $(DESTDIR)$(PYTHON_SITE_PKG)
$(INSTALL) -c -m 644 pythonmod/unboundmodule.py $(DESTDIR)$(PYTHON_SITE_PKG)/unboundmodule.py
pyunbound-install:
$(INSTALL) -m 755 -d $(DESTDIR)$(PYTHON_SITE_PKG)
$(INSTALL) -c -m 644 libunbound/python/unbound.py $(DESTDIR)$(PYTHON_SITE_PKG)/unbound.py
$(LIBTOOL) --mode=install cp _unbound.la $(DESTDIR)$(PYTHON_SITE_PKG)
$(LIBTOOL) --mode=finish $(DESTDIR)$(PYTHON_SITE_PKG)
unbound-event-install:
$(INSTALL) -m 755 -d $(DESTDIR)$(includedir)
$(LIBTOOL) --mode=install cp $(srcdir)/libunbound/unbound-event.h $(DESTDIR)$(includedir)/unbound-event.h
install: $(INSTALLTARGET)
install-lib: lib $(UNBOUND_EVENT_INSTALL)
$(INSTALL) -m 755 -d $(DESTDIR)$(libdir)
$(INSTALL) -m 755 -d $(DESTDIR)$(includedir)
$(INSTALL) -m 755 -d $(DESTDIR)$(mandir)
$(INSTALL) -m 755 -d $(DESTDIR)$(mandir)/man3
$(INSTALL) -c -m 644 doc/libunbound.3 $(DESTDIR)$(mandir)/man3
for mpage in ub_ctx ub_result ub_ctx_create ub_ctx_delete \
ub_ctx_set_option ub_ctx_get_option ub_ctx_config ub_ctx_set_fwd \
ub_ctx_resolvconf ub_ctx_hosts ub_ctx_add_ta ub_ctx_add_ta_file \
ub_ctx_trustedkeys ub_ctx_debugout ub_ctx_debuglevel ub_ctx_async \
ub_poll ub_wait ub_fd ub_process ub_resolve ub_resolve_async ub_cancel \
ub_resolve_free ub_strerror ub_ctx_print_local_zones ub_ctx_zone_add \
ub_ctx_zone_remove ub_ctx_data_add ub_ctx_data_remove; \
do \
echo ".so man3/libunbound.3" > $(DESTDIR)$(mandir)/man3/$$mpage.3 ; \
done
$(LIBTOOL) --mode=install cp unbound.h $(DESTDIR)$(includedir)/unbound.h
$(INSTALL) -m 755 -d $(DESTDIR)$(libdir)/pkgconfig
$(INSTALL) -m 644 contrib/libunbound.pc $(DESTDIR)$(libdir)/pkgconfig
$(LIBTOOL) --mode=install cp libunbound.la $(DESTDIR)$(libdir)
$(LIBTOOL) --mode=finish $(DESTDIR)$(libdir)
install-all: all $(PYTHONMOD_INSTALL) $(PYUNBOUND_INSTALL) $(UNBOUND_EVENT_INSTALL) install-lib
$(INSTALL) -m 755 -d $(DESTDIR)$(sbindir)
$(INSTALL) -m 755 -d $(DESTDIR)$(mandir)
$(INSTALL) -m 755 -d $(DESTDIR)$(mandir)/man8
$(INSTALL) -m 755 -d $(DESTDIR)$(mandir)/man5
$(INSTALL) -m 755 -d $(DESTDIR)$(mandir)/man1
$(LIBTOOL) --mode=install cp -f unbound$(EXEEXT) $(DESTDIR)$(sbindir)/unbound$(EXEEXT)
$(LIBTOOL) --mode=install cp -f unbound-checkconf$(EXEEXT) $(DESTDIR)$(sbindir)/unbound-checkconf$(EXEEXT)
$(LIBTOOL) --mode=install cp -f unbound-control$(EXEEXT) $(DESTDIR)$(sbindir)/unbound-control$(EXEEXT)
$(LIBTOOL) --mode=install cp -f unbound-host$(EXEEXT) $(DESTDIR)$(sbindir)/unbound-host$(EXEEXT)
$(LIBTOOL) --mode=install cp -f unbound-anchor$(EXEEXT) $(DESTDIR)$(sbindir)/unbound-anchor$(EXEEXT)
$(INSTALL) -c -m 644 doc/unbound.8 $(DESTDIR)$(mandir)/man8
$(INSTALL) -c -m 644 doc/unbound-checkconf.8 $(DESTDIR)$(mandir)/man8
$(INSTALL) -c -m 644 doc/unbound-control.8 $(DESTDIR)$(mandir)/man8
$(INSTALL) -c -m 644 doc/unbound-control.8 $(DESTDIR)$(mandir)/man8/unbound-control-setup.8
$(INSTALL) -c -m 644 doc/unbound-anchor.8 $(DESTDIR)$(mandir)/man8
$(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
pythonmod-uninstall:
rm -f -- $(DESTDIR)$(PYTHON_SITE_PKG)/unboundmodule.py
pyunbound-uninstall:
rm -f -- $(DESTDIR)$(PYTHON_SITE_PKG)/unbound.py
$(LIBTOOL) --mode=uninstall rm -f $(DESTDIR)$(PYTHON_SITE_PKG)/_unbound.la
unbound-event-uninstall:
rm -f -- $(DESTDIR)$(includedir)/unbound-event.h
uninstall: $(PYTHONMOD_UNINSTALL) $(PYUNBOUND_UNINSTALL) $(UNBOUND_EVENT_UNINSTALL)
rm -f -- $(DESTDIR)$(sbindir)/unbound$(EXEEXT) $(DESTDIR)$(sbindir)/unbound-checkconf$(EXEEXT) $(DESTDIR)$(sbindir)/unbound-host$(EXEEXT) $(DESTDIR)$(sbindir)/unbound-control$(EXEEXT) $(DESTDIR)$(sbindir)/unbound-anchor$(EXEEXT) $(DESTDIR)$(sbindir)/unbound-control-setup
rm -f -- $(DESTDIR)$(mandir)/man8/unbound.8 $(DESTDIR)$(mandir)/man8/unbound-checkconf.8 $(DESTDIR)$(mandir)/man5/unbound.conf.5 $(DESTDIR)$(mandir)/man8/unbound-control.8 $(DESTDIR)$(mandir)/man8/unbound-anchor.8 $(DESTDIR)$(mandir)/man8/unbound-control-setup.8
rm -f -- $(DESTDIR)$(mandir)/man1/unbound-host.1 $(DESTDIR)$(mandir)/man3/libunbound.3
for mpage in ub_ctx ub_result ub_ctx_create ub_ctx_delete \
ub_ctx_set_option ub_ctx_get_option ub_ctx_config ub_ctx_set_fwd \
ub_ctx_resolvconf ub_ctx_hosts ub_ctx_add_ta ub_ctx_add_ta_file \
ub_ctx_trustedkeys ub_ctx_debugout ub_ctx_debuglevel ub_ctx_async \
ub_poll ub_wait ub_fd ub_process ub_resolve ub_resolve_async ub_cancel \
ub_resolve_free ub_strerror ub_ctx_print_local_zones ub_ctx_zone_add \
ub_ctx_zone_remove ub_ctx_data_add ub_ctx_data_remove; \
do \
rm -f -- $(DESTDIR)$(mandir)/man3/$$mpage.3 ; \
done
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"
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
rm -f port-numbers.tmp
# dependency generation
DEPEND_TMP=depend1073.tmp
DEPEND_TMP2=depend1074.tmp
DEPEND_TARGET=Makefile
DEPEND_TARGET2=$(srcdir)/Makefile.in
# actions: generate deplines from gcc,
# then, filter out home/xx, /usr/xx and /opt/xx lines (some cc already do this)
# then, remove empty " \" lines
# then, add srcdir before .c and .h in deps.
# then, remove srcdir from the (generated) parser and lexer.
# and mention the .lo
depend:
(BUILDDIR=$$PWD; cd $(srcdir) ; $(CC) $(DEPFLAG) $(CPPFLAGS) $(CFLAGS) -I$$BUILDDIR @PTHREAD_CFLAGS_ONLY@ $(ALL_SRC) $(COMPAT_SRC)) | \
sed -e 's?'$$PWD'/config.h?config.h?g' | \
sed -e 's!'$$HOME'[^ ]* !!g' -e 's!'$$HOME'[^ ]*$$!!g' \
-e 's!/usr[^ ]* !!g' -e 's!/usr[^ ]*$$!!g' \
-e 's!/opt[^ ]* !!g' -e 's!/opt[^ ]*$$!!g' | \
sed -e '/^ \\$$/d' | \
sed -e 's? *\([^ ]*\.[ch]\)? $$(srcdir)/\1?g' | \
sed -e 's? *\([^ ]*\.inc\)? $$(srcdir)/\1?g' | \
sed -e 's?$$(srcdir)/config.h?config.h?g' \
-e 's?$$(srcdir)/util/configlexer.c?util/configlexer.c?g' \
-e 's?$$(srcdir)/util/configparser.c?util/configparser.c?g' \
-e 's?$$(srcdir)/util/configparser.h?util/configparser.h?g' \
-e 's?$$(srcdir)/dnstap/dnstap_config.h??g' \
-e 's?$$(srcdir)/dnstap/dnstap.pb-c.c?dnstap/dnstap.pb-c.c?g' \
-e 's?$$(srcdir)/dnstap/dnstap.pb-c.h?dnstap/dnstap.pb-c.h?g' \
-e 's?$$(srcdir)/dnscrypt/dnscrypt_config.h??g' \
-e 's?$$(srcdir)/pythonmod/pythonmod.h?$$(PYTHONMOD_HEADER)?g' \
-e 's?$$(srcdir)/edns-subnet/subnetmod.h $$(srcdir)/edns-subnet/subnet-whitelist.h $$(srcdir)/edns-subnet/edns-subnet.h $$(srcdir)/edns-subnet/addrtree.h?$$(SUBNET_HEADER)?g' \
-e 's?$$(srcdir)/ipsecmod/ipsecmod.h $$(srcdir)/ipsecmod/ipsecmod-whitelist.h?$$(IPSECMOD_HEADER)?g' \
-e 's?$$(srcdir)/dynlibmod/dynlibmod.h?$$(DYNLIBMOD_HEADER)?g' \
-e 's!\(.*\)\.o[ :]*!\1.lo \1.o: !g' \
> $(DEPEND_TMP)
cp $(DEPEND_TARGET) $(DEPEND_TMP2)
head -`egrep -n "# Dependencies" $(DEPEND_TARGET) | tail -1 | sed -e 's/:.*$$//'` $(DEPEND_TMP2) > $(DEPEND_TARGET)
cat $(DEPEND_TMP) >> $(DEPEND_TARGET)
@if diff $(DEPEND_TARGET) $(DEPEND_TMP2); then echo " $(DEPEND_TARGET) unchanged"; else echo " Updated $(DEPEND_TARGET))"; fi
@if test -f $(DEPEND_TARGET2); then \
cp $(DEPEND_TARGET2) $(DEPEND_TMP2); \
head -`egrep -n "# Dependencies" $(DEPEND_TARGET2) | tail -1 | sed -e 's/:.*$$//'` $(DEPEND_TMP2) > $(DEPEND_TARGET2); \
cat $(DEPEND_TMP) >> $(DEPEND_TARGET2); \
if diff $(DEPEND_TARGET2) $(DEPEND_TMP2); then echo " $(DEPEND_TARGET2) unchanged"; else echo " Updated $(DEPEND_TARGET2))"; fi; \
fi
rm -f $(DEPEND_TMP) $(DEPEND_TMP2)
# build rules
ipset.lo ipset.o: $(srcdir)/ipset/ipset.c
# Dependencies
dns.lo dns.o: $(srcdir)/services/cache/dns.c config.h $(srcdir)/iterator/iter_delegpt.h $(srcdir)/util/log.h \
$(srcdir)/iterator/iter_utils.h $(srcdir)/iterator/iter_resptype.h $(srcdir)/validator/val_nsec.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h \
$(srcdir)/validator/val_utils.h $(srcdir)/sldns/pkthdr.h $(srcdir)/services/cache/dns.h \
$(srcdir)/util/data/msgreply.h $(srcdir)/services/cache/rrset.h $(srcdir)/util/storage/slabhash.h \
$(srcdir)/util/data/msgparse.h $(srcdir)/sldns/rrdef.h $(srcdir)/util/data/dname.h $(srcdir)/util/module.h \
$(srcdir)/util/net_help.h $(srcdir)/util/regional.h $(srcdir)/util/config_file.h $(srcdir)/sldns/sbuffer.h
infra.lo infra.o: $(srcdir)/services/cache/infra.c config.h $(srcdir)/sldns/rrdef.h $(srcdir)/sldns/str2wire.h \
$(srcdir)/sldns/sbuffer.h $(srcdir)/sldns/wire2str.h $(srcdir)/services/cache/infra.h \
$(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/storage/dnstree.h \
$(srcdir)/util/rbtree.h $(srcdir)/util/rtt.h $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h \
$(srcdir)/util/storage/slabhash.h $(srcdir)/util/storage/lookup3.h $(srcdir)/util/data/dname.h \
$(srcdir)/util/net_help.h $(srcdir)/util/config_file.h $(srcdir)/iterator/iterator.h \
$(srcdir)/services/outbound_list.h $(srcdir)/util/module.h $(srcdir)/util/data/msgparse.h \
$(srcdir)/sldns/pkthdr.h
rrset.lo rrset.o: $(srcdir)/services/cache/rrset.c config.h $(srcdir)/services/cache/rrset.h \
$(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/storage/slabhash.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/sldns/rrdef.h $(srcdir)/util/config_file.h \
$(srcdir)/util/data/msgreply.h $(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h $(srcdir)/util/regional.h \
$(srcdir)/util/alloc.h $(srcdir)/util/net_help.h
as112.lo as112.o: $(srcdir)/util/as112.c $(srcdir)/util/as112.h
dname.lo dname.o: $(srcdir)/util/data/dname.c config.h $(srcdir)/util/data/dname.h \
$(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/data/msgparse.h \
$(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/util/storage/lookup3.h $(srcdir)/sldns/sbuffer.h
msgencode.lo msgencode.o: $(srcdir)/util/data/msgencode.c config.h $(srcdir)/util/data/msgencode.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/data/msgparse.h $(srcdir)/sldns/pkthdr.h \
$(srcdir)/sldns/rrdef.h $(srcdir)/util/data/dname.h $(srcdir)/util/regional.h $(srcdir)/util/net_help.h \
$(srcdir)/sldns/sbuffer.h $(srcdir)/services/localzone.h $(srcdir)/util/rbtree.h \
$(srcdir)/util/storage/dnstree.h $(srcdir)/util/module.h $(srcdir)/services/view.h
msgparse.lo msgparse.o: $(srcdir)/util/data/msgparse.c config.h $(srcdir)/util/data/msgparse.h \
$(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/sldns/pkthdr.h \
$(srcdir)/sldns/rrdef.h $(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h \
- $(srcdir)/util/data/dname.h $(srcdir)/util/storage/lookup3.h $(srcdir)/util/regional.h $(srcdir)/sldns/sbuffer.h \
+ $(srcdir)/util/data/dname.h $(srcdir)/util/storage/lookup3.h $(srcdir)/util/regional.h $(srcdir)/util/net_help.h $(srcdir)/sldns/sbuffer.h \
$(srcdir)/sldns/parseutil.h $(srcdir)/sldns/wire2str.h
msgreply.lo msgreply.o: $(srcdir)/util/data/msgreply.c config.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/storage/lookup3.h $(srcdir)/util/alloc.h $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/util/net_help.h $(srcdir)/util/data/dname.h \
$(srcdir)/util/regional.h $(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h \
$(srcdir)/util/data/msgencode.h $(srcdir)/sldns/sbuffer.h $(srcdir)/sldns/wire2str.h $(srcdir)/util/module.h \
$(srcdir)/util/fptr_wlist.h $(srcdir)/util/tube.h $(srcdir)/services/mesh.h $(srcdir)/util/rbtree.h \
$(srcdir)/services/modstack.h $(srcdir)/services/rpz.h $(srcdir)/services/localzone.h \
$(srcdir)/util/storage/dnstree.h $(srcdir)/services/view.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
packed_rrset.lo packed_rrset.o: $(srcdir)/util/data/packed_rrset.c config.h $(srcdir)/util/data/msgparse.h \
$(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/sldns/pkthdr.h \
$(srcdir)/sldns/rrdef.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/dname.h \
$(srcdir)/util/storage/lookup3.h $(srcdir)/util/alloc.h $(srcdir)/util/regional.h $(srcdir)/util/net_help.h \
$(srcdir)/sldns/sbuffer.h $(srcdir)/sldns/wire2str.h
iterator.lo iterator.o: $(srcdir)/iterator/iterator.c config.h $(srcdir)/iterator/iterator.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 $(srcdir)/iterator/iter_utils.h \
$(srcdir)/iterator/iter_resptype.h $(srcdir)/iterator/iter_hints.h $(srcdir)/util/storage/dnstree.h \
$(srcdir)/util/rbtree.h $(srcdir)/iterator/iter_fwd.h $(srcdir)/iterator/iter_donotq.h \
$(srcdir)/iterator/iter_delegpt.h $(srcdir)/iterator/iter_scrub.h $(srcdir)/iterator/iter_priv.h \
$(srcdir)/validator/val_neg.h $(srcdir)/services/cache/dns.h $(srcdir)/services/cache/infra.h \
$(srcdir)/util/rtt.h $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/services/authzone.h $(srcdir)/services/mesh.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)/daemon/stats.h $(srcdir)/util/timehist.h \
$(srcdir)/libunbound/unbound.h $(srcdir)/respip/respip.h $(srcdir)/util/net_help.h $(srcdir)/util/regional.h \
$(srcdir)/util/data/dname.h $(srcdir)/util/data/msgencode.h $(srcdir)/util/fptr_wlist.h $(srcdir)/util/tube.h \
$(srcdir)/util/random.h $(srcdir)/sldns/wire2str.h $(srcdir)/sldns/str2wire.h $(srcdir)/sldns/parseutil.h
iter_delegpt.lo iter_delegpt.o: $(srcdir)/iterator/iter_delegpt.c config.h $(srcdir)/iterator/iter_delegpt.h \
$(srcdir)/util/log.h $(srcdir)/services/cache/dns.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h \
$(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/regional.h \
$(srcdir)/util/data/dname.h $(srcdir)/util/net_help.h $(srcdir)/sldns/rrdef.h $(srcdir)/sldns/sbuffer.h
iter_donotq.lo iter_donotq.o: $(srcdir)/iterator/iter_donotq.c config.h $(srcdir)/iterator/iter_donotq.h \
$(srcdir)/util/storage/dnstree.h $(srcdir)/util/rbtree.h $(srcdir)/util/regional.h $(srcdir)/util/log.h \
$(srcdir)/util/config_file.h $(srcdir)/util/net_help.h
iter_fwd.lo iter_fwd.o: $(srcdir)/iterator/iter_fwd.c config.h $(srcdir)/iterator/iter_fwd.h \
$(srcdir)/util/rbtree.h $(srcdir)/iterator/iter_delegpt.h $(srcdir)/util/log.h $(srcdir)/util/config_file.h \
$(srcdir)/util/net_help.h $(srcdir)/util/data/dname.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h \
$(srcdir)/sldns/rrdef.h $(srcdir)/sldns/str2wire.h
iter_hints.lo iter_hints.o: $(srcdir)/iterator/iter_hints.c config.h $(srcdir)/iterator/iter_hints.h \
$(srcdir)/util/storage/dnstree.h $(srcdir)/util/rbtree.h $(srcdir)/iterator/iter_delegpt.h $(srcdir)/util/log.h \
$(srcdir)/util/config_file.h $(srcdir)/util/net_help.h $(srcdir)/util/data/dname.h \
$(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/sldns/rrdef.h $(srcdir)/sldns/str2wire.h \
$(srcdir)/sldns/wire2str.h
iter_priv.lo iter_priv.o: $(srcdir)/iterator/iter_priv.c config.h $(srcdir)/iterator/iter_priv.h \
$(srcdir)/util/rbtree.h $(srcdir)/util/regional.h $(srcdir)/util/log.h $(srcdir)/util/config_file.h \
$(srcdir)/util/data/dname.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h \
$(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/util/net_help.h \
$(srcdir)/util/storage/dnstree.h $(srcdir)/sldns/str2wire.h $(srcdir)/sldns/sbuffer.h
iter_resptype.lo iter_resptype.o: $(srcdir)/iterator/iter_resptype.c config.h \
- $(srcdir)/iterator/iter_resptype.h $(srcdir)/iterator/iter_delegpt.h $(srcdir)/util/log.h \
+ $(srcdir)/iterator/iter_resptype.h $(srcdir)/iterator/iter_delegpt.h $(srcdir)/iterator/iterator.h $(srcdir)/util/log.h \
$(srcdir)/services/cache/dns.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h \
$(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/net_help.h \
$(srcdir)/util/data/dname.h $(srcdir)/sldns/rrdef.h $(srcdir)/sldns/pkthdr.h
iter_scrub.lo iter_scrub.o: $(srcdir)/iterator/iter_scrub.c config.h $(srcdir)/iterator/iter_scrub.h \
$(srcdir)/iterator/iterator.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 \
$(srcdir)/iterator/iter_priv.h $(srcdir)/util/rbtree.h $(srcdir)/services/cache/rrset.h \
$(srcdir)/util/storage/slabhash.h $(srcdir)/util/net_help.h $(srcdir)/util/regional.h \
$(srcdir)/util/config_file.h $(srcdir)/util/data/dname.h $(srcdir)/util/alloc.h $(srcdir)/sldns/sbuffer.h
iter_utils.lo iter_utils.o: $(srcdir)/iterator/iter_utils.c config.h $(srcdir)/iterator/iter_utils.h \
$(srcdir)/iterator/iter_resptype.h $(srcdir)/iterator/iterator.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 $(srcdir)/iterator/iter_hints.h \
$(srcdir)/util/storage/dnstree.h $(srcdir)/util/rbtree.h $(srcdir)/iterator/iter_fwd.h \
$(srcdir)/iterator/iter_donotq.h $(srcdir)/iterator/iter_delegpt.h $(srcdir)/iterator/iter_priv.h \
$(srcdir)/services/cache/infra.h $(srcdir)/util/rtt.h $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/services/cache/dns.h $(srcdir)/services/cache/rrset.h \
$(srcdir)/util/storage/slabhash.h $(srcdir)/services/outside_network.h \
$(srcdir)/util/net_help.h $(srcdir)/util/config_file.h \
$(srcdir)/util/regional.h $(srcdir)/util/data/dname.h $(srcdir)/util/random.h $(srcdir)/util/fptr_wlist.h \
$(srcdir)/util/tube.h $(srcdir)/services/mesh.h $(srcdir)/services/modstack.h $(srcdir)/services/rpz.h \
$(srcdir)/services/localzone.h $(srcdir)/services/view.h $(srcdir)/sldns/sbuffer.h $(srcdir)/services/authzone.h \
$(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h $(srcdir)/libunbound/unbound.h $(srcdir)/respip/respip.h \
$(srcdir)/validator/val_anchor.h $(srcdir)/validator/val_kcache.h $(srcdir)/validator/val_kentry.h \
$(srcdir)/validator/val_utils.h $(srcdir)/validator/val_sigcrypt.h $(srcdir)/sldns/str2wire.h
listen_dnsport.lo listen_dnsport.o: $(srcdir)/services/listen_dnsport.c config.h \
$(srcdir)/services/listen_dnsport.h $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/services/outside_network.h $(srcdir)/util/rbtree.h \
$(srcdir)/util/log.h $(srcdir)/util/config_file.h $(srcdir)/util/net_help.h \
$(srcdir)/sldns/sbuffer.h $(srcdir)/sldns/parseutil.h $(srcdir)/services/mesh.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/module.h $(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h \
$(srcdir)/services/modstack.h $(srcdir)/services/rpz.h $(srcdir)/services/localzone.h \
$(srcdir)/util/storage/dnstree.h $(srcdir)/services/view.h $(srcdir)/services/authzone.h \
$(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h $(srcdir)/libunbound/unbound.h $(srcdir)/respip/respip.h \
$(srcdir)/util/fptr_wlist.h $(srcdir)/util/tube.h
localzone.lo localzone.o: $(srcdir)/services/localzone.c config.h $(srcdir)/services/localzone.h \
$(srcdir)/util/rbtree.h $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/storage/dnstree.h \
$(srcdir)/util/module.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/data/msgreply.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h \
$(srcdir)/sldns/rrdef.h $(srcdir)/services/view.h $(srcdir)/sldns/sbuffer.h $(srcdir)/sldns/str2wire.h \
$(srcdir)/util/regional.h $(srcdir)/util/config_file.h $(srcdir)/util/data/dname.h \
$(srcdir)/util/data/msgencode.h $(srcdir)/util/net_help.h $(srcdir)/util/netevent.h \
$(srcdir)/dnscrypt/dnscrypt.h $(srcdir)/util/as112.h
mesh.lo mesh.o: $(srcdir)/services/mesh.c config.h $(srcdir)/services/mesh.h $(srcdir)/util/rbtree.h \
$(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/util/data/msgparse.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h \
$(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/util/module.h $(srcdir)/util/data/msgreply.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/services/modstack.h $(srcdir)/services/rpz.h \
$(srcdir)/services/localzone.h $(srcdir)/util/storage/dnstree.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)/services/outbound_list.h $(srcdir)/services/cache/dns.h $(srcdir)/services/cache/rrset.h \
$(srcdir)/util/storage/slabhash.h $(srcdir)/util/net_help.h $(srcdir)/util/regional.h \
$(srcdir)/util/data/msgencode.h $(srcdir)/util/fptr_wlist.h $(srcdir)/util/tube.h $(srcdir)/util/alloc.h \
$(srcdir)/util/edns.h $(srcdir)/sldns/wire2str.h $(srcdir)/util/data/dname.h $(srcdir)/services/listen_dnsport.h
modstack.lo modstack.o: $(srcdir)/services/modstack.c config.h $(srcdir)/services/modstack.h \
$(srcdir)/util/module.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h \
$(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/msgparse.h \
$(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/util/fptr_wlist.h $(srcdir)/util/netevent.h \
$(srcdir)/dnscrypt/dnscrypt.h $(srcdir)/util/tube.h \
$(srcdir)/services/mesh.h $(srcdir)/util/rbtree.h $(srcdir)/services/rpz.h $(srcdir)/services/localzone.h \
$(srcdir)/util/storage/dnstree.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)/dns64/dns64.h $(srcdir)/iterator/iterator.h \
$(srcdir)/services/outbound_list.h $(srcdir)/validator/validator.h $(srcdir)/validator/val_utils.h
view.lo view.o: $(srcdir)/services/view.c config.h $(srcdir)/services/view.h $(srcdir)/util/rbtree.h \
$(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/services/localzone.h $(srcdir)/util/storage/dnstree.h \
$(srcdir)/util/module.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/data/msgreply.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h \
$(srcdir)/sldns/rrdef.h $(srcdir)/sldns/sbuffer.h $(srcdir)/util/config_file.h
rpz.lo rpz.o: $(srcdir)/services/rpz.c config.h $(srcdir)/services/rpz.h $(srcdir)/services/localzone.h \
$(srcdir)/util/rbtree.h $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/storage/dnstree.h \
$(srcdir)/util/module.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/data/msgreply.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h \
$(srcdir)/sldns/rrdef.h $(srcdir)/services/view.h $(srcdir)/sldns/sbuffer.h $(srcdir)/util/config_file.h \
$(srcdir)/services/authzone.h $(srcdir)/services/mesh.h $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/services/modstack.h $(srcdir)/libunbound/unbound.h \
$(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h $(srcdir)/respip/respip.h $(srcdir)/sldns/wire2str.h \
$(srcdir)/sldns/str2wire.h $(srcdir)/util/data/dname.h $(srcdir)/util/net_help.h $(srcdir)/util/regional.h
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 \
$(srcdir)/services/listen_dnsport.h $(srcdir)/services/cache/infra.h $(srcdir)/util/storage/lruhash.h \
$(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/storage/dnstree.h $(srcdir)/util/rtt.h \
$(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/iterator/iterator.h \
$(srcdir)/services/outbound_list.h $(srcdir)/util/module.h $(srcdir)/util/data/msgparse.h \
$(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/util/data/msgencode.h $(srcdir)/util/data/dname.h \
$(srcdir)/util/net_help.h $(srcdir)/util/random.h $(srcdir)/util/fptr_wlist.h $(srcdir)/util/tube.h \
$(srcdir)/services/mesh.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)/util/edns.h $(srcdir)/dnstap/dnstap.h
alloc.lo alloc.o: $(srcdir)/util/alloc.c config.h $(srcdir)/util/alloc.h $(srcdir)/util/locks.h $(srcdir)/util/log.h \
$(srcdir)/util/regional.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h \
$(srcdir)/util/fptr_wlist.h $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/util/module.h $(srcdir)/util/data/msgreply.h \
$(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/util/tube.h \
$(srcdir)/services/mesh.h $(srcdir)/util/rbtree.h $(srcdir)/services/modstack.h $(srcdir)/services/rpz.h \
$(srcdir)/services/localzone.h $(srcdir)/util/storage/dnstree.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
config_file.lo config_file.o: $(srcdir)/util/config_file.c config.h $(srcdir)/util/log.h \
$(srcdir)/util/configyyrename.h $(srcdir)/util/config_file.h util/configparser.h \
$(srcdir)/util/net_help.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/module.h $(srcdir)/util/data/msgreply.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/util/regional.h $(srcdir)/util/fptr_wlist.h \
$(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/util/tube.h $(srcdir)/services/mesh.h $(srcdir)/util/rbtree.h $(srcdir)/services/modstack.h \
$(srcdir)/services/rpz.h $(srcdir)/services/localzone.h $(srcdir)/util/storage/dnstree.h \
$(srcdir)/services/view.h $(srcdir)/sldns/sbuffer.h $(srcdir)/services/authzone.h $(srcdir)/daemon/stats.h \
$(srcdir)/util/timehist.h $(srcdir)/libunbound/unbound.h $(srcdir)/respip/respip.h $(srcdir)/util/data/dname.h \
$(srcdir)/util/rtt.h $(srcdir)/services/cache/infra.h $(srcdir)/sldns/wire2str.h $(srcdir)/sldns/parseutil.h \
$(srcdir)/iterator/iterator.h $(srcdir)/services/outbound_list.h $(srcdir)/util/iana_ports.inc
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)/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)/daemon/worker.h $(srcdir)/libunbound/worker.h \
$(srcdir)/sldns/sbuffer.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h \
$(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.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)/util/module.h $(srcdir)/dnstap/dnstap.h $(srcdir)/services/mesh.h \
$(srcdir)/util/rbtree.h $(srcdir)/services/rpz.h $(srcdir)/services/localzone.h $(srcdir)/util/storage/dnstree.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/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)/util/storage/lruhash.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/util/module.h \
$(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/services/modstack.h \
$(srcdir)/services/rpz.h $(srcdir)/services/localzone.h $(srcdir)/util/storage/dnstree.h \
$(srcdir)/services/view.h $(srcdir)/sldns/sbuffer.h $(srcdir)/util/config_file.h $(srcdir)/daemon/stats.h \
$(srcdir)/util/timehist.h $(srcdir)/libunbound/unbound.h $(srcdir)/respip/respip.h $(srcdir)/util/data/dname.h \
$(srcdir)/util/data/msgencode.h $(srcdir)/util/regional.h $(srcdir)/util/net_help.h $(srcdir)/util/random.h \
$(srcdir)/services/cache/dns.h $(srcdir)/services/outside_network.h \
$(srcdir)/services/listen_dnsport.h $(srcdir)/sldns/str2wire.h $(srcdir)/sldns/wire2str.h \
$(srcdir)/sldns/parseutil.h $(srcdir)/sldns/keyraw.h $(srcdir)/validator/val_nsec3.h \
$(srcdir)/validator/val_nsec.h $(srcdir)/validator/val_secalgo.h $(srcdir)/validator/val_sigcrypt.h \
$(srcdir)/validator/val_anchor.h $(srcdir)/validator/val_utils.h
fptr_wlist.lo fptr_wlist.o: $(srcdir)/util/fptr_wlist.c config.h $(srcdir)/util/fptr_wlist.h \
$(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/module.h \
$(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/msgparse.h \
$(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/util/tube.h $(srcdir)/services/mesh.h $(srcdir)/util/rbtree.h \
$(srcdir)/services/modstack.h $(srcdir)/services/rpz.h $(srcdir)/services/localzone.h \
$(srcdir)/util/storage/dnstree.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)/util/mini_event.h $(srcdir)/util/rbtree.h \
$(srcdir)/services/outside_network.h $(srcdir)/services/cache/infra.h \
$(srcdir)/util/rtt.h $(srcdir)/services/cache/rrset.h $(srcdir)/util/storage/slabhash.h $(srcdir)/dns64/dns64.h \
$(srcdir)/iterator/iterator.h $(srcdir)/services/outbound_list.h $(srcdir)/iterator/iter_fwd.h \
$(srcdir)/validator/validator.h $(srcdir)/validator/val_utils.h $(srcdir)/validator/val_anchor.h \
$(srcdir)/validator/val_nsec3.h $(srcdir)/validator/val_sigcrypt.h $(srcdir)/validator/val_kentry.h \
$(srcdir)/validator/val_neg.h $(srcdir)/validator/autotrust.h $(srcdir)/libunbound/libworker.h \
$(srcdir)/libunbound/context.h $(srcdir)/util/alloc.h $(srcdir)/libunbound/unbound-event.h \
$(srcdir)/libunbound/worker.h
locks.lo locks.o: $(srcdir)/util/locks.c config.h $(srcdir)/util/locks.h $(srcdir)/util/log.h
log.lo log.o: $(srcdir)/util/log.c config.h $(srcdir)/util/log.h $(srcdir)/util/locks.h $(srcdir)/sldns/sbuffer.h
mini_event.lo mini_event.o: $(srcdir)/util/mini_event.c config.h $(srcdir)/util/mini_event.h $(srcdir)/util/rbtree.h \
$(srcdir)/util/fptr_wlist.h $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h \
$(srcdir)/util/log.h $(srcdir)/util/module.h $(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h \
$(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/util/tube.h \
$(srcdir)/services/mesh.h $(srcdir)/util/rbtree.h $(srcdir)/services/modstack.h $(srcdir)/services/rpz.h \
$(srcdir)/services/localzone.h $(srcdir)/util/storage/dnstree.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
module.lo module.o: $(srcdir)/util/module.c config.h $(srcdir)/util/module.h $(srcdir)/util/storage/lruhash.h \
$(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h \
$(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/sldns/wire2str.h
netevent.lo netevent.o: $(srcdir)/util/netevent.c config.h $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/util/ub_event.h $(srcdir)/util/log.h $(srcdir)/util/net_help.h \
$(srcdir)/util/tcp_conn_limit.h $(srcdir)/util/storage/dnstree.h $(srcdir)/util/rbtree.h $(srcdir)/util/locks.h \
$(srcdir)/util/fptr_wlist.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/module.h \
$(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/msgparse.h \
$(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/util/tube.h $(srcdir)/services/mesh.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)/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)/util/data/dname.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/module.h \
$(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/msgparse.h \
$(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/util/regional.h $(srcdir)/util/config_file.h \
$(srcdir)/sldns/parseutil.h $(srcdir)/sldns/wire2str.h
random.lo random.o: $(srcdir)/util/random.c config.h $(srcdir)/util/random.h $(srcdir)/util/log.h
rbtree.lo rbtree.o: $(srcdir)/util/rbtree.c config.h $(srcdir)/util/log.h $(srcdir)/util/fptr_wlist.h \
$(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/module.h \
$(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/msgparse.h \
$(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/util/tube.h $(srcdir)/services/mesh.h $(srcdir)/util/rbtree.h \
$(srcdir)/services/modstack.h $(srcdir)/services/rpz.h $(srcdir)/services/localzone.h \
$(srcdir)/util/storage/dnstree.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
regional.lo regional.o: $(srcdir)/util/regional.c config.h $(srcdir)/util/log.h $(srcdir)/util/regional.h
rtt.lo rtt.o: $(srcdir)/util/rtt.c config.h $(srcdir)/util/rtt.h $(srcdir)/iterator/iterator.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/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 $(srcdir)/util/data/packed_rrset.h
dnstree.lo dnstree.o: $(srcdir)/util/storage/dnstree.c config.h $(srcdir)/util/storage/dnstree.h \
$(srcdir)/util/rbtree.h $(srcdir)/util/data/dname.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h \
$(srcdir)/util/log.h $(srcdir)/util/net_help.h
lookup3.lo lookup3.o: $(srcdir)/util/storage/lookup3.c config.h $(srcdir)/util/storage/lookup3.h
lruhash.lo lruhash.o: $(srcdir)/util/storage/lruhash.c config.h $(srcdir)/util/storage/lruhash.h \
$(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/fptr_wlist.h $(srcdir)/util/netevent.h \
$(srcdir)/dnscrypt/dnscrypt.h $(srcdir)/util/module.h \
$(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/msgparse.h \
$(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/util/tube.h $(srcdir)/services/mesh.h $(srcdir)/util/rbtree.h \
$(srcdir)/services/modstack.h $(srcdir)/services/rpz.h $(srcdir)/services/localzone.h \
$(srcdir)/util/storage/dnstree.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
slabhash.lo slabhash.o: $(srcdir)/util/storage/slabhash.c config.h $(srcdir)/util/storage/slabhash.h \
$(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h
tcp_conn_limit.lo tcp_conn_limit.o: $(srcdir)/util/tcp_conn_limit.c config.h $(srcdir)/util/regional.h \
$(srcdir)/util/log.h $(srcdir)/util/config_file.h $(srcdir)/util/net_help.h $(srcdir)/util/tcp_conn_limit.h \
$(srcdir)/util/storage/dnstree.h $(srcdir)/util/rbtree.h $(srcdir)/util/locks.h $(srcdir)/services/localzone.h \
$(srcdir)/util/module.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/data/msgreply.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h \
$(srcdir)/sldns/rrdef.h $(srcdir)/services/view.h $(srcdir)/sldns/sbuffer.h $(srcdir)/sldns/str2wire.h
timehist.lo timehist.o: $(srcdir)/util/timehist.c config.h $(srcdir)/util/timehist.h $(srcdir)/util/log.h
tube.lo tube.o: $(srcdir)/util/tube.c config.h $(srcdir)/util/tube.h $(srcdir)/util/log.h $(srcdir)/util/net_help.h \
$(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/util/fptr_wlist.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/module.h \
$(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/msgparse.h \
$(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/services/mesh.h $(srcdir)/util/rbtree.h \
$(srcdir)/services/modstack.h $(srcdir)/services/rpz.h $(srcdir)/services/localzone.h \
$(srcdir)/util/storage/dnstree.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)/util/ub_event.h
ub_event.lo ub_event.o: $(srcdir)/util/ub_event.c config.h $(srcdir)/util/ub_event.h $(srcdir)/util/log.h \
$(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/util/tube.h $(srcdir)/util/mini_event.h $(srcdir)/util/rbtree.h
ub_event_pluggable.lo ub_event_pluggable.o: $(srcdir)/util/ub_event_pluggable.c config.h $(srcdir)/util/ub_event.h \
$(srcdir)/libunbound/unbound-event.h $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/util/log.h $(srcdir)/util/fptr_wlist.h \
$(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/module.h $(srcdir)/util/data/msgreply.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h \
$(srcdir)/sldns/rrdef.h $(srcdir)/util/tube.h $(srcdir)/services/mesh.h $(srcdir)/util/rbtree.h \
$(srcdir)/services/modstack.h $(srcdir)/services/rpz.h $(srcdir)/services/localzone.h \
$(srcdir)/util/storage/dnstree.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)/util/mini_event.h $(srcdir)/util/rbtree.h
winsock_event.lo winsock_event.o: $(srcdir)/util/winsock_event.c config.h
autotrust.lo autotrust.o: $(srcdir)/validator/autotrust.c config.h $(srcdir)/validator/autotrust.h \
$(srcdir)/util/rbtree.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h \
$(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/validator/val_anchor.h $(srcdir)/validator/val_utils.h \
$(srcdir)/sldns/pkthdr.h $(srcdir)/validator/val_sigcrypt.h $(srcdir)/util/data/dname.h $(srcdir)/util/module.h \
$(srcdir)/util/data/msgreply.h $(srcdir)/util/data/msgparse.h $(srcdir)/sldns/rrdef.h $(srcdir)/util/net_help.h \
$(srcdir)/util/config_file.h $(srcdir)/util/regional.h $(srcdir)/util/random.h $(srcdir)/services/mesh.h \
$(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/services/modstack.h $(srcdir)/services/rpz.h $(srcdir)/services/localzone.h \
$(srcdir)/util/storage/dnstree.h $(srcdir)/services/view.h $(srcdir)/sldns/sbuffer.h \
$(srcdir)/services/authzone.h $(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h $(srcdir)/libunbound/unbound.h \
$(srcdir)/respip/respip.h $(srcdir)/services/cache/rrset.h $(srcdir)/util/storage/slabhash.h \
$(srcdir)/validator/val_kcache.h $(srcdir)/sldns/wire2str.h $(srcdir)/sldns/str2wire.h $(srcdir)/sldns/keyraw.h
val_anchor.lo val_anchor.o: $(srcdir)/validator/val_anchor.c config.h $(srcdir)/validator/val_anchor.h \
$(srcdir)/util/rbtree.h $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/validator/val_sigcrypt.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h $(srcdir)/sldns/pkthdr.h \
$(srcdir)/validator/autotrust.h $(srcdir)/util/data/dname.h $(srcdir)/util/net_help.h \
$(srcdir)/util/config_file.h $(srcdir)/util/as112.h $(srcdir)/sldns/sbuffer.h $(srcdir)/sldns/rrdef.h \
$(srcdir)/sldns/str2wire.h
validator.lo validator.o: $(srcdir)/validator/validator.c config.h $(srcdir)/validator/validator.h \
$(srcdir)/util/module.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h \
$(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/msgparse.h \
$(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/validator/val_utils.h \
$(srcdir)/validator/val_anchor.h $(srcdir)/util/rbtree.h $(srcdir)/validator/val_kcache.h \
$(srcdir)/util/storage/slabhash.h $(srcdir)/validator/val_kentry.h $(srcdir)/validator/val_nsec.h \
$(srcdir)/validator/val_nsec3.h $(srcdir)/validator/val_neg.h $(srcdir)/validator/val_sigcrypt.h \
$(srcdir)/validator/autotrust.h $(srcdir)/services/cache/dns.h $(srcdir)/services/cache/rrset.h \
$(srcdir)/util/data/dname.h $(srcdir)/util/net_help.h $(srcdir)/util/regional.h $(srcdir)/util/config_file.h \
$(srcdir)/util/fptr_wlist.h $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/util/tube.h $(srcdir)/services/mesh.h \
$(srcdir)/services/modstack.h $(srcdir)/services/rpz.h $(srcdir)/services/localzone.h \
$(srcdir)/util/storage/dnstree.h $(srcdir)/services/view.h $(srcdir)/sldns/sbuffer.h \
$(srcdir)/services/authzone.h $(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h $(srcdir)/libunbound/unbound.h \
$(srcdir)/respip/respip.h $(srcdir)/sldns/wire2str.h $(srcdir)/sldns/str2wire.h
val_kcache.lo val_kcache.o: $(srcdir)/validator/val_kcache.c config.h $(srcdir)/validator/val_kcache.h \
$(srcdir)/util/storage/slabhash.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h \
$(srcdir)/validator/val_kentry.h $(srcdir)/util/config_file.h $(srcdir)/util/data/dname.h \
$(srcdir)/util/module.h $(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h \
$(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h
val_kentry.lo val_kentry.o: $(srcdir)/validator/val_kentry.c config.h $(srcdir)/validator/val_kentry.h \
$(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/data/packed_rrset.h \
$(srcdir)/util/data/dname.h $(srcdir)/util/storage/lookup3.h $(srcdir)/util/regional.h $(srcdir)/util/net_help.h \
$(srcdir)/sldns/rrdef.h $(srcdir)/sldns/keyraw.h
val_neg.lo val_neg.o: $(srcdir)/validator/val_neg.c config.h $(srcdir)/validator/val_neg.h $(srcdir)/util/locks.h \
$(srcdir)/util/log.h $(srcdir)/util/rbtree.h $(srcdir)/validator/val_nsec.h $(srcdir)/util/data/packed_rrset.h \
$(srcdir)/util/storage/lruhash.h $(srcdir)/validator/val_nsec3.h $(srcdir)/validator/val_utils.h \
$(srcdir)/sldns/pkthdr.h $(srcdir)/util/data/dname.h $(srcdir)/util/data/msgreply.h $(srcdir)/util/net_help.h \
$(srcdir)/util/config_file.h $(srcdir)/services/cache/rrset.h $(srcdir)/util/storage/slabhash.h \
$(srcdir)/services/cache/dns.h $(srcdir)/sldns/rrdef.h $(srcdir)/sldns/sbuffer.h
val_nsec3.lo val_nsec3.o: $(srcdir)/validator/val_nsec3.c config.h $(srcdir)/validator/val_nsec3.h \
$(srcdir)/util/rbtree.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h \
$(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/validator/val_secalgo.h $(srcdir)/validator/validator.h \
$(srcdir)/util/module.h $(srcdir)/util/data/msgreply.h $(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h \
$(srcdir)/sldns/rrdef.h $(srcdir)/validator/val_utils.h $(srcdir)/validator/val_kentry.h \
$(srcdir)/services/cache/rrset.h $(srcdir)/util/storage/slabhash.h $(srcdir)/util/regional.h \
$(srcdir)/util/net_help.h $(srcdir)/util/data/dname.h $(srcdir)/validator/val_nsec.h $(srcdir)/sldns/sbuffer.h
val_nsec.lo val_nsec.o: $(srcdir)/validator/val_nsec.c config.h $(srcdir)/validator/val_nsec.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h \
$(srcdir)/validator/val_utils.h $(srcdir)/sldns/pkthdr.h $(srcdir)/util/data/msgreply.h \
$(srcdir)/util/data/dname.h $(srcdir)/util/net_help.h $(srcdir)/util/module.h $(srcdir)/util/data/msgparse.h \
$(srcdir)/sldns/rrdef.h $(srcdir)/services/cache/rrset.h $(srcdir)/util/storage/slabhash.h
val_secalgo.lo val_secalgo.o: $(srcdir)/validator/val_secalgo.c config.h $(srcdir)/util/data/packed_rrset.h \
$(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/validator/val_secalgo.h \
$(srcdir)/validator/val_nsec3.h $(srcdir)/util/rbtree.h $(srcdir)/sldns/rrdef.h $(srcdir)/sldns/keyraw.h \
$(srcdir)/sldns/sbuffer.h
val_sigcrypt.lo val_sigcrypt.o: $(srcdir)/validator/val_sigcrypt.c config.h \
$(srcdir)/validator/val_sigcrypt.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h \
$(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/sldns/pkthdr.h $(srcdir)/validator/val_secalgo.h \
$(srcdir)/validator/validator.h $(srcdir)/util/module.h $(srcdir)/util/data/msgreply.h \
$(srcdir)/util/data/msgparse.h $(srcdir)/sldns/rrdef.h $(srcdir)/validator/val_utils.h \
$(srcdir)/util/data/dname.h $(srcdir)/util/rbtree.h $(srcdir)/util/net_help.h $(srcdir)/util/regional.h \
$(srcdir)/util/config_file.h $(srcdir)/sldns/keyraw.h $(srcdir)/sldns/sbuffer.h $(srcdir)/sldns/parseutil.h \
$(srcdir)/sldns/wire2str.h
val_utils.lo val_utils.o: $(srcdir)/validator/val_utils.c config.h $(srcdir)/validator/val_utils.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h \
$(srcdir)/sldns/pkthdr.h $(srcdir)/validator/validator.h $(srcdir)/util/module.h $(srcdir)/util/data/msgreply.h \
$(srcdir)/util/data/msgparse.h $(srcdir)/sldns/rrdef.h $(srcdir)/validator/val_kentry.h \
$(srcdir)/validator/val_sigcrypt.h $(srcdir)/validator/val_anchor.h $(srcdir)/util/rbtree.h \
$(srcdir)/validator/val_nsec.h $(srcdir)/validator/val_neg.h $(srcdir)/services/cache/rrset.h \
$(srcdir)/util/storage/slabhash.h $(srcdir)/services/cache/dns.h $(srcdir)/util/data/dname.h \
$(srcdir)/util/net_help.h $(srcdir)/util/regional.h $(srcdir)/util/config_file.h $(srcdir)/sldns/wire2str.h \
$(srcdir)/sldns/parseutil.h
dns64.lo dns64.o: $(srcdir)/dns64/dns64.c config.h $(srcdir)/dns64/dns64.h $(srcdir)/util/module.h \
$(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/data/msgreply.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h \
$(srcdir)/sldns/rrdef.h $(srcdir)/services/cache/dns.h $(srcdir)/services/cache/rrset.h \
$(srcdir)/util/storage/slabhash.h $(srcdir)/util/config_file.h $(srcdir)/util/fptr_wlist.h \
$(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/util/tube.h $(srcdir)/services/mesh.h $(srcdir)/util/rbtree.h $(srcdir)/services/modstack.h \
$(srcdir)/services/rpz.h $(srcdir)/services/localzone.h $(srcdir)/util/storage/dnstree.h \
$(srcdir)/services/view.h $(srcdir)/sldns/sbuffer.h $(srcdir)/services/authzone.h $(srcdir)/daemon/stats.h \
$(srcdir)/util/timehist.h $(srcdir)/libunbound/unbound.h $(srcdir)/respip/respip.h $(srcdir)/util/net_help.h \
$(srcdir)/util/regional.h $(srcdir)/util/data/dname.h $(srcdir)/sldns/str2wire.h
edns-subnet.lo edns-subnet.o: $(srcdir)/edns-subnet/edns-subnet.c config.h
subnetmod.lo subnetmod.o: $(srcdir)/edns-subnet/subnetmod.c config.h
addrtree.lo addrtree.o: $(srcdir)/edns-subnet/addrtree.c config.h $(srcdir)/util/log.h \
$(srcdir)/util/data/msgreply.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/util/module.h $(srcdir)/util/data/msgparse.h \
$(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/edns-subnet/addrtree.h
subnet-whitelist.lo subnet-whitelist.o: $(srcdir)/edns-subnet/subnet-whitelist.c config.h
respip.lo respip.o: $(srcdir)/respip/respip.c config.h $(srcdir)/services/localzone.h $(srcdir)/util/rbtree.h \
$(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/storage/dnstree.h $(srcdir)/util/module.h \
$(srcdir)/util/storage/lruhash.h $(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h \
$(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/services/view.h \
$(srcdir)/sldns/sbuffer.h $(srcdir)/services/authzone.h $(srcdir)/services/mesh.h $(srcdir)/util/netevent.h \
$(srcdir)/dnscrypt/dnscrypt.h $(srcdir)/services/modstack.h \
$(srcdir)/services/rpz.h $(srcdir)/util/config_file.h $(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h \
$(srcdir)/libunbound/unbound.h $(srcdir)/respip/respip.h $(srcdir)/services/cache/dns.h \
$(srcdir)/sldns/str2wire.h $(srcdir)/util/fptr_wlist.h $(srcdir)/util/tube.h $(srcdir)/util/net_help.h \
$(srcdir)/util/regional.h
checklocks.lo checklocks.o: $(srcdir)/testcode/checklocks.c config.h $(srcdir)/util/locks.h $(srcdir)/util/log.h \
$(srcdir)/testcode/checklocks.h
ipsecmod.lo ipsecmod.o: $(srcdir)/ipsecmod/ipsecmod.c config.h
ipsecmod-whitelist.lo ipsecmod-whitelist.o: $(srcdir)/ipsecmod/ipsecmod-whitelist.c config.h
unitanchor.lo unitanchor.o: $(srcdir)/testcode/unitanchor.c config.h $(srcdir)/util/log.h $(srcdir)/util/data/dname.h \
$(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/testcode/unitmain.h \
$(srcdir)/validator/val_anchor.h $(srcdir)/util/rbtree.h $(srcdir)/sldns/sbuffer.h $(srcdir)/sldns/rrdef.h
unitdname.lo unitdname.o: $(srcdir)/testcode/unitdname.c config.h $(srcdir)/util/log.h $(srcdir)/testcode/unitmain.h \
$(srcdir)/util/data/dname.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/sldns/sbuffer.h \
$(srcdir)/sldns/str2wire.h $(srcdir)/sldns/rrdef.h
unitlruhash.lo unitlruhash.o: $(srcdir)/testcode/unitlruhash.c config.h $(srcdir)/testcode/unitmain.h \
$(srcdir)/util/log.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/storage/slabhash.h
unitmain.lo unitmain.o: $(srcdir)/testcode/unitmain.c config.h $(srcdir)/sldns/rrdef.h $(srcdir)/sldns/keyraw.h \
$(srcdir)/util/log.h $(srcdir)/testcode/unitmain.h $(srcdir)/util/alloc.h $(srcdir)/util/locks.h $(srcdir)/util/net_help.h \
$(srcdir)/util/config_file.h $(srcdir)/util/rtt.h $(srcdir)/util/timehist.h $(srcdir)/iterator/iterator.h \
$(srcdir)/services/outbound_list.h $(srcdir)/util/data/msgreply.h $(srcdir)/util/storage/lruhash.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/util/module.h $(srcdir)/util/data/msgparse.h \
$(srcdir)/sldns/pkthdr.h $(srcdir)/libunbound/unbound.h $(srcdir)/services/cache/infra.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
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 \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/msgencode.h $(srcdir)/util/data/dname.h \
$(srcdir)/util/alloc.h $(srcdir)/util/regional.h $(srcdir)/util/net_help.h $(srcdir)/testcode/readhex.h \
$(srcdir)/testcode/testpkts.h $(srcdir)/sldns/sbuffer.h $(srcdir)/sldns/str2wire.h $(srcdir)/sldns/wire2str.h
unitneg.lo unitneg.o: $(srcdir)/testcode/unitneg.c config.h $(srcdir)/util/log.h $(srcdir)/util/net_help.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h \
$(srcdir)/util/data/dname.h $(srcdir)/testcode/unitmain.h $(srcdir)/validator/val_neg.h $(srcdir)/util/rbtree.h \
$(srcdir)/sldns/rrdef.h
unitregional.lo unitregional.o: $(srcdir)/testcode/unitregional.c config.h $(srcdir)/testcode/unitmain.h \
$(srcdir)/util/log.h $(srcdir)/util/regional.h
unitslabhash.lo unitslabhash.o: $(srcdir)/testcode/unitslabhash.c config.h $(srcdir)/testcode/unitmain.h \
$(srcdir)/util/log.h $(srcdir)/util/storage/slabhash.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h
unitverify.lo unitverify.o: $(srcdir)/testcode/unitverify.c config.h $(srcdir)/util/log.h \
$(srcdir)/testcode/unitmain.h $(srcdir)/validator/val_sigcrypt.h $(srcdir)/util/data/packed_rrset.h \
$(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/sldns/pkthdr.h \
$(srcdir)/validator/val_secalgo.h $(srcdir)/validator/val_nsec.h $(srcdir)/validator/val_nsec3.h \
$(srcdir)/util/rbtree.h $(srcdir)/validator/validator.h $(srcdir)/util/module.h $(srcdir)/util/data/msgreply.h \
$(srcdir)/util/data/msgparse.h $(srcdir)/sldns/rrdef.h $(srcdir)/validator/val_utils.h \
$(srcdir)/testcode/testpkts.h $(srcdir)/util/data/dname.h $(srcdir)/util/regional.h $(srcdir)/util/alloc.h \
$(srcdir)/util/net_help.h $(srcdir)/util/config_file.h $(srcdir)/sldns/sbuffer.h $(srcdir)/sldns/keyraw.h \
$(srcdir)/sldns/str2wire.h $(srcdir)/sldns/wire2str.h
readhex.lo readhex.o: $(srcdir)/testcode/readhex.c config.h $(srcdir)/testcode/readhex.h $(srcdir)/util/log.h \
$(srcdir)/sldns/sbuffer.h $(srcdir)/sldns/parseutil.h
testpkts.lo testpkts.o: $(srcdir)/testcode/testpkts.c config.h $(srcdir)/testcode/testpkts.h \
$(srcdir)/util/net_help.h $(srcdir)/util/log.h $(srcdir)/sldns/sbuffer.h $(srcdir)/sldns/rrdef.h $(srcdir)/sldns/pkthdr.h \
$(srcdir)/sldns/str2wire.h $(srcdir)/sldns/wire2str.h
unitldns.lo unitldns.o: $(srcdir)/testcode/unitldns.c config.h $(srcdir)/util/log.h $(srcdir)/testcode/unitmain.h \
$(srcdir)/sldns/sbuffer.h $(srcdir)/sldns/str2wire.h $(srcdir)/sldns/rrdef.h $(srcdir)/sldns/wire2str.h \
$(srcdir)/sldns/parseutil.h
unitecs.lo unitecs.o: $(srcdir)/testcode/unitecs.c config.h
unitauth.lo unitauth.o: $(srcdir)/testcode/unitauth.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)/util/storage/lruhash.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/util/module.h \
$(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/services/modstack.h \
$(srcdir)/services/rpz.h $(srcdir)/services/localzone.h $(srcdir)/util/storage/dnstree.h \
$(srcdir)/services/view.h $(srcdir)/sldns/sbuffer.h $(srcdir)/util/config_file.h $(srcdir)/daemon/stats.h \
$(srcdir)/util/timehist.h $(srcdir)/libunbound/unbound.h $(srcdir)/respip/respip.h $(srcdir)/testcode/unitmain.h \
$(srcdir)/util/regional.h $(srcdir)/util/net_help.h $(srcdir)/services/cache/dns.h $(srcdir)/sldns/str2wire.h \
$(srcdir)/sldns/wire2str.h
unitzonemd.lo unitzonemd.o: $(srcdir)/testcode/unitzonemd.c config.h $(srcdir)/util/log.h \
$(srcdir)/testcode/unitmain.h $(srcdir)/sldns/str2wire.h $(srcdir)/sldns/rrdef.h $(srcdir)/services/authzone.h \
$(srcdir)/util/rbtree.h $(srcdir)/util/locks.h $(srcdir)/services/mesh.h $(srcdir)/util/netevent.h \
$(srcdir)/dnscrypt/dnscrypt.h $(srcdir)/util/data/msgparse.h \
$(srcdir)/util/storage/lruhash.h $(srcdir)/sldns/pkthdr.h $(srcdir)/util/module.h $(srcdir)/util/data/msgreply.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/services/modstack.h $(srcdir)/services/rpz.h \
$(srcdir)/services/localzone.h $(srcdir)/util/storage/dnstree.h $(srcdir)/services/view.h \
$(srcdir)/sldns/sbuffer.h $(srcdir)/util/config_file.h $(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h \
$(srcdir)/libunbound/unbound.h $(srcdir)/respip/respip.h $(srcdir)/util/data/dname.h $(srcdir)/util/regional.h \
$(srcdir)/validator/val_anchor.h
unittcpreuse.lo unittcpreuse.o: $(srcdir)/testcode/unittcpreuse.c config.h $(srcdir)/services/outside_network.h \
$(srcdir)/util/random.h
acl_list.lo acl_list.o: $(srcdir)/daemon/acl_list.c config.h $(srcdir)/daemon/acl_list.h \
$(srcdir)/util/storage/dnstree.h $(srcdir)/util/rbtree.h $(srcdir)/services/view.h $(srcdir)/util/locks.h \
$(srcdir)/util/log.h $(srcdir)/util/regional.h $(srcdir)/util/config_file.h $(srcdir)/util/net_help.h \
$(srcdir)/services/localzone.h $(srcdir)/util/module.h $(srcdir)/util/storage/lruhash.h \
$(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/msgparse.h \
$(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/sldns/sbuffer.h $(srcdir)/sldns/str2wire.h
cachedump.lo cachedump.o: $(srcdir)/daemon/cachedump.c config.h $(srcdir)/daemon/cachedump.h \
$(srcdir)/daemon/remote.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/log.h \
$(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.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)/services/cache/rrset.h $(srcdir)/util/storage/slabhash.h $(srcdir)/services/cache/dns.h \
$(srcdir)/services/cache/infra.h $(srcdir)/util/storage/dnstree.h $(srcdir)/util/rbtree.h $(srcdir)/util/rtt.h \
$(srcdir)/util/regional.h $(srcdir)/util/net_help.h $(srcdir)/util/data/dname.h $(srcdir)/iterator/iterator.h \
$(srcdir)/services/outbound_list.h $(srcdir)/iterator/iter_delegpt.h $(srcdir)/iterator/iter_utils.h \
$(srcdir)/iterator/iter_resptype.h $(srcdir)/iterator/iter_fwd.h $(srcdir)/iterator/iter_hints.h \
$(srcdir)/sldns/wire2str.h $(srcdir)/sldns/str2wire.h $(srcdir)/util/config_file.h $(srcdir)/services/outside_network.h
daemon.lo daemon.o: $(srcdir)/daemon/daemon.c config.h $(srcdir)/daemon/daemon.h $(srcdir)/util/locks.h \
$(srcdir)/util/log.h $(srcdir)/util/alloc.h $(srcdir)/services/modstack.h \
$(srcdir)/daemon/worker.h $(srcdir)/libunbound/worker.h \
$(srcdir)/sldns/sbuffer.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h \
$(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.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/remote.h $(srcdir)/daemon/acl_list.h $(srcdir)/util/storage/dnstree.h $(srcdir)/util/rbtree.h \
$(srcdir)/services/view.h $(srcdir)/util/config_file.h $(srcdir)/util/shm_side/shm_main.h \
$(srcdir)/util/storage/lookup3.h $(srcdir)/util/storage/slabhash.h $(srcdir)/util/tcp_conn_limit.h \
$(srcdir)/util/edns.h $(srcdir)/services/listen_dnsport.h $(srcdir)/services/cache/rrset.h \
$(srcdir)/services/cache/infra.h $(srcdir)/util/rtt.h $(srcdir)/services/localzone.h \
$(srcdir)/services/authzone.h $(srcdir)/services/mesh.h $(srcdir)/services/rpz.h $(srcdir)/respip/respip.h \
$(srcdir)/util/random.h $(srcdir)/util/tube.h $(srcdir)/util/net_help.h $(srcdir)/sldns/keyraw.h
remote.lo remote.o: $(srcdir)/daemon/remote.c config.h $(srcdir)/daemon/remote.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/log.h $(srcdir)/util/netevent.h \
$(srcdir)/dnscrypt/dnscrypt.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)/services/modstack.h $(srcdir)/daemon/cachedump.h $(srcdir)/util/config_file.h \
$(srcdir)/util/net_help.h $(srcdir)/services/listen_dnsport.h $(srcdir)/services/cache/rrset.h \
$(srcdir)/util/storage/slabhash.h $(srcdir)/services/cache/infra.h $(srcdir)/util/storage/dnstree.h \
$(srcdir)/util/rbtree.h $(srcdir)/util/rtt.h $(srcdir)/services/mesh.h $(srcdir)/services/rpz.h \
$(srcdir)/services/localzone.h $(srcdir)/services/view.h $(srcdir)/services/authzone.h $(srcdir)/respip/respip.h \
$(srcdir)/util/fptr_wlist.h $(srcdir)/util/tube.h $(srcdir)/util/data/dname.h $(srcdir)/validator/validator.h \
$(srcdir)/validator/val_utils.h $(srcdir)/validator/val_kcache.h $(srcdir)/validator/val_kentry.h \
$(srcdir)/validator/val_anchor.h $(srcdir)/iterator/iterator.h $(srcdir)/services/outbound_list.h \
$(srcdir)/iterator/iter_fwd.h $(srcdir)/iterator/iter_hints.h $(srcdir)/iterator/iter_delegpt.h \
$(srcdir)/services/outside_network.h $(srcdir)/sldns/str2wire.h $(srcdir)/sldns/parseutil.h \
$(srcdir)/sldns/wire2str.h
stats.lo stats.o: $(srcdir)/daemon/stats.c config.h $(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h \
$(srcdir)/libunbound/unbound.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/log.h \
$(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/util/alloc.h $(srcdir)/util/data/msgreply.h $(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h \
$(srcdir)/sldns/rrdef.h $(srcdir)/util/module.h $(srcdir)/dnstap/dnstap.h \
$(srcdir)/daemon/daemon.h $(srcdir)/services/modstack.h $(srcdir)/services/mesh.h $(srcdir)/util/rbtree.h \
$(srcdir)/services/rpz.h $(srcdir)/services/localzone.h $(srcdir)/util/storage/dnstree.h \
$(srcdir)/services/view.h $(srcdir)/util/config_file.h $(srcdir)/services/authzone.h $(srcdir)/respip/respip.h \
$(srcdir)/services/outside_network.h $(srcdir)/services/listen_dnsport.h $(srcdir)/util/tube.h \
$(srcdir)/util/net_help.h $(srcdir)/validator/validator.h $(srcdir)/validator/val_utils.h \
$(srcdir)/iterator/iterator.h $(srcdir)/services/outbound_list.h $(srcdir)/services/cache/rrset.h \
$(srcdir)/util/storage/slabhash.h $(srcdir)/services/cache/infra.h $(srcdir)/util/rtt.h \
$(srcdir)/validator/val_kcache.h $(srcdir)/validator/val_neg.h
unbound.lo unbound.o: $(srcdir)/daemon/unbound.c config.h $(srcdir)/util/log.h $(srcdir)/daemon/daemon.h \
$(srcdir)/util/locks.h $(srcdir)/util/alloc.h $(srcdir)/services/modstack.h \
$(srcdir)/daemon/remote.h $(srcdir)/util/config_file.h \
$(srcdir)/util/storage/slabhash.h $(srcdir)/util/storage/lruhash.h $(srcdir)/services/listen_dnsport.h \
$(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h $(srcdir)/services/cache/rrset.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/services/cache/infra.h $(srcdir)/util/storage/dnstree.h \
$(srcdir)/util/rbtree.h $(srcdir)/util/rtt.h $(srcdir)/util/data/msgreply.h $(srcdir)/util/fptr_wlist.h \
$(srcdir)/util/module.h $(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h \
$(srcdir)/util/tube.h $(srcdir)/services/mesh.h $(srcdir)/services/rpz.h $(srcdir)/services/localzone.h \
$(srcdir)/services/view.h $(srcdir)/sldns/sbuffer.h $(srcdir)/services/authzone.h $(srcdir)/daemon/stats.h \
$(srcdir)/util/timehist.h $(srcdir)/libunbound/unbound.h $(srcdir)/respip/respip.h $(srcdir)/util/net_help.h \
$(srcdir)/util/ub_event.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/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)/services/modstack.h $(srcdir)/daemon/remote.h $(srcdir)/daemon/acl_list.h \
$(srcdir)/util/storage/dnstree.h $(srcdir)/util/rbtree.h $(srcdir)/services/view.h $(srcdir)/util/config_file.h \
$(srcdir)/util/regional.h $(srcdir)/util/storage/slabhash.h $(srcdir)/services/listen_dnsport.h \
$(srcdir)/services/outside_network.h $(srcdir)/services/outbound_list.h \
$(srcdir)/services/cache/rrset.h $(srcdir)/services/cache/infra.h $(srcdir)/util/rtt.h \
$(srcdir)/services/cache/dns.h $(srcdir)/services/authzone.h $(srcdir)/services/mesh.h $(srcdir)/services/rpz.h \
$(srcdir)/services/localzone.h $(srcdir)/respip/respip.h $(srcdir)/util/data/msgencode.h \
$(srcdir)/util/data/dname.h $(srcdir)/util/fptr_wlist.h $(srcdir)/util/tube.h $(srcdir)/util/edns.h \
$(srcdir)/iterator/iter_fwd.h $(srcdir)/iterator/iter_hints.h $(srcdir)/iterator/iter_utils.h \
$(srcdir)/iterator/iter_resptype.h $(srcdir)/validator/autotrust.h $(srcdir)/validator/val_anchor.h \
$(srcdir)/libunbound/context.h $(srcdir)/libunbound/unbound-event.h $(srcdir)/libunbound/libworker.h \
$(srcdir)/sldns/wire2str.h $(srcdir)/util/shm_side/shm_main.h $(srcdir)/dnstap/dtstream.h
testbound.lo testbound.o: $(srcdir)/testcode/testbound.c config.h $(srcdir)/testcode/testpkts.h \
$(srcdir)/testcode/replay.h $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/util/rbtree.h $(srcdir)/testcode/fake_event.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)/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 \
$(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/util/tube.h \
$(srcdir)/services/mesh.h $(srcdir)/services/rpz.h $(srcdir)/services/localzone.h $(srcdir)/services/view.h \
$(srcdir)/services/authzone.h $(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h $(srcdir)/libunbound/unbound.h \
$(srcdir)/respip/respip.h $(srcdir)/util/net_help.h $(srcdir)/util/ub_event.h
testpkts.lo testpkts.o: $(srcdir)/testcode/testpkts.c config.h $(srcdir)/testcode/testpkts.h \
$(srcdir)/util/net_help.h $(srcdir)/util/log.h $(srcdir)/sldns/sbuffer.h $(srcdir)/sldns/rrdef.h $(srcdir)/sldns/pkthdr.h \
$(srcdir)/sldns/str2wire.h $(srcdir)/sldns/wire2str.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/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)/services/modstack.h $(srcdir)/daemon/remote.h $(srcdir)/daemon/acl_list.h \
$(srcdir)/util/storage/dnstree.h $(srcdir)/util/rbtree.h $(srcdir)/services/view.h $(srcdir)/util/config_file.h \
$(srcdir)/util/regional.h $(srcdir)/util/storage/slabhash.h $(srcdir)/services/listen_dnsport.h \
$(srcdir)/services/outside_network.h $(srcdir)/services/outbound_list.h \
$(srcdir)/services/cache/rrset.h $(srcdir)/services/cache/infra.h $(srcdir)/util/rtt.h \
$(srcdir)/services/cache/dns.h $(srcdir)/services/authzone.h $(srcdir)/services/mesh.h $(srcdir)/services/rpz.h \
$(srcdir)/services/localzone.h $(srcdir)/respip/respip.h $(srcdir)/util/data/msgencode.h \
$(srcdir)/util/data/dname.h $(srcdir)/util/fptr_wlist.h $(srcdir)/util/tube.h $(srcdir)/util/edns.h \
$(srcdir)/iterator/iter_fwd.h $(srcdir)/iterator/iter_hints.h $(srcdir)/iterator/iter_utils.h \
$(srcdir)/iterator/iter_resptype.h $(srcdir)/validator/autotrust.h $(srcdir)/validator/val_anchor.h \
$(srcdir)/libunbound/context.h $(srcdir)/libunbound/unbound-event.h $(srcdir)/libunbound/libworker.h \
$(srcdir)/sldns/wire2str.h $(srcdir)/util/shm_side/shm_main.h $(srcdir)/dnstap/dtstream.h
acl_list.lo acl_list.o: $(srcdir)/daemon/acl_list.c config.h $(srcdir)/daemon/acl_list.h \
$(srcdir)/util/storage/dnstree.h $(srcdir)/util/rbtree.h $(srcdir)/services/view.h $(srcdir)/util/locks.h \
$(srcdir)/util/log.h $(srcdir)/util/regional.h $(srcdir)/util/config_file.h $(srcdir)/util/net_help.h \
$(srcdir)/services/localzone.h $(srcdir)/util/module.h $(srcdir)/util/storage/lruhash.h \
$(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/msgparse.h \
$(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/sldns/sbuffer.h $(srcdir)/sldns/str2wire.h
daemon.lo daemon.o: $(srcdir)/daemon/daemon.c config.h $(srcdir)/daemon/daemon.h $(srcdir)/util/locks.h \
$(srcdir)/util/log.h $(srcdir)/util/alloc.h $(srcdir)/services/modstack.h \
$(srcdir)/daemon/worker.h $(srcdir)/libunbound/worker.h \
$(srcdir)/sldns/sbuffer.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h \
$(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.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/remote.h $(srcdir)/daemon/acl_list.h $(srcdir)/util/storage/dnstree.h $(srcdir)/util/rbtree.h \
$(srcdir)/services/view.h $(srcdir)/util/config_file.h $(srcdir)/util/shm_side/shm_main.h \
$(srcdir)/util/storage/lookup3.h $(srcdir)/util/storage/slabhash.h $(srcdir)/util/tcp_conn_limit.h \
$(srcdir)/util/edns.h $(srcdir)/services/listen_dnsport.h $(srcdir)/services/cache/rrset.h \
$(srcdir)/services/cache/infra.h $(srcdir)/util/rtt.h $(srcdir)/services/localzone.h \
$(srcdir)/services/authzone.h $(srcdir)/services/mesh.h $(srcdir)/services/rpz.h $(srcdir)/respip/respip.h \
$(srcdir)/util/random.h $(srcdir)/util/tube.h $(srcdir)/util/net_help.h $(srcdir)/sldns/keyraw.h
stats.lo stats.o: $(srcdir)/daemon/stats.c config.h $(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h \
$(srcdir)/libunbound/unbound.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/log.h \
$(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/util/alloc.h $(srcdir)/util/data/msgreply.h $(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h \
$(srcdir)/sldns/rrdef.h $(srcdir)/util/module.h $(srcdir)/dnstap/dnstap.h \
$(srcdir)/daemon/daemon.h $(srcdir)/services/modstack.h $(srcdir)/services/mesh.h $(srcdir)/util/rbtree.h \
$(srcdir)/services/rpz.h $(srcdir)/services/localzone.h $(srcdir)/util/storage/dnstree.h \
$(srcdir)/services/view.h $(srcdir)/util/config_file.h $(srcdir)/services/authzone.h $(srcdir)/respip/respip.h \
$(srcdir)/services/outside_network.h $(srcdir)/services/listen_dnsport.h $(srcdir)/util/tube.h \
$(srcdir)/util/net_help.h $(srcdir)/validator/validator.h $(srcdir)/validator/val_utils.h \
$(srcdir)/iterator/iterator.h $(srcdir)/services/outbound_list.h $(srcdir)/services/cache/rrset.h \
$(srcdir)/util/storage/slabhash.h $(srcdir)/services/cache/infra.h $(srcdir)/util/rtt.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)/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/net_help.h $(srcdir)/util/log.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 \
$(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)/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 \
$(srcdir)/services/localzone.h $(srcdir)/services/view.h $(srcdir)/sldns/sbuffer.h $(srcdir)/services/authzone.h \
$(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h $(srcdir)/libunbound/unbound.h $(srcdir)/respip/respip.h \
$(srcdir)/sldns/wire2str.h $(srcdir)/sldns/str2wire.h $(srcdir)/daemon/remote.h
lock_verify.lo lock_verify.o: $(srcdir)/testcode/lock_verify.c config.h $(srcdir)/util/log.h $(srcdir)/util/rbtree.h \
$(srcdir)/util/locks.h $(srcdir)/util/fptr_wlist.h $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/util/storage/lruhash.h $(srcdir)/util/module.h \
$(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/msgparse.h \
$(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/util/tube.h $(srcdir)/services/mesh.h \
$(srcdir)/services/modstack.h $(srcdir)/services/rpz.h $(srcdir)/services/localzone.h \
$(srcdir)/util/storage/dnstree.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
pktview.lo pktview.o: $(srcdir)/testcode/pktview.c config.h $(srcdir)/util/log.h $(srcdir)/util/data/dname.h \
$(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h \
$(srcdir)/sldns/rrdef.h $(srcdir)/testcode/unitmain.h $(srcdir)/testcode/readhex.h $(srcdir)/sldns/sbuffer.h \
$(srcdir)/sldns/parseutil.h
readhex.lo readhex.o: $(srcdir)/testcode/readhex.c config.h $(srcdir)/testcode/readhex.h $(srcdir)/util/log.h \
$(srcdir)/sldns/sbuffer.h $(srcdir)/sldns/parseutil.h
memstats.lo memstats.o: $(srcdir)/testcode/memstats.c config.h $(srcdir)/util/log.h $(srcdir)/util/rbtree.h \
$(srcdir)/util/locks.h $(srcdir)/util/fptr_wlist.h $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/util/storage/lruhash.h $(srcdir)/util/module.h \
$(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/msgparse.h \
$(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/util/tube.h $(srcdir)/services/mesh.h \
$(srcdir)/services/modstack.h $(srcdir)/services/rpz.h $(srcdir)/services/localzone.h \
$(srcdir)/util/storage/dnstree.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
unbound-checkconf.lo unbound-checkconf.o: $(srcdir)/smallapp/unbound-checkconf.c config.h $(srcdir)/util/log.h \
$(srcdir)/util/config_file.h $(srcdir)/util/module.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h \
$(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/msgparse.h \
$(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/util/net_help.h $(srcdir)/util/regional.h \
$(srcdir)/iterator/iterator.h $(srcdir)/services/outbound_list.h $(srcdir)/iterator/iter_fwd.h \
$(srcdir)/util/rbtree.h $(srcdir)/iterator/iter_hints.h $(srcdir)/util/storage/dnstree.h \
$(srcdir)/validator/validator.h $(srcdir)/validator/val_utils.h $(srcdir)/services/localzone.h \
$(srcdir)/services/view.h $(srcdir)/sldns/sbuffer.h $(srcdir)/services/authzone.h $(srcdir)/services/mesh.h \
$(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/services/modstack.h $(srcdir)/services/rpz.h $(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h \
$(srcdir)/libunbound/unbound.h $(srcdir)/respip/respip.h $(srcdir)/sldns/str2wire.h
worker_cb.lo worker_cb.o: $(srcdir)/smallapp/worker_cb.c config.h $(srcdir)/libunbound/context.h \
$(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/alloc.h $(srcdir)/util/rbtree.h $(srcdir)/services/modstack.h \
$(srcdir)/libunbound/unbound.h $(srcdir)/libunbound/unbound-event.h $(srcdir)/util/data/packed_rrset.h \
$(srcdir)/util/storage/lruhash.h $(srcdir)/libunbound/worker.h $(srcdir)/sldns/sbuffer.h \
$(srcdir)/util/fptr_wlist.h $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/util/module.h $(srcdir)/util/data/msgreply.h \
$(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/util/tube.h \
$(srcdir)/services/mesh.h $(srcdir)/services/rpz.h $(srcdir)/services/localzone.h \
$(srcdir)/util/storage/dnstree.h $(srcdir)/services/view.h $(srcdir)/util/config_file.h \
$(srcdir)/services/authzone.h $(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h $(srcdir)/respip/respip.h
context.lo context.o: $(srcdir)/libunbound/context.c config.h $(srcdir)/libunbound/context.h \
$(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/alloc.h $(srcdir)/util/rbtree.h $(srcdir)/services/modstack.h \
$(srcdir)/libunbound/unbound.h $(srcdir)/libunbound/unbound-event.h $(srcdir)/util/data/packed_rrset.h \
$(srcdir)/util/storage/lruhash.h $(srcdir)/util/module.h $(srcdir)/util/data/msgreply.h \
$(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/util/config_file.h \
$(srcdir)/util/net_help.h $(srcdir)/services/localzone.h $(srcdir)/util/storage/dnstree.h \
$(srcdir)/services/view.h $(srcdir)/sldns/sbuffer.h $(srcdir)/services/cache/rrset.h \
$(srcdir)/util/storage/slabhash.h $(srcdir)/services/cache/infra.h $(srcdir)/util/rtt.h \
$(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/services/authzone.h $(srcdir)/services/mesh.h $(srcdir)/services/rpz.h $(srcdir)/daemon/stats.h \
$(srcdir)/util/timehist.h $(srcdir)/respip/respip.h $(srcdir)/util/edns.h
libunbound.lo libunbound.o: $(srcdir)/libunbound/libunbound.c $(srcdir)/libunbound/unbound.h \
$(srcdir)/libunbound/unbound-event.h config.h $(srcdir)/libunbound/context.h $(srcdir)/util/locks.h \
$(srcdir)/util/log.h $(srcdir)/util/alloc.h $(srcdir)/util/rbtree.h $(srcdir)/services/modstack.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h $(srcdir)/libunbound/libworker.h \
$(srcdir)/util/config_file.h $(srcdir)/util/module.h $(srcdir)/util/data/msgreply.h \
$(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/util/regional.h \
$(srcdir)/util/random.h $(srcdir)/util/net_help.h $(srcdir)/util/tube.h $(srcdir)/util/ub_event.h $(srcdir)/util/edns.h \
$(srcdir)/util/storage/dnstree.h $(srcdir)/services/localzone.h $(srcdir)/services/view.h \
$(srcdir)/sldns/sbuffer.h $(srcdir)/services/cache/infra.h $(srcdir)/util/rtt.h $(srcdir)/util/netevent.h \
$(srcdir)/dnscrypt/dnscrypt.h $(srcdir)/services/cache/rrset.h \
$(srcdir)/util/storage/slabhash.h $(srcdir)/services/authzone.h $(srcdir)/services/mesh.h \
$(srcdir)/services/rpz.h $(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h $(srcdir)/respip/respip.h
libworker.lo libworker.o: $(srcdir)/libunbound/libworker.c config.h $(srcdir)/libunbound/libworker.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h \
$(srcdir)/libunbound/context.h $(srcdir)/util/alloc.h $(srcdir)/util/rbtree.h $(srcdir)/services/modstack.h \
$(srcdir)/libunbound/unbound.h $(srcdir)/libunbound/unbound-event.h $(srcdir)/libunbound/worker.h \
$(srcdir)/sldns/sbuffer.h $(srcdir)/services/outside_network.h $(srcdir)/util/netevent.h \
$(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/services/mesh.h $(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h \
$(srcdir)/util/module.h $(srcdir)/util/data/msgreply.h $(srcdir)/services/rpz.h $(srcdir)/services/localzone.h \
$(srcdir)/util/storage/dnstree.h $(srcdir)/services/view.h $(srcdir)/util/config_file.h \
$(srcdir)/services/authzone.h $(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h $(srcdir)/respip/respip.h \
$(srcdir)/services/cache/rrset.h $(srcdir)/util/storage/slabhash.h $(srcdir)/services/outbound_list.h \
$(srcdir)/util/fptr_wlist.h $(srcdir)/util/tube.h $(srcdir)/util/regional.h $(srcdir)/util/random.h \
$(srcdir)/util/storage/lookup3.h $(srcdir)/util/net_help.h $(srcdir)/util/data/dname.h \
$(srcdir)/util/data/msgencode.h $(srcdir)/iterator/iter_fwd.h $(srcdir)/iterator/iter_hints.h \
$(srcdir)/sldns/str2wire.h
unbound-host.lo unbound-host.o: $(srcdir)/smallapp/unbound-host.c config.h $(srcdir)/libunbound/unbound.h \
$(srcdir)/sldns/rrdef.h $(srcdir)/sldns/wire2str.h
asynclook.lo asynclook.o: $(srcdir)/testcode/asynclook.c config.h $(srcdir)/libunbound/unbound.h \
$(srcdir)/libunbound/context.h $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/alloc.h $(srcdir)/util/rbtree.h \
$(srcdir)/services/modstack.h $(srcdir)/libunbound/unbound-event.h $(srcdir)/util/data/packed_rrset.h \
$(srcdir)/util/storage/lruhash.h $(srcdir)/sldns/rrdef.h
streamtcp.lo streamtcp.o: $(srcdir)/testcode/streamtcp.c config.h $(srcdir)/util/locks.h $(srcdir)/util/log.h \
$(srcdir)/util/net_help.h $(srcdir)/util/proxy_protocol.h $(srcdir)/util/data/msgencode.h $(srcdir)/util/data/msgparse.h \
$(srcdir)/util/storage/lruhash.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/util/data/msgreply.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/dname.h $(srcdir)/sldns/sbuffer.h \
$(srcdir)/sldns/str2wire.h $(srcdir)/sldns/wire2str.h
perf.lo perf.o: $(srcdir)/testcode/perf.c config.h $(srcdir)/util/log.h $(srcdir)/util/locks.h $(srcdir)/util/net_help.h \
$(srcdir)/util/data/msgencode.h $(srcdir)/util/data/msgreply.h $(srcdir)/util/storage/lruhash.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h \
$(srcdir)/sldns/rrdef.h $(srcdir)/sldns/sbuffer.h $(srcdir)/sldns/wire2str.h $(srcdir)/sldns/str2wire.h
delayer.lo delayer.o: $(srcdir)/testcode/delayer.c config.h $(srcdir)/util/net_help.h $(srcdir)/util/log.h \
$(srcdir)/util/config_file.h $(srcdir)/sldns/sbuffer.h
unbound-control.lo unbound-control.o: $(srcdir)/smallapp/unbound-control.c config.h $(srcdir)/util/log.h \
$(srcdir)/util/config_file.h $(srcdir)/util/locks.h $(srcdir)/util/net_help.h $(srcdir)/util/shm_side/shm_main.h \
$(srcdir)/libunbound/unbound.h $(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h $(srcdir)/sldns/wire2str.h \
$(srcdir)/sldns/pkthdr.h $(srcdir)/services/rpz.h $(srcdir)/services/localzone.h $(srcdir)/util/rbtree.h \
$(srcdir)/util/storage/dnstree.h $(srcdir)/util/module.h $(srcdir)/util/storage/lruhash.h \
$(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/msgparse.h \
$(srcdir)/sldns/rrdef.h $(srcdir)/services/view.h $(srcdir)/sldns/sbuffer.h $(srcdir)/services/authzone.h \
$(srcdir)/services/mesh.h $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/services/modstack.h $(srcdir)/respip/respip.h \
$(srcdir)/services/listen_dnsport.h
unbound-anchor.lo unbound-anchor.o: $(srcdir)/smallapp/unbound-anchor.c config.h $(srcdir)/libunbound/unbound.h \
$(srcdir)/sldns/rrdef.h $(srcdir)/sldns/parseutil.h
petal.lo petal.o: $(srcdir)/testcode/petal.c config.h
unbound-dnstap-socket.lo unbound-dnstap-socket.o: $(srcdir)/dnstap/unbound-dnstap-socket.c config.h \
$(srcdir)/dnstap/dtstream.h $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/dnstap/dnstap_fstrm.h \
$(srcdir)/util/ub_event.h $(srcdir)/util/net_help.h $(srcdir)/services/listen_dnsport.h \
$(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/sldns/sbuffer.h $(srcdir)/sldns/wire2str.h $(srcdir)/util/config_file.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h $(srcdir)/daemon/worker.h \
$(srcdir)/libunbound/worker.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/remote.h $(srcdir)/util/fptr_wlist.h $(srcdir)/util/tube.h \
$(srcdir)/services/mesh.h $(srcdir)/util/rbtree.h $(srcdir)/services/modstack.h $(srcdir)/services/rpz.h \
$(srcdir)/services/localzone.h $(srcdir)/util/storage/dnstree.h $(srcdir)/services/view.h \
$(srcdir)/services/authzone.h $(srcdir)/respip/respip.h $(srcdir)/libunbound/context.h \
$(srcdir)/libunbound/unbound-event.h
pythonmod_utils.lo pythonmod_utils.o: $(srcdir)/pythonmod/pythonmod_utils.c config.h \
$(srcdir)/pythonmod/pythonmod_utils.h $(srcdir)/util/module.h $(srcdir)/util/storage/lruhash.h \
$(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h \
$(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/util/netevent.h \
$(srcdir)/dnscrypt/dnscrypt.h $(srcdir)/util/net_help.h \
$(srcdir)/services/cache/dns.h $(srcdir)/services/cache/rrset.h $(srcdir)/util/storage/slabhash.h \
$(srcdir)/util/regional.h $(srcdir)/iterator/iter_delegpt.h $(srcdir)/sldns/sbuffer.h
win_svc.lo win_svc.o: $(srcdir)/winrc/win_svc.c config.h $(srcdir)/winrc/win_svc.h $(srcdir)/winrc/w_inst.h \
$(srcdir)/daemon/daemon.h $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/alloc.h $(srcdir)/services/modstack.h \
$(srcdir)/daemon/worker.h \
$(srcdir)/libunbound/worker.h $(srcdir)/sldns/sbuffer.h $(srcdir)/util/data/packed_rrset.h \
$(srcdir)/util/storage/lruhash.h $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.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/remote.h $(srcdir)/util/config_file.h $(srcdir)/util/ub_event.h \
$(srcdir)/util/net_help.h
w_inst.lo w_inst.o: $(srcdir)/winrc/w_inst.c config.h $(srcdir)/winrc/w_inst.h $(srcdir)/winrc/win_svc.h
unbound-service-install.lo unbound-service-install.o: $(srcdir)/winrc/unbound-service-install.c config.h \
$(srcdir)/winrc/w_inst.h
unbound-service-remove.lo unbound-service-remove.o: $(srcdir)/winrc/unbound-service-remove.c config.h \
$(srcdir)/winrc/w_inst.h
anchor-update.lo anchor-update.o: $(srcdir)/winrc/anchor-update.c config.h $(srcdir)/libunbound/unbound.h \
$(srcdir)/sldns/rrdef.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/wire2str.h
keyraw.lo keyraw.o: $(srcdir)/sldns/keyraw.c config.h $(srcdir)/sldns/keyraw.h $(srcdir)/sldns/rrdef.h
sbuffer.lo sbuffer.o: $(srcdir)/sldns/sbuffer.c config.h $(srcdir)/sldns/sbuffer.h
wire2str.lo wire2str.o: $(srcdir)/sldns/wire2str.c config.h $(srcdir)/sldns/wire2str.h $(srcdir)/sldns/str2wire.h \
$(srcdir)/sldns/rrdef.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/parseutil.h $(srcdir)/sldns/sbuffer.h \
$(srcdir)/sldns/keyraw.h $(srcdir)/util/data/dname.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h \
$(srcdir)/util/log.h
parse.lo parse.o: $(srcdir)/sldns/parse.c config.h $(srcdir)/sldns/parse.h $(srcdir)/sldns/parseutil.h \
$(srcdir)/sldns/sbuffer.h
parseutil.lo parseutil.o: $(srcdir)/sldns/parseutil.c config.h $(srcdir)/sldns/parseutil.h
rrdef.lo rrdef.o: $(srcdir)/sldns/rrdef.c config.h $(srcdir)/sldns/rrdef.h $(srcdir)/sldns/parseutil.h
str2wire.lo str2wire.o: $(srcdir)/sldns/str2wire.c config.h $(srcdir)/sldns/str2wire.h $(srcdir)/sldns/rrdef.h \
$(srcdir)/sldns/wire2str.h $(srcdir)/sldns/sbuffer.h $(srcdir)/sldns/parse.h $(srcdir)/sldns/parseutil.h
dohclient.lo dohclient.o: $(srcdir)/testcode/dohclient.c config.h $(srcdir)/sldns/wire2str.h \
$(srcdir)/sldns/sbuffer.h $(srcdir)/sldns/str2wire.h $(srcdir)/sldns/rrdef.h $(srcdir)/sldns/parseutil.h \
$(srcdir)/util/data/msgencode.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/data/msgparse.h \
$(srcdir)/sldns/pkthdr.h $(srcdir)/util/net_help.h
readzone.lo readzone.o: $(srcdir)/testcode/readzone.c
ctime_r.lo ctime_r.o: $(srcdir)/compat/ctime_r.c config.h $(srcdir)/util/locks.h $(srcdir)/util/log.h
fake-rfc2553.lo fake-rfc2553.o: $(srcdir)/compat/fake-rfc2553.c $(srcdir)/compat/fake-rfc2553.h config.h
gmtime_r.lo gmtime_r.o: $(srcdir)/compat/gmtime_r.c config.h
inet_aton.lo inet_aton.o: $(srcdir)/compat/inet_aton.c config.h
inet_ntop.lo inet_ntop.o: $(srcdir)/compat/inet_ntop.c config.h
inet_pton.lo inet_pton.o: $(srcdir)/compat/inet_pton.c config.h
malloc.lo malloc.o: $(srcdir)/compat/malloc.c config.h
memcmp.lo memcmp.o: $(srcdir)/compat/memcmp.c config.h
memmove.lo memmove.o: $(srcdir)/compat/memmove.c config.h
snprintf.lo snprintf.o: $(srcdir)/compat/snprintf.c config.h
strlcat.lo strlcat.o: $(srcdir)/compat/strlcat.c config.h
strlcpy.lo strlcpy.o: $(srcdir)/compat/strlcpy.c config.h
strptime.lo strptime.o: $(srcdir)/compat/strptime.c config.h
getentropy_freebsd.lo getentropy_freebsd.o: $(srcdir)/compat/getentropy_freebsd.c
getentropy_linux.lo getentropy_linux.o: $(srcdir)/compat/getentropy_linux.c config.h
getentropy_osx.lo getentropy_osx.o: $(srcdir)/compat/getentropy_osx.c
getentropy_solaris.lo getentropy_solaris.o: $(srcdir)/compat/getentropy_solaris.c config.h
getentropy_win.lo getentropy_win.o: $(srcdir)/compat/getentropy_win.c
explicit_bzero.lo explicit_bzero.o: $(srcdir)/compat/explicit_bzero.c config.h
arc4random.lo arc4random.o: $(srcdir)/compat/arc4random.c config.h $(srcdir)/compat/chacha_private.h
arc4random_uniform.lo arc4random_uniform.o: $(srcdir)/compat/arc4random_uniform.c config.h
arc4_lock.lo arc4_lock.o: $(srcdir)/compat/arc4_lock.c config.h $(srcdir)/util/locks.h
sha512.lo sha512.o: $(srcdir)/compat/sha512.c config.h
reallocarray.lo reallocarray.o: $(srcdir)/compat/reallocarray.c config.h
isblank.lo isblank.o: $(srcdir)/compat/isblank.c config.h
strsep.lo strsep.o: $(srcdir)/compat/strsep.c config.h
diff --git a/contrib/unbound/README.md b/contrib/unbound/README.md
index c220da030458..3bbd38b3b78b 100644
--- a/contrib/unbound/README.md
+++ b/contrib/unbound/README.md
@@ -1,40 +1,42 @@
# 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
have any feedback, we would love to hear from you. Don’t hesitate to
[create an issue on Github](https://github.com/NLnetLabs/unbound/issues/new)
or post a message on the [Unbound mailing list](https://lists.nlnetlabs.nl/mailman/listinfo/unbound-users).
You can learn more about Unbound by reading our
[documentation](https://unbound.docs.nlnetlabs.nl/).
## Compiling
Make sure you have the C toolchain, OpenSSL and its include files, and libexpat
-installed. Unbound can be compiled and installed using:
+installed.
+If building from the repository source you also need flex and bison installed.
+Unbound can be compiled and installed using:
```
./configure && make && make install
```
You can use libevent if you want. libevent is useful when using many (10000)
outgoing ports. By default max 256 ports are opened at the same time and the
builtin alternative is equally capable and a little faster.
-Use the `--with-libevent=dir` configure option to compile Unbound with libevent
+Use the `--with-libevent` configure option to compile Unbound with libevent
support.
## Unbound configuration
All of Unbound's configuration options are described in the man pages, which
will be installed and are available on the Unbound
[documentation page](https://unbound.docs.nlnetlabs.nl/).
An example configuration file is located in
[doc/example.conf](https://github.com/NLnetLabs/unbound/blob/master/doc/example.conf.in).
diff --git a/contrib/unbound/cachedb/cachedb.c b/contrib/unbound/cachedb/cachedb.c
index 30645268ca23..b912be8ed54f 100644
--- a/contrib/unbound/cachedb/cachedb.c
+++ b/contrib/unbound/cachedb/cachedb.c
@@ -1,921 +1,926 @@
/*
* cachedb/cachedb.c - cache from a database external to the program module
*
* Copyright (c) 2016, 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file contains a module that uses an external database to cache
* dns responses.
*/
#include "config.h"
#ifdef USE_CACHEDB
#include "cachedb/cachedb.h"
#include "cachedb/redis.h"
#include "util/regional.h"
#include "util/net_help.h"
#include "util/config_file.h"
#include "util/data/msgreply.h"
#include "util/data/msgencode.h"
#include "services/cache/dns.h"
#include "validator/val_neg.h"
#include "validator/val_secalgo.h"
#include "iterator/iter_utils.h"
#include "sldns/parseutil.h"
#include "sldns/wire2str.h"
#include "sldns/sbuffer.h"
/* header file for htobe64 */
#ifdef HAVE_ENDIAN_H
# include <endian.h>
#endif
#ifdef HAVE_SYS_ENDIAN_H
# include <sys/endian.h>
#endif
#ifndef HAVE_HTOBE64
# ifdef HAVE_LIBKERN_OSBYTEORDER_H
/* In practice this is specific to MacOS X. We assume it doesn't have
* htobe64/be64toh but has alternatives with a different name. */
# include <libkern/OSByteOrder.h>
# define htobe64(x) OSSwapHostToBigInt64(x)
# define be64toh(x) OSSwapBigToHostInt64(x)
# else
/* not OSX */
/* Some compilers do not define __BYTE_ORDER__, like IBM XLC on AIX */
# if __BIG_ENDIAN__
# define be64toh(n) (n)
# define htobe64(n) (n)
# else
# define be64toh(n) (((uint64_t)htonl((n) & 0xFFFFFFFF) << 32) | htonl((n) >> 32))
# define htobe64(n) (((uint64_t)htonl((n) & 0xFFFFFFFF) << 32) | htonl((n) >> 32))
# endif /* _ENDIAN */
# endif /* HAVE_LIBKERN_OSBYTEORDER_H */
#endif /* HAVE_BE64TOH */
/** the unit test testframe for cachedb, its module state contains
* a cache for a couple queries (in memory). */
struct testframe_moddata {
/** lock for mutex */
lock_basic_type lock;
/** key for single stored data element, NULL if none */
char* stored_key;
/** data for single stored data element, NULL if none */
uint8_t* stored_data;
/** length of stored data */
size_t stored_datalen;
};
static int
testframe_init(struct module_env* env, struct cachedb_env* cachedb_env)
{
struct testframe_moddata* d;
verbose(VERB_ALGO, "testframe_init");
d = (struct testframe_moddata*)calloc(1,
sizeof(struct testframe_moddata));
cachedb_env->backend_data = (void*)d;
if(!cachedb_env->backend_data) {
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;
}
static void
testframe_deinit(struct module_env* env, struct cachedb_env* cachedb_env)
{
struct testframe_moddata* d = (struct testframe_moddata*)
cachedb_env->backend_data;
(void)env;
verbose(VERB_ALGO, "testframe_deinit");
if(!d)
return;
lock_basic_destroy(&d->lock);
free(d->stored_key);
free(d->stored_data);
free(d);
}
static int
testframe_lookup(struct module_env* env, struct cachedb_env* cachedb_env,
char* key, struct sldns_buffer* result_buffer)
{
struct testframe_moddata* d = (struct testframe_moddata*)
cachedb_env->backend_data;
(void)env;
verbose(VERB_ALGO, "testframe_lookup of %s", key);
lock_basic_lock(&d->lock);
if(d->stored_key && strcmp(d->stored_key, key) == 0) {
if(d->stored_datalen > sldns_buffer_capacity(result_buffer)) {
lock_basic_unlock(&d->lock);
return 0; /* too large */
}
verbose(VERB_ALGO, "testframe_lookup found %d bytes",
(int)d->stored_datalen);
sldns_buffer_clear(result_buffer);
sldns_buffer_write(result_buffer, d->stored_data,
d->stored_datalen);
sldns_buffer_flip(result_buffer);
lock_basic_unlock(&d->lock);
return 1;
}
lock_basic_unlock(&d->lock);
return 0;
}
static void
testframe_store(struct module_env* env, struct cachedb_env* cachedb_env,
char* key, uint8_t* data, size_t data_len, time_t ATTR_UNUSED(ttl))
{
struct testframe_moddata* d = (struct testframe_moddata*)
cachedb_env->backend_data;
(void)env;
lock_basic_lock(&d->lock);
verbose(VERB_ALGO, "testframe_store %s (%d bytes)", key, (int)data_len);
/* free old data element (if any) */
free(d->stored_key);
d->stored_key = NULL;
free(d->stored_data);
d->stored_data = NULL;
d->stored_datalen = 0;
d->stored_data = memdup(data, data_len);
if(!d->stored_data) {
lock_basic_unlock(&d->lock);
log_err("out of memory");
return;
}
d->stored_datalen = data_len;
d->stored_key = strdup(key);
if(!d->stored_key) {
free(d->stored_data);
d->stored_data = NULL;
d->stored_datalen = 0;
lock_basic_unlock(&d->lock);
return;
}
lock_basic_unlock(&d->lock);
/* (key,data) successfully stored */
}
/** The testframe backend is for unit tests */
static struct cachedb_backend testframe_backend = { "testframe",
testframe_init, testframe_deinit, testframe_lookup, testframe_store
};
/** find a particular backend from possible backends */
static struct cachedb_backend*
cachedb_find_backend(const char* str)
{
#ifdef USE_REDIS
if(strcmp(str, redis_backend.name) == 0)
return &redis_backend;
#endif
if(strcmp(str, testframe_backend.name) == 0)
return &testframe_backend;
/* TODO add more backends here */
return NULL;
}
/** apply configuration to cachedb module 'global' state */
static int
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 0;
}
/* TODO see if more configuration needs to be applied or not */
return 1;
}
int
cachedb_init(struct module_env* env, int id)
{
struct cachedb_env* cachedb_env = (struct cachedb_env*)calloc(1,
sizeof(struct cachedb_env));
if(!cachedb_env) {
log_err("malloc failure");
return 0;
}
env->modinfo[id] = (void*)cachedb_env;
if(!cachedb_apply_cfg(cachedb_env, env->cfg)) {
log_err("cachedb: could not apply configuration settings.");
free(cachedb_env);
env->modinfo[id] = NULL;
return 0;
}
/* see if a backend is selected */
if(!cachedb_env->backend || !cachedb_env->backend->name)
return 1;
if(!(*cachedb_env->backend->init)(env, cachedb_env)) {
log_err("cachedb: could not init %s backend",
cachedb_env->backend->name);
free(cachedb_env);
env->modinfo[id] = NULL;
return 0;
}
cachedb_env->enabled = 1;
- if(env->cfg->serve_expired_reply_ttl)
+ if(env->cfg->serve_expired && env->cfg->serve_expired_reply_ttl)
log_warn(
"cachedb: serve-expired-reply-ttl is set but not working for data "
- "originating from the external cache; 0 TLL is used for those.");
- if(env->cfg->serve_expired_client_timeout)
+ "originating from the external cache; 0 TTL is used for those.");
+ if(env->cfg->serve_expired && env->cfg->serve_expired_client_timeout)
log_warn(
"cachedb: serve-expired-client-timeout is set but not working for "
"data originating from the external cache; expired data are used "
"in the reply without first trying to refresh the data.");
return 1;
}
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];
if(cachedb_env->enabled) {
(*cachedb_env->backend->deinit)(env, cachedb_env);
}
free(cachedb_env);
env->modinfo[id] = NULL;
}
/** new query for cachedb */
static int
cachedb_new(struct module_qstate* qstate, int id)
{
struct cachedb_qstate* iq = (struct cachedb_qstate*)regional_alloc(
qstate->region, sizeof(struct cachedb_qstate));
qstate->minfo[id] = iq;
if(!iq)
return 0;
memset(iq, 0, sizeof(*iq));
/* initialise it */
/* TODO */
return 1;
}
/**
* Return an error
* @param qstate: our query state
* @param id: module id
* @param rcode: error code (DNS errcode).
* @return: 0 for use by caller, to make notation easy, like:
* return error_response(..).
*/
static int
error_response(struct module_qstate* qstate, int id, int rcode)
{
verbose(VERB_QUERY, "return error response %s",
sldns_lookup_by_id(sldns_rcodes, rcode)?
sldns_lookup_by_id(sldns_rcodes, rcode)->name:"??");
qstate->return_rcode = rcode;
qstate->return_msg = NULL;
qstate->ext_state[id] = module_finished;
return 0;
}
/**
* Hash the query name, type, class and dbacess-secret into lookup buffer.
* @param qstate: query state with query info
* and env->cfg with secret.
* @param buf: returned buffer with hash to lookup
* @param len: length of the buffer.
*/
static void
calc_hash(struct module_qstate* qstate, char* buf, size_t len)
{
uint8_t clear[1024];
size_t clen = 0;
uint8_t hash[CACHEDB_HASHSIZE/8];
const char* hex = "0123456789ABCDEF";
const char* secret = qstate->env->cfg->cachedb_secret;
size_t i;
/* copy the hash info into the clear buffer */
if(clen + qstate->qinfo.qname_len < sizeof(clear)) {
memmove(clear+clen, qstate->qinfo.qname,
qstate->qinfo.qname_len);
clen += qstate->qinfo.qname_len;
}
if(clen + 4 < sizeof(clear)) {
uint16_t t = htons(qstate->qinfo.qtype);
uint16_t c = htons(qstate->qinfo.qclass);
memmove(clear+clen, &t, 2);
memmove(clear+clen+2, &c, 2);
clen += 4;
}
if(secret && secret[0] && clen + strlen(secret) < sizeof(clear)) {
memmove(clear+clen, secret, strlen(secret));
clen += strlen(secret);
}
/* hash the buffer */
secalgo_hash_sha256(clear, clen, hash);
#ifdef HAVE_EXPLICIT_BZERO
explicit_bzero(clear, clen);
#else
memset(clear, 0, clen);
#endif
/* hex encode output for portability (some online dbs need
* no nulls, no control characters, and so on) */
log_assert(len >= sizeof(hash)*2 + 1);
(void)len;
for(i=0; i<sizeof(hash); i++) {
buf[i*2] = hex[(hash[i]&0xf0)>>4];
buf[i*2+1] = hex[hash[i]&0x0f];
}
buf[sizeof(hash)*2] = 0;
}
/** convert data from return_msg into the data buffer */
static int
prep_data(struct module_qstate* qstate, struct sldns_buffer* buf)
{
uint64_t timestamp, expiry;
size_t oldlim;
struct edns_data edns;
memset(&edns, 0, sizeof(edns));
edns.edns_present = 1;
edns.bits = EDNS_DO;
edns.ext_rcode = 0;
edns.edns_version = EDNS_ADVERTISED_VERSION;
edns.udp_size = EDNS_ADVERTISED_SIZE;
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
* dns_cache_store_msg(). */
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);
if(!reply_info_answer_encode(&qstate->return_msg->qinfo,
qstate->return_msg->rep, 0, qstate->query_flags,
buf, 0, 1, qstate->env->scratch, 65535, &edns, 1, 0))
return 0;
/* TTLs in the return_msg are relative to time(0) so we have to
* store that, we also store the smallest ttl in the packet+time(0)
* as the packet expiry time */
/* qstate->return_msg->rep->ttl contains that relative shortest ttl */
timestamp = (uint64_t)*qstate->env->now;
expiry = timestamp + (uint64_t)qstate->return_msg->rep->ttl;
timestamp = htobe64(timestamp);
expiry = htobe64(expiry);
oldlim = sldns_buffer_limit(buf);
if(oldlim + sizeof(timestamp)+sizeof(expiry) >=
sldns_buffer_capacity(buf))
return 0; /* doesn't fit. */
sldns_buffer_set_limit(buf, oldlim + sizeof(timestamp)+sizeof(expiry));
sldns_buffer_write_at(buf, oldlim, &timestamp, sizeof(timestamp));
sldns_buffer_write_at(buf, oldlim+sizeof(timestamp), &expiry,
sizeof(expiry));
return 1;
}
/** check expiry, return true if matches OK */
static int
good_expiry_and_qinfo(struct module_qstate* qstate, struct sldns_buffer* buf)
{
uint64_t expiry;
/* the expiry time is the last bytes of the buffer */
if(sldns_buffer_limit(buf) < sizeof(expiry))
return 0;
sldns_buffer_read_at(buf, sldns_buffer_limit(buf)-sizeof(expiry),
&expiry, sizeof(expiry));
expiry = be64toh(expiry);
/* Check if we are allowed to return expired entries:
* - serve_expired needs to be set
* - if SERVE_EXPIRED_TTL is set make sure that the record is not older
* than that. */
if((time_t)expiry < *qstate->env->now &&
(!qstate->env->cfg->serve_expired ||
(SERVE_EXPIRED_TTL &&
*qstate->env->now - (time_t)expiry > SERVE_EXPIRED_TTL)))
return 0;
return 1;
}
/* Adjust the TTL of the given RRset by 'subtract'. If 'subtract' is
* negative, set the TTL to 0. */
static void
packed_rrset_ttl_subtract(struct packed_rrset_data* data, time_t subtract)
{
size_t i;
size_t total = data->count + data->rrsig_count;
if(subtract >= 0 && data->ttl > subtract)
data->ttl -= subtract;
else data->ttl = 0;
for(i=0; i<total; i++) {
if(subtract >= 0 && data->rr_ttl[i] > subtract)
data->rr_ttl[i] -= subtract;
else data->rr_ttl[i] = 0;
}
data->ttl_add = (subtract < data->ttl_add) ? (data->ttl_add - subtract) : 0;
}
/* Adjust the TTL of a DNS message and its RRs by 'adjust'. If 'adjust' is
* negative, set the TTLs to 0. */
static void
adjust_msg_ttl(struct dns_msg* msg, time_t adjust)
{
size_t i;
if(adjust >= 0 && msg->rep->ttl > adjust)
msg->rep->ttl -= adjust;
else
msg->rep->ttl = 0;
msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl);
msg->rep->serve_expired_ttl = msg->rep->ttl + SERVE_EXPIRED_TTL;
for(i=0; i<msg->rep->rrset_count; i++) {
packed_rrset_ttl_subtract((struct packed_rrset_data*)msg->
rep->rrsets[i]->entry.data, adjust);
}
}
/** convert dns message in buffer to return_msg */
static int
parse_data(struct module_qstate* qstate, struct sldns_buffer* buf)
{
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(lim < LDNS_HEADER_SIZE+sizeof(timestamp)+sizeof(expiry))
return 0; /* too short */
/* remove timestamp and expiry from end */
sldns_buffer_read_at(buf, lim-sizeof(expiry), &expiry, sizeof(expiry));
sldns_buffer_read_at(buf, lim-sizeof(expiry)-sizeof(timestamp),
&timestamp, sizeof(timestamp));
expiry = be64toh(expiry);
timestamp = be64toh(timestamp);
/* parse DNS packet */
regional_free_all(qstate->env->scratch);
prs = (struct msg_parse*)regional_alloc(qstate->env->scratch,
sizeof(struct msg_parse));
if(!prs)
return 0; /* out of memory */
memset(prs, 0, sizeof(*prs));
memset(&edns, 0, sizeof(edns));
sldns_buffer_set_limit(buf, lim - sizeof(expiry)-sizeof(timestamp));
if(parse_packet(buf, prs, qstate->env->scratch) != LDNS_RCODE_NOERROR) {
sldns_buffer_set_limit(buf, lim);
return 0;
}
if(parse_extract_edns_from_response_msg(prs, &edns, qstate->env->scratch) !=
LDNS_RCODE_NOERROR) {
sldns_buffer_set_limit(buf, lim);
return 0;
}
qstate->return_msg = dns_alloc_msg(buf, prs, qstate->region);
sldns_buffer_set_limit(buf, lim);
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 */
if(*qstate->env->now <= (time_t)timestamp) {
verbose(VERB_ALGO, "cachedb msg adjust by zero");
return 1; /* message from the future (clock skew?) */
}
adjust = *qstate->env->now - (time_t)timestamp;
if(qstate->return_msg->rep->ttl < adjust) {
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 ||
(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);
/* Similar to the unbound worker, if serve-expired is enabled and
* the msg would be considered to be expired, mark the state so a
* refetch will be scheduled. The comparison between 'expiry' and
* 'now' should be redundant given how these values were calculated,
* but we check it just in case as does good_expiry_and_qinfo(). */
if(qstate->env->cfg->serve_expired &&
(adjust == -1 || (time_t)expiry < *qstate->env->now)) {
qstate->need_refetch = 1;
}
return 1;
}
/**
* Lookup the qstate.qinfo in extcache, store in qstate.return_msg.
* return true if lookup was successful.
*/
static int
cachedb_extcache_lookup(struct module_qstate* qstate, struct cachedb_env* ie)
{
char key[(CACHEDB_HASHSIZE/8)*2+1];
calc_hash(qstate, key, sizeof(key));
/* call backend to fetch data for key into scratch buffer */
if( !(*ie->backend->lookup)(qstate->env, ie, key,
qstate->env->scratch_buffer)) {
return 0;
}
/* check expiry date and check if query-data matches */
if( !good_expiry_and_qinfo(qstate, qstate->env->scratch_buffer) ) {
return 0;
}
/* parse dns message into return_msg */
if( !parse_data(qstate, qstate->env->scratch_buffer) ) {
return 0;
}
return 1;
}
/**
* Store the qstate.return_msg in extcache for key qstate.info
*/
static void
cachedb_extcache_store(struct module_qstate* qstate, struct cachedb_env* ie)
{
char key[(CACHEDB_HASHSIZE/8)*2+1];
calc_hash(qstate, key, sizeof(key));
/* prepare data in scratch buffer */
if(!prep_data(qstate, qstate->env->scratch_buffer))
return;
/* call backend */
(*ie->backend->store)(qstate->env, ie, key,
sldns_buffer_begin(qstate->env->scratch_buffer),
sldns_buffer_limit(qstate->env->scratch_buffer),
qstate->return_msg->rep->ttl);
}
/**
* See if unbound's internal cache can answer the query
*/
static int
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 */
msg = dns_cache_lookup(qstate->env, qstate->qinfo.qname,
qstate->qinfo.qname_len, qstate->qinfo.qtype,
qstate->qinfo.qclass, qstate->query_flags,
qstate->region, qstate->env->scratch,
1, /* no partial messages with only a CNAME */
dpname, dpnamelen
);
if(!msg && qstate->env->neg_cache &&
iter_qname_indicates_dnssec(qstate->env, &qstate->qinfo)) {
/* lookup in negative cache; may result in
* NOERROR/NODATA or NXDOMAIN answers that need validation */
msg = val_neg_getmsg(qstate->env->neg_cache, &qstate->qinfo,
qstate->region, qstate->env->rrset_cache,
qstate->env->scratch_buffer,
*qstate->env->now, 1/*add SOA*/, NULL,
qstate->env->cfg);
}
if(!msg)
return 0;
/* this is the returned msg */
qstate->return_rcode = LDNS_RCODE_NOERROR;
qstate->return_msg = msg;
return 1;
}
/**
* Store query into the internal cache of unbound.
*/
static void
cachedb_intcache_store(struct module_qstate* qstate)
{
uint32_t store_flags = qstate->query_flags;
if(qstate->env->cfg->serve_expired)
store_flags |= DNSCACHE_STORE_ZEROTTL;
if(!qstate->return_msg)
return;
(void)dns_cache_store(qstate->env, &qstate->qinfo,
qstate->return_msg->rep, 0, qstate->prefetch_leeway, 0,
qstate->region, store_flags, qstate->qstarttime);
}
/**
* Handle a cachedb module event with a query
* @param qstate: query state (from the mesh), passed between modules.
* contains qstate->env module environment with global caches and so on.
* @param iq: query state specific for this module. per-query.
* @param ie: environment specific for this module. global.
* @param id: module id.
*/
static void
cachedb_handle_query(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 enabled, and skip if so */
if(!ie->enabled) {
/* pass request to next module */
qstate->ext_state[id] = module_wait_module;
return;
}
if(qstate->blacklist || qstate->no_cache_lookup) {
/* cache is blacklisted or we are instructed from edns to not look */
/* pass request to next module */
qstate->ext_state[id] = module_wait_module;
return;
}
/* lookup inside unbound's internal cache.
* This does not look for expired entries. */
if(cachedb_intcache_lookup(qstate, ie)) {
if(verbosity >= VERB_ALGO) {
if(qstate->return_msg->rep)
log_dns_msg("cachedb internal cache lookup",
&qstate->return_msg->qinfo,
qstate->return_msg->rep);
else log_info("cachedb internal cache lookup: rcode %s",
sldns_lookup_by_id(sldns_rcodes, qstate->return_rcode)
?sldns_lookup_by_id(sldns_rcodes, qstate->return_rcode)->name
:"??");
}
/* we are done with the query */
qstate->ext_state[id] = module_finished;
return;
}
/* ask backend cache to see if we have data */
if(cachedb_extcache_lookup(qstate, ie)) {
if(verbosity >= VERB_ALGO)
log_dns_msg(ie->backend->name,
&qstate->return_msg->qinfo,
qstate->return_msg->rep);
/* store this result in internal cache */
cachedb_intcache_store(qstate);
/* In case we have expired data but there is a client timer for expired
* answers, pass execution to next module in order to try updating the
* data first.
* TODO: this needs revisit. The expired data stored from cachedb has
* 0 TTL which is picked up by iterator later when looking in the cache.
* Document that ext cachedb does not work properly with
* serve_stale_reply_ttl yet. */
if(qstate->need_refetch && qstate->serve_expired_data &&
qstate->serve_expired_data->timer) {
qstate->return_msg = NULL;
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;
}
/* no cache fetches */
/* pass request to next module */
qstate->ext_state[id] = module_wait_module;
}
/**
* Handle a cachedb module event with a response from the iterator.
* @param qstate: query state (from the mesh), passed between modules.
* contains qstate->env module environment with global caches and so on.
* @param iq: query state specific for this module. per-query.
* @param ie: environment specific for this module. global.
* @param id: module id.
*/
static void
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 */
qstate->ext_state[id] = module_finished;
return;
}
+ if(qstate->env->cfg->cachedb_no_store) {
+ /* do not store the item in the external cache */
+ qstate->ext_state[id] = module_finished;
+ return;
+ }
/* store the item into the backend cache */
cachedb_extcache_store(qstate, ie);
/* we are done with the query */
qstate->ext_state[id] = module_finished;
}
void
cachedb_operate(struct module_qstate* qstate, enum module_ev event, int id,
struct outbound_entry* outbound)
{
struct cachedb_env* ie = (struct cachedb_env*)qstate->env->modinfo[id];
struct cachedb_qstate* iq = (struct cachedb_qstate*)qstate->minfo[id];
verbose(VERB_QUERY, "cachedb[module %d] operate: extstate:%s event:%s",
id, strextstate(qstate->ext_state[id]), strmodulevent(event));
if(iq) log_query_info(VERB_QUERY, "cachedb operate: query",
&qstate->qinfo);
/* perform cachedb state machine */
if((event == module_event_new || event == module_event_pass) &&
iq == NULL) {
if(!cachedb_new(qstate, id)) {
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
return;
}
iq = (struct cachedb_qstate*)qstate->minfo[id];
}
if(iq && (event == module_event_pass || event == module_event_new)) {
cachedb_handle_query(qstate, iq, ie, id);
return;
}
if(iq && (event == module_event_moddone)) {
cachedb_handle_response(qstate, iq, ie, id);
return;
}
if(iq && outbound) {
/* cachedb does not need to process responses at this time
* ignore it.
cachedb_process_response(qstate, iq, ie, id, outbound, event);
*/
return;
}
if(event == module_event_error) {
verbose(VERB_ALGO, "got called with event error, giving up");
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
return;
}
if(!iq && (event == module_event_moddone)) {
/* during priming, module done but we never started */
qstate->ext_state[id] = module_finished;
return;
}
log_err("bad event for cachedb");
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
void
cachedb_inform_super(struct module_qstate* ATTR_UNUSED(qstate),
int ATTR_UNUSED(id), struct module_qstate* ATTR_UNUSED(super))
{
/* cachedb does not use subordinate requests at this time */
verbose(VERB_ALGO, "cachedb inform_super was called");
}
void
cachedb_clear(struct module_qstate* qstate, int id)
{
struct cachedb_qstate* iq;
if(!qstate)
return;
iq = (struct cachedb_qstate*)qstate->minfo[id];
if(iq) {
/* free contents of iq */
/* TODO */
}
qstate->minfo[id] = NULL;
}
size_t
cachedb_get_mem(struct module_env* env, int id)
{
struct cachedb_env* ie = (struct cachedb_env*)env->modinfo[id];
if(!ie)
return 0;
return sizeof(*ie); /* TODO - more mem */
}
/**
* The cachedb function block
*/
static struct module_func_block cachedb_block = {
"cachedb",
&cachedb_init, &cachedb_deinit, &cachedb_operate,
&cachedb_inform_super, &cachedb_clear, &cachedb_get_mem
};
struct module_func_block*
cachedb_get_funcblock(void)
{
return &cachedb_block;
}
#endif /* USE_CACHEDB */
diff --git a/contrib/unbound/cachedb/redis.c b/contrib/unbound/cachedb/redis.c
index 93a575a4c6d2..6cc975901df2 100644
--- a/contrib/unbound/cachedb/redis.c
+++ b/contrib/unbound/cachedb/redis.c
@@ -1,350 +1,376 @@
/*
* cachedb/redis.c - cachedb redis module
*
* Copyright (c) 2018, 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file contains a module that uses the redis database to cache
* dns responses.
*/
#include "config.h"
#ifdef USE_CACHEDB
#include "cachedb/redis.h"
#include "cachedb/cachedb.h"
#include "util/alloc.h"
#include "util/config_file.h"
#include "sldns/sbuffer.h"
#ifdef USE_REDIS
#include "hiredis/hiredis.h"
struct redis_moddata {
redisContext** ctxs; /* thread-specific redis contexts */
int numctxs; /* number of ctx entries */
const char* server_host; /* server's IP address or host name */
int server_port; /* server's TCP port */
const char* server_path; /* server's unix path, or "", NULL if unused */
const char* server_password; /* server's AUTH password, or "", NULL if unused */
struct timeval timeout; /* timeout for connection setup and commands */
+ int logical_db; /* the redis logical database to use */
};
static redisReply* redis_command(struct module_env*, struct cachedb_env*,
const char*, const uint8_t*, size_t);
+static void
+moddata_clean(struct redis_moddata** moddata) {
+ if(!moddata || !*moddata)
+ return;
+ if((*moddata)->ctxs) {
+ int i;
+ for(i = 0; i < (*moddata)->numctxs; i++) {
+ if((*moddata)->ctxs[i])
+ redisFree((*moddata)->ctxs[i]);
+ }
+ free((*moddata)->ctxs);
+ }
+ free(*moddata);
+ *moddata = NULL;
+}
+
static redisContext*
redis_connect(const struct redis_moddata* moddata)
{
redisContext* ctx;
if(moddata->server_path && moddata->server_path[0]!=0) {
ctx = redisConnectUnixWithTimeout(moddata->server_path,
moddata->timeout);
} else {
ctx = redisConnectWithTimeout(moddata->server_host,
moddata->server_port, moddata->timeout);
}
if(!ctx || ctx->err) {
const char *errstr = "out of memory";
if(ctx)
errstr = ctx->errstr;
log_err("failed to connect to redis server: %s", errstr);
goto fail;
}
if(redisSetTimeout(ctx, moddata->timeout) != REDIS_OK) {
log_err("failed to set redis timeout");
goto fail;
}
if(moddata->server_password && moddata->server_password[0]!=0) {
redisReply* rep;
rep = redisCommand(ctx, "AUTH %s", moddata->server_password);
if(!rep || rep->type == REDIS_REPLY_ERROR) {
log_err("failed to authenticate with password");
freeReplyObject(rep);
goto fail;
}
freeReplyObject(rep);
}
+ if(moddata->logical_db > 0) {
+ redisReply* rep;
+ rep = redisCommand(ctx, "SELECT %d", moddata->logical_db);
+ if(!rep || rep->type == REDIS_REPLY_ERROR) {
+ log_err("failed to set logical database (%d)",
+ moddata->logical_db);
+ freeReplyObject(rep);
+ goto fail;
+ }
+ freeReplyObject(rep);
+ }
verbose(VERB_OPS, "Connection to Redis established");
return ctx;
- fail:
+fail:
if(ctx)
redisFree(ctx);
return NULL;
}
static int
redis_init(struct module_env* env, struct cachedb_env* cachedb_env)
{
int i;
struct redis_moddata* moddata = NULL;
verbose(VERB_OPS, "Redis initialization");
moddata = calloc(1, sizeof(struct redis_moddata));
if(!moddata) {
log_err("out of memory");
- return 0;
+ goto fail;
}
moddata->numctxs = env->cfg->num_threads;
moddata->ctxs = calloc(env->cfg->num_threads, sizeof(redisContext*));
if(!moddata->ctxs) {
log_err("out of memory");
- free(moddata);
- return 0;
+ goto fail;
}
/* note: server_host is a shallow reference to configured string.
* we don't have to free it in this module. */
moddata->server_host = env->cfg->redis_server_host;
moddata->server_port = env->cfg->redis_server_port;
moddata->server_path = env->cfg->redis_server_path;
moddata->server_password = env->cfg->redis_server_password;
moddata->timeout.tv_sec = env->cfg->redis_timeout / 1000;
moddata->timeout.tv_usec = (env->cfg->redis_timeout % 1000) * 1000;
- for(i = 0; i < moddata->numctxs; i++)
- moddata->ctxs[i] = redis_connect(moddata);
+ moddata->logical_db = env->cfg->redis_logical_db;
+ for(i = 0; i < moddata->numctxs; i++) {
+ redisContext* ctx = redis_connect(moddata);
+ if(!ctx) {
+ log_err("redis_init: failed to init redis");
+ goto fail;
+ }
+ moddata->ctxs[i] = ctx;
+ }
cachedb_env->backend_data = moddata;
if(env->cfg->redis_expire_records) {
redisReply* rep = NULL;
int redis_reply_type = 0;
/** check if setex command is supported */
rep = redis_command(env, cachedb_env,
"SETEX __UNBOUND_REDIS_CHECK__ 1 none", NULL, 0);
if(!rep) {
/** init failed, no response from redis server*/
log_err("redis_init: failed to init redis, the "
"redis-expire-records option requires the SETEX command "
"(redis >= 2.0.0)");
- return 0;
+ goto fail;
}
redis_reply_type = rep->type;
freeReplyObject(rep);
switch(redis_reply_type) {
case REDIS_REPLY_STATUS:
break;
default:
/** init failed, setex command not supported */
log_err("redis_init: failed to init redis, the "
"redis-expire-records option requires the SETEX command "
"(redis >= 2.0.0)");
- return 0;
+ goto fail;
}
}
-
return 1;
+
+fail:
+ moddata_clean(&moddata);
+ return 0;
}
static void
redis_deinit(struct module_env* env, struct cachedb_env* cachedb_env)
{
struct redis_moddata* moddata = (struct redis_moddata*)
cachedb_env->backend_data;
(void)env;
verbose(VERB_OPS, "Redis deinitialization");
-
- if(!moddata)
- return;
- if(moddata->ctxs) {
- int i;
- for(i = 0; i < moddata->numctxs; i++) {
- if(moddata->ctxs[i])
- redisFree(moddata->ctxs[i]);
- }
- free(moddata->ctxs);
- }
- free(moddata);
+ moddata_clean(&moddata);
}
/*
* Send a redis command and get a reply. Unified so that it can be used for
* both SET and GET. If 'data' is non-NULL the command is supposed to be
* SET and GET otherwise, but the implementation of this function is agnostic
* about the semantics (except for logging): 'command', 'data', and 'data_len'
* are opaquely passed to redisCommand().
* This function first checks whether a connection with a redis server has
* been established; if not it tries to set up a new one.
* It returns redisReply returned from redisCommand() or NULL if some low
* level error happens. The caller is responsible to check the return value,
* if it's non-NULL, it has to free it with freeReplyObject().
*/
static redisReply*
redis_command(struct module_env* env, struct cachedb_env* cachedb_env,
const char* command, const uint8_t* data, size_t data_len)
{
redisContext* ctx;
redisReply* rep;
struct redis_moddata* d = (struct redis_moddata*)
cachedb_env->backend_data;
/* We assume env->alloc->thread_num is a unique ID for each thread
* in [0, num-of-threads). We could treat it as an error condition
* if the assumption didn't hold, but it seems to be a fundamental
* assumption throughout the unbound architecture, so we simply assert
* it. */
log_assert(env->alloc->thread_num < d->numctxs);
ctx = d->ctxs[env->alloc->thread_num];
/* If we've not established a connection to the server or we've closed
* it on a failure, try to re-establish a new one. Failures will be
* logged in redis_connect(). */
if(!ctx) {
ctx = redis_connect(d);
d->ctxs[env->alloc->thread_num] = ctx;
}
if(!ctx)
return NULL;
/* Send the command and get a reply, synchronously. */
rep = (redisReply*)redisCommand(ctx, command, data, data_len);
if(!rep) {
/* Once an error as a NULL-reply is returned the context cannot
* be reused and we'll need to set up a new connection. */
log_err("redis_command: failed to receive a reply, "
"closing connection: %s", ctx->errstr);
redisFree(ctx);
d->ctxs[env->alloc->thread_num] = NULL;
return NULL;
}
/* Check error in reply to unify logging in that case.
* The caller may perform context-dependent checks and logging. */
if(rep->type == REDIS_REPLY_ERROR)
log_err("redis: %s resulted in an error: %s",
data ? "set" : "get", rep->str);
return rep;
}
static int
redis_lookup(struct module_env* env, struct cachedb_env* cachedb_env,
char* key, struct sldns_buffer* result_buffer)
{
redisReply* rep;
char cmdbuf[4+(CACHEDB_HASHSIZE/8)*2+1]; /* "GET " + key */
int n;
int ret = 0;
verbose(VERB_ALGO, "redis_lookup of %s", key);
n = snprintf(cmdbuf, sizeof(cmdbuf), "GET %s", key);
if(n < 0 || n >= (int)sizeof(cmdbuf)) {
log_err("redis_lookup: unexpected failure to build command");
return 0;
}
rep = redis_command(env, cachedb_env, cmdbuf, NULL, 0);
if(!rep)
return 0;
switch(rep->type) {
case REDIS_REPLY_NIL:
verbose(VERB_ALGO, "redis_lookup: no data cached");
break;
case REDIS_REPLY_STRING:
verbose(VERB_ALGO, "redis_lookup found %d bytes",
(int)rep->len);
if((size_t)rep->len > sldns_buffer_capacity(result_buffer)) {
log_err("redis_lookup: replied data too long: %lu",
(size_t)rep->len);
break;
}
sldns_buffer_clear(result_buffer);
sldns_buffer_write(result_buffer, rep->str, rep->len);
sldns_buffer_flip(result_buffer);
ret = 1;
break;
case REDIS_REPLY_ERROR:
break; /* already logged */
default:
log_err("redis_lookup: unexpected type of reply for (%d)",
rep->type);
break;
}
freeReplyObject(rep);
return ret;
}
static void
redis_store(struct module_env* env, struct cachedb_env* cachedb_env,
char* key, uint8_t* data, size_t data_len, time_t ttl)
{
redisReply* rep;
int n;
int set_ttl = (env->cfg->redis_expire_records &&
(!env->cfg->serve_expired || env->cfg->serve_expired_ttl > 0));
/* Supported commands:
* - "SET " + key + " %b"
* - "SETEX " + key + " " + ttl + " %b"
*/
char cmdbuf[6+(CACHEDB_HASHSIZE/8)*2+11+3+1];
if (!set_ttl) {
verbose(VERB_ALGO, "redis_store %s (%d bytes)", key, (int)data_len);
/* build command to set to a binary safe string */
n = snprintf(cmdbuf, sizeof(cmdbuf), "SET %s %%b", key);
} else {
/* add expired ttl time to redis ttl to avoid premature eviction of key */
ttl += env->cfg->serve_expired_ttl;
verbose(VERB_ALGO, "redis_store %s (%d bytes) with ttl %u",
key, (int)data_len, (uint32_t)ttl);
/* build command to set to a binary safe string */
n = snprintf(cmdbuf, sizeof(cmdbuf), "SETEX %s %u %%b", key,
(uint32_t)ttl);
}
if(n < 0 || n >= (int)sizeof(cmdbuf)) {
log_err("redis_store: unexpected failure to build command");
return;
}
rep = redis_command(env, cachedb_env, cmdbuf, data, data_len);
if(rep) {
verbose(VERB_ALGO, "redis_store set completed");
if(rep->type != REDIS_REPLY_STATUS &&
rep->type != REDIS_REPLY_ERROR) {
log_err("redis_store: unexpected type of reply (%d)",
rep->type);
}
freeReplyObject(rep);
}
}
struct cachedb_backend redis_backend = { "redis",
redis_init, redis_deinit, redis_lookup, redis_store
};
#endif /* USE_REDIS */
#endif /* USE_CACHEDB */
diff --git a/contrib/unbound/config.guess b/contrib/unbound/config.guess
index b187213930f1..cdfc4392047c 100755
--- a/contrib/unbound/config.guess
+++ b/contrib/unbound/config.guess
@@ -1,1803 +1,1807 @@
#! /bin/sh
# Attempt to guess a canonical system name.
# Copyright 1992-2023 Free Software Foundation, Inc.
# shellcheck disable=SC2006,SC2268 # see below for rationale
-timestamp='2023-07-20'
+timestamp='2023-08-22'
# 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
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <https://www.gnu.org/licenses/>.
#
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that
# program. This Exception is an additional permission under section 7
# of the GNU General Public License, version 3 ("GPLv3").
#
# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
#
# You can get the latest version of this script from:
# https://git.savannah.gnu.org/cgit/config.git/plain/config.guess
#
# Please send patches to <config-patches@gnu.org>.
# The "shellcheck disable" line above the timestamp inhibits complaints
# about features and limitations of the classic Bourne shell that were
# superseded or lifted in POSIX. However, this script identifies a wide
# variety of pre-POSIX systems that do not have POSIX shells at all, and
# even some reasonably current systems (Solaris 10 as case-in-point) still
# have a pre-POSIX /bin/sh.
me=`echo "$0" | sed -e 's,.*/,,'`
usage="\
Usage: $0 [OPTION]
Output the configuration name of the system '$me' is run on.
Options:
-h, --help print this help, then exit
-t, --time-stamp print date of last modification, then exit
-v, --version print version number, then exit
Report bugs and patches to <config-patches@gnu.org>."
version="\
GNU config.guess ($timestamp)
Originally written by Per Bothner.
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."
# Parse command line
while test $# -gt 0 ; do
case $1 in
--time-stamp | --time* | -t )
echo "$timestamp" ; exit ;;
--version | -v )
echo "$version" ; exit ;;
--help | --h* | -h )
echo "$usage"; exit ;;
-- ) # Stop option processing
shift; break ;;
- ) # Use stdin as input.
break ;;
-* )
echo "$me: invalid option $1$help" >&2
exit 1 ;;
* )
break ;;
esac
done
if test $# != 0; then
echo "$me: too many arguments$help" >&2
exit 1
fi
# Just in case it came from the environment.
GUESS=
# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
# compiler to aid in system detection is discouraged as it requires
# 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.
# Portable tmp directory creation inspired by the Autoconf team.
tmp=
# shellcheck disable=SC2172
trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15
set_cc_for_build() {
# prevent multiple calls if $tmp is already set
test "$tmp" && return 0
: "${TMPDIR=/tmp}"
# shellcheck disable=SC2039,SC3028
{ tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
{ test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } ||
{ tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } ||
{ echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; }
dummy=$tmp/dummy
case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in
,,) echo "int x;" > "$dummy.c"
for driver in cc gcc c89 c99 ; do
if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then
CC_FOR_BUILD=$driver
break
fi
done
if test x"$CC_FOR_BUILD" = x ; then
CC_FOR_BUILD=no_compiler_found
fi
;;
,,*) CC_FOR_BUILD=$CC ;;
,*,*) CC_FOR_BUILD=$HOST_CC ;;
esac
}
# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
# (ghazi@noc.rutgers.edu 1994-08-24)
if test -f /.attbin/uname ; then
PATH=$PATH:/.attbin ; export PATH
fi
UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
case $UNAME_SYSTEM in
Linux|GNU|GNU/*)
LIBC=unknown
set_cc_for_build
cat <<-EOF > "$dummy.c"
+ #if defined(__ANDROID__)
+ LIBC=android
+ #else
#include <features.h>
#if defined(__UCLIBC__)
LIBC=uclibc
#elif defined(__dietlibc__)
LIBC=dietlibc
#elif defined(__GLIBC__)
LIBC=gnu
#else
#include <stdarg.h>
/* First heuristic to detect musl libc. */
#ifdef __DEFINED_va_list
LIBC=musl
#endif
#endif
+ #endif
EOF
cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`
eval "$cc_set_libc"
# Second heuristic to detect musl libc.
if [ "$LIBC" = unknown ] &&
command -v ldd >/dev/null &&
ldd --version 2>&1 | grep -q ^musl; then
LIBC=musl
fi
# If the system lacks a compiler, then just pick glibc.
# We could probably try harder.
if [ "$LIBC" = unknown ]; then
LIBC=gnu
fi
;;
esac
# Note: order is significant - the case branches are not exclusive.
case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION in
*:NetBSD:*:*)
# NetBSD (nbsd) targets should (where applicable) match one or
# more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
# *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
# switched to ELF, *-*-netbsd* would select the old
# object file format. This provides both forward
# compatibility and a consistent mechanism for selecting the
# object file format.
#
# Note: NetBSD doesn't particularly care about the vendor
# portion of the name. We always set it to "unknown".
UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \
/sbin/sysctl -n hw.machine_arch 2>/dev/null || \
/usr/sbin/sysctl -n hw.machine_arch 2>/dev/null || \
echo unknown)`
case $UNAME_MACHINE_ARCH in
aarch64eb) machine=aarch64_be-unknown ;;
armeb) machine=armeb-unknown ;;
arm*) machine=arm-unknown ;;
sh3el) machine=shl-unknown ;;
sh3eb) machine=sh-unknown ;;
sh5el) machine=sh5le-unknown ;;
earmv*)
arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'`
endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'`
machine=${arch}${endian}-unknown
;;
*) machine=$UNAME_MACHINE_ARCH-unknown ;;
esac
# The Operating System including object format, if it has switched
# to ELF recently (or will in the future) and ABI.
case $UNAME_MACHINE_ARCH in
earm*)
os=netbsdelf
;;
arm*|i386|m68k|ns32k|sh3*|sparc|vax)
set_cc_for_build
if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
| grep -q __ELF__
then
# Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
# Return netbsd for either. FIX?
os=netbsd
else
os=netbsdelf
fi
;;
*)
os=netbsd
;;
esac
# Determine ABI tags.
case $UNAME_MACHINE_ARCH in
earm*)
expr='s/^earmv[0-9]/-eabi/;s/eb$//'
abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"`
;;
esac
# The OS release
# Debian GNU/NetBSD machines have a different userland, and
# thus, need a distinct triplet. However, they do not need
# kernel version information, so it can be replaced with a
# suitable tag, in the style of linux-gnu.
case $UNAME_VERSION in
Debian*)
release='-gnu'
;;
*)
release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2`
;;
esac
# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
# contains redundant information, the shorter form:
# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
GUESS=$machine-${os}${release}${abi-}
;;
*:Bitrig:*:*)
UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
GUESS=$UNAME_MACHINE_ARCH-unknown-bitrig$UNAME_RELEASE
;;
*:OpenBSD:*:*)
UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
GUESS=$UNAME_MACHINE_ARCH-unknown-openbsd$UNAME_RELEASE
;;
*:SecBSD:*:*)
UNAME_MACHINE_ARCH=`arch | sed 's/SecBSD.//'`
GUESS=$UNAME_MACHINE_ARCH-unknown-secbsd$UNAME_RELEASE
;;
*:LibertyBSD:*:*)
UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'`
GUESS=$UNAME_MACHINE_ARCH-unknown-libertybsd$UNAME_RELEASE
;;
*:MidnightBSD:*:*)
GUESS=$UNAME_MACHINE-unknown-midnightbsd$UNAME_RELEASE
;;
*:ekkoBSD:*:*)
GUESS=$UNAME_MACHINE-unknown-ekkobsd$UNAME_RELEASE
;;
*:SolidBSD:*:*)
GUESS=$UNAME_MACHINE-unknown-solidbsd$UNAME_RELEASE
;;
*:OS108:*:*)
GUESS=$UNAME_MACHINE-unknown-os108_$UNAME_RELEASE
;;
macppc:MirBSD:*:*)
GUESS=powerpc-unknown-mirbsd$UNAME_RELEASE
;;
*:MirBSD:*:*)
GUESS=$UNAME_MACHINE-unknown-mirbsd$UNAME_RELEASE
;;
*:Sortix:*:*)
GUESS=$UNAME_MACHINE-unknown-sortix
;;
*:Twizzler:*:*)
GUESS=$UNAME_MACHINE-unknown-twizzler
;;
*:Redox:*:*)
GUESS=$UNAME_MACHINE-unknown-redox
;;
mips:OSF1:*.*)
GUESS=mips-dec-osf1
;;
alpha:OSF1:*:*)
# Reset EXIT trap before exiting to avoid spurious non-zero exit code.
trap '' 0
case $UNAME_RELEASE in
*4.0)
UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
;;
*5.*)
UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
;;
esac
# According to Compaq, /usr/sbin/psrinfo has been available on
# OSF/1 and Tru64 systems produced since 1995. I hope that
# covers most systems running today. This code pipes the CPU
# types through head -n 1, so we only detect the type of CPU 0.
ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
case $ALPHA_CPU_TYPE in
"EV4 (21064)")
UNAME_MACHINE=alpha ;;
"EV4.5 (21064)")
UNAME_MACHINE=alpha ;;
"LCA4 (21066/21068)")
UNAME_MACHINE=alpha ;;
"EV5 (21164)")
UNAME_MACHINE=alphaev5 ;;
"EV5.6 (21164A)")
UNAME_MACHINE=alphaev56 ;;
"EV5.6 (21164PC)")
UNAME_MACHINE=alphapca56 ;;
"EV5.7 (21164PC)")
UNAME_MACHINE=alphapca57 ;;
"EV6 (21264)")
UNAME_MACHINE=alphaev6 ;;
"EV6.7 (21264A)")
UNAME_MACHINE=alphaev67 ;;
"EV6.8CB (21264C)")
UNAME_MACHINE=alphaev68 ;;
"EV6.8AL (21264B)")
UNAME_MACHINE=alphaev68 ;;
"EV6.8CX (21264D)")
UNAME_MACHINE=alphaev68 ;;
"EV6.9A (21264/EV69A)")
UNAME_MACHINE=alphaev69 ;;
"EV7 (21364)")
UNAME_MACHINE=alphaev7 ;;
"EV7.9 (21364A)")
UNAME_MACHINE=alphaev79 ;;
esac
# A Pn.n version is a patched version.
# A Vn.n version is a released version.
# A Tn.n version is a released field test version.
# A Xn.n version is an unreleased experimental baselevel.
# 1.2 uses "1.2" for uname -r.
OSF_REL=`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
GUESS=$UNAME_MACHINE-dec-osf$OSF_REL
;;
Amiga*:UNIX_System_V:4.0:*)
GUESS=m68k-unknown-sysv4
;;
*:[Aa]miga[Oo][Ss]:*:*)
GUESS=$UNAME_MACHINE-unknown-amigaos
;;
*:[Mm]orph[Oo][Ss]:*:*)
GUESS=$UNAME_MACHINE-unknown-morphos
;;
*:OS/390:*:*)
GUESS=i370-ibm-openedition
;;
*:z/VM:*:*)
GUESS=s390-ibm-zvmoe
;;
*:OS400:*:*)
GUESS=powerpc-ibm-os400
;;
arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
GUESS=arm-acorn-riscix$UNAME_RELEASE
;;
arm*:riscos:*:*|arm*:RISCOS:*:*)
GUESS=arm-unknown-riscos
;;
SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
GUESS=hppa1.1-hitachi-hiuxmpp
;;
Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
# akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
case `(/bin/universe) 2>/dev/null` in
att) GUESS=pyramid-pyramid-sysv3 ;;
*) GUESS=pyramid-pyramid-bsd ;;
esac
;;
NILE*:*:*:dcosx)
GUESS=pyramid-pyramid-svr4
;;
DRS?6000:unix:4.0:6*)
GUESS=sparc-icl-nx6
;;
DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
case `/usr/bin/uname -p` in
sparc) GUESS=sparc-icl-nx7 ;;
esac
;;
s390x:SunOS:*:*)
SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
GUESS=$UNAME_MACHINE-ibm-solaris2$SUN_REL
;;
sun4H:SunOS:5.*:*)
SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
GUESS=sparc-hal-solaris2$SUN_REL
;;
sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
GUESS=sparc-sun-solaris2$SUN_REL
;;
i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
GUESS=i386-pc-auroraux$UNAME_RELEASE
;;
i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
set_cc_for_build
SUN_ARCH=i386
# If there is a compiler, see if it is configured for 64-bit objects.
# Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
# This test works for both compilers.
if test "$CC_FOR_BUILD" != no_compiler_found; then
if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
(CCOPTS="" $CC_FOR_BUILD -m64 -E - 2>/dev/null) | \
grep IS_64BIT_ARCH >/dev/null
then
SUN_ARCH=x86_64
fi
fi
SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
GUESS=$SUN_ARCH-pc-solaris2$SUN_REL
;;
sun4*:SunOS:6*:*)
# According to config.sub, this is the proper way to canonicalize
# SunOS6. Hard to guess exactly what SunOS6 will be like, but
# it's likely to be more like Solaris than SunOS4.
SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
GUESS=sparc-sun-solaris3$SUN_REL
;;
sun4*:SunOS:*:*)
case `/usr/bin/arch -k` in
Series*|S4*)
UNAME_RELEASE=`uname -v`
;;
esac
# 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
;;
sun3*:SunOS:*:*)
GUESS=m68k-sun-sunos$UNAME_RELEASE
;;
sun*:*:4.2BSD:*)
UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3
case `/bin/arch` in
sun3)
GUESS=m68k-sun-sunos$UNAME_RELEASE
;;
sun4)
GUESS=sparc-sun-sunos$UNAME_RELEASE
;;
esac
;;
aushp:SunOS:*:*)
GUESS=sparc-auspex-sunos$UNAME_RELEASE
;;
# The situation for MiNT is a little confusing. The machine name
# can be virtually everything (everything which is not
# "atarist" or "atariste" at least should have a processor
# > m68000). The system name ranges from "MiNT" over "FreeMiNT"
# to the lowercase version "mint" (or "freemint"). Finally
# the system name "TOS" denotes a system which is actually not
# MiNT. But MiNT is downward compatible to TOS, so this should
# be no problem.
atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
GUESS=m68k-atari-mint$UNAME_RELEASE
;;
atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
GUESS=m68k-atari-mint$UNAME_RELEASE
;;
*falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
GUESS=m68k-atari-mint$UNAME_RELEASE
;;
milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
GUESS=m68k-milan-mint$UNAME_RELEASE
;;
hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
GUESS=m68k-hades-mint$UNAME_RELEASE
;;
*:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
GUESS=m68k-unknown-mint$UNAME_RELEASE
;;
m68k:machten:*:*)
GUESS=m68k-apple-machten$UNAME_RELEASE
;;
powerpc:machten:*:*)
GUESS=powerpc-apple-machten$UNAME_RELEASE
;;
RISC*:Mach:*:*)
GUESS=mips-dec-mach_bsd4.3
;;
RISC*:ULTRIX:*:*)
GUESS=mips-dec-ultrix$UNAME_RELEASE
;;
VAX*:ULTRIX*:*:*)
GUESS=vax-dec-ultrix$UNAME_RELEASE
;;
2020:CLIX:*:* | 2430:CLIX:*:*)
GUESS=clipper-intergraph-clix$UNAME_RELEASE
;;
mips:*:*:UMIPS | mips:*:*:RISCos)
set_cc_for_build
sed 's/^ //' << EOF > "$dummy.c"
#ifdef __cplusplus
#include <stdio.h> /* for printf() prototype */
int main (int argc, char *argv[]) {
#else
int main (argc, argv) int argc; char *argv[]; {
#endif
#if defined (host_mips) && defined (MIPSEB)
#if defined (SYSTYPE_SYSV)
printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0);
#endif
#if defined (SYSTYPE_SVR4)
printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0);
#endif
#if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0);
#endif
#endif
exit (-1);
}
EOF
$CC_FOR_BUILD -o "$dummy" "$dummy.c" &&
dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` &&
SYSTEM_NAME=`"$dummy" "$dummyarg"` &&
{ echo "$SYSTEM_NAME"; exit; }
GUESS=mips-mips-riscos$UNAME_RELEASE
;;
Motorola:PowerMAX_OS:*:*)
GUESS=powerpc-motorola-powermax
;;
Motorola:*:4.3:PL8-*)
GUESS=powerpc-harris-powermax
;;
Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
GUESS=powerpc-harris-powermax
;;
Night_Hawk:Power_UNIX:*:*)
GUESS=powerpc-harris-powerunix
;;
m88k:CX/UX:7*:*)
GUESS=m88k-harris-cxux7
;;
m88k:*:4*:R4*)
GUESS=m88k-motorola-sysv4
;;
m88k:*:3*:R3*)
GUESS=m88k-motorola-sysv3
;;
AViiON:dgux:*:*)
# DG/UX returns AViiON for all architectures
UNAME_PROCESSOR=`/usr/bin/uname -p`
if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110
then
if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \
test "$TARGET_BINARY_INTERFACE"x = x
then
GUESS=m88k-dg-dgux$UNAME_RELEASE
else
GUESS=m88k-dg-dguxbcs$UNAME_RELEASE
fi
else
GUESS=i586-dg-dgux$UNAME_RELEASE
fi
;;
M88*:DolphinOS:*:*) # DolphinOS (SVR3)
GUESS=m88k-dolphin-sysv3
;;
M88*:*:R3*:*)
# Delta 88k system running SVR3
GUESS=m88k-motorola-sysv3
;;
XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
GUESS=m88k-tektronix-sysv3
;;
Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
GUESS=m68k-tektronix-bsd
;;
*:IRIX*:*:*)
IRIX_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/g'`
GUESS=mips-sgi-irix$IRIX_REL
;;
????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
GUESS=romp-ibm-aix # uname -m gives an 8 hex-code CPU id
;; # Note that: echo "'`uname -s`'" gives 'AIX '
i*86:AIX:*:*)
GUESS=i386-ibm-aix
;;
ia64:AIX:*:*)
if test -x /usr/bin/oslevel ; then
IBM_REV=`/usr/bin/oslevel`
else
IBM_REV=$UNAME_VERSION.$UNAME_RELEASE
fi
GUESS=$UNAME_MACHINE-ibm-aix$IBM_REV
;;
*:AIX:2:3)
if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
set_cc_for_build
sed 's/^ //' << EOF > "$dummy.c"
#include <sys/systemcfg.h>
main()
{
if (!__power_pc())
exit(1);
puts("powerpc-ibm-aix3.2.5");
exit(0);
}
EOF
if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"`
then
GUESS=$SYSTEM_NAME
else
GUESS=rs6000-ibm-aix3.2.5
fi
elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
GUESS=rs6000-ibm-aix3.2.4
else
GUESS=rs6000-ibm-aix3.2
fi
;;
*:AIX:*:[4567])
IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then
IBM_ARCH=rs6000
else
IBM_ARCH=powerpc
fi
if test -x /usr/bin/lslpp ; then
IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | \
awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`
else
IBM_REV=$UNAME_VERSION.$UNAME_RELEASE
fi
GUESS=$IBM_ARCH-ibm-aix$IBM_REV
;;
*:AIX:*:*)
GUESS=rs6000-ibm-aix
;;
ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*)
GUESS=romp-ibm-bsd4.4
;;
ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
GUESS=romp-ibm-bsd$UNAME_RELEASE # 4.3 with uname added to
;; # report: romp-ibm BSD 4.3
*:BOSX:*:*)
GUESS=rs6000-bull-bosx
;;
DPX/2?00:B.O.S.:*:*)
GUESS=m68k-bull-sysv3
;;
9000/[34]??:4.3bsd:1.*:*)
GUESS=m68k-hp-bsd
;;
hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
GUESS=m68k-hp-bsd4.4
;;
9000/[34678]??:HP-UX:*:*)
HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'`
case $UNAME_MACHINE in
9000/31?) HP_ARCH=m68000 ;;
9000/[34]??) HP_ARCH=m68k ;;
9000/[678][0-9][0-9])
if test -x /usr/bin/getconf; then
sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
case $sc_cpu_version in
523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0
528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1
532) # CPU_PA_RISC2_0
case $sc_kernel_bits in
32) HP_ARCH=hppa2.0n ;;
64) HP_ARCH=hppa2.0w ;;
'') HP_ARCH=hppa2.0 ;; # HP-UX 10.20
esac ;;
esac
fi
if test "$HP_ARCH" = ""; then
set_cc_for_build
sed 's/^ //' << EOF > "$dummy.c"
#define _HPUX_SOURCE
#include <stdlib.h>
#include <unistd.h>
int main ()
{
#if defined(_SC_KERNEL_BITS)
long bits = sysconf(_SC_KERNEL_BITS);
#endif
long cpu = sysconf (_SC_CPU_VERSION);
switch (cpu)
{
case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
case CPU_PA_RISC2_0:
#if defined(_SC_KERNEL_BITS)
switch (bits)
{
case 64: puts ("hppa2.0w"); break;
case 32: puts ("hppa2.0n"); break;
default: puts ("hppa2.0"); break;
} break;
#else /* !defined(_SC_KERNEL_BITS) */
puts ("hppa2.0"); break;
#endif
default: puts ("hppa1.0"); break;
}
exit (0);
}
EOF
(CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"`
test -z "$HP_ARCH" && HP_ARCH=hppa
fi ;;
esac
if test "$HP_ARCH" = hppa2.0w
then
set_cc_for_build
# hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
# 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler
# generating 64-bit code. GNU and HP use different nomenclature:
#
# $ CC_FOR_BUILD=cc ./config.guess
# => hppa2.0w-hp-hpux11.23
# $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
# => hppa64-hp-hpux11.23
if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) |
grep -q __LP64__
then
HP_ARCH=hppa2.0w
else
HP_ARCH=hppa64
fi
fi
GUESS=$HP_ARCH-hp-hpux$HPUX_REV
;;
ia64:HP-UX:*:*)
HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'`
GUESS=ia64-hp-hpux$HPUX_REV
;;
3050*:HI-UX:*:*)
set_cc_for_build
sed 's/^ //' << EOF > "$dummy.c"
#include <unistd.h>
int
main ()
{
long cpu = sysconf (_SC_CPU_VERSION);
/* The order matters, because CPU_IS_HP_MC68K erroneously returns
true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
results, however. */
if (CPU_IS_PA_RISC (cpu))
{
switch (cpu)
{
case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
default: puts ("hppa-hitachi-hiuxwe2"); break;
}
}
else if (CPU_IS_HP_MC68K (cpu))
puts ("m68k-hitachi-hiuxwe2");
else puts ("unknown-hitachi-hiuxwe2");
exit (0);
}
EOF
$CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` &&
{ echo "$SYSTEM_NAME"; exit; }
GUESS=unknown-hitachi-hiuxwe2
;;
9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*)
GUESS=hppa1.1-hp-bsd
;;
9000/8??:4.3bsd:*:*)
GUESS=hppa1.0-hp-bsd
;;
*9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
GUESS=hppa1.0-hp-mpeix
;;
hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*)
GUESS=hppa1.1-hp-osf
;;
hp8??:OSF1:*:*)
GUESS=hppa1.0-hp-osf
;;
i*86:OSF1:*:*)
if test -x /usr/sbin/sysversion ; then
GUESS=$UNAME_MACHINE-unknown-osf1mk
else
GUESS=$UNAME_MACHINE-unknown-osf1
fi
;;
parisc*:Lites*:*:*)
GUESS=hppa1.1-hp-lites
;;
C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
GUESS=c1-convex-bsd
;;
C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
if getsysinfo -f scalar_acc
then echo c32-convex-bsd
else echo c2-convex-bsd
fi
exit ;;
C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
GUESS=c34-convex-bsd
;;
C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
GUESS=c38-convex-bsd
;;
C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
GUESS=c4-convex-bsd
;;
CRAY*Y-MP:*:*:*)
CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
GUESS=ymp-cray-unicos$CRAY_REL
;;
CRAY*[A-Z]90:*:*:*)
echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \
| sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
-e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
-e 's/\.[^.]*$/.X/'
exit ;;
CRAY*TS:*:*:*)
CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
GUESS=t90-cray-unicos$CRAY_REL
;;
CRAY*T3E:*:*:*)
CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
GUESS=alphaev5-cray-unicosmk$CRAY_REL
;;
CRAY*SV1:*:*:*)
CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
GUESS=sv1-cray-unicos$CRAY_REL
;;
*:UNICOS/mp:*:*)
CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
GUESS=craynv-cray-unicosmp$CRAY_REL
;;
F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'`
GUESS=${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}
;;
5000:UNIX_System_V:4.*:*)
FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'`
GUESS=sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}
;;
i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
GUESS=$UNAME_MACHINE-pc-bsdi$UNAME_RELEASE
;;
sparc*:BSD/OS:*:*)
GUESS=sparc-unknown-bsdi$UNAME_RELEASE
;;
*:BSD/OS:*:*)
GUESS=$UNAME_MACHINE-unknown-bsdi$UNAME_RELEASE
;;
arm:FreeBSD:*:*)
UNAME_PROCESSOR=`uname -p`
set_cc_for_build
if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
| grep -q __ARM_PCS_VFP
then
FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabi
else
FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabihf
fi
;;
*:FreeBSD:*:*)
- UNAME_PROCESSOR=`/usr/bin/uname -p`
+ UNAME_PROCESSOR=`uname -p`
case $UNAME_PROCESSOR in
amd64)
UNAME_PROCESSOR=x86_64 ;;
i386)
UNAME_PROCESSOR=i586 ;;
esac
FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL
;;
i*:CYGWIN*:*)
GUESS=$UNAME_MACHINE-pc-cygwin
;;
*:MINGW64*:*)
GUESS=$UNAME_MACHINE-pc-mingw64
;;
*:MINGW*:*)
GUESS=$UNAME_MACHINE-pc-mingw32
;;
*:MSYS*:*)
GUESS=$UNAME_MACHINE-pc-msys
;;
i*:PW*:*)
GUESS=$UNAME_MACHINE-pc-pw32
;;
*:SerenityOS:*:*)
GUESS=$UNAME_MACHINE-pc-serenity
;;
*:Interix*:*)
case $UNAME_MACHINE in
x86)
GUESS=i586-pc-interix$UNAME_RELEASE
;;
authenticamd | genuineintel | EM64T)
GUESS=x86_64-unknown-interix$UNAME_RELEASE
;;
IA64)
GUESS=ia64-unknown-interix$UNAME_RELEASE
;;
esac ;;
i*:UWIN*:*)
GUESS=$UNAME_MACHINE-pc-uwin
;;
amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
GUESS=x86_64-pc-cygwin
;;
prep*:SunOS:5.*:*)
SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
GUESS=powerpcle-unknown-solaris2$SUN_REL
;;
*:GNU:*:*)
# the GNU system
GNU_ARCH=`echo "$UNAME_MACHINE" | sed -e 's,[-/].*$,,'`
GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's,/.*$,,'`
GUESS=$GNU_ARCH-unknown-$LIBC$GNU_REL
;;
*:GNU/*:*:*)
# other systems with GNU libc and userland
GNU_SYS=`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"`
GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC
;;
x86_64:[Mm]anagarm:*:*|i?86:[Mm]anagarm:*:*)
GUESS="$UNAME_MACHINE-pc-managarm-mlibc"
;;
*:[Mm]anagarm:*:*)
GUESS="$UNAME_MACHINE-unknown-managarm-mlibc"
;;
*:Minix:*:*)
GUESS=$UNAME_MACHINE-unknown-minix
;;
aarch64:Linux:*:*)
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
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
alpha:Linux:*:*)
case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null` in
EV5) UNAME_MACHINE=alphaev5 ;;
EV56) UNAME_MACHINE=alphaev56 ;;
PCA56) UNAME_MACHINE=alphapca56 ;;
PCA57) UNAME_MACHINE=alphapca56 ;;
EV6) UNAME_MACHINE=alphaev6 ;;
EV67) UNAME_MACHINE=alphaev67 ;;
EV68*) UNAME_MACHINE=alphaev68 ;;
esac
objdump --private-headers /bin/sh | grep -q ld.so.1
if test "$?" = 0 ; then LIBC=gnulibc1 ; fi
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
arc:Linux:*:* | arceb:Linux:*:* | arc32:Linux:*:* | arc64:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
arm*:Linux:*:*)
set_cc_for_build
if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
| grep -q __ARM_EABI__
then
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
else
if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
| grep -q __ARM_PCS_VFP
then
GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabi
else
GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabihf
fi
fi
;;
avr32*:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
cris:Linux:*:*)
GUESS=$UNAME_MACHINE-axis-linux-$LIBC
;;
crisv32:Linux:*:*)
GUESS=$UNAME_MACHINE-axis-linux-$LIBC
;;
e2k:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
frv:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
hexagon:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
i*86:Linux:*:*)
GUESS=$UNAME_MACHINE-pc-linux-$LIBC
;;
ia64:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
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
;;
m32r*:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
m68*:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
mips:Linux:*:* | mips64:Linux:*:*)
set_cc_for_build
IS_GLIBC=0
test x"${LIBC}" = xgnu && IS_GLIBC=1
sed 's/^ //' << EOF > "$dummy.c"
#undef CPU
#undef mips
#undef mipsel
#undef mips64
#undef mips64el
#if ${IS_GLIBC} && defined(_ABI64)
LIBCABI=gnuabi64
#else
#if ${IS_GLIBC} && defined(_ABIN32)
LIBCABI=gnuabin32
#else
LIBCABI=${LIBC}
#endif
#endif
#if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6
CPU=mipsisa64r6
#else
#if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6
CPU=mipsisa32r6
#else
#if defined(__mips64)
CPU=mips64
#else
CPU=mips
#endif
#endif
#endif
#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
MIPS_ENDIAN=el
#else
#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
MIPS_ENDIAN=
#else
MIPS_ENDIAN=
#endif
#endif
EOF
cc_set_vars=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'`
eval "$cc_set_vars"
test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; }
;;
mips64el:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
openrisc*:Linux:*:*)
GUESS=or1k-unknown-linux-$LIBC
;;
or32:Linux:*:* | or1k*:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
padre:Linux:*:*)
GUESS=sparc-unknown-linux-$LIBC
;;
parisc64:Linux:*:* | hppa64:Linux:*:*)
GUESS=hppa64-unknown-linux-$LIBC
;;
parisc:Linux:*:* | hppa:Linux:*:*)
# Look for CPU level
case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
PA7*) GUESS=hppa1.1-unknown-linux-$LIBC ;;
PA8*) GUESS=hppa2.0-unknown-linux-$LIBC ;;
*) GUESS=hppa-unknown-linux-$LIBC ;;
esac
;;
ppc64:Linux:*:*)
GUESS=powerpc64-unknown-linux-$LIBC
;;
ppc:Linux:*:*)
GUESS=powerpc-unknown-linux-$LIBC
;;
ppc64le:Linux:*:*)
GUESS=powerpc64le-unknown-linux-$LIBC
;;
ppcle:Linux:*:*)
GUESS=powerpcle-unknown-linux-$LIBC
;;
riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
s390:Linux:*:* | s390x:Linux:*:*)
GUESS=$UNAME_MACHINE-ibm-linux-$LIBC
;;
sh64*:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
sh*:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
sparc:Linux:*:* | sparc64:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
tile*:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
vax:Linux:*:*)
GUESS=$UNAME_MACHINE-dec-linux-$LIBC
;;
x86_64:Linux:*:*)
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 __i386__
ABI=x86
#else
#ifdef __ILP32__
ABI=x32
#endif
#endif
EOF
cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'`
eval "$cc_set_abi"
case $ABI in
x86) CPU=i686 ;;
x32) LIBCABI=${LIBC}x32 ;;
esac
fi
GUESS=$CPU-pc-linux-$LIBCABI
;;
xtensa*:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
i*86:DYNIX/ptx:4*:*)
# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
# earlier versions are messed up and put the nodename in both
# sysname and nodename.
GUESS=i386-sequent-sysv4
;;
i*86:UNIX_SV:4.2MP:2.*)
# Unixware is an offshoot of SVR4, but it has its own version
# number series starting with 2...
# I am not positive that other SVR4 systems won't match this,
# I just have to hope. -- rms.
# Use sysv4.2uw... so that sysv4* matches it.
GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION
;;
i*86:OS/2:*:*)
# If we were able to find 'uname', then EMX Unix compatibility
# is probably installed.
GUESS=$UNAME_MACHINE-pc-os2-emx
;;
i*86:XTS-300:*:STOP)
GUESS=$UNAME_MACHINE-unknown-stop
;;
i*86:atheos:*:*)
GUESS=$UNAME_MACHINE-unknown-atheos
;;
i*86:syllable:*:*)
GUESS=$UNAME_MACHINE-pc-syllable
;;
i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
GUESS=i386-unknown-lynxos$UNAME_RELEASE
;;
i*86:*DOS:*:*)
GUESS=$UNAME_MACHINE-pc-msdosdjgpp
;;
i*86:*:4.*:*)
UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'`
if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
GUESS=$UNAME_MACHINE-univel-sysv$UNAME_REL
else
GUESS=$UNAME_MACHINE-pc-sysv$UNAME_REL
fi
;;
i*86:*:5:[678]*)
# UnixWare 7.x, OpenUNIX and OpenServer 6.
case `/bin/uname -X | grep "^Machine"` in
*486*) UNAME_MACHINE=i486 ;;
*Pentium) UNAME_MACHINE=i586 ;;
*Pent*|*Celeron) UNAME_MACHINE=i686 ;;
esac
GUESS=$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
;;
i*86:*:3.2:*)
if test -f /usr/options/cb.name; then
UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
GUESS=$UNAME_MACHINE-pc-isc$UNAME_REL
elif /bin/uname -X 2>/dev/null >/dev/null ; then
UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
(/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
(/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
&& UNAME_MACHINE=i586
(/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
&& UNAME_MACHINE=i686
(/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
&& UNAME_MACHINE=i686
GUESS=$UNAME_MACHINE-pc-sco$UNAME_REL
else
GUESS=$UNAME_MACHINE-pc-sysv32
fi
;;
pc:*:*:*)
# Left here for compatibility:
# uname -m prints for DJGPP always 'pc', but it prints nothing about
# the processor, so we play safe by assuming i586.
# Note: whatever this is, it MUST be the same as what config.sub
# prints for the "djgpp" host, or else GDB configure will decide that
# this is a cross-build.
GUESS=i586-pc-msdosdjgpp
;;
Intel:Mach:3*:*)
GUESS=i386-pc-mach3
;;
paragon:*:*:*)
GUESS=i860-intel-osf1
;;
i860:*:4.*:*) # i860-SVR4
if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
GUESS=i860-stardent-sysv$UNAME_RELEASE # Stardent Vistra i860-SVR4
else # Add other i860-SVR4 vendors below as they are discovered.
GUESS=i860-unknown-sysv$UNAME_RELEASE # Unknown i860-SVR4
fi
;;
mini*:CTIX:SYS*5:*)
# "miniframe"
GUESS=m68010-convergent-sysv
;;
mc68k:UNIX:SYSTEM5:3.51m)
GUESS=m68k-convergent-sysv
;;
M680?0:D-NIX:5.3:*)
GUESS=m68k-diab-dnix
;;
M68*:*:R3V[5678]*:*)
test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
OS_REL=''
test -r /etc/.relid \
&& OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
&& { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
&& { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
&& { echo i486-ncr-sysv4; exit; } ;;
NCR*:*:4.2:* | MPRAS*:*:4.2:*)
OS_REL='.3'
test -r /etc/.relid \
&& OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
&& { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
&& { echo i586-ncr-sysv4.3"$OS_REL"; exit; }
/bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
&& { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
GUESS=m68k-unknown-lynxos$UNAME_RELEASE
;;
mc68030:UNIX_System_V:4.*:*)
GUESS=m68k-atari-sysv4
;;
TSUNAMI:LynxOS:2.*:*)
GUESS=sparc-unknown-lynxos$UNAME_RELEASE
;;
rs6000:LynxOS:2.*:*)
GUESS=rs6000-unknown-lynxos$UNAME_RELEASE
;;
PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
GUESS=powerpc-unknown-lynxos$UNAME_RELEASE
;;
SM[BE]S:UNIX_SV:*:*)
GUESS=mips-dde-sysv$UNAME_RELEASE
;;
RM*:ReliantUNIX-*:*:*)
GUESS=mips-sni-sysv4
;;
RM*:SINIX-*:*:*)
GUESS=mips-sni-sysv4
;;
*:SINIX-*:*:*)
if uname -p 2>/dev/null >/dev/null ; then
UNAME_MACHINE=`(uname -p) 2>/dev/null`
GUESS=$UNAME_MACHINE-sni-sysv4
else
GUESS=ns32k-sni-sysv
fi
;;
PENTIUM:*:4.0*:*) # Unisys 'ClearPath HMP IX 4000' SVR4/MP effort
# says <Richard.M.Bartel@ccMail.Census.GOV>
GUESS=i586-unisys-sysv4
;;
*:UNIX_System_V:4*:FTX*)
# From Gerald Hewes <hewes@openmarket.com>.
# How about differentiating between stratus architectures? -djm
GUESS=hppa1.1-stratus-sysv4
;;
*:*:*:FTX*)
# From seanf@swdc.stratus.com.
GUESS=i860-stratus-sysv4
;;
i*86:VOS:*:*)
# From Paul.Green@stratus.com.
GUESS=$UNAME_MACHINE-stratus-vos
;;
*:VOS:*:*)
# From Paul.Green@stratus.com.
GUESS=hppa1.1-stratus-vos
;;
mc68*:A/UX:*:*)
GUESS=m68k-apple-aux$UNAME_RELEASE
;;
news*:NEWS-OS:6*:*)
GUESS=mips-sony-newsos6
;;
R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
if test -d /usr/nec; then
GUESS=mips-nec-sysv$UNAME_RELEASE
else
GUESS=mips-unknown-sysv$UNAME_RELEASE
fi
;;
BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
GUESS=powerpc-be-beos
;;
BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
GUESS=powerpc-apple-beos
;;
BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
GUESS=i586-pc-beos
;;
BePC:Haiku:*:*) # Haiku running on Intel PC compatible.
GUESS=i586-pc-haiku
;;
ppc:Haiku:*:*) # Haiku running on Apple PowerPC
GUESS=powerpc-apple-haiku
;;
*:Haiku:*:*) # Haiku modern gcc (not bound by BeOS compat)
GUESS=$UNAME_MACHINE-unknown-haiku
;;
SX-4:SUPER-UX:*:*)
GUESS=sx4-nec-superux$UNAME_RELEASE
;;
SX-5:SUPER-UX:*:*)
GUESS=sx5-nec-superux$UNAME_RELEASE
;;
SX-6:SUPER-UX:*:*)
GUESS=sx6-nec-superux$UNAME_RELEASE
;;
SX-7:SUPER-UX:*:*)
GUESS=sx7-nec-superux$UNAME_RELEASE
;;
SX-8:SUPER-UX:*:*)
GUESS=sx8-nec-superux$UNAME_RELEASE
;;
SX-8R:SUPER-UX:*:*)
GUESS=sx8r-nec-superux$UNAME_RELEASE
;;
SX-ACE:SUPER-UX:*:*)
GUESS=sxace-nec-superux$UNAME_RELEASE
;;
Power*:Rhapsody:*:*)
GUESS=powerpc-apple-rhapsody$UNAME_RELEASE
;;
*:Rhapsody:*:*)
GUESS=$UNAME_MACHINE-apple-rhapsody$UNAME_RELEASE
;;
arm64:Darwin:*:*)
GUESS=aarch64-apple-darwin$UNAME_RELEASE
;;
*:Darwin:*:*)
UNAME_PROCESSOR=`uname -p`
case $UNAME_PROCESSOR in
unknown) UNAME_PROCESSOR=powerpc ;;
esac
if command -v xcode-select > /dev/null 2> /dev/null && \
! xcode-select --print-path > /dev/null 2> /dev/null ; then
# Avoid executing cc if there is no toolchain installed as
# cc will be a stub that puts up a graphical alert
# prompting the user to install developer tools.
CC_FOR_BUILD=no_compiler_found
else
set_cc_for_build
fi
if test "$CC_FOR_BUILD" != no_compiler_found; then
if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
(CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
grep IS_64BIT_ARCH >/dev/null
then
case $UNAME_PROCESSOR in
i386) UNAME_PROCESSOR=x86_64 ;;
powerpc) UNAME_PROCESSOR=powerpc64 ;;
esac
fi
# On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc
if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \
(CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
grep IS_PPC >/dev/null
then
UNAME_PROCESSOR=powerpc
fi
elif test "$UNAME_PROCESSOR" = i386 ; then
# uname -m returns i386 or x86_64
UNAME_PROCESSOR=$UNAME_MACHINE
fi
GUESS=$UNAME_PROCESSOR-apple-darwin$UNAME_RELEASE
;;
*:procnto*:*:* | *:QNX:[0123456789]*:*)
UNAME_PROCESSOR=`uname -p`
if test "$UNAME_PROCESSOR" = x86; then
UNAME_PROCESSOR=i386
UNAME_MACHINE=pc
fi
GUESS=$UNAME_PROCESSOR-$UNAME_MACHINE-nto-qnx$UNAME_RELEASE
;;
*:QNX:*:4*)
GUESS=i386-pc-qnx
;;
NEO-*:NONSTOP_KERNEL:*:*)
GUESS=neo-tandem-nsk$UNAME_RELEASE
;;
NSE-*:NONSTOP_KERNEL:*:*)
GUESS=nse-tandem-nsk$UNAME_RELEASE
;;
NSR-*:NONSTOP_KERNEL:*:*)
GUESS=nsr-tandem-nsk$UNAME_RELEASE
;;
NSV-*:NONSTOP_KERNEL:*:*)
GUESS=nsv-tandem-nsk$UNAME_RELEASE
;;
NSX-*:NONSTOP_KERNEL:*:*)
GUESS=nsx-tandem-nsk$UNAME_RELEASE
;;
*:NonStop-UX:*:*)
GUESS=mips-compaq-nonstopux
;;
BS2000:POSIX*:*:*)
GUESS=bs2000-siemens-sysv
;;
DS/*:UNIX_System_V:*:*)
GUESS=$UNAME_MACHINE-$UNAME_SYSTEM-$UNAME_RELEASE
;;
*:Plan9:*:*)
# "uname -m" is not consistent, so use $cputype instead. 386
# is converted to i386 for consistency with other x86
# operating systems.
if test "${cputype-}" = 386; then
UNAME_MACHINE=i386
elif test "x${cputype-}" != x; then
UNAME_MACHINE=$cputype
fi
GUESS=$UNAME_MACHINE-unknown-plan9
;;
*:TOPS-10:*:*)
GUESS=pdp10-unknown-tops10
;;
*:TENEX:*:*)
GUESS=pdp10-unknown-tenex
;;
KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
GUESS=pdp10-dec-tops20
;;
XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
GUESS=pdp10-xkl-tops20
;;
*:TOPS-20:*:*)
GUESS=pdp10-unknown-tops20
;;
*:ITS:*:*)
GUESS=pdp10-unknown-its
;;
SEI:*:*:SEIUX)
GUESS=mips-sei-seiux$UNAME_RELEASE
;;
*:DragonFly:*:*)
DRAGONFLY_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
GUESS=$UNAME_MACHINE-unknown-dragonfly$DRAGONFLY_REL
;;
*:*VMS:*:*)
UNAME_MACHINE=`(uname -p) 2>/dev/null`
case $UNAME_MACHINE in
A*) GUESS=alpha-dec-vms ;;
I*) GUESS=ia64-dec-vms ;;
V*) GUESS=vax-dec-vms ;;
esac ;;
*:XENIX:*:SysV)
GUESS=i386-pc-xenix
;;
i*86:skyos:*:*)
SKYOS_REL=`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`
GUESS=$UNAME_MACHINE-pc-skyos$SKYOS_REL
;;
i*86:rdos:*:*)
GUESS=$UNAME_MACHINE-pc-rdos
;;
i*86:Fiwix:*:*)
GUESS=$UNAME_MACHINE-pc-fiwix
;;
*:AROS:*:*)
GUESS=$UNAME_MACHINE-unknown-aros
;;
x86_64:VMkernel:*:*)
GUESS=$UNAME_MACHINE-unknown-esx
;;
amd64:Isilon\ OneFS:*:*)
GUESS=x86_64-unknown-onefs
;;
*:Unleashed:*:*)
GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE
;;
esac
# Do we have a guess based on uname results?
if test "x$GUESS" != x; then
echo "$GUESS"
exit
fi
# No uname command or uname output not recognized.
set_cc_for_build
cat > "$dummy.c" <<EOF
#ifdef _SEQUENT_
#include <sys/types.h>
#include <sys/utsname.h>
#endif
#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__)
#if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__)
#include <signal.h>
#if defined(_SIZE_T_) || defined(SIGLOST)
#include <sys/utsname.h>
#endif
#endif
#endif
main ()
{
#if defined (sony)
#if defined (MIPSEB)
/* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
I don't know.... */
printf ("mips-sony-bsd\n"); exit (0);
#else
#include <sys/param.h>
printf ("m68k-sony-newsos%s\n",
#ifdef NEWSOS4
"4"
#else
""
#endif
); exit (0);
#endif
#endif
#if defined (NeXT)
#if !defined (__ARCHITECTURE__)
#define __ARCHITECTURE__ "m68k"
#endif
int version;
version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
if (version < 4)
printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
else
printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
exit (0);
#endif
#if defined (MULTIMAX) || defined (n16)
#if defined (UMAXV)
printf ("ns32k-encore-sysv\n"); exit (0);
#else
#if defined (CMU)
printf ("ns32k-encore-mach\n"); exit (0);
#else
printf ("ns32k-encore-bsd\n"); exit (0);
#endif
#endif
#endif
#if defined (__386BSD__)
printf ("i386-pc-bsd\n"); exit (0);
#endif
#if defined (sequent)
#if defined (i386)
printf ("i386-sequent-dynix\n"); exit (0);
#endif
#if defined (ns32000)
printf ("ns32k-sequent-dynix\n"); exit (0);
#endif
#endif
#if defined (_SEQUENT_)
struct utsname un;
uname(&un);
if (strncmp(un.version, "V2", 2) == 0) {
printf ("i386-sequent-ptx2\n"); exit (0);
}
if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
printf ("i386-sequent-ptx1\n"); exit (0);
}
printf ("i386-sequent-ptx\n"); exit (0);
#endif
#if defined (vax)
#if !defined (ultrix)
#include <sys/param.h>
#if defined (BSD)
#if BSD == 43
printf ("vax-dec-bsd4.3\n"); exit (0);
#else
#if BSD == 199006
printf ("vax-dec-bsd4.3reno\n"); exit (0);
#else
printf ("vax-dec-bsd\n"); exit (0);
#endif
#endif
#else
printf ("vax-dec-bsd\n"); exit (0);
#endif
#else
#if defined(_SIZE_T_) || defined(SIGLOST)
struct utsname un;
uname (&un);
printf ("vax-dec-ultrix%s\n", un.release); exit (0);
#else
printf ("vax-dec-ultrix\n"); exit (0);
#endif
#endif
#endif
#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__)
#if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__)
#if defined(_SIZE_T_) || defined(SIGLOST)
struct utsname *un;
uname (&un);
printf ("mips-dec-ultrix%s\n", un.release); exit (0);
#else
printf ("mips-dec-ultrix\n"); exit (0);
#endif
#endif
#endif
#if defined (alliant) && defined (i860)
printf ("i860-alliant-bsd\n"); exit (0);
#endif
exit (1);
}
EOF
$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`"$dummy"` &&
{ echo "$SYSTEM_NAME"; exit; }
# Apollos put the system type in the environment.
test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; }
echo "$0: unable to guess system type" >&2
case $UNAME_MACHINE:$UNAME_SYSTEM in
mips:Linux | mips64:Linux)
# If we got here on MIPS GNU/Linux, output extra information.
cat >&2 <<EOF
NOTE: MIPS GNU/Linux systems require a C compiler to fully recognize
the system type. Please install a C compiler and try again.
EOF
;;
esac
cat >&2 <<EOF
This script (version $timestamp), has failed to recognize the
operating system you are using. If your script is old, overwrite *all*
copies of config.guess and config.sub with the latest versions from:
https://git.savannah.gnu.org/cgit/config.git/plain/config.guess
and
https://git.savannah.gnu.org/cgit/config.git/plain/config.sub
EOF
our_year=`echo $timestamp | sed 's,-.*,,'`
thisyear=`date +%Y`
# shellcheck disable=SC2003
script_age=`expr "$thisyear" - "$our_year"`
if test "$script_age" -lt 3 ; then
cat >&2 <<EOF
If $0 has already been updated, send the following data and any
information you think might be pertinent to config-patches@gnu.org to
provide the necessary information to handle your system.
config.guess timestamp = $timestamp
uname -m = `(uname -m) 2>/dev/null || echo unknown`
uname -r = `(uname -r) 2>/dev/null || echo unknown`
uname -s = `(uname -s) 2>/dev/null || echo unknown`
uname -v = `(uname -v) 2>/dev/null || echo unknown`
/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
/bin/uname -X = `(/bin/uname -X) 2>/dev/null`
hostinfo = `(hostinfo) 2>/dev/null`
/bin/universe = `(/bin/universe) 2>/dev/null`
/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`
/bin/arch = `(/bin/arch) 2>/dev/null`
/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
UNAME_MACHINE = "$UNAME_MACHINE"
UNAME_RELEASE = "$UNAME_RELEASE"
UNAME_SYSTEM = "$UNAME_SYSTEM"
UNAME_VERSION = "$UNAME_VERSION"
EOF
fi
exit 1
# Local variables:
# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "timestamp='"
# time-stamp-format: "%:y-%02m-%02d"
# time-stamp-end: "'"
# End:
diff --git a/contrib/unbound/config.h.in~ b/contrib/unbound/config.h.in~
new file mode 100644
index 000000000000..f31354d01408
--- /dev/null
+++ b/contrib/unbound/config.h.in~
@@ -0,0 +1,1456 @@
+/* config.h.in. Generated from configure.ac by autoheader. */
+
+/* apply the noreturn attribute to a function that exits the program */
+#undef ATTR_NORETURN
+
+/* apply the weak attribute to a symbol */
+#undef ATTR_WEAK
+
+/* Directory to chroot to */
+#undef CHROOT_DIR
+
+/* Define this to enable client subnet option. */
+#undef CLIENT_SUBNET
+
+/* Do sha512 definitions in config.h */
+#undef COMPAT_SHA512
+
+/* Command line arguments used with configure */
+#undef CONFCMDLINE
+
+/* Pathname to the Unbound configuration file */
+#undef CONFIGFILE
+
+/* Define this if on macOSX10.4-darwin8 and setreuid and setregid do not work
+ */
+#undef DARWIN_BROKEN_SETREUID
+
+/* Whether daemon is deprecated */
+#undef DEPRECATED_DAEMON
+
+/* Deprecate RSA 1024 bit length, makes that an unsupported key */
+#undef DEPRECATE_RSA_1024
+
+/* Define this to enable kernel based UDP source port randomization. */
+#undef DISABLE_EXPLICIT_PORT_RANDOMISATION
+
+/* default dnstap socket path */
+#undef DNSTAP_SOCKET_PATH
+
+/* Define if you want to use debug lock checking (slow). */
+#undef ENABLE_LOCK_CHECKS
+
+/* Define this if you enabled-allsymbols from libunbound to link binaries to
+ it for smaller install size, but the libunbound export table is polluted by
+ internal symbols */
+#undef EXPORT_ALL_SYMBOLS
+
+/* Define to 1 if you have the `accept4' function. */
+#undef HAVE_ACCEPT4
+
+/* Define to 1 if you have the `arc4random' function. */
+#undef HAVE_ARC4RANDOM
+
+/* Define to 1 if you have the `arc4random_uniform' function. */
+#undef HAVE_ARC4RANDOM_UNIFORM
+
+/* Define to 1 if you have the <arpa/inet.h> header file. */
+#undef HAVE_ARPA_INET_H
+
+/* Whether the C compiler accepts the "format" attribute */
+#undef HAVE_ATTR_FORMAT
+
+/* Whether the C compiler accepts the "noreturn" attribute */
+#undef HAVE_ATTR_NORETURN
+
+/* Whether the C compiler accepts the "unused" attribute */
+#undef HAVE_ATTR_UNUSED
+
+/* Whether the C compiler accepts the "weak" attribute */
+#undef HAVE_ATTR_WEAK
+
+/* If we have be64toh */
+#undef HAVE_BE64TOH
+
+/* Define to 1 if you have the `BIO_set_callback_ex' function. */
+#undef HAVE_BIO_SET_CALLBACK_EX
+
+/* Define to 1 if you have the <bsd/stdlib.h> header file. */
+#undef HAVE_BSD_STDLIB_H
+
+/* Define to 1 if you have the <bsd/string.h> header file. */
+#undef HAVE_BSD_STRING_H
+
+/* Define to 1 if you have the `chown' function. */
+#undef HAVE_CHOWN
+
+/* Define to 1 if you have the `chroot' function. */
+#undef HAVE_CHROOT
+
+/* Define to 1 if you have the `CRYPTO_cleanup_all_ex_data' function. */
+#undef HAVE_CRYPTO_CLEANUP_ALL_EX_DATA
+
+/* Define to 1 if you have the `CRYPTO_THREADID_set_callback' function. */
+#undef HAVE_CRYPTO_THREADID_SET_CALLBACK
+
+/* Define to 1 if you have the `ctime_r' function. */
+#undef HAVE_CTIME_R
+
+/* Define to 1 if you have the `daemon' function. */
+#undef HAVE_DAEMON
+
+/* Define to 1 if you have the declaration of `arc4random', and to 0 if you
+ don't. */
+#undef HAVE_DECL_ARC4RANDOM
+
+/* Define to 1 if you have the declaration of `arc4random_uniform', and to 0
+ if you don't. */
+#undef HAVE_DECL_ARC4RANDOM_UNIFORM
+
+/* Define to 1 if you have the declaration of `evsignal_assign', and to 0 if
+ you don't. */
+#undef HAVE_DECL_EVSIGNAL_ASSIGN
+
+/* Define to 1 if you have the declaration of `inet_ntop', and to 0 if you
+ don't. */
+#undef HAVE_DECL_INET_NTOP
+
+/* Define to 1 if you have the declaration of `inet_pton', and to 0 if you
+ don't. */
+#undef HAVE_DECL_INET_PTON
+
+/* Define to 1 if you have the declaration of `nghttp2_session_server_new',
+ and to 0 if you don't. */
+#undef HAVE_DECL_NGHTTP2_SESSION_SERVER_NEW
+
+/* Define to 1 if you have the declaration of `NID_ED25519', and to 0 if you
+ don't. */
+#undef HAVE_DECL_NID_ED25519
+
+/* Define to 1 if you have the declaration of `NID_ED448', and to 0 if you
+ don't. */
+#undef HAVE_DECL_NID_ED448
+
+/* Define to 1 if you have the declaration of `NID_secp384r1', and to 0 if you
+ don't. */
+#undef HAVE_DECL_NID_SECP384R1
+
+/* Define to 1 if you have the declaration of `NID_X9_62_prime256v1', and to 0
+ if you don't. */
+#undef HAVE_DECL_NID_X9_62_PRIME256V1
+
+/* Define to 1 if you have the declaration of `reallocarray', and to 0 if you
+ don't. */
+#undef HAVE_DECL_REALLOCARRAY
+
+/* Define to 1 if you have the declaration of `redisConnect', and to 0 if you
+ don't. */
+#undef HAVE_DECL_REDISCONNECT
+
+/* Define to 1 if you have the declaration of `sk_SSL_COMP_pop_free', and to 0
+ if you don't. */
+#undef HAVE_DECL_SK_SSL_COMP_POP_FREE
+
+/* Define to 1 if you have the declaration of
+ `SSL_COMP_get_compression_methods', and to 0 if you don't. */
+#undef HAVE_DECL_SSL_COMP_GET_COMPRESSION_METHODS
+
+/* Define to 1 if you have the declaration of `SSL_CTX_set_ecdh_auto', and to
+ 0 if you don't. */
+#undef HAVE_DECL_SSL_CTX_SET_ECDH_AUTO
+
+/* Define to 1 if you have the declaration of `strlcat', and to 0 if you
+ don't. */
+#undef HAVE_DECL_STRLCAT
+
+/* Define to 1 if you have the declaration of `strlcpy', and to 0 if you
+ don't. */
+#undef HAVE_DECL_STRLCPY
+
+/* Define to 1 if you have the declaration of `XML_StopParser', and to 0 if
+ you don't. */
+#undef HAVE_DECL_XML_STOPPARSER
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* Define to 1 if you have the `DSA_SIG_set0' function. */
+#undef HAVE_DSA_SIG_SET0
+
+/* Define to 1 if you have the <endian.h> header file. */
+#undef HAVE_ENDIAN_H
+
+/* Define to 1 if you have the `endprotoent' function. */
+#undef HAVE_ENDPROTOENT
+
+/* Define to 1 if you have the `endpwent' function. */
+#undef HAVE_ENDPWENT
+
+/* Define to 1 if you have the `endservent' function. */
+#undef HAVE_ENDSERVENT
+
+/* Define to 1 if you have the `ENGINE_cleanup' function. */
+#undef HAVE_ENGINE_CLEANUP
+
+/* Define to 1 if you have the `ERR_free_strings' function. */
+#undef HAVE_ERR_FREE_STRINGS
+
+/* Define to 1 if you have the `ERR_load_crypto_strings' function. */
+#undef HAVE_ERR_LOAD_CRYPTO_STRINGS
+
+/* Define to 1 if you have the `event_assign' function. */
+#undef HAVE_EVENT_ASSIGN
+
+/* Define to 1 if you have the `event_base_free' function. */
+#undef HAVE_EVENT_BASE_FREE
+
+/* Define to 1 if you have the `event_base_get_method' function. */
+#undef HAVE_EVENT_BASE_GET_METHOD
+
+/* Define to 1 if you have the `event_base_new' function. */
+#undef HAVE_EVENT_BASE_NEW
+
+/* Define to 1 if you have the `event_base_once' function. */
+#undef HAVE_EVENT_BASE_ONCE
+
+/* Define to 1 if you have the <event.h> header file. */
+#undef HAVE_EVENT_H
+
+/* Define to 1 if you have the `EVP_aes_256_cbc' function. */
+#undef HAVE_EVP_AES_256_CBC
+
+/* Define to 1 if you have the `EVP_cleanup' function. */
+#undef HAVE_EVP_CLEANUP
+
+/* Define to 1 if you have the `EVP_default_properties_is_fips_enabled'
+ function. */
+#undef HAVE_EVP_DEFAULT_PROPERTIES_IS_FIPS_ENABLED
+
+/* Define to 1 if you have the `EVP_DigestVerify' function. */
+#undef HAVE_EVP_DIGESTVERIFY
+
+/* Define to 1 if you have the `EVP_dss1' function. */
+#undef HAVE_EVP_DSS1
+
+/* Define to 1 if you have the `EVP_EncryptInit_ex' function. */
+#undef HAVE_EVP_ENCRYPTINIT_EX
+
+/* Define to 1 if you have the `EVP_MAC_CTX_set_params' function. */
+#undef HAVE_EVP_MAC_CTX_SET_PARAMS
+
+/* Define to 1 if you have the `EVP_MD_CTX_new' function. */
+#undef HAVE_EVP_MD_CTX_NEW
+
+/* Define to 1 if you have the `EVP_sha1' function. */
+#undef HAVE_EVP_SHA1
+
+/* Define to 1 if you have the `EVP_sha256' function. */
+#undef HAVE_EVP_SHA256
+
+/* Define to 1 if you have the `EVP_sha512' function. */
+#undef HAVE_EVP_SHA512
+
+/* Define to 1 if you have the `ev_default_loop' function. */
+#undef HAVE_EV_DEFAULT_LOOP
+
+/* Define to 1 if you have the `ev_loop' function. */
+#undef HAVE_EV_LOOP
+
+/* Define to 1 if you have the <expat.h> header file. */
+#undef HAVE_EXPAT_H
+
+/* Define to 1 if you have the `explicit_bzero' function. */
+#undef HAVE_EXPLICIT_BZERO
+
+/* Define to 1 if you have the `fcntl' function. */
+#undef HAVE_FCNTL
+
+/* Define to 1 if you have the `FIPS_mode' function. */
+#undef HAVE_FIPS_MODE
+
+/* Define to 1 if you have the `fork' function. */
+#undef HAVE_FORK
+
+/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
+#undef HAVE_FSEEKO
+
+/* Define to 1 if you have the `fsync' function. */
+#undef HAVE_FSYNC
+
+/* Whether getaddrinfo is available */
+#undef HAVE_GETADDRINFO
+
+/* Define to 1 if you have the `getauxval' function. */
+#undef HAVE_GETAUXVAL
+
+/* Define to 1 if you have the `getentropy' function. */
+#undef HAVE_GETENTROPY
+
+/* Define to 1 if you have the `getifaddrs' function. */
+#undef HAVE_GETIFADDRS
+
+/* Define to 1 if you have the <getopt.h> header file. */
+#undef HAVE_GETOPT_H
+
+/* Define to 1 if you have the `getpwnam' function. */
+#undef HAVE_GETPWNAM
+
+/* Define to 1 if you have the `getrlimit' function. */
+#undef HAVE_GETRLIMIT
+
+/* Define to 1 if you have the `gettid' function. */
+#undef HAVE_GETTID
+
+/* Define to 1 if you have the `glob' function. */
+#undef HAVE_GLOB
+
+/* Define to 1 if you have the <glob.h> header file. */
+#undef HAVE_GLOB_H
+
+/* Define to 1 if you have the `gmtime_r' function. */
+#undef HAVE_GMTIME_R
+
+/* Define to 1 if you have the <grp.h> header file. */
+#undef HAVE_GRP_H
+
+/* Define to 1 if you have the <hiredis/hiredis.h> header file. */
+#undef HAVE_HIREDIS_HIREDIS_H
+
+/* Define to 1 if you have the `HMAC_Init_ex' function. */
+#undef HAVE_HMAC_INIT_EX
+
+/* If we have htobe64 */
+#undef HAVE_HTOBE64
+
+/* Define to 1 if you have the <ifaddrs.h> header file. */
+#undef HAVE_IFADDRS_H
+
+/* Define to 1 if you have the `if_nametoindex' function. */
+#undef HAVE_IF_NAMETOINDEX
+
+/* Define to 1 if you have the `inet_aton' function. */
+#undef HAVE_INET_ATON
+
+/* Define to 1 if you have the `inet_ntop' function. */
+#undef HAVE_INET_NTOP
+
+/* Define to 1 if you have the `inet_pton' function. */
+#undef HAVE_INET_PTON
+
+/* Define to 1 if you have the `initgroups' function. */
+#undef HAVE_INITGROUPS
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* if the function 'ioctlsocket' is available */
+#undef HAVE_IOCTLSOCKET
+
+/* Define to 1 if you have the <iphlpapi.h> header file. */
+#undef HAVE_IPHLPAPI_H
+
+/* Define to 1 if you have the `isblank' function. */
+#undef HAVE_ISBLANK
+
+/* Define to 1 if you have the `kill' function. */
+#undef HAVE_KILL
+
+/* Use portable libbsd functions */
+#undef HAVE_LIBBSD
+
+/* Define to 1 if you have the <libkern/OSByteOrder.h> header file. */
+#undef HAVE_LIBKERN_OSBYTEORDER_H
+
+/* 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
+
+/* Define to 1 if you have the <login_cap.h> header file. */
+#undef HAVE_LOGIN_CAP_H
+
+/* If have GNU libc compatible malloc */
+#undef HAVE_MALLOC
+
+/* Define to 1 if you have the `memmove' function. */
+#undef HAVE_MEMMOVE
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the <netdb.h> header file. */
+#undef HAVE_NETDB_H
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+#undef HAVE_NETINET_IN_H
+
+/* Define to 1 if you have the <netinet/tcp.h> header file. */
+#undef HAVE_NETINET_TCP_H
+
+/* Define to 1 if you have the <netioapi.h> header file. */
+#undef HAVE_NETIOAPI_H
+
+/* Use libnettle for crypto */
+#undef HAVE_NETTLE
+
+/* Define to 1 if you have the <nettle/dsa-compat.h> header file. */
+#undef HAVE_NETTLE_DSA_COMPAT_H
+
+/* Define to 1 if you have the <nettle/eddsa.h> header file. */
+#undef HAVE_NETTLE_EDDSA_H
+
+/* Define to 1 if you have the <net/if.h> header file. */
+#undef HAVE_NET_IF_H
+
+/* Define this to use nghttp2 client. */
+#undef HAVE_NGHTTP2
+
+/* Define to 1 if you have the <nghttp2/nghttp2.h> header file. */
+#undef HAVE_NGHTTP2_NGHTTP2_H
+
+/* Use libnss for crypto */
+#undef HAVE_NSS
+
+/* Define to 1 if you have the `OpenSSL_add_all_digests' function. */
+#undef HAVE_OPENSSL_ADD_ALL_DIGESTS
+
+/* Define to 1 if you have the <openssl/bn.h> header file. */
+#undef HAVE_OPENSSL_BN_H
+
+/* Define to 1 if you have the `OPENSSL_config' function. */
+#undef HAVE_OPENSSL_CONFIG
+
+/* Define to 1 if you have the <openssl/conf.h> header file. */
+#undef HAVE_OPENSSL_CONF_H
+
+/* Define to 1 if you have the <openssl/core_names.h> header file. */
+#undef HAVE_OPENSSL_CORE_NAMES_H
+
+/* Define to 1 if you have the <openssl/dh.h> header file. */
+#undef HAVE_OPENSSL_DH_H
+
+/* Define to 1 if you have the <openssl/dsa.h> header file. */
+#undef HAVE_OPENSSL_DSA_H
+
+/* Define to 1 if you have the <openssl/engine.h> header file. */
+#undef HAVE_OPENSSL_ENGINE_H
+
+/* Define to 1 if you have the <openssl/err.h> header file. */
+#undef HAVE_OPENSSL_ERR_H
+
+/* Define to 1 if you have the `OPENSSL_init_crypto' function. */
+#undef HAVE_OPENSSL_INIT_CRYPTO
+
+/* Define to 1 if you have the `OPENSSL_init_ssl' function. */
+#undef HAVE_OPENSSL_INIT_SSL
+
+/* Define to 1 if you have the <openssl/param_build.h> header file. */
+#undef HAVE_OPENSSL_PARAM_BUILD_H
+
+/* Define to 1 if you have the <openssl/rand.h> header file. */
+#undef HAVE_OPENSSL_RAND_H
+
+/* Define to 1 if you have the <openssl/rsa.h> header file. */
+#undef HAVE_OPENSSL_RSA_H
+
+/* Define to 1 if you have the <openssl/ssl.h> header file. */
+#undef HAVE_OPENSSL_SSL_H
+
+/* Define to 1 if you have the `OSSL_PARAM_BLD_new' function. */
+#undef HAVE_OSSL_PARAM_BLD_NEW
+
+/* Define to 1 if you have the `poll' function. */
+#undef HAVE_POLL
+
+/* Define to 1 if you have the <poll.h> header file. */
+#undef HAVE_POLL_H
+
+/* Define if you have POSIX threads libraries and header files. */
+#undef HAVE_PTHREAD
+
+/* Have PTHREAD_PRIO_INHERIT. */
+#undef HAVE_PTHREAD_PRIO_INHERIT
+
+/* Define to 1 if the system has the type `pthread_rwlock_t'. */
+#undef HAVE_PTHREAD_RWLOCK_T
+
+/* Define to 1 if the system has the type `pthread_spinlock_t'. */
+#undef HAVE_PTHREAD_SPINLOCK_T
+
+/* Define to 1 if you have the <pwd.h> header file. */
+#undef HAVE_PWD_H
+
+/* Define if you have Python libraries and header files. */
+#undef HAVE_PYTHON
+
+/* Define to 1 if you have the `random' function. */
+#undef HAVE_RANDOM
+
+/* Define to 1 if you have the `RAND_cleanup' function. */
+#undef HAVE_RAND_CLEANUP
+
+/* If we have reallocarray(3) */
+#undef HAVE_REALLOCARRAY
+
+/* Define to 1 if you have the `recvmsg' function. */
+#undef HAVE_RECVMSG
+
+/* Define to 1 if you have the `sendmsg' function. */
+#undef HAVE_SENDMSG
+
+/* Define to 1 if you have the `setregid' function. */
+#undef HAVE_SETREGID
+
+/* Define to 1 if you have the `setresgid' function. */
+#undef HAVE_SETRESGID
+
+/* Define to 1 if you have the `setresuid' function. */
+#undef HAVE_SETRESUID
+
+/* Define to 1 if you have the `setreuid' function. */
+#undef HAVE_SETREUID
+
+/* Define to 1 if you have the `setrlimit' function. */
+#undef HAVE_SETRLIMIT
+
+/* Define to 1 if you have the `setsid' function. */
+#undef HAVE_SETSID
+
+/* Define to 1 if you have the `setusercontext' function. */
+#undef HAVE_SETUSERCONTEXT
+
+/* Define to 1 if you have the `SHA512_Update' function. */
+#undef HAVE_SHA512_UPDATE
+
+/* Define to 1 if you have the `shmget' function. */
+#undef HAVE_SHMGET
+
+/* Define to 1 if you have the `sigprocmask' function. */
+#undef HAVE_SIGPROCMASK
+
+/* Define to 1 if you have the `sleep' function. */
+#undef HAVE_SLEEP
+
+/* Define to 1 if you have the `snprintf' function. */
+#undef HAVE_SNPRINTF
+
+/* Define to 1 if you have the `socketpair' function. */
+#undef HAVE_SOCKETPAIR
+
+/* Using Solaris threads */
+#undef HAVE_SOLARIS_THREADS
+
+/* Define to 1 if you have the `srandom' function. */
+#undef HAVE_SRANDOM
+
+/* Define if you have the SSL libraries installed. */
+#undef HAVE_SSL
+
+/* Define to 1 if you have the `SSL_CTX_set_alpn_protos' function. */
+#undef HAVE_SSL_CTX_SET_ALPN_PROTOS
+
+/* Define to 1 if you have the `SSL_CTX_set_alpn_select_cb' function. */
+#undef HAVE_SSL_CTX_SET_ALPN_SELECT_CB
+
+/* Define to 1 if you have the `SSL_CTX_set_ciphersuites' function. */
+#undef HAVE_SSL_CTX_SET_CIPHERSUITES
+
+/* Define to 1 if you have the `SSL_CTX_set_security_level' function. */
+#undef HAVE_SSL_CTX_SET_SECURITY_LEVEL
+
+/* Define to 1 if you have the `SSL_CTX_set_tlsext_ticket_key_evp_cb'
+ function. */
+#undef HAVE_SSL_CTX_SET_TLSEXT_TICKET_KEY_EVP_CB
+
+/* Define to 1 if you have the `SSL_get0_alpn_selected' function. */
+#undef HAVE_SSL_GET0_ALPN_SELECTED
+
+/* Define to 1 if you have the `SSL_get0_peername' function. */
+#undef HAVE_SSL_GET0_PEERNAME
+
+/* Define to 1 if you have the `SSL_get1_peer_certificate' function. */
+#undef HAVE_SSL_GET1_PEER_CERTIFICATE
+
+/* Define to 1 if you have the `SSL_set1_host' function. */
+#undef HAVE_SSL_SET1_HOST
+
+/* Define to 1 if you have the <stdarg.h> header file. */
+#undef HAVE_STDARG_H
+
+/* Define to 1 if you have the <stdbool.h> header file. */
+#undef HAVE_STDBOOL_H
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the `strftime' function. */
+#undef HAVE_STRFTIME
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the `strlcat' function. */
+#undef HAVE_STRLCAT
+
+/* Define to 1 if you have the `strlcpy' function. */
+#undef HAVE_STRLCPY
+
+/* Define to 1 if you have the `strptime' function. */
+#undef HAVE_STRPTIME
+
+/* Define to 1 if you have the `strsep' function. */
+#undef HAVE_STRSEP
+
+/* Define to 1 if `ipi_spec_dst' is a member of `struct in_pktinfo'. */
+#undef HAVE_STRUCT_IN_PKTINFO_IPI_SPEC_DST
+
+/* Define to 1 if `sun_len' is a member of `struct sockaddr_un'. */
+#undef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
+
+/* Define if you have Swig libraries and header files. */
+#undef HAVE_SWIG
+
+/* Define to 1 if you have the <syslog.h> header file. */
+#undef HAVE_SYSLOG_H
+
+/* Define to 1 if systemd should be used */
+#undef HAVE_SYSTEMD
+
+/* Define to 1 if you have the <sys/endian.h> header file. */
+#undef HAVE_SYS_ENDIAN_H
+
+/* Define to 1 if you have the <sys/ipc.h> header file. */
+#undef HAVE_SYS_IPC_H
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#undef HAVE_SYS_PARAM_H
+
+/* Define to 1 if you have the <sys/resource.h> header file. */
+#undef HAVE_SYS_RESOURCE_H
+
+/* Define to 1 if you have the <sys/select.h> header file. */
+#undef HAVE_SYS_SELECT_H
+
+/* Define to 1 if you have the <sys/sha2.h> header file. */
+#undef HAVE_SYS_SHA2_H
+
+/* Define to 1 if you have the <sys/shm.h> header file. */
+#undef HAVE_SYS_SHM_H
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#undef HAVE_SYS_SOCKET_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/sysctl.h> header file. */
+#undef HAVE_SYS_SYSCTL_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <sys/uio.h> header file. */
+#undef HAVE_SYS_UIO_H
+
+/* Define to 1 if you have the <sys/un.h> header file. */
+#undef HAVE_SYS_UN_H
+
+/* Define to 1 if you have the <sys/wait.h> header file. */
+#undef HAVE_SYS_WAIT_H
+
+/* Define to 1 if you have the <TargetConditionals.h> header file. */
+#undef HAVE_TARGETCONDITIONALS_H
+
+/* Define to 1 if you have the <time.h> header file. */
+#undef HAVE_TIME_H
+
+/* Define to 1 if you have the `tzset' function. */
+#undef HAVE_TZSET
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to 1 if you have the `usleep' function. */
+#undef HAVE_USLEEP
+
+/* Define to 1 if you have the `vfork' function. */
+#undef HAVE_VFORK
+
+/* Define to 1 if you have the <vfork.h> header file. */
+#undef HAVE_VFORK_H
+
+/* Define to 1 if you have the <windows.h> header file. */
+#undef HAVE_WINDOWS_H
+
+/* Using Windows threads */
+#undef HAVE_WINDOWS_THREADS
+
+/* Define to 1 if you have the <winsock2.h> header file. */
+#undef HAVE_WINSOCK2_H
+
+/* Define to 1 if `fork' works. */
+#undef HAVE_WORKING_FORK
+
+/* Define to 1 if `vfork' works. */
+#undef HAVE_WORKING_VFORK
+
+/* Define to 1 if you have the `writev' function. */
+#undef HAVE_WRITEV
+
+/* Define to 1 if you have the <ws2tcpip.h> header file. */
+#undef HAVE_WS2TCPIP_H
+
+/* Define to 1 if you have the `X509_VERIFY_PARAM_set1_host' function. */
+#undef HAVE_X509_VERIFY_PARAM_SET1_HOST
+
+/* Define to 1 if you have the `_beginthreadex' function. */
+#undef HAVE__BEGINTHREADEX
+
+/* If HMAC_Init_ex() returns void */
+#undef HMAC_INIT_EX_RETURNS_VOID
+
+/* if lex has yylex_destroy */
+#undef LEX_HAS_YYLEX_DESTROY
+
+/* Define to the sub-directory where libtool stores uninstalled libraries. */
+#undef LT_OBJDIR
+
+/* Define to the maximum message length to pass to syslog. */
+#undef MAXSYSLOGMSGLEN
+
+/* Define if memcmp() does not compare unsigned bytes */
+#undef MEMCMP_IS_BROKEN
+
+/* Define if mkdir has one argument. */
+#undef MKDIR_HAS_ONE_ARG
+
+/* Define if the network stack does not fully support nonblocking io (causes
+ lower performance). */
+#undef NONBLOCKING_IS_BROKEN
+
+/* Put -D_ALL_SOURCE define in config.h */
+#undef OMITTED__D_ALL_SOURCE
+
+/* Put -D_BSD_SOURCE define in config.h */
+#undef OMITTED__D_BSD_SOURCE
+
+/* Put -D_DEFAULT_SOURCE define in config.h */
+#undef OMITTED__D_DEFAULT_SOURCE
+
+/* Put -D_GNU_SOURCE define in config.h */
+#undef OMITTED__D_GNU_SOURCE
+
+/* Put -D_LARGEFILE_SOURCE=1 define in config.h */
+#undef OMITTED__D_LARGEFILE_SOURCE_1
+
+/* Put -D_POSIX_C_SOURCE=200112 define in config.h */
+#undef OMITTED__D_POSIX_C_SOURCE_200112
+
+/* Put -D_XOPEN_SOURCE=600 define in config.h */
+#undef OMITTED__D_XOPEN_SOURCE_600
+
+/* Put -D_XOPEN_SOURCE_EXTENDED=1 define in config.h */
+#undef OMITTED__D_XOPEN_SOURCE_EXTENDED_1
+
+/* Put -D__EXTENSIONS__ define in config.h */
+#undef OMITTED__D__EXTENSIONS__
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* default pidfile location */
+#undef PIDFILE
+
+/* Define to necessary symbol if this constant uses a non-standard name on
+ your system. */
+#undef PTHREAD_CREATE_JOINABLE
+
+/* Return type of signal handlers, but autoconf 2.70 says 'your code may
+ safely assume C89 semantics that RETSIGTYPE is void.' */
+#undef RETSIGTYPE
+
+/* if REUSEPORT is enabled by default */
+#undef REUSEPORT_DEFAULT
+
+/* default rootkey location */
+#undef ROOT_ANCHOR_FILE
+
+/* default rootcert location */
+#undef ROOT_CERT_FILE
+
+/* version number for resource files */
+#undef RSRC_PACKAGE_VERSION
+
+/* Directory to chdir to */
+#undef RUN_DIR
+
+/* Shared data */
+#undef SHARE_DIR
+
+/* The size of `pthread_t', as computed by sizeof. */
+#undef SIZEOF_PTHREAD_T
+
+/* The size of `size_t', as computed by sizeof. */
+#undef SIZEOF_SIZE_T
+
+/* The size of `time_t', as computed by sizeof. */
+#undef SIZEOF_TIME_T
+
+/* The size of `unsigned long', as computed by sizeof. */
+#undef SIZEOF_UNSIGNED_LONG
+
+/* define if (v)snprintf does not return length needed, (but length used) */
+#undef SNPRINTF_RET_BROKEN
+
+/* Define to 1 if libsodium supports sodium_set_misuse_handler */
+#undef SODIUM_MISUSE_HANDLER
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* use default strptime. */
+#undef STRPTIME_WORKS
+
+/* Use win32 resources and API */
+#undef UB_ON_WINDOWS
+
+/* the SYSLOG_FACILITY to use, default LOG_DAEMON */
+#undef UB_SYSLOG_FACILITY
+
+/* default username */
+#undef UB_USERNAME
+
+/* use to enable lightweight alloc assertions, for debug use */
+#undef UNBOUND_ALLOC_LITE
+
+/* use malloc not regions, for debug use */
+#undef UNBOUND_ALLOC_NONREGIONAL
+
+/* use statistics for allocs and frees, for debug use */
+#undef UNBOUND_ALLOC_STATS
+
+/* define this to enable debug checks. */
+#undef UNBOUND_DEBUG
+
+/* Define to 1 to use cachedb support */
+#undef USE_CACHEDB
+
+/* Define to 1 to enable dnscrypt support */
+#undef USE_DNSCRYPT
+
+/* Define to 1 to enable dnscrypt with xchacha20 support */
+#undef USE_DNSCRYPT_XCHACHA20
+
+/* Define to 1 to enable dnstap support */
+#undef USE_DNSTAP
+
+/* Define this to enable DSA support. */
+#undef USE_DSA
+
+/* Define this to enable ECDSA support. */
+#undef USE_ECDSA
+
+/* Define this to enable an EVP workaround for older openssl */
+#undef USE_ECDSA_EVP_WORKAROUND
+
+/* Define this to enable ED25519 support. */
+#undef USE_ED25519
+
+/* Define this to enable ED448 support. */
+#undef USE_ED448
+
+/* Define this to enable GOST support. */
+#undef USE_GOST
+
+/* Define to 1 to use ipsecmod support. */
+#undef USE_IPSECMOD
+
+/* Define to 1 to use ipset support */
+#undef USE_IPSET
+
+/* Define if you enable libevent */
+#undef USE_LIBEVENT
+
+/* Define this to enable use of /proc/sys/net/ipv4/ip_local_port_range as a
+ default outgoing port range. This is only for the libunbound on Linux and
+ does not affect unbound resolving daemon itself. This may severely limit
+ the number of available outgoing ports and thus decrease randomness. Define
+ this only when the target system restricts (e.g. some of SELinux enabled
+ distributions) the use of non-ephemeral ports. */
+#undef USE_LINUX_IP_LOCAL_PORT_RANGE
+
+/* Define if you want to use internal select based events */
+#undef USE_MINI_EVENT
+
+/* Define this to enable client TCP Fast Open. */
+#undef USE_MSG_FASTOPEN
+
+/* Define this to enable client TCP Fast Open. */
+#undef USE_OSX_MSG_FASTOPEN
+
+/* Define this to use hiredis client. */
+#undef USE_REDIS
+
+/* Define this to enable SHA1 support. */
+#undef USE_SHA1
+
+/* Define this to enable SHA256 and SHA512 support. */
+#undef USE_SHA2
+
+/* Enable extensions on AIX 3, Interix. */
+#ifndef _ALL_SOURCE
+# undef _ALL_SOURCE
+#endif
+/* Enable GNU extensions on systems that have them. */
+#ifndef _GNU_SOURCE
+# undef _GNU_SOURCE
+#endif
+/* Enable threading extensions on Solaris. */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# undef _POSIX_PTHREAD_SEMANTICS
+#endif
+/* Enable extensions on HP NonStop. */
+#ifndef _TANDEM_SOURCE
+# undef _TANDEM_SOURCE
+#endif
+/* Enable general extensions on Solaris. */
+#ifndef __EXTENSIONS__
+# undef __EXTENSIONS__
+#endif
+
+
+/* Define this to enable server TCP Fast Open. */
+#undef USE_TCP_FASTOPEN
+
+/* Whether the windows socket API is used */
+#undef USE_WINSOCK
+
+/* the version of the windows API enabled */
+#undef WINVER
+
+/* Define if you want dynlib module. */
+#undef WITH_DYNLIBMODULE
+
+/* Define if you want Python module. */
+#undef WITH_PYTHONMODULE
+
+/* Define if you want PyUnbound. */
+#undef WITH_PYUNBOUND
+
+/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a
+ `char[]'. */
+#undef YYTEXT_POINTER
+
+/* Enable large inode numbers on Mac OS X 10.5. */
+#ifndef _DARWIN_USE_64_BIT_INODE
+# define _DARWIN_USE_64_BIT_INODE 1
+#endif
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+#undef _FILE_OFFSET_BITS
+
+/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
+#undef _LARGEFILE_SOURCE
+
+/* Define for large files, on AIX-style hosts. */
+#undef _LARGE_FILES
+
+/* Define to 1 if on MINIX. */
+#undef _MINIX
+
+/* Enable for compile on Minix */
+#undef _NETBSD_SOURCE
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+ this defined. */
+#undef _POSIX_1_SOURCE
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+#undef _POSIX_SOURCE
+
+/* defined to use gcc ansi snprintf and sscanf that understands %lld when
+ compiled for windows. */
+#undef __USE_MINGW_ANSI_STDIO
+
+/* Define to empty if `const' does not conform to ANSI C. */
+#undef const
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#undef gid_t
+
+/* in_addr_t */
+#undef in_addr_t
+
+/* in_port_t */
+#undef in_port_t
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+ calls it, or to nothing if 'inline' is not supported under any name. */
+#ifndef __cplusplus
+#undef inline
+#endif
+
+/* Define to `short' if <sys/types.h> does not define. */
+#undef int16_t
+
+/* Define to `int' if <sys/types.h> does not define. */
+#undef int32_t
+
+/* Define to `long long' if <sys/types.h> does not define. */
+#undef int64_t
+
+/* Define to `signed char' if <sys/types.h> does not define. */
+#undef int8_t
+
+/* Define if replacement function should be used. */
+#undef malloc
+
+/* Define to `long int' if <sys/types.h> does not define. */
+#undef off_t
+
+/* Define to `int' if <sys/types.h> does not define. */
+#undef pid_t
+
+/* Define to 'int' if not defined */
+#undef rlim_t
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+#undef size_t
+
+/* Define to 'int' if not defined */
+#undef socklen_t
+
+/* Define to `int' if <sys/types.h> does not define. */
+#undef ssize_t
+
+/* Define to 'unsigned char if not defined */
+#undef u_char
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#undef uid_t
+
+/* Define to `unsigned short' if <sys/types.h> does not define. */
+#undef uint16_t
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+#undef uint32_t
+
+/* Define to `unsigned long long' if <sys/types.h> does not define. */
+#undef uint64_t
+
+/* Define to `unsigned char' if <sys/types.h> does not define. */
+#undef uint8_t
+
+/* Define as `fork' if `vfork' does not work. */
+#undef vfork
+
+#if defined(OMITTED__D_GNU_SOURCE) && !defined(_GNU_SOURCE)
+#define _GNU_SOURCE 1
+#endif
+
+#if defined(OMITTED__D_BSD_SOURCE) && !defined(_BSD_SOURCE)
+#define _BSD_SOURCE 1
+#endif
+
+#if defined(OMITTED__D_DEFAULT_SOURCE) && !defined(_DEFAULT_SOURCE)
+#define _DEFAULT_SOURCE 1
+#endif
+
+#if defined(OMITTED__D__EXTENSIONS__) && !defined(__EXTENSIONS__)
+#define __EXTENSIONS__ 1
+#endif
+
+#if defined(OMITTED__D_POSIX_C_SOURCE_200112) && !defined(_POSIX_C_SOURCE)
+#define _POSIX_C_SOURCE 200112
+#endif
+
+#if defined(OMITTED__D_XOPEN_SOURCE_600) && !defined(_XOPEN_SOURCE)
+#define _XOPEN_SOURCE 600
+#endif
+
+#if defined(OMITTED__D_XOPEN_SOURCE_EXTENDED_1) && !defined(_XOPEN_SOURCE_EXTENDED)
+#define _XOPEN_SOURCE_EXTENDED 1
+#endif
+
+#if defined(OMITTED__D_ALL_SOURCE) && !defined(_ALL_SOURCE)
+#define _ALL_SOURCE 1
+#endif
+
+#if defined(OMITTED__D_LARGEFILE_SOURCE_1) && !defined(_LARGEFILE_SOURCE)
+#define _LARGEFILE_SOURCE 1
+#endif
+
+
+
+
+#ifndef _OPENBSD_SOURCE
+#define _OPENBSD_SOURCE 1
+#endif
+
+#ifndef UNBOUND_DEBUG
+# ifndef NDEBUG
+# define NDEBUG
+# endif
+#endif
+
+/** Use small-ldns codebase */
+#define USE_SLDNS 1
+#ifdef HAVE_SSL
+# define LDNS_BUILD_CONFIG_HAVE_SSL 1
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+
+#include <errno.h>
+
+#if HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#ifdef HAVE_WINSOCK2_H
+#include <winsock2.h>
+#endif
+
+#ifdef HAVE_WS2TCPIP_H
+#include <ws2tcpip.h>
+#endif
+
+#if !defined(USE_WINSOCK) || !defined(HAVE_SNPRINTF) || defined(SNPRINTF_RET_BROKEN) || defined(__USE_MINGW_ANSI_STDIO)
+#define ARG_LL "%ll"
+#else
+#define ARG_LL "%I64"
+#endif
+
+#ifndef AF_LOCAL
+#define AF_LOCAL AF_UNIX
+#endif
+
+
+
+#ifdef HAVE_ATTR_FORMAT
+# define ATTR_FORMAT(archetype, string_index, first_to_check) \
+ __attribute__ ((format (archetype, string_index, first_to_check)))
+#else /* !HAVE_ATTR_FORMAT */
+# define ATTR_FORMAT(archetype, string_index, first_to_check) /* empty */
+#endif /* !HAVE_ATTR_FORMAT */
+
+
+#if defined(DOXYGEN)
+# define ATTR_UNUSED(x) x
+#elif defined(__cplusplus)
+# define ATTR_UNUSED(x)
+#elif defined(HAVE_ATTR_UNUSED)
+# define ATTR_UNUSED(x) x __attribute__((unused))
+#else /* !HAVE_ATTR_UNUSED */
+# define ATTR_UNUSED(x) x
+#endif /* !HAVE_ATTR_UNUSED */
+
+
+#ifndef HAVE_FSEEKO
+#define fseeko fseek
+#define ftello ftell
+#endif /* HAVE_FSEEKO */
+
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 256
+#endif
+
+#if !defined(HAVE_SNPRINTF) || defined(SNPRINTF_RET_BROKEN)
+#define snprintf snprintf_unbound
+#define vsnprintf vsnprintf_unbound
+#include <stdarg.h>
+int snprintf (char *str, size_t count, const char *fmt, ...);
+int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);
+#endif /* HAVE_SNPRINTF or SNPRINTF_RET_BROKEN */
+
+#ifndef HAVE_INET_PTON
+#define inet_pton inet_pton_unbound
+int inet_pton(int af, const char* src, void* dst);
+#endif /* HAVE_INET_PTON */
+
+
+#ifndef HAVE_INET_NTOP
+#define inet_ntop inet_ntop_unbound
+const char *inet_ntop(int af, const void *src, char *dst, size_t size);
+#endif
+
+
+#ifndef HAVE_INET_ATON
+#define inet_aton inet_aton_unbound
+int inet_aton(const char *cp, struct in_addr *addr);
+#endif
+
+
+#ifndef HAVE_MEMMOVE
+#define memmove memmove_unbound
+void *memmove(void *dest, const void *src, size_t n);
+#endif
+
+
+#ifndef HAVE_STRLCAT
+#define strlcat strlcat_unbound
+size_t strlcat(char *dst, const char *src, size_t siz);
+#endif
+
+
+#ifndef HAVE_STRLCPY
+#define strlcpy strlcpy_unbound
+size_t strlcpy(char *dst, const char *src, size_t siz);
+#endif
+
+
+#ifndef HAVE_GMTIME_R
+#define gmtime_r gmtime_r_unbound
+struct tm *gmtime_r(const time_t *timep, struct tm *result);
+#endif
+
+
+#ifndef HAVE_REALLOCARRAY
+#define reallocarray reallocarrayunbound
+void* reallocarray(void *ptr, size_t nmemb, size_t size);
+#endif
+
+
+#if !defined(HAVE_SLEEP) || defined(HAVE_WINDOWS_H)
+#define sleep(x) Sleep((x)*1000) /* on win32 */
+#endif /* HAVE_SLEEP */
+
+
+#ifndef HAVE_USLEEP
+#define usleep(x) Sleep((x)/1000 + 1) /* on win32 */
+#endif /* HAVE_USLEEP */
+
+
+#ifndef HAVE_RANDOM
+#define random rand /* on win32, for tests only (bad random) */
+#endif /* HAVE_RANDOM */
+
+
+#ifndef HAVE_SRANDOM
+#define srandom(x) srand(x) /* on win32, for tests only (bad random) */
+#endif /* HAVE_SRANDOM */
+
+
+/* detect if we need to cast to unsigned int for FD_SET to avoid warnings */
+#ifdef HAVE_WINSOCK2_H
+#define FD_SET_T (u_int)
+#else
+#define FD_SET_T
+#endif
+
+
+#ifndef IPV6_MIN_MTU
+#define IPV6_MIN_MTU 1280
+#endif /* IPV6_MIN_MTU */
+
+
+#ifdef MEMCMP_IS_BROKEN
+#include "compat/memcmp.h"
+#define memcmp memcmp_unbound
+int memcmp(const void *x, const void *y, size_t n);
+#endif
+
+
+
+#ifndef HAVE_CTIME_R
+#define ctime_r unbound_ctime_r
+char *ctime_r(const time_t *timep, char *buf);
+#endif
+
+#ifndef HAVE_STRSEP
+#define strsep unbound_strsep
+char *strsep(char **stringp, const char *delim);
+#endif
+
+#ifndef HAVE_ISBLANK
+#define isblank unbound_isblank
+int isblank(int c);
+#endif
+
+#ifndef HAVE_EXPLICIT_BZERO
+#define explicit_bzero unbound_explicit_bzero
+void explicit_bzero(void* buf, size_t len);
+#endif
+
+#if defined(HAVE_INET_NTOP) && !HAVE_DECL_INET_NTOP
+const char *inet_ntop(int af, const void *src, char *dst, size_t size);
+#endif
+
+#if defined(HAVE_INET_PTON) && !HAVE_DECL_INET_PTON
+int inet_pton(int af, const char* src, void* dst);
+#endif
+
+#if !defined(HAVE_STRPTIME) || !defined(STRPTIME_WORKS)
+#define strptime unbound_strptime
+struct tm;
+char *strptime(const char *s, const char *format, struct tm *tm);
+#endif
+
+#if !HAVE_DECL_REALLOCARRAY
+void *reallocarray(void *ptr, size_t nmemb, size_t size);
+#endif
+
+#ifdef HAVE_LIBBSD
+#include <bsd/string.h>
+#include <bsd/stdlib.h>
+#endif
+
+#ifdef HAVE_LIBRESSL
+# if !HAVE_DECL_STRLCPY
+size_t strlcpy(char *dst, const char *src, size_t siz);
+# endif
+# if !HAVE_DECL_STRLCAT
+size_t strlcat(char *dst, const char *src, size_t siz);
+# endif
+# if !HAVE_DECL_ARC4RANDOM && defined(HAVE_ARC4RANDOM)
+uint32_t arc4random(void);
+# endif
+# if !HAVE_DECL_ARC4RANDOM_UNIFORM && defined(HAVE_ARC4RANDOM_UNIFORM)
+uint32_t arc4random_uniform(uint32_t upper_bound);
+# endif
+#endif /* HAVE_LIBRESSL */
+#ifndef HAVE_ARC4RANDOM
+int getentropy(void* buf, size_t len);
+uint32_t arc4random(void);
+void arc4random_buf(void* buf, size_t n);
+void _ARC4_LOCK(void);
+void _ARC4_UNLOCK(void);
+void _ARC4_LOCK_DESTROY(void);
+#endif
+#ifndef HAVE_ARC4RANDOM_UNIFORM
+uint32_t arc4random_uniform(uint32_t upper_bound);
+#endif
+#ifdef COMPAT_SHA512
+#ifndef SHA512_DIGEST_LENGTH
+#define SHA512_BLOCK_LENGTH 128
+#define SHA512_DIGEST_LENGTH 64
+#define SHA512_DIGEST_STRING_LENGTH (SHA512_DIGEST_LENGTH * 2 + 1)
+typedef struct _SHA512_CTX {
+ uint64_t state[8];
+ uint64_t bitcount[2];
+ uint8_t buffer[SHA512_BLOCK_LENGTH];
+} SHA512_CTX;
+#endif /* SHA512_DIGEST_LENGTH */
+void SHA512_Init(SHA512_CTX*);
+void SHA512_Update(SHA512_CTX*, void*, size_t);
+void SHA512_Final(uint8_t[SHA512_DIGEST_LENGTH], SHA512_CTX*);
+unsigned char *SHA512(void* data, unsigned int data_len, unsigned char *digest);
+#endif /* COMPAT_SHA512 */
+
+
+
+#if defined(HAVE_EVENT_H) && !defined(HAVE_EVENT_BASE_ONCE) && !(defined(HAVE_EV_LOOP) || defined(HAVE_EV_DEFAULT_LOOP)) && (defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS))
+ /* using version of libevent that is not threadsafe. */
+# define LIBEVENT_SIGNAL_PROBLEM 1
+#endif
+
+#ifndef CHECKED_INET6
+# define CHECKED_INET6
+# ifdef AF_INET6
+# define INET6
+# else
+# define AF_INET6 28
+# endif
+#endif /* CHECKED_INET6 */
+
+#ifndef HAVE_GETADDRINFO
+struct sockaddr_storage;
+#include "compat/fake-rfc2553.h"
+#endif
+
+#ifdef UNBOUND_ALLOC_STATS
+# define malloc(s) unbound_stat_malloc_log(s, __FILE__, __LINE__, __func__)
+# define calloc(n,s) unbound_stat_calloc_log(n, s, __FILE__, __LINE__, __func__)
+# define free(p) unbound_stat_free_log(p, __FILE__, __LINE__, __func__)
+# define realloc(p,s) unbound_stat_realloc_log(p, s, __FILE__, __LINE__, __func__)
+void *unbound_stat_malloc(size_t size);
+void *unbound_stat_calloc(size_t nmemb, size_t size);
+void unbound_stat_free(void *ptr);
+void *unbound_stat_realloc(void *ptr, size_t size);
+void *unbound_stat_malloc_log(size_t size, const char* file, int line,
+ const char* func);
+void *unbound_stat_calloc_log(size_t nmemb, size_t size, const char* file,
+ int line, const char* func);
+void unbound_stat_free_log(void *ptr, const char* file, int line,
+ const char* func);
+void *unbound_stat_realloc_log(void *ptr, size_t size, const char* file,
+ int line, const char* func);
+#elif defined(UNBOUND_ALLOC_LITE)
+# include "util/alloc.h"
+#endif /* UNBOUND_ALLOC_LITE and UNBOUND_ALLOC_STATS */
+
+/** default port for DNS traffic. */
+#define UNBOUND_DNS_PORT 53
+/** default port for DNS over TLS traffic. */
+#define UNBOUND_DNS_OVER_TLS_PORT 853
+/** default port for DNS over HTTPS traffic. */
+#define UNBOUND_DNS_OVER_HTTPS_PORT 443
+/** default port for unbound control traffic, registered port with IANA,
+ ub-dns-control 8953/tcp unbound dns nameserver control */
+#define UNBOUND_CONTROL_PORT 8953
+/** the version of unbound-control that this software implements */
+#define UNBOUND_CONTROL_VERSION 1
+
+
diff --git a/contrib/unbound/config.sub b/contrib/unbound/config.sub
index 6ae25027537a..defe52c0c874 100755
--- a/contrib/unbound/config.sub
+++ b/contrib/unbound/config.sub
@@ -1,1895 +1,1960 @@
#! /bin/sh
# Configuration validation subroutine script.
# Copyright 1992-2023 Free Software Foundation, Inc.
# shellcheck disable=SC2006,SC2268 # see below for rationale
-timestamp='2023-07-31'
+timestamp='2023-09-19'
# 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
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <https://www.gnu.org/licenses/>.
#
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that
# program. This Exception is an additional permission under section 7
# of the GNU General Public License, version 3 ("GPLv3").
# Please send patches to <config-patches@gnu.org>.
#
# Configuration subroutine to validate and canonicalize a configuration type.
# Supply the specified configuration type as an argument.
# If it is invalid, we print an error message on stderr and exit with code 1.
# Otherwise, we print the canonical config type on stdout and succeed.
# You can get the latest version of this script from:
# https://git.savannah.gnu.org/cgit/config.git/plain/config.sub
# This file is supposed to be the same for all GNU packages
# and recognize all the CPU types, system types and aliases
# that are meaningful with *any* GNU software.
# Each package is responsible for reporting which valid configurations
# it does not support. The user should be able to distinguish
# a failure to support a valid configuration from a meaningless
# configuration.
# The goal of this file is to map all the various variations of a given
# machine specification into a single specification in the form:
# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
# or in some cases, the newer four-part form:
# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
# It is wrong to echo any other type of specification.
# The "shellcheck disable" line above the timestamp inhibits complaints
# about features and limitations of the classic Bourne shell that were
# superseded or lifted in POSIX. However, this script identifies a wide
# variety of pre-POSIX systems that do not have POSIX shells at all, and
# even some reasonably current systems (Solaris 10 as case-in-point) still
# have a pre-POSIX /bin/sh.
me=`echo "$0" | sed -e 's,.*/,,'`
usage="\
Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS
Canonicalize a configuration name.
Options:
-h, --help print this help, then exit
-t, --time-stamp print date of last modification, then exit
-v, --version print version number, then exit
Report bugs and patches to <config-patches@gnu.org>."
version="\
GNU config.sub ($timestamp)
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."
# Parse command line
while test $# -gt 0 ; do
case $1 in
--time-stamp | --time* | -t )
echo "$timestamp" ; exit ;;
--version | -v )
echo "$version" ; exit ;;
--help | --h* | -h )
echo "$usage"; exit ;;
-- ) # Stop option processing
shift; break ;;
- ) # Use stdin as input.
break ;;
-* )
echo "$me: invalid option $1$help" >&2
exit 1 ;;
*local*)
# First pass through any local machine types.
echo "$1"
exit ;;
* )
break ;;
esac
done
case $# in
0) echo "$me: missing argument$help" >&2
exit 1;;
1) ;;
*) echo "$me: too many arguments$help" >&2
exit 1;;
esac
# Split fields of configuration type
# shellcheck disable=SC2162
saved_IFS=$IFS
IFS="-" read field1 field2 field3 field4 <<EOF
$1
EOF
IFS=$saved_IFS
# Separate into logical components for further validation
case $1 in
*-*-*-*-*)
echo "Invalid configuration '$1': more than four components" >&2
exit 1
;;
*-*-*-*)
basic_machine=$field1-$field2
basic_os=$field3-$field4
;;
*-*-*)
# Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two
# parts
maybe_os=$field2-$field3
case $maybe_os in
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-* \
| windows-* )
basic_machine=$field1
basic_os=$maybe_os
;;
android-linux)
basic_machine=$field1-unknown
basic_os=linux-android
;;
*)
basic_machine=$field1-$field2
basic_os=$field3
;;
esac
;;
*-*)
# A lone config we happen to match not fitting any pattern
case $field1-$field2 in
decstation-3100)
basic_machine=mips-dec
basic_os=
;;
*-*)
# Second component is usually, but not always the OS
case $field2 in
# Prevent following clause from handling this valid os
sun*os*)
basic_machine=$field1
basic_os=$field2
;;
zephyr*)
basic_machine=$field1-unknown
basic_os=$field2
;;
# Manufacturers
dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \
| att* | 7300* | 3300* | delta* | motorola* | sun[234]* \
| unicom* | ibm* | next | hp | isi* | apollo | altos* \
| convergent* | ncr* | news | 32* | 3600* | 3100* \
| hitachi* | c[123]* | convex* | sun | crds | omron* | dg \
| ultra | tti* | harris | dolphin | highlevel | gould \
| cbm | ns | masscomp | apple | axis | knuth | cray \
| microblaze* | sim | cisco \
| oki | wec | wrs | winbond)
basic_machine=$field1-$field2
basic_os=
;;
*)
basic_machine=$field1
basic_os=$field2
;;
esac
;;
esac
;;
*)
# Convert single-component short-hands not valid as part of
# multi-component configurations.
case $field1 in
386bsd)
basic_machine=i386-pc
basic_os=bsd
;;
a29khif)
basic_machine=a29k-amd
basic_os=udi
;;
adobe68k)
basic_machine=m68010-adobe
basic_os=scout
;;
alliant)
basic_machine=fx80-alliant
basic_os=
;;
altos | altos3068)
basic_machine=m68k-altos
basic_os=
;;
am29k)
basic_machine=a29k-none
basic_os=bsd
;;
amdahl)
basic_machine=580-amdahl
basic_os=sysv
;;
amiga)
basic_machine=m68k-unknown
basic_os=
;;
amigaos | amigados)
basic_machine=m68k-unknown
basic_os=amigaos
;;
amigaunix | amix)
basic_machine=m68k-unknown
basic_os=sysv4
;;
apollo68)
basic_machine=m68k-apollo
basic_os=sysv
;;
apollo68bsd)
basic_machine=m68k-apollo
basic_os=bsd
;;
aros)
basic_machine=i386-pc
basic_os=aros
;;
aux)
basic_machine=m68k-apple
basic_os=aux
;;
balance)
basic_machine=ns32k-sequent
basic_os=dynix
;;
blackfin)
basic_machine=bfin-unknown
basic_os=linux
;;
cegcc)
basic_machine=arm-unknown
basic_os=cegcc
;;
convex-c1)
basic_machine=c1-convex
basic_os=bsd
;;
convex-c2)
basic_machine=c2-convex
basic_os=bsd
;;
convex-c32)
basic_machine=c32-convex
basic_os=bsd
;;
convex-c34)
basic_machine=c34-convex
basic_os=bsd
;;
convex-c38)
basic_machine=c38-convex
basic_os=bsd
;;
cray)
basic_machine=j90-cray
basic_os=unicos
;;
crds | unos)
basic_machine=m68k-crds
basic_os=
;;
da30)
basic_machine=m68k-da30
basic_os=
;;
decstation | pmax | pmin | dec3100 | decstatn)
basic_machine=mips-dec
basic_os=
;;
delta88)
basic_machine=m88k-motorola
basic_os=sysv3
;;
dicos)
basic_machine=i686-pc
basic_os=dicos
;;
djgpp)
basic_machine=i586-pc
basic_os=msdosdjgpp
;;
ebmon29k)
basic_machine=a29k-amd
basic_os=ebmon
;;
es1800 | OSE68k | ose68k | ose | OSE)
basic_machine=m68k-ericsson
basic_os=ose
;;
gmicro)
basic_machine=tron-gmicro
basic_os=sysv
;;
go32)
basic_machine=i386-pc
basic_os=go32
;;
h8300hms)
basic_machine=h8300-hitachi
basic_os=hms
;;
h8300xray)
basic_machine=h8300-hitachi
basic_os=xray
;;
h8500hms)
basic_machine=h8500-hitachi
basic_os=hms
;;
harris)
basic_machine=m88k-harris
basic_os=sysv3
;;
hp300 | hp300hpux)
basic_machine=m68k-hp
basic_os=hpux
;;
hp300bsd)
basic_machine=m68k-hp
basic_os=bsd
;;
hppaosf)
basic_machine=hppa1.1-hp
basic_os=osf
;;
hppro)
basic_machine=hppa1.1-hp
basic_os=proelf
;;
i386mach)
basic_machine=i386-mach
basic_os=mach
;;
isi68 | isi)
basic_machine=m68k-isi
basic_os=sysv
;;
m68knommu)
basic_machine=m68k-unknown
basic_os=linux
;;
magnum | m3230)
basic_machine=mips-mips
basic_os=sysv
;;
merlin)
basic_machine=ns32k-utek
basic_os=sysv
;;
mingw64)
basic_machine=x86_64-pc
basic_os=mingw64
;;
mingw32)
basic_machine=i686-pc
basic_os=mingw32
;;
mingw32ce)
basic_machine=arm-unknown
basic_os=mingw32ce
;;
monitor)
basic_machine=m68k-rom68k
basic_os=coff
;;
morphos)
basic_machine=powerpc-unknown
basic_os=morphos
;;
moxiebox)
basic_machine=moxie-unknown
basic_os=moxiebox
;;
msdos)
basic_machine=i386-pc
basic_os=msdos
;;
msys)
basic_machine=i686-pc
basic_os=msys
;;
mvs)
basic_machine=i370-ibm
basic_os=mvs
;;
nacl)
basic_machine=le32-unknown
basic_os=nacl
;;
ncr3000)
basic_machine=i486-ncr
basic_os=sysv4
;;
netbsd386)
basic_machine=i386-pc
basic_os=netbsd
;;
netwinder)
basic_machine=armv4l-rebel
basic_os=linux
;;
news | news700 | news800 | news900)
basic_machine=m68k-sony
basic_os=newsos
;;
news1000)
basic_machine=m68030-sony
basic_os=newsos
;;
necv70)
basic_machine=v70-nec
basic_os=sysv
;;
nh3000)
basic_machine=m68k-harris
basic_os=cxux
;;
nh[45]000)
basic_machine=m88k-harris
basic_os=cxux
;;
nindy960)
basic_machine=i960-intel
basic_os=nindy
;;
mon960)
basic_machine=i960-intel
basic_os=mon960
;;
nonstopux)
basic_machine=mips-compaq
basic_os=nonstopux
;;
os400)
basic_machine=powerpc-ibm
basic_os=os400
;;
OSE68000 | ose68000)
basic_machine=m68000-ericsson
basic_os=ose
;;
os68k)
basic_machine=m68k-none
basic_os=os68k
;;
paragon)
basic_machine=i860-intel
basic_os=osf
;;
parisc)
basic_machine=hppa-unknown
basic_os=linux
;;
psp)
basic_machine=mipsallegrexel-sony
basic_os=psp
;;
pw32)
basic_machine=i586-unknown
basic_os=pw32
;;
rdos | rdos64)
basic_machine=x86_64-pc
basic_os=rdos
;;
rdos32)
basic_machine=i386-pc
basic_os=rdos
;;
rom68k)
basic_machine=m68k-rom68k
basic_os=coff
;;
sa29200)
basic_machine=a29k-amd
basic_os=udi
;;
sei)
basic_machine=mips-sei
basic_os=seiux
;;
sequent)
basic_machine=i386-sequent
basic_os=
;;
sps7)
basic_machine=m68k-bull
basic_os=sysv2
;;
st2000)
basic_machine=m68k-tandem
basic_os=
;;
stratus)
basic_machine=i860-stratus
basic_os=sysv4
;;
sun2)
basic_machine=m68000-sun
basic_os=
;;
sun2os3)
basic_machine=m68000-sun
basic_os=sunos3
;;
sun2os4)
basic_machine=m68000-sun
basic_os=sunos4
;;
sun3)
basic_machine=m68k-sun
basic_os=
;;
sun3os3)
basic_machine=m68k-sun
basic_os=sunos3
;;
sun3os4)
basic_machine=m68k-sun
basic_os=sunos4
;;
sun4)
basic_machine=sparc-sun
basic_os=
;;
sun4os3)
basic_machine=sparc-sun
basic_os=sunos3
;;
sun4os4)
basic_machine=sparc-sun
basic_os=sunos4
;;
sun4sol2)
basic_machine=sparc-sun
basic_os=solaris2
;;
sun386 | sun386i | roadrunner)
basic_machine=i386-sun
basic_os=
;;
sv1)
basic_machine=sv1-cray
basic_os=unicos
;;
symmetry)
basic_machine=i386-sequent
basic_os=dynix
;;
t3e)
basic_machine=alphaev5-cray
basic_os=unicos
;;
t90)
basic_machine=t90-cray
basic_os=unicos
;;
toad1)
basic_machine=pdp10-xkl
basic_os=tops20
;;
tpf)
basic_machine=s390x-ibm
basic_os=tpf
;;
udi29k)
basic_machine=a29k-amd
basic_os=udi
;;
ultra3)
basic_machine=a29k-nyu
basic_os=sym1
;;
v810 | necv810)
basic_machine=v810-nec
basic_os=none
;;
vaxv)
basic_machine=vax-dec
basic_os=sysv
;;
vms)
basic_machine=vax-dec
basic_os=vms
;;
vsta)
basic_machine=i386-pc
basic_os=vsta
;;
vxworks960)
basic_machine=i960-wrs
basic_os=vxworks
;;
vxworks68)
basic_machine=m68k-wrs
basic_os=vxworks
;;
vxworks29k)
basic_machine=a29k-wrs
basic_os=vxworks
;;
xbox)
basic_machine=i686-pc
basic_os=mingw32
;;
ymp)
basic_machine=ymp-cray
basic_os=unicos
;;
*)
basic_machine=$1
basic_os=
;;
esac
;;
esac
# Decode 1-component or ad-hoc basic machines
case $basic_machine in
# Here we handle the default manufacturer of certain CPU types. It is in
# some cases the only manufacturer, in others, it is the most popular.
w89k)
cpu=hppa1.1
vendor=winbond
;;
op50n)
cpu=hppa1.1
vendor=oki
;;
op60c)
cpu=hppa1.1
vendor=oki
;;
ibm*)
cpu=i370
vendor=ibm
;;
orion105)
cpu=clipper
vendor=highlevel
;;
mac | mpw | mac-mpw)
cpu=m68k
vendor=apple
;;
pmac | pmac-mpw)
cpu=powerpc
vendor=apple
;;
# Recognize the various machine names and aliases which stand
# for a CPU type and a company and sometimes even an OS.
3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
cpu=m68000
vendor=att
;;
3b*)
cpu=we32k
vendor=att
;;
bluegene*)
cpu=powerpc
vendor=ibm
basic_os=cnk
;;
decsystem10* | dec10*)
cpu=pdp10
vendor=dec
basic_os=tops10
;;
decsystem20* | dec20*)
cpu=pdp10
vendor=dec
basic_os=tops20
;;
delta | 3300 | motorola-3300 | motorola-delta \
| 3300-motorola | delta-motorola)
cpu=m68k
vendor=motorola
;;
dpx2*)
cpu=m68k
vendor=bull
basic_os=sysv3
;;
encore | umax | mmax)
cpu=ns32k
vendor=encore
;;
elxsi)
cpu=elxsi
vendor=elxsi
basic_os=${basic_os:-bsd}
;;
fx2800)
cpu=i860
vendor=alliant
;;
genix)
cpu=ns32k
vendor=ns
;;
h3050r* | hiux*)
cpu=hppa1.1
vendor=hitachi
basic_os=hiuxwe2
;;
hp3k9[0-9][0-9] | hp9[0-9][0-9])
cpu=hppa1.0
vendor=hp
;;
hp9k2[0-9][0-9] | hp9k31[0-9])
cpu=m68000
vendor=hp
;;
hp9k3[2-9][0-9])
cpu=m68k
vendor=hp
;;
hp9k6[0-9][0-9] | hp6[0-9][0-9])
cpu=hppa1.0
vendor=hp
;;
hp9k7[0-79][0-9] | hp7[0-79][0-9])
cpu=hppa1.1
vendor=hp
;;
hp9k78[0-9] | hp78[0-9])
# FIXME: really hppa2.0-hp
cpu=hppa1.1
vendor=hp
;;
hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
# FIXME: really hppa2.0-hp
cpu=hppa1.1
vendor=hp
;;
hp9k8[0-9][13679] | hp8[0-9][13679])
cpu=hppa1.1
vendor=hp
;;
hp9k8[0-9][0-9] | hp8[0-9][0-9])
cpu=hppa1.0
vendor=hp
;;
i*86v32)
cpu=`echo "$1" | sed -e 's/86.*/86/'`
vendor=pc
basic_os=sysv32
;;
i*86v4*)
cpu=`echo "$1" | sed -e 's/86.*/86/'`
vendor=pc
basic_os=sysv4
;;
i*86v)
cpu=`echo "$1" | sed -e 's/86.*/86/'`
vendor=pc
basic_os=sysv
;;
i*86sol2)
cpu=`echo "$1" | sed -e 's/86.*/86/'`
vendor=pc
basic_os=solaris2
;;
j90 | j90-cray)
cpu=j90
vendor=cray
basic_os=${basic_os:-unicos}
;;
iris | iris4d)
cpu=mips
vendor=sgi
case $basic_os in
irix*)
;;
*)
basic_os=irix4
;;
esac
;;
miniframe)
cpu=m68000
vendor=convergent
;;
*mint | mint[0-9]* | *MiNT | *MiNT[0-9]*)
cpu=m68k
vendor=atari
basic_os=mint
;;
news-3600 | risc-news)
cpu=mips
vendor=sony
basic_os=newsos
;;
next | m*-next)
cpu=m68k
vendor=next
case $basic_os in
openstep*)
;;
nextstep*)
;;
ns2*)
basic_os=nextstep2
;;
*)
basic_os=nextstep3
;;
esac
;;
np1)
cpu=np1
vendor=gould
;;
op50n-* | op60c-*)
cpu=hppa1.1
vendor=oki
basic_os=proelf
;;
pa-hitachi)
cpu=hppa1.1
vendor=hitachi
basic_os=hiuxwe2
;;
pbd)
cpu=sparc
vendor=tti
;;
pbb)
cpu=m68k
vendor=tti
;;
pc532)
cpu=ns32k
vendor=pc532
;;
pn)
cpu=pn
vendor=gould
;;
power)
cpu=power
vendor=ibm
;;
ps2)
cpu=i386
vendor=ibm
;;
rm[46]00)
cpu=mips
vendor=siemens
;;
rtpc | rtpc-*)
cpu=romp
vendor=ibm
;;
sde)
cpu=mipsisa32
vendor=sde
basic_os=${basic_os:-elf}
;;
simso-wrs)
cpu=sparclite
vendor=wrs
basic_os=vxworks
;;
tower | tower-32)
cpu=m68k
vendor=ncr
;;
vpp*|vx|vx-*)
cpu=f301
vendor=fujitsu
;;
w65)
cpu=w65
vendor=wdc
;;
w89k-*)
cpu=hppa1.1
vendor=winbond
basic_os=proelf
;;
none)
cpu=none
vendor=none
;;
leon|leon[3-9])
cpu=sparc
vendor=$basic_machine
;;
leon-*|leon[3-9]-*)
cpu=sparc
vendor=`echo "$basic_machine" | sed 's/-.*//'`
;;
*-*)
# shellcheck disable=SC2162
saved_IFS=$IFS
IFS="-" read cpu vendor <<EOF
$basic_machine
EOF
IFS=$saved_IFS
;;
# 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)
cpu=$basic_machine
vendor=pc
;;
# These rules are duplicated from below for sake of the special case above;
# i.e. things that normalized to x86 arches should also default to "pc"
pc98)
cpu=i386
vendor=pc
;;
x64 | amd64)
cpu=x86_64
vendor=pc
;;
# Recognize the basic CPU types without company name.
*)
cpu=$basic_machine
vendor=unknown
;;
esac
unset -v basic_machine
# Decode basic machines in the full and proper CPU-Company form.
case $cpu-$vendor in
# Here we handle the default manufacturer of certain CPU types in canonical form. It is in
# some cases the only manufacturer, in others, it is the most popular.
craynv-unknown)
vendor=cray
basic_os=${basic_os:-unicosmp}
;;
c90-unknown | c90-cray)
vendor=cray
basic_os=${Basic_os:-unicos}
;;
fx80-unknown)
vendor=alliant
;;
romp-unknown)
vendor=ibm
;;
mmix-unknown)
vendor=knuth
;;
microblaze-unknown | microblazeel-unknown)
vendor=xilinx
;;
rs6000-unknown)
vendor=ibm
;;
vax-unknown)
vendor=dec
;;
pdp11-unknown)
vendor=dec
;;
we32k-unknown)
vendor=att
;;
cydra-unknown)
vendor=cydrome
;;
i370-ibm*)
vendor=ibm
;;
orion-unknown)
vendor=highlevel
;;
xps-unknown | xps100-unknown)
cpu=xps100
vendor=honeywell
;;
# Here we normalize CPU types with a missing or matching vendor
armh-unknown | armh-alt)
cpu=armv7l
vendor=alt
basic_os=${basic_os:-linux-gnueabihf}
;;
dpx20-unknown | dpx20-bull)
cpu=rs6000
vendor=bull
basic_os=${basic_os:-bosx}
;;
# Here we normalize CPU types irrespective of the vendor
amd64-*)
cpu=x86_64
;;
blackfin-*)
cpu=bfin
basic_os=linux
;;
c54x-*)
cpu=tic54x
;;
c55x-*)
cpu=tic55x
;;
c6x-*)
cpu=tic6x
;;
e500v[12]-*)
cpu=powerpc
basic_os=${basic_os}"spe"
;;
mips3*-*)
cpu=mips64
;;
ms1-*)
cpu=mt
;;
m68knommu-*)
cpu=m68k
basic_os=linux
;;
m9s12z-* | m68hcs12z-* | hcs12z-* | s12z-*)
cpu=s12z
;;
openrisc-*)
cpu=or32
;;
parisc-*)
cpu=hppa
basic_os=linux
;;
pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
cpu=i586
;;
pentiumpro-* | p6-* | 6x86-* | athlon-* | athlon_*-*)
cpu=i686
;;
pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
cpu=i686
;;
pentium4-*)
cpu=i786
;;
pc98-*)
cpu=i386
;;
ppc-* | ppcbe-*)
cpu=powerpc
;;
ppcle-* | powerpclittle-*)
cpu=powerpcle
;;
ppc64-*)
cpu=powerpc64
;;
ppc64le-* | powerpc64little-*)
cpu=powerpc64le
;;
sb1-*)
cpu=mipsisa64sb1
;;
sb1el-*)
cpu=mipsisa64sb1el
;;
sh5e[lb]-*)
cpu=`echo "$cpu" | sed 's/^\(sh.\)e\(.\)$/\1\2e/'`
;;
spur-*)
cpu=spur
;;
strongarm-* | thumb-*)
cpu=arm
;;
tx39-*)
cpu=mipstx39
;;
tx39el-*)
cpu=mipstx39el
;;
x64-*)
cpu=x86_64
;;
xscale-* | xscalee[bl]-*)
cpu=`echo "$cpu" | sed 's/^xscale/arm/'`
;;
arm64-* | aarch64le-*)
cpu=aarch64
;;
# Recognize the canonical CPU Types that limit and/or modify the
# company names they are paired with.
cr16-*)
basic_os=${basic_os:-elf}
;;
crisv32-* | etraxfs*-*)
cpu=crisv32
vendor=axis
;;
cris-* | etrax*-*)
cpu=cris
vendor=axis
;;
crx-*)
basic_os=${basic_os:-elf}
;;
neo-tandem)
cpu=neo
vendor=tandem
;;
nse-tandem)
cpu=nse
vendor=tandem
;;
nsr-tandem)
cpu=nsr
vendor=tandem
;;
nsv-tandem)
cpu=nsv
vendor=tandem
;;
nsx-tandem)
cpu=nsx
vendor=tandem
;;
mipsallegrexel-sony)
cpu=mipsallegrexel
vendor=sony
;;
tile*-*)
basic_os=${basic_os:-linux-gnu}
;;
*)
# Recognize the canonical CPU types that are allowed with any
# company name.
case $cpu in
1750a | 580 \
| a29k \
- | aarch64 | aarch64_be \
+ | aarch64 | aarch64_be | aarch64c | arm64ec \
| abacus \
| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] \
| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] \
| alphapca5[67] | alpha64pca5[67] \
| am33_2.0 \
| amdgcn \
| arc | arceb | arc32 | arc64 \
| arm | arm[lb]e | arme[lb] | armv* \
| avr | avr32 \
| asmjs \
| ba \
| be32 | be64 \
| bfin | bpf | bs2000 \
| c[123]* | c30 | [cjt]90 | c4x \
| c8051 | clipper | craynv | csky | cydra \
| d10v | d30v | dlx | dsp16xx \
| e2k | elxsi | epiphany \
| f30[01] | f700 | fido | fr30 | frv | ft32 | fx80 \
+ | javascript \
| h8300 | h8500 \
| hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
| hexagon \
| i370 | i*86 | i860 | i960 | ia16 | ia64 \
| ip2k | iq2000 \
| k1om \
| kvx \
| le32 | le64 \
| lm32 \
| loongarch32 | loongarch64 \
| m32c | m32r | m32rle \
| m5200 | m68000 | m680[012346]0 | m68360 | m683?2 | m68k \
| m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x \
| m88110 | m88k | maxq | mb | mcore | mep | metag \
| microblaze | microblazeel \
| mips* \
| mmix \
| mn10200 | mn10300 \
| moxie \
| mt \
| msp430 \
| nds32 | nds32le | nds32be \
| nfp \
| nios | nios2 | nios2eb | nios2el \
| none | np1 | ns16k | ns32k | nvptx \
| open8 \
| or1k* \
| or32 \
| orion \
| picochip \
| pdp10 | pdp11 | pj | pjl | pn | power \
| powerpc | powerpc64 | powerpc64le | powerpcle | powerpcspe \
| pru \
| pyramid \
| riscv | riscv32 | riscv32be | riscv64 | riscv64be \
| rl78 | romp | rs6000 | rx \
| s390 | s390x \
| score \
| sh | shl \
| sh[1234] | sh[24]a | sh[24]ae[lb] | sh[23]e | she[lb] | sh[lb]e \
| sh[1234]e[lb] | sh[12345][lb]e | sh[23]ele | sh64 | sh64le \
| sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet \
| sparclite \
| sparcv8 | sparcv9 | sparcv9b | sparcv9v | sv1 | sx* \
| spu \
| tahoe \
| thumbv7* \
| tic30 | tic4x | tic54x | tic55x | tic6x | tic80 \
| tron \
| ubicom32 \
| v70 | v850 | v850e | v850e1 | v850es | v850e2 | v850e2v3 \
| vax \
| visium \
| w65 \
| wasm32 | wasm64 \
| we32k \
| x86 | x86_64 | xc16x | xgate | xps100 \
| xstormy16 | xtensa* \
| ymp \
| z8k | z80)
;;
*)
echo "Invalid configuration '$1': machine '$cpu-$vendor' not recognized" 1>&2
exit 1
;;
esac
;;
esac
# Here we canonicalize certain aliases for manufacturers.
case $vendor in
digital*)
vendor=dec
;;
commodore*)
vendor=cbm
;;
*)
;;
esac
# Decode manufacturer-specific aliases for certain operating systems.
-if test x$basic_os != x
+if test x"$basic_os" != x
then
# First recognize some ad-hoc cases, or perhaps split kernel-os, or else just
# set os.
+obj=
case $basic_os in
gnu/linux*)
kernel=linux
os=`echo "$basic_os" | sed -e 's|gnu/linux|gnu|'`
;;
os2-emx)
kernel=os2
os=`echo "$basic_os" | sed -e 's|os2-emx|emx|'`
;;
nto-qnx*)
kernel=nto
os=`echo "$basic_os" | sed -e 's|nto-qnx|qnx|'`
;;
*-*)
# shellcheck disable=SC2162
saved_IFS=$IFS
IFS="-" read kernel os <<EOF
$basic_os
EOF
IFS=$saved_IFS
;;
# Default OS when just kernel was specified
nto*)
kernel=nto
os=`echo "$basic_os" | sed -e 's|nto|qnx|'`
;;
linux*)
kernel=linux
os=`echo "$basic_os" | sed -e 's|linux|gnu|'`
;;
managarm*)
kernel=managarm
os=`echo "$basic_os" | sed -e 's|managarm|mlibc|'`
;;
*)
kernel=
os=$basic_os
;;
esac
# Now, normalize the OS (knowing we just have one component, it's not a kernel,
# etc.)
case $os in
# First match some system type aliases that might get confused
# with valid system types.
# solaris* is a basic system type, with this one exception.
auroraux)
os=auroraux
;;
bluegene*)
os=cnk
;;
solaris1 | solaris1.*)
os=`echo "$os" | sed -e 's|solaris1|sunos4|'`
;;
solaris)
os=solaris2
;;
unixware*)
os=sysv4.2uw
;;
# es1800 is here to avoid being matched by es* (a different OS)
es1800*)
os=ose
;;
# Some version numbers need modification
chorusos*)
os=chorusos
;;
isc)
os=isc2.2
;;
sco6)
os=sco5v6
;;
sco5)
os=sco3.2v5
;;
sco4)
os=sco3.2v4
;;
sco3.2.[4-9]*)
os=`echo "$os" | sed -e 's/sco3.2./sco3.2v/'`
;;
sco*v* | scout)
# Don't match below
;;
sco*)
os=sco3.2v2
;;
psos*)
os=psos
;;
qnx*)
os=qnx
;;
hiux*)
os=hiuxwe2
;;
lynx*178)
os=lynxos178
;;
lynx*5)
os=lynxos5
;;
lynxos*)
# don't get caught up in next wildcard
;;
lynx*)
os=lynxos
;;
mac[0-9]*)
os=`echo "$os" | sed -e 's|mac|macos|'`
;;
opened*)
os=openedition
;;
os400*)
os=os400
;;
sunos5*)
os=`echo "$os" | sed -e 's|sunos5|solaris2|'`
;;
sunos6*)
os=`echo "$os" | sed -e 's|sunos6|solaris3|'`
;;
wince*)
os=wince
;;
utek*)
os=bsd
;;
dynix*)
os=bsd
;;
acis*)
os=aos
;;
atheos*)
os=atheos
;;
syllable*)
os=syllable
;;
386bsd)
os=bsd
;;
ctix* | uts*)
os=sysv
;;
nova*)
os=rtmk-nova
;;
ns2)
os=nextstep2
;;
# Preserve the version number of sinix5.
sinix5.*)
os=`echo "$os" | sed -e 's|sinix|sysv|'`
;;
sinix*)
os=sysv4
;;
tpf*)
os=tpf
;;
triton*)
os=sysv3
;;
oss*)
os=sysv3
;;
svr4*)
os=sysv4
;;
svr3)
os=sysv3
;;
sysvr4)
os=sysv4
;;
ose*)
os=ose
;;
*mint | mint[0-9]* | *MiNT | MiNT[0-9]*)
os=mint
;;
dicos*)
os=dicos
;;
pikeos*)
# Until real need of OS specific support for
# particular features comes up, bare metal
# configurations are quite functional.
case $cpu in
arm*)
os=eabi
;;
*)
- os=elf
+ os=
+ obj=elf
;;
esac
;;
+ aout* | coff* | elf* | pe*)
+ # These are machine code file formats, not OSes
+ obj=$os
+ os=
+ ;;
*)
# No normalization, but not necessarily accepted, that comes below.
;;
esac
else
# Here we handle the default operating systems that come with various machines.
# The value should be what the vendor currently ships out the door with their
# machine or put another way, the most popular os provided with the machine.
# Note that if you're going to try to match "-MANUFACTURER" here (say,
# "-sun"), then you have to tell the case statement up towards the top
# that MANUFACTURER isn't an operating system. Otherwise, code above
# will signal an error saying that MANUFACTURER isn't an operating
# system, and we'll never get to this point.
kernel=
+obj=
case $cpu-$vendor in
score-*)
- os=elf
+ os=
+ obj=elf
;;
spu-*)
- os=elf
+ os=
+ obj=elf
;;
*-acorn)
os=riscix1.2
;;
arm*-rebel)
kernel=linux
os=gnu
;;
arm*-semi)
- os=aout
+ os=
+ obj=aout
;;
c4x-* | tic4x-*)
- os=coff
+ os=
+ obj=coff
;;
c8051-*)
- os=elf
+ os=
+ obj=elf
;;
clipper-intergraph)
os=clix
;;
hexagon-*)
- os=elf
+ os=
+ obj=elf
;;
tic54x-*)
- os=coff
+ os=
+ obj=coff
;;
tic55x-*)
- os=coff
+ os=
+ obj=coff
;;
tic6x-*)
- os=coff
+ os=
+ obj=coff
;;
# This must come before the *-dec entry.
pdp10-*)
os=tops20
;;
pdp11-*)
os=none
;;
*-dec | vax-*)
os=ultrix4.2
;;
m68*-apollo)
os=domain
;;
i386-sun)
os=sunos4.0.2
;;
m68000-sun)
os=sunos3
;;
m68*-cisco)
- os=aout
+ os=
+ obj=aout
;;
mep-*)
- os=elf
+ os=
+ obj=elf
;;
mips*-cisco)
- os=elf
+ os=
+ obj=elf
;;
mips*-*)
- os=elf
+ os=
+ obj=elf
;;
or32-*)
- os=coff
+ os=
+ obj=coff
;;
*-tti) # must be before sparc entry or we get the wrong os.
os=sysv3
;;
sparc-* | *-sun)
os=sunos4.1.1
;;
pru-*)
- os=elf
+ os=
+ obj=elf
;;
*-be)
os=beos
;;
*-ibm)
os=aix
;;
*-knuth)
os=mmixware
;;
*-wec)
os=proelf
;;
*-winbond)
os=proelf
;;
*-oki)
os=proelf
;;
*-hp)
os=hpux
;;
*-hitachi)
os=hiux
;;
i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
os=sysv
;;
*-cbm)
os=amigaos
;;
*-dg)
os=dgux
;;
*-dolphin)
os=sysv3
;;
m68k-ccur)
os=rtu
;;
m88k-omron*)
os=luna
;;
*-next)
os=nextstep
;;
*-sequent)
os=ptx
;;
*-crds)
os=unos
;;
*-ns)
os=genix
;;
i370-*)
os=mvs
;;
*-gould)
os=sysv
;;
*-highlevel)
os=bsd
;;
*-encore)
os=bsd
;;
*-sgi)
os=irix
;;
*-siemens)
os=sysv4
;;
*-masscomp)
os=rtu
;;
f30[01]-fujitsu | f700-fujitsu)
os=uxpv
;;
*-rom68k)
- os=coff
+ os=
+ obj=coff
;;
*-*bug)
- os=coff
+ os=
+ obj=coff
;;
*-apple)
os=macos
;;
*-atari*)
os=mint
;;
*-wrs)
os=vxworks
;;
*)
os=none
;;
esac
fi
-# Now, validate our (potentially fixed-up) OS.
+# Now, validate our (potentially fixed-up) individual pieces (OS, OBJ).
+
case $os in
# Sometimes we do "kernel-libc", so those need to count as OSes.
musl* | newlib* | relibc* | uclibc*)
;;
# Likewise for "kernel-abi"
eabi* | gnueabi*)
;;
# VxWorks passes extra cpu info in the 4th filed.
simlinux | simwindows | spe)
;;
+ # See `case $cpu-$os` validation below
+ ghcjs)
+ ;;
# Now accept the basic system types.
# The portable systems comes first.
# Each alternative MUST end in a * to match a version number.
gnu* | android* | bsd* | mach* | minix* | genix* | ultrix* | irix* \
| *vms* | esix* | aix* | cnk* | sunos | sunos[34]* \
| hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \
| sym* | plan9* | psp* | sim* | xray* | os68k* | v88r* \
| hiux* | abug | nacl* | netware* | windows* \
| os9* | macos* | osx* | ios* | tvos* | watchos* \
| mpw* | magic* | mmixware* | mon960* | lnews* \
| amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \
| aos* | aros* | cloudabi* | sortix* | twizzler* \
| nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \
| clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \
| mirbsd* | netbsd* | dicos* | openedition* | ose* \
| bitrig* | openbsd* | secbsd* | solidbsd* | libertybsd* | os108* \
| ekkobsd* | freebsd* | riscix* | lynxos* | os400* \
- | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \
- | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \
+ | bosx* | nextstep* | cxux* | oabi* \
+ | ptx* | ecoff* | winnt* | domain* | vsta* \
| udi* | lites* | ieee* | go32* | aux* | hcos* \
| chorusrdb* | cegcc* | glidix* | serenity* \
- | cygwin* | msys* | pe* | moss* | proelf* | rtems* \
+ | cygwin* | msys* | moss* | proelf* | rtems* \
| midipix* | mingw32* | mingw64* | mint* \
| uxpv* | beos* | mpeix* | udk* | moxiebox* \
| interix* | uwin* | mks* | rhapsody* | darwin* \
| openstep* | oskit* | conix* | pw32* | nonstopux* \
| storm-chaos* | tops10* | tenex* | tops20* | its* \
| os2* | vos* | palmos* | uclinux* | nucleus* | morphos* \
| scout* | superux* | sysv* | rtmk* | tpf* | windiss* \
| powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \
| skyos* | haiku* | rdos* | toppers* | drops* | es* \
| onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \
| midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \
| nsk* | powerunix* | genode* | zvmoe* | qnx* | emx* | zephyr* \
| fiwix* | mlibc* | cos* | mbr* )
;;
# This one is extra strict with allowed versions
sco3.2v2 | sco3.2v[4-9]* | sco5v6*)
# Don't forget version if it is 3.2v4 or newer.
;;
none)
;;
kernel* | msvc* )
# Restricted further below
;;
+ '')
+ if test x"$obj" = x
+ then
+ echo "Invalid configuration '$1': Blank OS only allowed with explicit machine code file format" 1>&2
+ fi
+ ;;
*)
echo "Invalid configuration '$1': OS '$os' not recognized" 1>&2
exit 1
;;
esac
+case $obj in
+ aout* | coff* | elf* | pe*)
+ ;;
+ '')
+ # empty is fine
+ ;;
+ *)
+ echo "Invalid configuration '$1': Machine code format '$obj' not recognized" 1>&2
+ exit 1
+ ;;
+esac
+
+# Here we handle the constraint that a (synthetic) cpu and os are
+# valid only in combination with each other and nowhere else.
+case $cpu-$os in
+ # The "javascript-unknown-ghcjs" triple is used by GHC; we
+ # accept it here in order to tolerate that, but reject any
+ # variations.
+ javascript-ghcjs)
+ ;;
+ javascript-* | *-ghcjs)
+ echo "Invalid configuration '$1': cpu '$cpu' is not valid with os '$os$obj'" 1>&2
+ exit 1
+ ;;
+esac
+
# As a final step for OS-related things, validate the OS-kernel combination
# (given a valid OS), if there is a kernel.
-case $kernel-$os in
- linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* \
- | linux-musl* | linux-relibc* | linux-uclibc* | linux-mlibc* )
+case $kernel-$os-$obj in
+ linux-gnu*- | linux-dietlibc*- | linux-android*- | linux-newlib*- \
+ | linux-musl*- | linux-relibc*- | linux-uclibc*- | linux-mlibc*- )
;;
- uclinux-uclibc* )
+ uclinux-uclibc*- )
;;
- managarm-mlibc* | managarm-kernel* )
+ managarm-mlibc*- | managarm-kernel*- )
;;
- windows*-gnu* | windows*-msvc*)
+ windows*-msvc*-)
;;
- -dietlibc* | -newlib* | -musl* | -relibc* | -uclibc* | -mlibc* )
+ -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
exit 1
;;
- -kernel* )
+ -kernel*- )
echo "Invalid configuration '$1': '$os' needs explicit kernel." 1>&2
exit 1
;;
- *-kernel* )
+ *-kernel*- )
echo "Invalid configuration '$1': '$kernel' does not support '$os'." 1>&2
exit 1
;;
- *-msvc* )
+ *-msvc*- )
echo "Invalid configuration '$1': '$os' needs 'windows'." 1>&2
exit 1
;;
- kfreebsd*-gnu* | kopensolaris*-gnu*)
+ kfreebsd*-gnu*- | kopensolaris*-gnu*-)
;;
- vxworks-simlinux | vxworks-simwindows | vxworks-spe)
+ vxworks-simlinux- | vxworks-simwindows- | vxworks-spe-)
;;
- nto-qnx*)
+ nto-qnx*-)
;;
- os2-emx)
+ os2-emx-)
;;
- *-eabi* | *-gnueabi*)
+ *-eabi*- | *-gnueabi*-)
;;
- none-coff* | none-elf*)
+ none--*)
# None (no kernel, i.e. freestanding / bare metal),
- # can be paired with an output format "OS"
+ # can be paired with an machine code file format
;;
- -*)
+ -*-)
# Blank kernel with real OS is always fine.
;;
- *-*)
+ --*)
+ # Blank kernel and OS with real machine code file format is always fine.
+ ;;
+ *-*-*)
echo "Invalid configuration '$1': Kernel '$kernel' not known to work with OS '$os'." 1>&2
exit 1
;;
esac
# Here we handle the case where we know the os, and the CPU type, but not the
# manufacturer. We pick the logical manufacturer.
case $vendor in
unknown)
case $cpu-$os in
*-riscix*)
vendor=acorn
;;
*-sunos*)
vendor=sun
;;
*-cnk* | *-aix*)
vendor=ibm
;;
*-beos*)
vendor=be
;;
*-hpux*)
vendor=hp
;;
*-mpeix*)
vendor=hp
;;
*-hiux*)
vendor=hitachi
;;
*-unos*)
vendor=crds
;;
*-dgux*)
vendor=dg
;;
*-luna*)
vendor=omron
;;
*-genix*)
vendor=ns
;;
*-clix*)
vendor=intergraph
;;
*-mvs* | *-opened*)
vendor=ibm
;;
*-os400*)
vendor=ibm
;;
s390-* | s390x-*)
vendor=ibm
;;
*-ptx*)
vendor=sequent
;;
*-tpf*)
vendor=ibm
;;
*-vxsim* | *-vxworks* | *-windiss*)
vendor=wrs
;;
*-aux*)
vendor=apple
;;
*-hms*)
vendor=hitachi
;;
*-mpw* | *-macos*)
vendor=apple
;;
*-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*)
vendor=atari
;;
*-vos*)
vendor=stratus
;;
esac
;;
esac
-echo "$cpu-$vendor-${kernel:+$kernel-}$os"
+echo "$cpu-$vendor${kernel:+-$kernel}${os:+-$os}${obj:+-$obj}"
exit
# Local variables:
# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "timestamp='"
# time-stamp-format: "%:y-%02m-%02d"
# time-stamp-end: "'"
# End:
diff --git a/contrib/unbound/configure b/contrib/unbound/configure
index a77094ff7671..fbe6f8697742 100755
--- a/contrib/unbound/configure
+++ b/contrib/unbound/configure
@@ -1,24301 +1,24047 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for unbound 1.18.0.
+# Generated by GNU Autoconf 2.69 for unbound 1.19.0.
#
# Report bugs to <unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues>.
#
#
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
#
#
# This configure script is free software; the Free Software Foundation
# gives unlimited permission to copy, distribute and modify it.
## -------------------- ##
## M4sh Initialization. ##
## -------------------- ##
# Be more Bourne compatible
DUALCASE=1; export DUALCASE # for MKS sh
if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
emulate sh
NULLCMD=:
# Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
# is contrary to our usage. Disable this feature.
alias -g '${1+"$@"}'='"$@"'
setopt NO_GLOB_SUBST
else
case `(set -o) 2>/dev/null` in #(
*posix*) :
set -o posix ;; #(
*) :
;;
esac
fi
as_nl='
'
export as_nl
# Printing a long string crashes Solaris 7 /usr/bin/printf.
as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
# Prefer a ksh shell builtin over an external printf program on Solaris,
# but without wasting forks for bash or zsh.
if test -z "$BASH_VERSION$ZSH_VERSION" \
&& (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
as_echo='print -r --'
as_echo_n='print -rn --'
elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
as_echo='printf %s\n'
as_echo_n='printf %s'
else
if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
as_echo_n='/usr/ucb/echo -n'
else
as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
as_echo_n_body='eval
arg=$1;
case $arg in #(
*"$as_nl"*)
expr "X$arg" : "X\\(.*\\)$as_nl";
arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
esac;
expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
'
export as_echo_n_body
as_echo_n='sh -c $as_echo_n_body as_echo'
fi
export as_echo_body
as_echo='sh -c $as_echo_body as_echo'
fi
# The user is always right.
if test "${PATH_SEPARATOR+set}" != set; then
PATH_SEPARATOR=:
(PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
(PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
PATH_SEPARATOR=';'
}
fi
# IFS
# We need space, tab and new line, in precisely that order. Quoting is
# there to prevent editors from complaining about space-tab.
# (If _AS_PATH_WALK were called with IFS unset, it would disable word
# splitting by setting IFS to empty value.)
IFS=" "" $as_nl"
# Find who we are. Look in the path if we contain no directory separator.
as_myself=
case $0 in #((
*[\\/]* ) as_myself=$0 ;;
*) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
done
IFS=$as_save_IFS
;;
esac
# We did not find ourselves, most probably we were run as `sh COMMAND'
# in which case we are not to be found in the path.
if test "x$as_myself" = x; then
as_myself=$0
fi
if test ! -f "$as_myself"; then
$as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
exit 1
fi
# Unset variables that we do not need and which cause bugs (e.g. in
# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
# suppresses any "Segmentation fault" message there. '((' could
# trigger a bug in pdksh 5.2.14.
for as_var in BASH_ENV ENV MAIL MAILPATH
do eval test x\${$as_var+set} = xset \
&& ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
done
PS1='$ '
PS2='> '
PS4='+ '
# NLS nuisances.
LC_ALL=C
export LC_ALL
LANGUAGE=C
export LANGUAGE
# CDPATH.
(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
# Use a proper internal environment variable to ensure we don't fall
# into an infinite loop, continuously re-executing ourselves.
if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
_as_can_reexec=no; export _as_can_reexec;
# We cannot yet assume a decent shell, so we have to provide a
# neutralization value for shells without unset; and this also
# works around shells that cannot unset nonexistent variables.
# Preserve -v and -x to the replacement shell.
BASH_ENV=/dev/null
ENV=/dev/null
(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
case $- in # ((((
*v*x* | *x*v* ) as_opts=-vx ;;
*v* ) as_opts=-v ;;
*x* ) as_opts=-x ;;
* ) as_opts= ;;
esac
exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
# Admittedly, this is quite paranoid, since all the known shells bail
# out after a failed `exec'.
$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
as_fn_exit 255
fi
# We don't want this to propagate to other subprocesses.
{ _as_can_reexec=; unset _as_can_reexec;}
if test "x$CONFIG_SHELL" = x; then
as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
emulate sh
NULLCMD=:
# Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
# is contrary to our usage. Disable this feature.
alias -g '\${1+\"\$@\"}'='\"\$@\"'
setopt NO_GLOB_SUBST
else
case \`(set -o) 2>/dev/null\` in #(
*posix*) :
set -o posix ;; #(
*) :
;;
esac
fi
"
as_required="as_fn_return () { (exit \$1); }
as_fn_success () { as_fn_return 0; }
as_fn_failure () { as_fn_return 1; }
as_fn_ret_success () { return 0; }
as_fn_ret_failure () { return 1; }
exitcode=0
as_fn_success || { exitcode=1; echo as_fn_success failed.; }
as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
else
exitcode=1; echo positional parameters were not saved.
fi
test x\$exitcode = x0 || exit 1
test -x / || exit 1"
as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1
test \$(( 1 + 1 )) = 2 || exit 1
test -n \"\${ZSH_VERSION+set}\${BASH_VERSION+set}\" || (
ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO
ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO
PATH=/empty FPATH=/empty; export PATH FPATH
test \"X\`printf %s \$ECHO\`\" = \"X\$ECHO\" \\
|| test \"X\`print -r -- \$ECHO\`\" = \"X\$ECHO\" ) || exit 1"
if (eval "$as_required") 2>/dev/null; then :
as_have_required=yes
else
as_have_required=no
fi
if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
as_found=false
for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
as_found=:
case $as_dir in #(
/*)
for as_base in sh bash ksh sh5; do
# Try only shells that exist, to save several forks.
as_shell=$as_dir/$as_base
if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
{ $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :
CONFIG_SHELL=$as_shell as_have_required=yes
if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :
break 2
fi
fi
done;;
esac
as_found=false
done
$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
{ $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
CONFIG_SHELL=$SHELL as_have_required=yes
fi; }
IFS=$as_save_IFS
if test "x$CONFIG_SHELL" != x; then :
export CONFIG_SHELL
# We cannot yet assume a decent shell, so we have to provide a
# neutralization value for shells without unset; and this also
# works around shells that cannot unset nonexistent variables.
# Preserve -v and -x to the replacement shell.
BASH_ENV=/dev/null
ENV=/dev/null
(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
case $- in # ((((
*v*x* | *x*v* ) as_opts=-vx ;;
*v* ) as_opts=-v ;;
*x* ) as_opts=-x ;;
* ) as_opts= ;;
esac
exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
# Admittedly, this is quite paranoid, since all the known shells bail
# out after a failed `exec'.
$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
exit 255
fi
if test x$as_have_required = xno; then :
$as_echo "$0: This script requires a shell more modern than all"
$as_echo "$0: the shells that I found on your system."
if test x${ZSH_VERSION+set} = xset ; then
$as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
$as_echo "$0: be upgraded to zsh 4.3.4 or later."
else
$as_echo "$0: Please tell bug-autoconf@gnu.org and
$0: unbound-bugs@nlnetlabs.nl or
$0: https://github.com/NLnetLabs/unbound/issues about your
$0: system, including any error possibly output before this
$0: message. Then install a modern shell, or manually run
$0: the script under such a shell if you do have one."
fi
exit 1
fi
fi
fi
SHELL=${CONFIG_SHELL-/bin/sh}
export SHELL
# Unset more variables known to interfere with behavior of common tools.
CLICOLOR_FORCE= GREP_OPTIONS=
unset CLICOLOR_FORCE GREP_OPTIONS
## --------------------- ##
## M4sh Shell Functions. ##
## --------------------- ##
# as_fn_unset VAR
# ---------------
# Portably unset VAR.
as_fn_unset ()
{
{ eval $1=; unset $1;}
}
as_unset=as_fn_unset
# as_fn_set_status STATUS
# -----------------------
# Set $? to STATUS, without forking.
as_fn_set_status ()
{
return $1
} # as_fn_set_status
# as_fn_exit STATUS
# -----------------
# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
as_fn_exit ()
{
set +e
as_fn_set_status $1
exit $1
} # as_fn_exit
# as_fn_mkdir_p
# -------------
# Create "$as_dir" as a directory, including parents if necessary.
as_fn_mkdir_p ()
{
case $as_dir in #(
-*) as_dir=./$as_dir;;
esac
test -d "$as_dir" || eval $as_mkdir_p || {
as_dirs=
while :; do
case $as_dir in #(
*\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
*) as_qdir=$as_dir;;
esac
as_dirs="'$as_qdir' $as_dirs"
as_dir=`$as_dirname -- "$as_dir" ||
$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
X"$as_dir" : 'X\(//\)[^/]' \| \
X"$as_dir" : 'X\(//\)$' \| \
X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
$as_echo X"$as_dir" |
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
s//\1/
q
}
/^X\(\/\/\)[^/].*/{
s//\1/
q
}
/^X\(\/\/\)$/{
s//\1/
q
}
/^X\(\/\).*/{
s//\1/
q
}
s/.*/./; q'`
test -d "$as_dir" && break
done
test -z "$as_dirs" || eval "mkdir $as_dirs"
} || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
} # as_fn_mkdir_p
# as_fn_executable_p FILE
# -----------------------
# Test if FILE is an executable regular file.
as_fn_executable_p ()
{
test -f "$1" && test -x "$1"
} # as_fn_executable_p
# as_fn_append VAR VALUE
# ----------------------
# Append the text in VALUE to the end of the definition contained in VAR. Take
# advantage of any shell optimizations that allow amortized linear growth over
# repeated appends, instead of the typical quadratic growth present in naive
# implementations.
if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
eval 'as_fn_append ()
{
eval $1+=\$2
}'
else
as_fn_append ()
{
eval $1=\$$1\$2
}
fi # as_fn_append
# as_fn_arith ARG...
# ------------------
# Perform arithmetic evaluation on the ARGs, and store the result in the
# global $as_val. Take advantage of shells that can avoid forks. The arguments
# must be portable across $(()) and expr.
if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
eval 'as_fn_arith ()
{
as_val=$(( $* ))
}'
else
as_fn_arith ()
{
as_val=`expr "$@" || test $? -eq 1`
}
fi # as_fn_arith
# as_fn_error STATUS ERROR [LINENO LOG_FD]
# ----------------------------------------
# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
# script with STATUS, using 1 if that was 0.
as_fn_error ()
{
as_status=$1; test $as_status -eq 0 && as_status=1
if test "$4"; then
as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
$as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
fi
$as_echo "$as_me: error: $2" >&2
as_fn_exit $as_status
} # as_fn_error
if expr a : '\(a\)' >/dev/null 2>&1 &&
test "X`expr 00001 : '.*\(...\)'`" = X001; then
as_expr=expr
else
as_expr=false
fi
if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
as_basename=basename
else
as_basename=false
fi
if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
as_dirname=dirname
else
as_dirname=false
fi
as_me=`$as_basename -- "$0" ||
$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
X"$0" : 'X\(//\)$' \| \
X"$0" : 'X\(/\)' \| . 2>/dev/null ||
$as_echo X/"$0" |
sed '/^.*\/\([^/][^/]*\)\/*$/{
s//\1/
q
}
/^X\/\(\/\/\)$/{
s//\1/
q
}
/^X\/\(\/\).*/{
s//\1/
q
}
s/.*/./; q'`
# Avoid depending upon Character Ranges.
as_cr_letters='abcdefghijklmnopqrstuvwxyz'
as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
as_cr_Letters=$as_cr_letters$as_cr_LETTERS
as_cr_digits='0123456789'
as_cr_alnum=$as_cr_Letters$as_cr_digits
as_lineno_1=$LINENO as_lineno_1a=$LINENO
as_lineno_2=$LINENO as_lineno_2a=$LINENO
eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
# Blame Lee E. McMahon (1931-1989) for sed's syntax. :-)
sed -n '
p
/[$]LINENO/=
' <$as_myself |
sed '
s/[$]LINENO.*/&-/
t lineno
b
:lineno
N
:loop
s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
t loop
s/-\n.*//
' >$as_me.lineno &&
chmod +x "$as_me.lineno" ||
{ $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
# If we had to re-execute with $CONFIG_SHELL, we're ensured to have
# already done that, so ensure we don't try to do so again and fall
# in an infinite loop. This has already happened in practice.
_as_can_reexec=no; export _as_can_reexec
# Don't try to exec as it changes $[0], causing all sort of problems
# (the dirname of $[0] is not the place where we might find the
# original and so on. Autoconf is especially sensitive to this).
. "./$as_me.lineno"
# Exit status is that of the last command.
exit
}
ECHO_C= ECHO_N= ECHO_T=
case `echo -n x` in #(((((
-n*)
case `echo 'xy\c'` in
*c*) ECHO_T=' ';; # ECHO_T is single tab character.
xy) ECHO_C='\c';;
*) echo `echo ksh88 bug on AIX 6.1` > /dev/null
ECHO_T=' ';;
esac;;
*)
ECHO_N='-n';;
esac
rm -f conf$$ conf$$.exe conf$$.file
if test -d conf$$.dir; then
rm -f conf$$.dir/conf$$.file
else
rm -f conf$$.dir
mkdir conf$$.dir 2>/dev/null
fi
if (echo >conf$$.file) 2>/dev/null; then
if ln -s conf$$.file conf$$ 2>/dev/null; then
as_ln_s='ln -s'
# ... but there are two gotchas:
# 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
# 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
# In both cases, we have to default to `cp -pR'.
ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
as_ln_s='cp -pR'
elif ln conf$$.file conf$$ 2>/dev/null; then
as_ln_s=ln
else
as_ln_s='cp -pR'
fi
else
as_ln_s='cp -pR'
fi
rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
rmdir conf$$.dir 2>/dev/null
if mkdir -p . 2>/dev/null; then
as_mkdir_p='mkdir -p "$as_dir"'
else
test -d ./-p && rmdir ./-p
as_mkdir_p=false
fi
as_test_x='test -x'
as_executable_p=as_fn_executable_p
# Sed expression to map a string onto a valid CPP name.
as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
# Sed expression to map a string onto a valid variable name.
as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
SHELL=${CONFIG_SHELL-/bin/sh}
test -n "$DJDIR" || exec 7<&0 </dev/null
exec 6>&1
# Name of the host.
# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
# so uname gets run too.
ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
#
# Initializations.
#
ac_default_prefix=/usr/local
ac_clean_files=
ac_config_libobj_dir=.
LIBOBJS=
cross_compiling=no
subdirs=
MFLAGS=
MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='unbound'
PACKAGE_TARNAME='unbound'
-PACKAGE_VERSION='1.18.0'
-PACKAGE_STRING='unbound 1.18.0'
+PACKAGE_VERSION='1.19.0'
+PACKAGE_STRING='unbound 1.19.0'
PACKAGE_BUGREPORT='unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues'
PACKAGE_URL=''
# Factoring default headers for most tests.
ac_includes_default="\
#include <stdio.h>
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif
#ifdef HAVE_STRING_H
# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
# include <memory.h>
# endif
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#ifdef HAVE_INTTYPES_H
# include <inttypes.h>
#endif
#ifdef HAVE_STDINT_H
# include <stdint.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif"
ac_subst_vars='LTLIBOBJS
date
version
INSTALLTARGET
ALLTARGET
SOURCEFILE
SOURCEDETERMINE
IPSET_OBJ
IPSET_SRC
IPSECMOD_HEADER
IPSECMOD_OBJ
CACHEDB_OBJ
CACHEDB_SRC
DNSCRYPT_OBJ
DNSCRYPT_SRC
ENABLE_DNSCRYPT
ENABLE_DNSCRYPT_XCHACHA20
DNSTAP_OBJ
DNSTAP_SRC
DNSTAP_SOCKET_TESTBIN
DNSTAP_SOCKET_PATH
opt_dnstap_socket_path
ENABLE_DNSTAP
PROTOC_C
UBSYMS
EXTRALINK
COMMON_OBJ_ALL_SYMBOLS
LIBOBJ_WITHOUT_CTIME
LIBOBJ_WITHOUT_CTIMEARC4
WIN_CHECKCONF_OBJ_LINK
WIN_CONTROL_OBJ_LINK
WIN_UBANCHOR_OBJ_LINK
WIN_HOST_OBJ_LINK
WIN_DAEMON_OBJ_LINK
WIN_DAEMON_OBJ
WIN_DAEMON_SRC
WINAPPS
WINDRES
CHECKLOCK_OBJ
staticexe
PC_LIBEVENT_DEPENDENCY
UNBOUND_EVENT_UNINSTALL
UNBOUND_EVENT_INSTALL
SUBNET_HEADER
SUBNET_OBJ
PC_LIBBSD_DEPENDENCY
SSLLIB
HAVE_SSL
PC_CRYPTO_DEPENDENCY
CONFIG_DATE
GCC_DOCKER_LINTFLAGS
NETBSD_LINTFLAGS
PYUNBOUND_UNINSTALL
PYUNBOUND_INSTALL
PYUNBOUND_TARGET
PYUNBOUND_OBJ
WITH_PYUNBOUND
PYTHONMOD_UNINSTALL
PYTHONMOD_INSTALL
PYTHONMOD_HEADER
PYTHONMOD_OBJ
WITH_PYTHONMODULE
swig
SWIG_LIB
SWIG
PC_PY_DEPENDENCY
PYTHON_LIBS
PY_MAJOR_VERSION
PYTHON_SITE_PKG
PYTHON_LDFLAGS
PYTHON_CPPFLAGS
PYTHON
PYTHON_VERSION
DYNLIBMOD_EXTRALIBS
DYNLIBMOD_HEADER
DYNLIBMOD_OBJ
WITH_DYNLIBMODULE
PTHREAD_CFLAGS_ONLY
PTHREAD_CFLAGS
PTHREAD_LIBS
PTHREAD_CC
ax_pthread_config
ASYNCLOOK_ALLOCCHECK_EXTRA_OBJ
SLDNS_ALLOCCHECK_EXTRA_OBJ
USE_SYSTEMD_FALSE
USE_SYSTEMD_TRUE
SYSTEMD_DAEMON_LIBS
SYSTEMD_DAEMON_CFLAGS
SYSTEMD_LIBS
SYSTEMD_CFLAGS
RUNTIME_PATH
LIBOBJS
PKG_CONFIG_LIBDIR
PKG_CONFIG_PATH
PKG_CONFIG
LT_SYS_LIBRARY_PATH
OTOOL64
OTOOL
LIPO
NMEDIT
DSYMUTIL
MANIFEST_TOOL
AWK
RANLIB
ac_ct_AR
DLLTOOL
OBJDUMP
LN_S
NM
ac_ct_DUMPBIN
DUMPBIN
LD
FGREP
SED
LIBTOOL
AR
host_os
host_vendor
host_cpu
host
build_os
build_vendor
build_cpu
build
libtool
STRIP
doxygen
YFLAGS
YACC
LEXLIB
LEX_OUTPUT_ROOT
LEX
debug_enabled
DEPFLAG
UNBOUND_USERNAME
UNBOUND_ROOTCERT_FILE
UNBOUND_ROOTKEY_FILE
UNBOUND_PIDFILE
UNBOUND_SHARE_DIR
UNBOUND_CHROOT_DIR
UNBOUND_RUN_DIR
ub_conf_dir
ub_conf_file
UNBOUND_LOCALSTATE_DIR
UNBOUND_SYSCONF_DIR
UNBOUND_SBIN_DIR
EGREP
GREP
CPP
OBJEXT
EXEEXT
ac_ct_CC
CPPFLAGS
LDFLAGS
CFLAGS
CC
LIBUNBOUND_AGE
LIBUNBOUND_REVISION
LIBUNBOUND_CURRENT
UNBOUND_VERSION_MICRO
UNBOUND_VERSION_MINOR
UNBOUND_VERSION_MAJOR
target_alias
host_alias
build_alias
LIBS
ECHO_T
ECHO_N
ECHO_C
DEFS
mandir
localedir
libdir
psdir
pdfdir
dvidir
htmldir
infodir
docdir
oldincludedir
includedir
runstatedir
localstatedir
sharedstatedir
sysconfdir
datadir
datarootdir
libexecdir
sbindir
bindir
program_transform_name
prefix
exec_prefix
PACKAGE_URL
PACKAGE_BUGREPORT
PACKAGE_STRING
PACKAGE_VERSION
PACKAGE_TARNAME
PACKAGE_NAME
PATH_SEPARATOR
SHELL'
ac_subst_files=''
ac_user_opts='
enable_option_checking
with_conf_file
with_run_dir
with_chroot_dir
with_share_dir
with_pidfile
with_rootkey_file
with_rootcert_file
with_username
enable_checking
enable_debug
enable_flto
enable_pie
enable_relro_now
enable_shared
enable_static
with_pic
enable_fast_install
with_aix_soname
with_gnu_ld
with_sysroot
enable_libtool_lock
enable_rpath
enable_largefile
enable_systemd
enable_alloc_checks
enable_alloc_lite
enable_alloc_nonregional
with_pthreads
with_solaris_threads
with_syslog_facility
with_dynlibmodule
with_pyunbound
with_pythonmodule
enable_swig_version_check
with_nss
with_nettle
with_ssl
with_libbsd
enable_sha1
enable_sha2
enable_subnet
enable_gost
enable_ecdsa
enable_dsa
with_deprecate_rsa_1024
enable_ed25519
enable_ed448
enable_event_api
enable_tfo_client
enable_tfo_server
with_libevent
with_libexpat
with_libhiredis
with_libnghttp2
enable_static_exe
enable_fully_static
enable_lock_checks
enable_allsymbols
enable_dnstap
with_dnstap_socket_path
with_protobuf_c
enable_dnscrypt
with_libsodium
enable_cachedb
enable_ipsecmod
enable_ipset
with_libmnl
enable_explicit_port_randomisation
enable_linux_ip_local_port_range
with_libunbound_only
'
ac_precious_vars='build_alias
host_alias
target_alias
CC
CFLAGS
LDFLAGS
LIBS
CPPFLAGS
CPP
YACC
YFLAGS
LT_SYS_LIBRARY_PATH
PKG_CONFIG
PKG_CONFIG_PATH
PKG_CONFIG_LIBDIR
SYSTEMD_CFLAGS
SYSTEMD_LIBS
SYSTEMD_DAEMON_CFLAGS
SYSTEMD_DAEMON_LIBS
PYTHON_VERSION'
# Initialize some variables set by options.
ac_init_help=
ac_init_version=false
ac_unrecognized_opts=
ac_unrecognized_sep=
# The variables have the same names as the options, with
# dashes changed to underlines.
cache_file=/dev/null
exec_prefix=NONE
no_create=
no_recursion=
prefix=NONE
program_prefix=NONE
program_suffix=NONE
program_transform_name=s,x,x,
silent=
site=
srcdir=
verbose=
x_includes=NONE
x_libraries=NONE
# Installation directory options.
# These are left unexpanded so users can "make install exec_prefix=/foo"
# and all the variables that are supposed to be based on exec_prefix
# by default will actually change.
# Use braces instead of parens because sh, perl, etc. also accept them.
# (The list follows the same order as the GNU Coding Standards.)
bindir='${exec_prefix}/bin'
sbindir='${exec_prefix}/sbin'
libexecdir='${exec_prefix}/libexec'
datarootdir='${prefix}/share'
datadir='${datarootdir}'
sysconfdir='${prefix}/etc'
sharedstatedir='${prefix}/com'
localstatedir='${prefix}/var'
runstatedir='${localstatedir}/run'
includedir='${prefix}/include'
oldincludedir='/usr/include'
docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
infodir='${datarootdir}/info'
htmldir='${docdir}'
dvidir='${docdir}'
pdfdir='${docdir}'
psdir='${docdir}'
libdir='${exec_prefix}/lib'
localedir='${datarootdir}/locale'
mandir='${datarootdir}/man'
ac_prev=
ac_dashdash=
for ac_option
do
# If the previous option needs an argument, assign it.
if test -n "$ac_prev"; then
eval $ac_prev=\$ac_option
ac_prev=
continue
fi
case $ac_option in
*=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
*=) ac_optarg= ;;
*) ac_optarg=yes ;;
esac
# Accept the important Cygnus configure options, so we can diagnose typos.
case $ac_dashdash$ac_option in
--)
ac_dashdash=yes ;;
-bindir | --bindir | --bindi | --bind | --bin | --bi)
ac_prev=bindir ;;
-bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
bindir=$ac_optarg ;;
-build | --build | --buil | --bui | --bu)
ac_prev=build_alias ;;
-build=* | --build=* | --buil=* | --bui=* | --bu=*)
build_alias=$ac_optarg ;;
-cache-file | --cache-file | --cache-fil | --cache-fi \
| --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
ac_prev=cache_file ;;
-cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
| --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
cache_file=$ac_optarg ;;
--config-cache | -C)
cache_file=config.cache ;;
-datadir | --datadir | --datadi | --datad)
ac_prev=datadir ;;
-datadir=* | --datadir=* | --datadi=* | --datad=*)
datadir=$ac_optarg ;;
-datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
| --dataroo | --dataro | --datar)
ac_prev=datarootdir ;;
-datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
| --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
datarootdir=$ac_optarg ;;
-disable-* | --disable-*)
ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
# Reject names that are not valid shell variable names.
expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
as_fn_error $? "invalid feature name: $ac_useropt"
ac_useropt_orig=$ac_useropt
ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
case $ac_user_opts in
*"
"enable_$ac_useropt"
"*) ;;
*) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
ac_unrecognized_sep=', ';;
esac
eval enable_$ac_useropt=no ;;
-docdir | --docdir | --docdi | --doc | --do)
ac_prev=docdir ;;
-docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
docdir=$ac_optarg ;;
-dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
ac_prev=dvidir ;;
-dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
dvidir=$ac_optarg ;;
-enable-* | --enable-*)
ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
# Reject names that are not valid shell variable names.
expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
as_fn_error $? "invalid feature name: $ac_useropt"
ac_useropt_orig=$ac_useropt
ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
case $ac_user_opts in
*"
"enable_$ac_useropt"
"*) ;;
*) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
ac_unrecognized_sep=', ';;
esac
eval enable_$ac_useropt=\$ac_optarg ;;
-exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
| --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
| --exec | --exe | --ex)
ac_prev=exec_prefix ;;
-exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
| --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
| --exec=* | --exe=* | --ex=*)
exec_prefix=$ac_optarg ;;
-gas | --gas | --ga | --g)
# Obsolete; use --with-gas.
with_gas=yes ;;
-help | --help | --hel | --he | -h)
ac_init_help=long ;;
-help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
ac_init_help=recursive ;;
-help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
ac_init_help=short ;;
-host | --host | --hos | --ho)
ac_prev=host_alias ;;
-host=* | --host=* | --hos=* | --ho=*)
host_alias=$ac_optarg ;;
-htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
ac_prev=htmldir ;;
-htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
| --ht=*)
htmldir=$ac_optarg ;;
-includedir | --includedir | --includedi | --included | --include \
| --includ | --inclu | --incl | --inc)
ac_prev=includedir ;;
-includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
| --includ=* | --inclu=* | --incl=* | --inc=*)
includedir=$ac_optarg ;;
-infodir | --infodir | --infodi | --infod | --info | --inf)
ac_prev=infodir ;;
-infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
infodir=$ac_optarg ;;
-libdir | --libdir | --libdi | --libd)
ac_prev=libdir ;;
-libdir=* | --libdir=* | --libdi=* | --libd=*)
libdir=$ac_optarg ;;
-libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
| --libexe | --libex | --libe)
ac_prev=libexecdir ;;
-libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
| --libexe=* | --libex=* | --libe=*)
libexecdir=$ac_optarg ;;
-localedir | --localedir | --localedi | --localed | --locale)
ac_prev=localedir ;;
-localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
localedir=$ac_optarg ;;
-localstatedir | --localstatedir | --localstatedi | --localstated \
| --localstate | --localstat | --localsta | --localst | --locals)
ac_prev=localstatedir ;;
-localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
| --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
localstatedir=$ac_optarg ;;
-mandir | --mandir | --mandi | --mand | --man | --ma | --m)
ac_prev=mandir ;;
-mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
mandir=$ac_optarg ;;
-nfp | --nfp | --nf)
# Obsolete; use --without-fp.
with_fp=no ;;
-no-create | --no-create | --no-creat | --no-crea | --no-cre \
| --no-cr | --no-c | -n)
no_create=yes ;;
-no-recursion | --no-recursion | --no-recursio | --no-recursi \
| --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
no_recursion=yes ;;
-oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
| --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
| --oldin | --oldi | --old | --ol | --o)
ac_prev=oldincludedir ;;
-oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
| --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
| --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
oldincludedir=$ac_optarg ;;
-prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
ac_prev=prefix ;;
-prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
prefix=$ac_optarg ;;
-program-prefix | --program-prefix | --program-prefi | --program-pref \
| --program-pre | --program-pr | --program-p)
ac_prev=program_prefix ;;
-program-prefix=* | --program-prefix=* | --program-prefi=* \
| --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
program_prefix=$ac_optarg ;;
-program-suffix | --program-suffix | --program-suffi | --program-suff \
| --program-suf | --program-su | --program-s)
ac_prev=program_suffix ;;
-program-suffix=* | --program-suffix=* | --program-suffi=* \
| --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
program_suffix=$ac_optarg ;;
-program-transform-name | --program-transform-name \
| --program-transform-nam | --program-transform-na \
| --program-transform-n | --program-transform- \
| --program-transform | --program-transfor \
| --program-transfo | --program-transf \
| --program-trans | --program-tran \
| --progr-tra | --program-tr | --program-t)
ac_prev=program_transform_name ;;
-program-transform-name=* | --program-transform-name=* \
| --program-transform-nam=* | --program-transform-na=* \
| --program-transform-n=* | --program-transform-=* \
| --program-transform=* | --program-transfor=* \
| --program-transfo=* | --program-transf=* \
| --program-trans=* | --program-tran=* \
| --progr-tra=* | --program-tr=* | --program-t=*)
program_transform_name=$ac_optarg ;;
-pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
ac_prev=pdfdir ;;
-pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
pdfdir=$ac_optarg ;;
-psdir | --psdir | --psdi | --psd | --ps)
ac_prev=psdir ;;
-psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
psdir=$ac_optarg ;;
-q | -quiet | --quiet | --quie | --qui | --qu | --q \
| -silent | --silent | --silen | --sile | --sil)
silent=yes ;;
-runstatedir | --runstatedir | --runstatedi | --runstated \
| --runstate | --runstat | --runsta | --runst | --runs \
| --run | --ru | --r)
ac_prev=runstatedir ;;
-runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
| --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
| --run=* | --ru=* | --r=*)
runstatedir=$ac_optarg ;;
-sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
ac_prev=sbindir ;;
-sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
| --sbi=* | --sb=*)
sbindir=$ac_optarg ;;
-sharedstatedir | --sharedstatedir | --sharedstatedi \
| --sharedstated | --sharedstate | --sharedstat | --sharedsta \
| --sharedst | --shareds | --shared | --share | --shar \
| --sha | --sh)
ac_prev=sharedstatedir ;;
-sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
| --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
| --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
| --sha=* | --sh=*)
sharedstatedir=$ac_optarg ;;
-site | --site | --sit)
ac_prev=site ;;
-site=* | --site=* | --sit=*)
site=$ac_optarg ;;
-srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
ac_prev=srcdir ;;
-srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
srcdir=$ac_optarg ;;
-sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
| --syscon | --sysco | --sysc | --sys | --sy)
ac_prev=sysconfdir ;;
-sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
| --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
sysconfdir=$ac_optarg ;;
-target | --target | --targe | --targ | --tar | --ta | --t)
ac_prev=target_alias ;;
-target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
target_alias=$ac_optarg ;;
-v | -verbose | --verbose | --verbos | --verbo | --verb)
verbose=yes ;;
-version | --version | --versio | --versi | --vers | -V)
ac_init_version=: ;;
-with-* | --with-*)
ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
# Reject names that are not valid shell variable names.
expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
as_fn_error $? "invalid package name: $ac_useropt"
ac_useropt_orig=$ac_useropt
ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
case $ac_user_opts in
*"
"with_$ac_useropt"
"*) ;;
*) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
ac_unrecognized_sep=', ';;
esac
eval with_$ac_useropt=\$ac_optarg ;;
-without-* | --without-*)
ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
# Reject names that are not valid shell variable names.
expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
as_fn_error $? "invalid package name: $ac_useropt"
ac_useropt_orig=$ac_useropt
ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
case $ac_user_opts in
*"
"with_$ac_useropt"
"*) ;;
*) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
ac_unrecognized_sep=', ';;
esac
eval with_$ac_useropt=no ;;
--x)
# Obsolete; use --with-x.
with_x=yes ;;
-x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
| --x-incl | --x-inc | --x-in | --x-i)
ac_prev=x_includes ;;
-x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
| --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
x_includes=$ac_optarg ;;
-x-libraries | --x-libraries | --x-librarie | --x-librari \
| --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
ac_prev=x_libraries ;;
-x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
| --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
x_libraries=$ac_optarg ;;
-*) as_fn_error $? "unrecognized option: \`$ac_option'
Try \`$0 --help' for more information"
;;
*=*)
ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
# Reject names that are not valid shell variable names.
case $ac_envvar in #(
'' | [0-9]* | *[!_$as_cr_alnum]* )
as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
esac
eval $ac_envvar=\$ac_optarg
export $ac_envvar ;;
*)
# FIXME: should be removed in autoconf 3.0.
$as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
$as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
: "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
;;
esac
done
if test -n "$ac_prev"; then
ac_option=--`echo $ac_prev | sed 's/_/-/g'`
as_fn_error $? "missing argument to $ac_option"
fi
if test -n "$ac_unrecognized_opts"; then
case $enable_option_checking in
no) ;;
fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
*) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
esac
fi
# Check all directory arguments for consistency.
for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
datadir sysconfdir sharedstatedir localstatedir includedir \
oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
libdir localedir mandir runstatedir
do
eval ac_val=\$$ac_var
# Remove trailing slashes.
case $ac_val in
*/ )
ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
eval $ac_var=\$ac_val;;
esac
# Be sure to have absolute directory names.
case $ac_val in
[\\/$]* | ?:[\\/]* ) continue;;
NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
esac
as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
done
# There might be people who depend on the old broken behavior: `$host'
# used to hold the argument of --host etc.
# FIXME: To remove some day.
build=$build_alias
host=$host_alias
target=$target_alias
# FIXME: To remove some day.
if test "x$host_alias" != x; then
if test "x$build_alias" = x; then
cross_compiling=maybe
elif test "x$build_alias" != "x$host_alias"; then
cross_compiling=yes
fi
fi
ac_tool_prefix=
test -n "$host_alias" && ac_tool_prefix=$host_alias-
test "$silent" = yes && exec 6>/dev/null
ac_pwd=`pwd` && test -n "$ac_pwd" &&
ac_ls_di=`ls -di .` &&
ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
as_fn_error $? "working directory cannot be determined"
test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
as_fn_error $? "pwd does not report name of working directory"
# Find the source files, if location was not specified.
if test -z "$srcdir"; then
ac_srcdir_defaulted=yes
# Try the directory containing this script, then the parent directory.
ac_confdir=`$as_dirname -- "$as_myself" ||
$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
X"$as_myself" : 'X\(//\)[^/]' \| \
X"$as_myself" : 'X\(//\)$' \| \
X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
$as_echo X"$as_myself" |
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
s//\1/
q
}
/^X\(\/\/\)[^/].*/{
s//\1/
q
}
/^X\(\/\/\)$/{
s//\1/
q
}
/^X\(\/\).*/{
s//\1/
q
}
s/.*/./; q'`
srcdir=$ac_confdir
if test ! -r "$srcdir/$ac_unique_file"; then
srcdir=..
fi
else
ac_srcdir_defaulted=no
fi
if test ! -r "$srcdir/$ac_unique_file"; then
test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
fi
ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
ac_abs_confdir=`(
cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
pwd)`
# When building in place, set srcdir=.
if test "$ac_abs_confdir" = "$ac_pwd"; then
srcdir=.
fi
# Remove unnecessary trailing slashes from srcdir.
# Double slashes in file names in object file debugging info
# mess up M-x gdb in Emacs.
case $srcdir in
*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
esac
for ac_var in $ac_precious_vars; do
eval ac_env_${ac_var}_set=\${${ac_var}+set}
eval ac_env_${ac_var}_value=\$${ac_var}
eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
eval ac_cv_env_${ac_var}_value=\$${ac_var}
done
#
# Report the --help message.
#
if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures unbound 1.18.0 to adapt to many kinds of systems.
+\`configure' configures unbound 1.19.0 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
To assign environment variables (e.g., CC, CFLAGS...), specify them as
VAR=VALUE. See below for descriptions of some of the useful variables.
Defaults for the options are specified in brackets.
Configuration:
-h, --help display this help and exit
--help=short display options specific to this package
--help=recursive display the short help of all the included packages
-V, --version display version information and exit
-q, --quiet, --silent do not print \`checking ...' messages
--cache-file=FILE cache test results in FILE [disabled]
-C, --config-cache alias for \`--cache-file=config.cache'
-n, --no-create do not create output files
--srcdir=DIR find the sources in DIR [configure dir or \`..']
Installation directories:
--prefix=PREFIX install architecture-independent files in PREFIX
[$ac_default_prefix]
--exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
[PREFIX]
By default, \`make install' will install all the files in
\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
an installation prefix other than \`$ac_default_prefix' using \`--prefix',
for instance \`--prefix=\$HOME'.
For better control, use the options below.
Fine tuning of the installation directories:
--bindir=DIR user executables [EPREFIX/bin]
--sbindir=DIR system admin executables [EPREFIX/sbin]
--libexecdir=DIR program executables [EPREFIX/libexec]
--sysconfdir=DIR read-only single-machine data [PREFIX/etc]
--sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
--localstatedir=DIR modifiable single-machine data [PREFIX/var]
--runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
--libdir=DIR object code libraries [EPREFIX/lib]
--includedir=DIR C header files [PREFIX/include]
--oldincludedir=DIR C header files for non-gcc [/usr/include]
--datarootdir=DIR read-only arch.-independent data root [PREFIX/share]
--datadir=DIR read-only architecture-independent data [DATAROOTDIR]
--infodir=DIR info documentation [DATAROOTDIR/info]
--localedir=DIR locale-dependent data [DATAROOTDIR/locale]
--mandir=DIR man documentation [DATAROOTDIR/man]
--docdir=DIR documentation root [DATAROOTDIR/doc/unbound]
--htmldir=DIR html documentation [DOCDIR]
--dvidir=DIR dvi documentation [DOCDIR]
--pdfdir=DIR pdf documentation [DOCDIR]
--psdir=DIR ps documentation [DOCDIR]
_ACEOF
cat <<\_ACEOF
System types:
--build=BUILD configure for building on BUILD [guessed]
--host=HOST cross-compile to build programs to run on HOST [BUILD]
_ACEOF
fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of unbound 1.18.0:";;
+ short | recursive ) echo "Configuration of unbound 1.19.0:";;
esac
cat <<\_ACEOF
Optional Features:
--disable-option-checking ignore unrecognized --enable/--with options
--disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
--enable-FEATURE[=ARG] include FEATURE [ARG=yes]
--enable-checking Enable warnings, asserts, makefile-dependencies
--enable-debug same as enable-checking
--disable-flto Disable link-time optimization (gcc specific option)
--enable-pie Enable Position-Independent Executable (eg. to fully
benefit from ASLR, small performance penalty)
--enable-relro-now Enable full relocation binding at load-time (RELRO
NOW, to protect GOT and .dtor areas)
--enable-shared[=PKGS] build shared libraries [default=yes]
--enable-static[=PKGS] build static libraries [default=yes]
--enable-fast-install[=PKGS]
optimize for fast installation [default=yes]
--disable-libtool-lock avoid locking (might break parallel builds)
--disable-rpath disable hardcoded rpath (default=enabled)
--disable-largefile omit support for large files
--enable-systemd compile with systemd support
--enable-alloc-checks enable to memory allocation statistics, for debug
purposes
--enable-alloc-lite enable for lightweight alloc assertions, for debug
purposes
--enable-alloc-nonregional
enable nonregional allocs, slow but exposes regional
allocations to other memory purifiers, for debug
purposes
--disable-swig-version-check
Disable swig version check to build python modules
with older swig even though that is unreliable
--disable-sha1 Disable SHA1 RRSIG support, does not disable nsec3
support
--disable-sha2 Disable SHA256 and SHA512 RRSIG support
--enable-subnet Enable client subnet
--disable-gost Disable GOST support
--disable-ecdsa Disable ECDSA support
--disable-dsa Disable DSA support
--disable-ed25519 Disable ED25519 support
--disable-ed448 Disable ED448 support
--enable-event-api Enable (experimental) pluggable event base
libunbound API installed to unbound-event.h
--enable-tfo-client Enable TCP Fast Open for client mode
--enable-tfo-server Enable TCP Fast Open for server mode
--enable-static-exe enable to compile executables statically against
(event) uninstalled libs, for debug purposes
--enable-fully-static enable to compile fully static
--enable-lock-checks enable to check lock and unlock calls, for debug
purposes
--enable-allsymbols export all symbols from libunbound and link binaries
to it, smaller install size but libunbound export
table is polluted by internal symbols
--enable-dnstap Enable dnstap support (requires protobuf-c)
--enable-dnscrypt Enable dnscrypt support (requires libsodium)
--enable-cachedb enable cachedb module that can use external cache
storage
--enable-ipsecmod Enable ipsecmod module that facilitates
opportunistic IPsec
--enable-ipset enable ipset module
--disable-explicit-port-randomisation
disable explicit source port randomisation and rely
on the kernel to provide random source ports
--enable-linux-ip-local-port-range
Define this to enable use of
/proc/sys/net/ipv4/ip_local_port_range as a default
outgoing port range. This is only for the libunbound
on Linux and does not affect unbound resolving
daemon itself. This may severely limit the number of
available outgoing ports and thus decrease
randomness. Define this only when the target system
restricts (e.g. some of SELinux enabled
distributions) the use of non-ephemeral ports.
Optional Packages:
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
--without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
--with-conf-file=path Pathname to the Unbound configuration file
--with-run-dir=path set default directory to chdir to (by default dir
part of cfg file)
--with-chroot-dir=path set default directory to chroot to (by default same
as run-dir)
--with-share-dir=path set default directory with shared data (by default
same as share/unbound)
--with-pidfile=filename set default pathname to unbound pidfile (default
run-dir/unbound.pid)
--with-rootkey-file=filename
set default pathname to root key file (default
run-dir/root.key). This file is read and written.
--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.
--with-username=user set default user that unbound changes to (default
user is unbound)
--with-pic[=PKGS] try to use only PIC/non-PIC objects [default=use
both]
--with-aix-soname=aix|svr4|both
shared library versioning (aka "SONAME") variant to
provide on AIX, [default=aix].
--with-gnu-ld assume the C compiler uses GNU ld [default=no]
--with-sysroot[=DIR] Search for dependent libraries within DIR (or the
compiler's sysroot if not specified).
--with-pthreads use pthreads library, or --without-pthreads to
disable threading support.
--with-solaris-threads use solaris native thread library.
--with-syslog-facility=LOCAL0 - LOCAL7
set SYSLOG_FACILITY, default DAEMON
--with-dynlibmodule build dynamic library module, or
--without-dynlibmodule to disable it. (default=no)
--with-pyunbound build PyUnbound, or --without-pyunbound to skip it.
(default=no)
--with-pythonmodule build Python module, or --without-pythonmodule to
disable script engine. (default=no)
--with-nss=path use libnss instead of openssl, installed at path.
--with-nettle=path use libnettle as crypto library, installed at path.
--with-ssl=pathname enable SSL (will check /usr/local/ssl /usr/lib/ssl
/usr/ssl /usr/pkg /usr/local /opt/local /usr/sfw
/usr or specify like /usr/include/openssl11)
--with-libbsd Use portable libbsd functions
--with-deprecate-rsa-1024
Deprecate RSA 1024 bit length, makes that an
unsupported key, for use when OpenSSL FIPS refuses
1024 bit verification
--with-libevent=pathname
use libevent (will check /usr/local /opt/local
/usr/lib /usr/pkg /usr/sfw /usr or you can specify
an explicit path). Slower, but allows use of large
outgoing port ranges.
--with-libexpat=path specify explicit path for libexpat.
--with-libhiredis=path specify explicit path for libhiredis.
--with-libnghttp2=path specify explicit path for libnghttp2.
--with-dnstap-socket-path=pathname
set default dnstap socket path
--with-protobuf-c=path Path where protobuf-c is installed, for dnstap
--with-libsodium=path Path where libsodium is installed, for dnscrypt
--with-libmnl=path specify explicit path for libmnl.
--with-libunbound-only do not build daemon and tool programs
Some influential environment variables:
CC C compiler command
CFLAGS C compiler flags
LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
nonstandard directory <lib dir>
LIBS libraries to pass to the linker, e.g. -l<library>
CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
you have headers in a nonstandard directory <include dir>
CPP C preprocessor
YACC The `Yet Another Compiler Compiler' implementation to use.
Defaults to the first program found out of: `bison -y', `byacc',
`yacc'.
YFLAGS The list of arguments that will be passed by default to $YACC.
This script will default YFLAGS to the empty string to avoid a
default value of `-d' given by some make applications.
LT_SYS_LIBRARY_PATH
User-defined run-time library search path.
PKG_CONFIG path to pkg-config utility
PKG_CONFIG_PATH
directories to add to pkg-config's search path
PKG_CONFIG_LIBDIR
path overriding pkg-config's built-in search path
SYSTEMD_CFLAGS
C compiler flags for SYSTEMD, overriding pkg-config
SYSTEMD_LIBS
linker flags for SYSTEMD, overriding pkg-config
SYSTEMD_DAEMON_CFLAGS
C compiler flags for SYSTEMD_DAEMON, overriding pkg-config
SYSTEMD_DAEMON_LIBS
linker flags for SYSTEMD_DAEMON, overriding pkg-config
PYTHON_VERSION
The installed Python version to use, for example '2.3'. This
string will be appended to the Python interpreter canonical
name.
Use these variables to override the choices made by `configure' or to help
it to find libraries and programs with nonstandard names/locations.
Report bugs to <unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues>.
_ACEOF
ac_status=$?
fi
if test "$ac_init_help" = "recursive"; then
# If there are subdirs, report their specific --help.
for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
test -d "$ac_dir" ||
{ cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
continue
ac_builddir=.
case "$ac_dir" in
.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
*)
ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
# A ".." for each directory in $ac_dir_suffix.
ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
case $ac_top_builddir_sub in
"") ac_top_builddir_sub=. ac_top_build_prefix= ;;
*) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
esac ;;
esac
ac_abs_top_builddir=$ac_pwd
ac_abs_builddir=$ac_pwd$ac_dir_suffix
# for backward compatibility:
ac_top_builddir=$ac_top_build_prefix
case $srcdir in
.) # We are building in place.
ac_srcdir=.
ac_top_srcdir=$ac_top_builddir_sub
ac_abs_top_srcdir=$ac_pwd ;;
[\\/]* | ?:[\\/]* ) # Absolute name.
ac_srcdir=$srcdir$ac_dir_suffix;
ac_top_srcdir=$srcdir
ac_abs_top_srcdir=$srcdir ;;
*) # Relative name.
ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
ac_top_srcdir=$ac_top_build_prefix$srcdir
ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
esac
ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
cd "$ac_dir" || { ac_status=$?; continue; }
# Check for guested configure.
if test -f "$ac_srcdir/configure.gnu"; then
echo &&
$SHELL "$ac_srcdir/configure.gnu" --help=recursive
elif test -f "$ac_srcdir/configure"; then
echo &&
$SHELL "$ac_srcdir/configure" --help=recursive
else
$as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
fi || ac_status=$?
cd "$ac_pwd" || { ac_status=$?; break; }
done
fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-unbound configure 1.18.0
+unbound configure 1.19.0
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
This configure script is free software; the Free Software Foundation
gives unlimited permission to copy, distribute and modify it.
_ACEOF
exit
fi
## ------------------------ ##
## Autoconf initialization. ##
## ------------------------ ##
# ac_fn_c_try_compile LINENO
# --------------------------
# Try to compile conftest.$ac_ext, and return whether this succeeded.
ac_fn_c_try_compile ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
rm -f conftest.$ac_objext
if { { ac_try="$ac_compile"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
(eval "$ac_compile") 2>conftest.err
ac_status=$?
if test -s conftest.err; then
grep -v '^ *+' conftest.err >conftest.er1
cat conftest.er1 >&5
mv -f conftest.er1 conftest.err
fi
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; } && {
test -z "$ac_c_werror_flag" ||
test ! -s conftest.err
} && test -s conftest.$ac_objext; then :
ac_retval=0
else
$as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
ac_retval=1
fi
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
as_fn_set_status $ac_retval
} # ac_fn_c_try_compile
# ac_fn_c_try_cpp LINENO
# ----------------------
# Try to preprocess conftest.$ac_ext, and return whether this succeeded.
ac_fn_c_try_cpp ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
if { { ac_try="$ac_cpp conftest.$ac_ext"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
(eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
ac_status=$?
if test -s conftest.err; then
grep -v '^ *+' conftest.err >conftest.er1
cat conftest.er1 >&5
mv -f conftest.er1 conftest.err
fi
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; } > conftest.i && {
test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
test ! -s conftest.err
}; then :
ac_retval=0
else
$as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
ac_retval=1
fi
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
as_fn_set_status $ac_retval
} # ac_fn_c_try_cpp
# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES
# -------------------------------------------------------
# Tests whether HEADER exists, giving a warning if it cannot be compiled using
# the include files in INCLUDES and setting the cache variable VAR
# accordingly.
ac_fn_c_check_header_mongrel ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
if eval \${$3+:} false; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
$as_echo_n "checking for $2... " >&6; }
if eval \${$3+:} false; then :
$as_echo_n "(cached) " >&6
fi
eval ac_res=\$$3
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
else
# Is the header compilable?
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5
$as_echo_n "checking $2 usability... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
#include <$2>
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
ac_header_compiler=yes
else
ac_header_compiler=no
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5
$as_echo "$ac_header_compiler" >&6; }
# Is the header present?
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5
$as_echo_n "checking $2 presence... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <$2>
_ACEOF
if ac_fn_c_try_cpp "$LINENO"; then :
ac_header_preproc=yes
else
ac_header_preproc=no
fi
rm -f conftest.err conftest.i conftest.$ac_ext
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5
$as_echo "$ac_header_preproc" >&6; }
# So? What about this header?
case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #((
yes:no: )
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5
$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
;;
no:yes:* )
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5
$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5
$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5
$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5
$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
( $as_echo "## --------------------------------------------------------------------------------------- ##
## Report this to unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues ##
## --------------------------------------------------------------------------------------- ##"
) | sed "s/^/$as_me: WARNING: /" >&2
;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
$as_echo_n "checking for $2... " >&6; }
if eval \${$3+:} false; then :
$as_echo_n "(cached) " >&6
else
eval "$3=\$ac_header_compiler"
fi
eval ac_res=\$$3
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
fi
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_header_mongrel
# ac_fn_c_try_run LINENO
# ----------------------
# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes
# that executables *can* be run.
ac_fn_c_try_run ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
if { { ac_try="$ac_link"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
(eval "$ac_link") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'
{ { case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
(eval "$ac_try") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; }; then :
ac_retval=0
else
$as_echo "$as_me: program exited with status $ac_status" >&5
$as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
ac_retval=$ac_status
fi
rm -rf conftest.dSYM conftest_ipa8_conftest.oo
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
as_fn_set_status $ac_retval
} # ac_fn_c_try_run
# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
# -------------------------------------------------------
# Tests whether HEADER exists and can be compiled using the include files in
# INCLUDES, setting the cache variable VAR accordingly.
ac_fn_c_check_header_compile ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
$as_echo_n "checking for $2... " >&6; }
if eval \${$3+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
#include <$2>
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
eval "$3=yes"
else
eval "$3=no"
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
eval ac_res=\$$3
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_header_compile
# ac_fn_c_try_link LINENO
# -----------------------
# Try to link conftest.$ac_ext, and return whether this succeeded.
ac_fn_c_try_link ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
rm -f conftest.$ac_objext conftest$ac_exeext
if { { ac_try="$ac_link"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
(eval "$ac_link") 2>conftest.err
ac_status=$?
if test -s conftest.err; then
grep -v '^ *+' conftest.err >conftest.er1
cat conftest.er1 >&5
mv -f conftest.er1 conftest.err
fi
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; } && {
test -z "$ac_c_werror_flag" ||
test ! -s conftest.err
} && test -s conftest$ac_exeext && {
test "$cross_compiling" = yes ||
test -x conftest$ac_exeext
}; then :
ac_retval=0
else
$as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
ac_retval=1
fi
# Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
# created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
# interfere with the next link command; also delete a directory that is
# left behind by Apple's compiler. We do this before executing the actions.
rm -rf conftest.dSYM conftest_ipa8_conftest.oo
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
as_fn_set_status $ac_retval
} # ac_fn_c_try_link
# ac_fn_c_check_func LINENO FUNC VAR
# ----------------------------------
# Tests whether FUNC exists, setting the cache variable VAR accordingly
ac_fn_c_check_func ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
$as_echo_n "checking for $2... " >&6; }
if eval \${$3+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
For example, HP-UX 11i <limits.h> declares gettimeofday. */
#define $2 innocuous_$2
/* System header to define __stub macros and hopefully few prototypes,
which can conflict with char $2 (); below.
Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
<limits.h> exists even on freestanding compilers. */
#ifdef __STDC__
# include <limits.h>
#else
# include <assert.h>
#endif
#undef $2
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char $2 ();
/* The GNU C library defines this for functions which it implements
to always fail with ENOSYS. Some functions are actually named
something starting with __ and the normal name is an alias. */
#if defined __stub_$2 || defined __stub___$2
choke me
#endif
int
main ()
{
return $2 ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
eval "$3=yes"
else
eval "$3=no"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
fi
eval ac_res=\$$3
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_func
# ac_fn_c_check_type LINENO TYPE VAR INCLUDES
# -------------------------------------------
# Tests whether TYPE exists after having included INCLUDES, setting cache
# variable VAR accordingly.
ac_fn_c_check_type ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
$as_echo_n "checking for $2... " >&6; }
if eval \${$3+:} false; then :
$as_echo_n "(cached) " >&6
else
eval "$3=no"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
int
main ()
{
if (sizeof ($2))
return 0;
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
int
main ()
{
if (sizeof (($2)))
return 0;
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
else
eval "$3=yes"
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
eval ac_res=\$$3
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_type
# ac_fn_c_compute_int LINENO EXPR VAR INCLUDES
# --------------------------------------------
# Tries to find the compile-time value of EXPR in a program that includes
# INCLUDES, setting VAR accordingly. Returns whether the value could be
# computed
ac_fn_c_compute_int ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
if test "$cross_compiling" = yes; then
# Depending upon the size, compute the lo and hi bounds.
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
int
main ()
{
static int test_array [1 - 2 * !(($2) >= 0)];
test_array [0] = 0;
return test_array [0];
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
ac_lo=0 ac_mid=0
while :; do
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
int
main ()
{
static int test_array [1 - 2 * !(($2) <= $ac_mid)];
test_array [0] = 0;
return test_array [0];
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
ac_hi=$ac_mid; break
else
as_fn_arith $ac_mid + 1 && ac_lo=$as_val
if test $ac_lo -le $ac_mid; then
ac_lo= ac_hi=
break
fi
as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
done
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
int
main ()
{
static int test_array [1 - 2 * !(($2) < 0)];
test_array [0] = 0;
return test_array [0];
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
ac_hi=-1 ac_mid=-1
while :; do
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
int
main ()
{
static int test_array [1 - 2 * !(($2) >= $ac_mid)];
test_array [0] = 0;
return test_array [0];
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
ac_lo=$ac_mid; break
else
as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val
if test $ac_mid -le $ac_hi; then
ac_lo= ac_hi=
break
fi
as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
done
else
ac_lo= ac_hi=
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
# Binary search between lo and hi bounds.
while test "x$ac_lo" != "x$ac_hi"; do
as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
int
main ()
{
static int test_array [1 - 2 * !(($2) <= $ac_mid)];
test_array [0] = 0;
return test_array [0];
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
ac_hi=$ac_mid
else
as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
done
case $ac_lo in #((
?*) eval "$3=\$ac_lo"; ac_retval=0 ;;
'') ac_retval=1 ;;
esac
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
static long int longval () { return $2; }
static unsigned long int ulongval () { return $2; }
#include <stdio.h>
#include <stdlib.h>
int
main ()
{
FILE *f = fopen ("conftest.val", "w");
if (! f)
return 1;
if (($2) < 0)
{
long int i = longval ();
if (i != ($2))
return 1;
fprintf (f, "%ld", i);
}
else
{
unsigned long int i = ulongval ();
if (i != ($2))
return 1;
fprintf (f, "%lu", i);
}
/* Do not output a trailing newline, as this causes \r\n confusion
on some platforms. */
return ferror (f) || fclose (f) != 0;
;
return 0;
}
_ACEOF
if ac_fn_c_try_run "$LINENO"; then :
echo >>conftest.val; read $3 <conftest.val; ac_retval=0
else
ac_retval=1
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
conftest.$ac_objext conftest.beam conftest.$ac_ext
rm -f conftest.val
fi
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
as_fn_set_status $ac_retval
} # ac_fn_c_compute_int
# ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES
# ---------------------------------------------
# Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR
# accordingly.
ac_fn_c_check_decl ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
as_decl_name=`echo $2|sed 's/ *(.*//'`
as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'`
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5
$as_echo_n "checking whether $as_decl_name is declared... " >&6; }
if eval \${$3+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
int
main ()
{
#ifndef $as_decl_name
#ifdef __cplusplus
(void) $as_decl_use;
#else
(void) $as_decl_name;
#endif
#endif
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
eval "$3=yes"
else
eval "$3=no"
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
eval ac_res=\$$3
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_decl
# ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES
# ----------------------------------------------------
# Tries to find if the field MEMBER exists in type AGGR, after including
# INCLUDES, setting cache variable VAR accordingly.
ac_fn_c_check_member ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5
$as_echo_n "checking for $2.$3... " >&6; }
if eval \${$4+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$5
int
main ()
{
static $2 ac_aggr;
if (ac_aggr.$3)
return 0;
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
eval "$4=yes"
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$5
int
main ()
{
static $2 ac_aggr;
if (sizeof ac_aggr.$3)
return 0;
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
eval "$4=yes"
else
eval "$4=no"
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
eval ac_res=\$$4
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_member
cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by unbound $as_me 1.18.0, which was
+It was created by unbound $as_me 1.19.0, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
_ACEOF
exec 5>>config.log
{
cat <<_ASUNAME
## --------- ##
## Platform. ##
## --------- ##
hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
uname -m = `(uname -m) 2>/dev/null || echo unknown`
uname -r = `(uname -r) 2>/dev/null || echo unknown`
uname -s = `(uname -s) 2>/dev/null || echo unknown`
uname -v = `(uname -v) 2>/dev/null || echo unknown`
/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`
/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`
/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`
/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown`
/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
_ASUNAME
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
$as_echo "PATH: $as_dir"
done
IFS=$as_save_IFS
} >&5
cat >&5 <<_ACEOF
## ----------- ##
## Core tests. ##
## ----------- ##
_ACEOF
# Keep a trace of the command line.
# Strip out --no-create and --no-recursion so they do not pile up.
# Strip out --silent because we don't want to record it for future runs.
# Also quote any args containing shell meta-characters.
# Make two passes to allow for proper duplicate-argument suppression.
ac_configure_args=
ac_configure_args0=
ac_configure_args1=
ac_must_keep_next=false
for ac_pass in 1 2
do
for ac_arg
do
case $ac_arg in
-no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
-q | -quiet | --quiet | --quie | --qui | --qu | --q \
| -silent | --silent | --silen | --sile | --sil)
continue ;;
*\'*)
ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
esac
case $ac_pass in
1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
2)
as_fn_append ac_configure_args1 " '$ac_arg'"
if test $ac_must_keep_next = true; then
ac_must_keep_next=false # Got value, back to normal.
else
case $ac_arg in
*=* | --config-cache | -C | -disable-* | --disable-* \
| -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
| -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
| -with-* | --with-* | -without-* | --without-* | --x)
case "$ac_configure_args0 " in
"$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
esac
;;
-* ) ac_must_keep_next=true ;;
esac
fi
as_fn_append ac_configure_args " '$ac_arg'"
;;
esac
done
done
{ ac_configure_args0=; unset ac_configure_args0;}
{ ac_configure_args1=; unset ac_configure_args1;}
# When interrupted or exit'd, cleanup temporary files, and complete
# config.log. We remove comments because anyway the quotes in there
# would cause problems or look ugly.
# WARNING: Use '\'' to represent an apostrophe within the trap.
# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
trap 'exit_status=$?
# Save into config.log some information that might help in debugging.
{
echo
$as_echo "## ---------------- ##
## Cache variables. ##
## ---------------- ##"
echo
# The following way of writing the cache mishandles newlines in values,
(
for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
eval ac_val=\$$ac_var
case $ac_val in #(
*${as_nl}*)
case $ac_var in #(
*_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
esac
case $ac_var in #(
_ | IFS | as_nl) ;; #(
BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
*) { eval $ac_var=; unset $ac_var;} ;;
esac ;;
esac
done
(set) 2>&1 |
case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
*${as_nl}ac_space=\ *)
sed -n \
"s/'\''/'\''\\\\'\'''\''/g;
s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
;; #(
*)
sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
;;
esac |
sort
)
echo
$as_echo "## ----------------- ##
## Output variables. ##
## ----------------- ##"
echo
for ac_var in $ac_subst_vars
do
eval ac_val=\$$ac_var
case $ac_val in
*\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
esac
$as_echo "$ac_var='\''$ac_val'\''"
done | sort
echo
if test -n "$ac_subst_files"; then
$as_echo "## ------------------- ##
## File substitutions. ##
## ------------------- ##"
echo
for ac_var in $ac_subst_files
do
eval ac_val=\$$ac_var
case $ac_val in
*\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
esac
$as_echo "$ac_var='\''$ac_val'\''"
done | sort
echo
fi
if test -s confdefs.h; then
$as_echo "## ----------- ##
## confdefs.h. ##
## ----------- ##"
echo
cat confdefs.h
echo
fi
test "$ac_signal" != 0 &&
$as_echo "$as_me: caught signal $ac_signal"
$as_echo "$as_me: exit $exit_status"
} >&5
rm -f core *.core core.conftest.* &&
rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
exit $exit_status
' 0
for ac_signal in 1 2 13 15; do
trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
done
ac_signal=0
# confdefs.h avoids OS command line length limits that DEFS can exceed.
rm -f -r conftest* confdefs.h
$as_echo "/* confdefs.h */" > confdefs.h
# Predefined preprocessor variables.
cat >>confdefs.h <<_ACEOF
#define PACKAGE_NAME "$PACKAGE_NAME"
_ACEOF
cat >>confdefs.h <<_ACEOF
#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
_ACEOF
cat >>confdefs.h <<_ACEOF
#define PACKAGE_VERSION "$PACKAGE_VERSION"
_ACEOF
cat >>confdefs.h <<_ACEOF
#define PACKAGE_STRING "$PACKAGE_STRING"
_ACEOF
cat >>confdefs.h <<_ACEOF
#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
_ACEOF
cat >>confdefs.h <<_ACEOF
#define PACKAGE_URL "$PACKAGE_URL"
_ACEOF
# Let the site file select an alternate cache file if it wants to.
# Prefer an explicitly selected file to automatically selected ones.
ac_site_file1=NONE
ac_site_file2=NONE
if test -n "$CONFIG_SITE"; then
# We do not want a PATH search for config.site.
case $CONFIG_SITE in #((
-*) ac_site_file1=./$CONFIG_SITE;;
*/*) ac_site_file1=$CONFIG_SITE;;
*) ac_site_file1=./$CONFIG_SITE;;
esac
elif test "x$prefix" != xNONE; then
ac_site_file1=$prefix/share/config.site
ac_site_file2=$prefix/etc/config.site
else
ac_site_file1=$ac_default_prefix/share/config.site
ac_site_file2=$ac_default_prefix/etc/config.site
fi
for ac_site_file in "$ac_site_file1" "$ac_site_file2"
do
test "x$ac_site_file" = xNONE && continue
if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
$as_echo "$as_me: loading site script $ac_site_file" >&6;}
sed 's/^/| /' "$ac_site_file" >&5
. "$ac_site_file" \
|| { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "failed to load site script $ac_site_file
See \`config.log' for more details" "$LINENO" 5; }
fi
done
if test -r "$cache_file"; then
# Some versions of bash will fail to source /dev/null (special files
# actually), so we avoid doing that. DJGPP emulates it as a regular file.
if test /dev/null != "$cache_file" && test -f "$cache_file"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
$as_echo "$as_me: loading cache $cache_file" >&6;}
case $cache_file in
[\\/]* | ?:[\\/]* ) . "$cache_file";;
*) . "./$cache_file";;
esac
fi
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
$as_echo "$as_me: creating cache $cache_file" >&6;}
>$cache_file
fi
# Check that the precious variables saved in the cache have kept the same
# value.
ac_cache_corrupted=false
for ac_var in $ac_precious_vars; do
eval ac_old_set=\$ac_cv_env_${ac_var}_set
eval ac_new_set=\$ac_env_${ac_var}_set
eval ac_old_val=\$ac_cv_env_${ac_var}_value
eval ac_new_val=\$ac_env_${ac_var}_value
case $ac_old_set,$ac_new_set in
set,)
{ $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
ac_cache_corrupted=: ;;
,set)
{ $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
ac_cache_corrupted=: ;;
,);;
*)
if test "x$ac_old_val" != "x$ac_new_val"; then
# differences in whitespace do not lead to failure.
ac_old_val_w=`echo x $ac_old_val`
ac_new_val_w=`echo x $ac_new_val`
if test "$ac_old_val_w" != "$ac_new_val_w"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
ac_cache_corrupted=:
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
eval $ac_var=\$ac_old_val
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5
$as_echo "$as_me: former value: \`$ac_old_val'" >&2;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5
$as_echo "$as_me: current value: \`$ac_new_val'" >&2;}
fi;;
esac
# Pass precious variables to config.status.
if test "$ac_new_set" = set; then
case $ac_new_val in
*\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
*) ac_arg=$ac_var=$ac_new_val ;;
esac
case " $ac_configure_args " in
*" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
*) as_fn_append ac_configure_args " '$ac_arg'" ;;
esac
fi
done
if $ac_cache_corrupted; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
fi
## -------------------- ##
## Main body of script. ##
## -------------------- ##
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
UNBOUND_VERSION_MAJOR=1
-UNBOUND_VERSION_MINOR=18
+UNBOUND_VERSION_MINOR=19
UNBOUND_VERSION_MICRO=0
LIBUNBOUND_CURRENT=9
-LIBUNBOUND_REVISION=22
+LIBUNBOUND_REVISION=23
LIBUNBOUND_AGE=1
# 1.0.0 had 0:12:0
# 1.0.1 had 0:13:0
# 1.0.2 had 0:14:0
# 1.1.0 had 0:15:0
# 1.1.1 had 0:16:0
# 1.2.0 had 0:17:0
# 1.2.1 had 0:18:0
# 1.3.0 had 1:0:0 # ub_cancel and -export-symbols.
# 1.3.1 had 1:1:0
# 1.3.2 had 1:2:0
# 1.3.3 had 1:3:0
# 1.3.4 had 1:4:0
# 1.4.0-snapshots had 1:5:0
# 1.4.0 had 1:5:0 (not 2:0:0) # ub_result.why_bogus
# 1.4.1 had 2:1:0
# 1.4.2 had 2:2:0
# 1.4.3 had 2:3:0
# 1.4.4 had 2:4:0
# 1.4.5 had 2:5:0
# 1.4.6 had 2:6:0
# 1.4.7 had 2:7:0
# 1.4.8 had 2:8:0
# 1.4.9 had 2:9:0
# 1.4.10 had 2:10:0
# 1.4.11 had 2:11:0
# 1.4.12 had 2:12:0
# 1.4.13 had 2:13:0
# and 1.4.13p1 and 1.4.13.p2
# 1.4.14 had 2:14:0
# 1.4.15 had 3:0:1 # adds ub_version()
# 1.4.16 had 3:1:1
# 1.4.17 had 3:2:1
# 1.4.18 had 3:3:1
# 1.4.19 had 3:4:1
# 1.4.20 had 4:0:2 # adds libunbound.ttl # but shipped 3:5:1
# 1.4.21 had 4:1:2
# 1.4.22 had 4:1:2
# 1.5.0 had 5:3:3 # adds ub_ctx_add_ta_autr
# 1.5.1 had 5:3:3
# 1.5.2 had 5:5:3
# 1.5.3 had 5:6:3
# 1.5.4 had 5:7:3
# 1.5.5 had 5:8:3
# 1.5.6 had 5:9:3
# 1.5.7 had 5:10:3
# 1.5.8 had 6:0:4 # adds ub_ctx_set_stub
# 1.5.9 had 6:1:4
# 1.5.10 had 6:2:4
# 1.6.0 had 6:3:4
# 1.6.1 had 7:0:5 # ub_callback_t typedef renamed to ub_callback_type
# 1.6.2 had 7:1:5
# 1.6.3 had 7:2:5
# 1.6.4 had 7:3:5
# 1.6.5 had 7:4:5
# 1.6.6 had 7:5:5
# 1.6.7 had 7:6:5
# 1.6.8 had 7:7:5
# 1.7.0 had 7:8:5
# 1.7.1 had 7:9:5
# 1.7.2 had 7:10:5
# 1.7.3 had 7:11:5
# 1.8.0 had 8:0:0 # changes the event callback function signature
# 1.8.1 had 8:1:0
# 1.8.2 had 8:2:0
# 1.8.3 had 8:3:0
# 1.9.0 had 9:0:1 # add ub_ctx_set_tls
# 1.9.1 had 9:1:1
# 1.9.2 had 9:2:1
# 1.9.3 had 9:3:1
# 1.9.4 had 9:4:1
# 1.9.5 had 9:5:1
# 1.9.6 had 9:6:1
# 1.10.0 had 9:7:1
# 1.10.1 had 9:8:1
# 1.11.0 had 9:9:1
# 1.12.0 had 9:10:1
# 1.13.0 had 9:11:1
# 1.13.1 had 9:12:1
# 1.13.2 had 9:13:1
# 1.14.0 had 9:14:1
# 1.15.0 had 9:15:1
# 1.16.0 had 9:16:1
# 1.16.1 had 9:17:1
# 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
+# 1.19.0 had 9:23:1
# Current -- the number of the binary API that we're implementing
# Revision -- which iteration of the implementation of the binary
# API are we supplying?
# Age -- How many previous binary API versions do we also
# support?
#
# If we release a new version that does not change the binary API,
# increment Revision.
#
# If we release a new version that changes the binary API, but does
# not break programs compiled against the old binary API, increment
# Current and Age. Set Revision to 0, since this is the first
# implementation of the new API.
#
# Otherwise, we're changing the binary API and breaking backward
# compatibility with old binaries. Increment Current. Set Age to 0,
# since we're backward compatible with no previous APIs. Set Revision
# to 0 too.
cmdln="`echo $@ | sed -e 's/\\\\/\\\\\\\\/g' | sed -e 's/"/\\\\"/'g`"
cat >>confdefs.h <<_ACEOF
#define CONFCMDLINE "$cmdln"
_ACEOF
CFLAGS="$CFLAGS"
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
set dummy ${ac_tool_prefix}gcc; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_CC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$CC"; then
ac_cv_prog_CC="$CC" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_CC="${ac_tool_prefix}gcc"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
CC=$ac_cv_prog_CC
if test -n "$CC"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
$as_echo "$CC" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
if test -z "$ac_cv_prog_CC"; then
ac_ct_CC=$CC
# Extract the first word of "gcc", so it can be a program name with args.
set dummy gcc; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ac_ct_CC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_CC"; then
ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_CC="gcc"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
ac_ct_CC=$ac_cv_prog_ac_ct_CC
if test -n "$ac_ct_CC"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
$as_echo "$ac_ct_CC" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test "x$ac_ct_CC" = x; then
CC=""
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
CC=$ac_ct_CC
fi
else
CC="$ac_cv_prog_CC"
fi
if test -z "$CC"; then
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
set dummy ${ac_tool_prefix}cc; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_CC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$CC"; then
ac_cv_prog_CC="$CC" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_CC="${ac_tool_prefix}cc"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
CC=$ac_cv_prog_CC
if test -n "$CC"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
$as_echo "$CC" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
fi
if test -z "$CC"; then
# Extract the first word of "cc", so it can be a program name with args.
set dummy cc; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_CC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$CC"; then
ac_cv_prog_CC="$CC" # Let the user override the test.
else
ac_prog_rejected=no
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
ac_prog_rejected=yes
continue
fi
ac_cv_prog_CC="cc"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
if test $ac_prog_rejected = yes; then
# We found a bogon in the path, so make sure we never use it.
set dummy $ac_cv_prog_CC
shift
if test $# != 0; then
# We chose a different compiler from the bogus one.
# However, it has the same basename, so the bogon will be chosen
# first if we set CC to just the basename; use the full file name.
shift
ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
fi
fi
fi
fi
CC=$ac_cv_prog_CC
if test -n "$CC"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
$as_echo "$CC" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
if test -z "$CC"; then
if test -n "$ac_tool_prefix"; then
for ac_prog in cl.exe
do
# Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
set dummy $ac_tool_prefix$ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_CC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$CC"; then
ac_cv_prog_CC="$CC" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
CC=$ac_cv_prog_CC
if test -n "$CC"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
$as_echo "$CC" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
test -n "$CC" && break
done
fi
if test -z "$CC"; then
ac_ct_CC=$CC
for ac_prog in cl.exe
do
# Extract the first word of "$ac_prog", so it can be a program name with args.
set dummy $ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ac_ct_CC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_CC"; then
ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_CC="$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
ac_ct_CC=$ac_cv_prog_ac_ct_CC
if test -n "$ac_ct_CC"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
$as_echo "$ac_ct_CC" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
test -n "$ac_ct_CC" && break
done
if test "x$ac_ct_CC" = x; then
CC=""
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
CC=$ac_ct_CC
fi
fi
fi
test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "no acceptable C compiler found in \$PATH
See \`config.log' for more details" "$LINENO" 5; }
# Provide some information about the compiler.
$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
set X $ac_compile
ac_compiler=$2
for ac_option in --version -v -V -qversion; do
{ { ac_try="$ac_compiler $ac_option >&5"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
(eval "$ac_compiler $ac_option >&5") 2>conftest.err
ac_status=$?
if test -s conftest.err; then
sed '10a\
... rest of stderr output deleted ...
10q' conftest.err >conftest.er1
cat conftest.er1 >&5
fi
rm -f conftest.er1 conftest.err
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }
done
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
ac_clean_files_save=$ac_clean_files
ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
# Try to create an executable without -o first, disregard a.out.
# It will help us diagnose broken compilers, and finding out an intuition
# of exeext.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
$as_echo_n "checking whether the C compiler works... " >&6; }
ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
# The possible output files:
ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
ac_rmfiles=
for ac_file in $ac_files
do
case $ac_file in
*.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
* ) ac_rmfiles="$ac_rmfiles $ac_file";;
esac
done
rm -f $ac_rmfiles
if { { ac_try="$ac_link_default"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
(eval "$ac_link_default") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then :
# Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
# in a Makefile. We should not override ac_cv_exeext if it was cached,
# so that the user can short-circuit this test for compilers unknown to
# Autoconf.
for ac_file in $ac_files ''
do
test -f "$ac_file" || continue
case $ac_file in
*.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
;;
[ab].out )
# We found the default executable, but exeext='' is most
# certainly right.
break;;
*.* )
if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
then :; else
ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
fi
# We set ac_cv_exeext here because the later test for it is not
# safe: cross compilers may not add the suffix if given an `-o'
# argument, so we may need to know it at that point already.
# Even if this section looks crufty: it has the advantage of
# actually working.
break;;
* )
break;;
esac
done
test "$ac_cv_exeext" = no && ac_cv_exeext=
else
ac_file=''
fi
if test -z "$ac_file"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
$as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error 77 "C compiler cannot create executables
See \`config.log' for more details" "$LINENO" 5; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
$as_echo_n "checking for C compiler default output file name... " >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
$as_echo "$ac_file" >&6; }
ac_exeext=$ac_cv_exeext
rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
ac_clean_files=$ac_clean_files_save
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
$as_echo_n "checking for suffix of executables... " >&6; }
if { { ac_try="$ac_link"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
(eval "$ac_link") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then :
# If both `conftest.exe' and `conftest' are `present' (well, observable)
# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will
# work properly (i.e., refer to `conftest.exe'), while it won't with
# `rm'.
for ac_file in conftest.exe conftest conftest.*; do
test -f "$ac_file" || continue
case $ac_file in
*.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
*.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
break;;
* ) break;;
esac
done
else
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "cannot compute suffix of executables: cannot compile and link
See \`config.log' for more details" "$LINENO" 5; }
fi
rm -f conftest conftest$ac_cv_exeext
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
$as_echo "$ac_cv_exeext" >&6; }
rm -f conftest.$ac_ext
EXEEXT=$ac_cv_exeext
ac_exeext=$EXEEXT
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdio.h>
int
main ()
{
FILE *f = fopen ("conftest.out", "w");
return ferror (f) || fclose (f) != 0;
;
return 0;
}
_ACEOF
ac_clean_files="$ac_clean_files conftest.out"
# Check that the compiler produces executables we can run. If not, either
# the compiler is broken, or we cross compile.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
$as_echo_n "checking whether we are cross compiling... " >&6; }
if test "$cross_compiling" != yes; then
{ { ac_try="$ac_link"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
(eval "$ac_link") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }
if { ac_try='./conftest$ac_cv_exeext'
{ { case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
(eval "$ac_try") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; }; then
cross_compiling=no
else
if test "$cross_compiling" = maybe; then
cross_compiling=yes
else
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "cannot run C compiled programs.
If you meant to cross compile, use \`--host'.
See \`config.log' for more details" "$LINENO" 5; }
fi
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
$as_echo "$cross_compiling" >&6; }
rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
ac_clean_files=$ac_clean_files_save
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
$as_echo_n "checking for suffix of object files... " >&6; }
if ${ac_cv_objext+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
rm -f conftest.o conftest.obj
if { { ac_try="$ac_compile"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
(eval "$ac_compile") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then :
for ac_file in conftest.o conftest.obj conftest.*; do
test -f "$ac_file" || continue;
case $ac_file in
*.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
*) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
break;;
esac
done
else
$as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "cannot compute suffix of object files: cannot compile
See \`config.log' for more details" "$LINENO" 5; }
fi
rm -f conftest.$ac_cv_objext conftest.$ac_ext
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
$as_echo "$ac_cv_objext" >&6; }
OBJEXT=$ac_cv_objext
ac_objext=$OBJEXT
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
if ${ac_cv_c_compiler_gnu+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
#ifndef __GNUC__
choke me
#endif
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
ac_compiler_gnu=yes
else
ac_compiler_gnu=no
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
ac_cv_c_compiler_gnu=$ac_compiler_gnu
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
$as_echo "$ac_cv_c_compiler_gnu" >&6; }
if test $ac_compiler_gnu = yes; then
GCC=yes
else
GCC=
fi
ac_test_CFLAGS=${CFLAGS+set}
ac_save_CFLAGS=$CFLAGS
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
$as_echo_n "checking whether $CC accepts -g... " >&6; }
if ${ac_cv_prog_cc_g+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_save_c_werror_flag=$ac_c_werror_flag
ac_c_werror_flag=yes
ac_cv_prog_cc_g=no
CFLAGS="-g"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
ac_cv_prog_cc_g=yes
else
CFLAGS=""
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
else
ac_c_werror_flag=$ac_save_c_werror_flag
CFLAGS="-g"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
ac_cv_prog_cc_g=yes
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
ac_c_werror_flag=$ac_save_c_werror_flag
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
$as_echo "$ac_cv_prog_cc_g" >&6; }
if test "$ac_test_CFLAGS" = set; then
CFLAGS=$ac_save_CFLAGS
elif test $ac_cv_prog_cc_g = yes; then
if test "$GCC" = yes; then
CFLAGS="-g -O2"
else
CFLAGS="-g"
fi
else
if test "$GCC" = yes; then
CFLAGS="-O2"
else
CFLAGS=
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
if ${ac_cv_prog_cc_c89+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_prog_cc_c89=no
ac_save_CC=$CC
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdarg.h>
#include <stdio.h>
struct stat;
/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
struct buf { int x; };
FILE * (*rcsopen) (struct buf *, struct stat *, int);
static char *e (p, i)
char **p;
int i;
{
return p[i];
}
static char *f (char * (*g) (char **, int), char **p, ...)
{
char *s;
va_list v;
va_start (v,p);
s = g (p, va_arg (v,int));
va_end (v);
return s;
}
/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
function prototypes and stuff, but not '\xHH' hex character constants.
These don't provoke an error unfortunately, instead are silently treated
as 'x'. The following induces an error, until -std is added to get
proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
array size at least. It's necessary to write '\x00'==0 to get something
that's true only with -std. */
int osf4_cc_array ['\x00' == 0 ? 1 : -1];
/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
inside strings and character constants. */
#define FOO(x) 'x'
int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
int test (int i, double x);
struct s1 {int (*f) (int a);};
struct s2 {int (*f) (double a);};
int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
int argc;
char **argv;
int
main ()
{
return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
;
return 0;
}
_ACEOF
for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
-Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
do
CC="$ac_save_CC $ac_arg"
if ac_fn_c_try_compile "$LINENO"; then :
ac_cv_prog_cc_c89=$ac_arg
fi
rm -f core conftest.err conftest.$ac_objext
test "x$ac_cv_prog_cc_c89" != "xno" && break
done
rm -f conftest.$ac_ext
CC=$ac_save_CC
fi
# AC_CACHE_VAL
case "x$ac_cv_prog_cc_c89" in
x)
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
$as_echo "none needed" >&6; } ;;
xno)
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
$as_echo "unsupported" >&6; } ;;
*)
CC="$CC $ac_cv_prog_cc_c89"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
esac
if test "x$ac_cv_prog_cc_c89" != xno; then :
fi
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
$as_echo_n "checking how to run the C preprocessor... " >&6; }
# On Suns, sometimes $CPP names a directory.
if test -n "$CPP" && test -d "$CPP"; then
CPP=
fi
if test -z "$CPP"; then
if ${ac_cv_prog_CPP+:} false; then :
$as_echo_n "(cached) " >&6
else
# Double quotes because CPP needs to be expanded
for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
do
ac_preproc_ok=false
for ac_c_preproc_warn_flag in '' yes
do
# Use a header file that comes with gcc, so configuring glibc
# with a fresh cross-compiler works.
# Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
# <limits.h> exists even on freestanding compilers.
# On the NeXT, cc -E runs the code through the compiler's parser,
# not just through cpp. "Syntax error" is here to catch this case.
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#ifdef __STDC__
# include <limits.h>
#else
# include <assert.h>
#endif
Syntax error
_ACEOF
if ac_fn_c_try_cpp "$LINENO"; then :
else
# Broken: fails on valid input.
continue
fi
rm -f conftest.err conftest.i conftest.$ac_ext
# OK, works on sane cases. Now check whether nonexistent headers
# can be detected and how.
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <ac_nonexistent.h>
_ACEOF
if ac_fn_c_try_cpp "$LINENO"; then :
# Broken: success on invalid input.
continue
else
# Passes both tests.
ac_preproc_ok=:
break
fi
rm -f conftest.err conftest.i conftest.$ac_ext
done
# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
rm -f conftest.i conftest.err conftest.$ac_ext
if $ac_preproc_ok; then :
break
fi
done
ac_cv_prog_CPP=$CPP
fi
CPP=$ac_cv_prog_CPP
else
ac_cv_prog_CPP=$CPP
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
$as_echo "$CPP" >&6; }
ac_preproc_ok=false
for ac_c_preproc_warn_flag in '' yes
do
# Use a header file that comes with gcc, so configuring glibc
# with a fresh cross-compiler works.
# Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
# <limits.h> exists even on freestanding compilers.
# On the NeXT, cc -E runs the code through the compiler's parser,
# not just through cpp. "Syntax error" is here to catch this case.
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#ifdef __STDC__
# include <limits.h>
#else
# include <assert.h>
#endif
Syntax error
_ACEOF
if ac_fn_c_try_cpp "$LINENO"; then :
else
# Broken: fails on valid input.
continue
fi
rm -f conftest.err conftest.i conftest.$ac_ext
# OK, works on sane cases. Now check whether nonexistent headers
# can be detected and how.
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <ac_nonexistent.h>
_ACEOF
if ac_fn_c_try_cpp "$LINENO"; then :
# Broken: success on invalid input.
continue
else
# Passes both tests.
ac_preproc_ok=:
break
fi
rm -f conftest.err conftest.i conftest.$ac_ext
done
# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
rm -f conftest.i conftest.err conftest.$ac_ext
if $ac_preproc_ok; then :
else
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
See \`config.log' for more details" "$LINENO" 5; }
fi
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
if ${ac_cv_path_GREP+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -z "$GREP"; then
ac_path_GREP_found=false
# Loop through the user's path and test for each of PROGNAME-LIST
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_prog in grep ggrep; do
for ac_exec_ext in '' $ac_executable_extensions; do
ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
as_fn_executable_p "$ac_path_GREP" || continue
# Check for GNU ac_path_GREP and select it if it is found.
# Check for GNU $ac_path_GREP
case `"$ac_path_GREP" --version 2>&1` in
*GNU*)
ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
*)
ac_count=0
$as_echo_n 0123456789 >"conftest.in"
while :
do
cat "conftest.in" "conftest.in" >"conftest.tmp"
mv "conftest.tmp" "conftest.in"
cp "conftest.in" "conftest.nl"
$as_echo 'GREP' >> "conftest.nl"
"$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
as_fn_arith $ac_count + 1 && ac_count=$as_val
if test $ac_count -gt ${ac_path_GREP_max-0}; then
# Best one so far, save it but keep looking for a better one
ac_cv_path_GREP="$ac_path_GREP"
ac_path_GREP_max=$ac_count
fi
# 10*(2^10) chars as input seems more than enough
test $ac_count -gt 10 && break
done
rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
esac
$ac_path_GREP_found && break 3
done
done
done
IFS=$as_save_IFS
if test -z "$ac_cv_path_GREP"; then
as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
fi
else
ac_cv_path_GREP=$GREP
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5
$as_echo "$ac_cv_path_GREP" >&6; }
GREP="$ac_cv_path_GREP"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5
$as_echo_n "checking for egrep... " >&6; }
if ${ac_cv_path_EGREP+:} false; then :
$as_echo_n "(cached) " >&6
else
if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
then ac_cv_path_EGREP="$GREP -E"
else
if test -z "$EGREP"; then
ac_path_EGREP_found=false
# Loop through the user's path and test for each of PROGNAME-LIST
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_prog in egrep; do
for ac_exec_ext in '' $ac_executable_extensions; do
ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
as_fn_executable_p "$ac_path_EGREP" || continue
# Check for GNU ac_path_EGREP and select it if it is found.
# Check for GNU $ac_path_EGREP
case `"$ac_path_EGREP" --version 2>&1` in
*GNU*)
ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
*)
ac_count=0
$as_echo_n 0123456789 >"conftest.in"
while :
do
cat "conftest.in" "conftest.in" >"conftest.tmp"
mv "conftest.tmp" "conftest.in"
cp "conftest.in" "conftest.nl"
$as_echo 'EGREP' >> "conftest.nl"
"$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
as_fn_arith $ac_count + 1 && ac_count=$as_val
if test $ac_count -gt ${ac_path_EGREP_max-0}; then
# Best one so far, save it but keep looking for a better one
ac_cv_path_EGREP="$ac_path_EGREP"
ac_path_EGREP_max=$ac_count
fi
# 10*(2^10) chars as input seems more than enough
test $ac_count -gt 10 && break
done
rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
esac
$ac_path_EGREP_found && break 3
done
done
done
IFS=$as_save_IFS
if test -z "$ac_cv_path_EGREP"; then
as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
fi
else
ac_cv_path_EGREP=$EGREP
fi
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5
$as_echo "$ac_cv_path_EGREP" >&6; }
EGREP="$ac_cv_path_EGREP"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
$as_echo_n "checking for ANSI C header files... " >&6; }
if ${ac_cv_header_stdc+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <float.h>
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
ac_cv_header_stdc=yes
else
ac_cv_header_stdc=no
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
if test $ac_cv_header_stdc = yes; then
# SunOS 4.x string.h does not declare mem*, contrary to ANSI.
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <string.h>
_ACEOF
if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
$EGREP "memchr" >/dev/null 2>&1; then :
else
ac_cv_header_stdc=no
fi
rm -f conftest*
fi
if test $ac_cv_header_stdc = yes; then
# ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdlib.h>
_ACEOF
if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
$EGREP "free" >/dev/null 2>&1; then :
else
ac_cv_header_stdc=no
fi
rm -f conftest*
fi
if test $ac_cv_header_stdc = yes; then
# /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
if test "$cross_compiling" = yes; then :
:
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <ctype.h>
#include <stdlib.h>
#if ((' ' & 0x0FF) == 0x020)
# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
#else
# define ISLOWER(c) \
(('a' <= (c) && (c) <= 'i') \
|| ('j' <= (c) && (c) <= 'r') \
|| ('s' <= (c) && (c) <= 'z'))
# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
#endif
#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
int
main ()
{
int i;
for (i = 0; i < 256; i++)
if (XOR (islower (i), ISLOWER (i))
|| toupper (i) != TOUPPER (i))
return 2;
return 0;
}
_ACEOF
if ac_fn_c_try_run "$LINENO"; then :
else
ac_cv_header_stdc=no
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
$as_echo "$ac_cv_header_stdc" >&6; }
if test $ac_cv_header_stdc = yes; then
$as_echo "#define STDC_HEADERS 1" >>confdefs.h
fi
# On IRIX 5.3, sys/types and inttypes.h are conflicting.
for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
inttypes.h stdint.h unistd.h
do :
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
"
if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
cat >>confdefs.h <<_ACEOF
#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
_ACEOF
fi
done
ac_fn_c_check_header_mongrel "$LINENO" "minix/config.h" "ac_cv_header_minix_config_h" "$ac_includes_default"
if test "x$ac_cv_header_minix_config_h" = xyes; then :
MINIX=yes
else
MINIX=
fi
if test "$MINIX" = yes; then
$as_echo "#define _POSIX_SOURCE 1" >>confdefs.h
$as_echo "#define _POSIX_1_SOURCE 2" >>confdefs.h
$as_echo "#define _MINIX 1" >>confdefs.h
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to define __EXTENSIONS__" >&5
$as_echo_n "checking whether it is safe to define __EXTENSIONS__... " >&6; }
if ${ac_cv_safe_to_define___extensions__+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
# define __EXTENSIONS__ 1
$ac_includes_default
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
ac_cv_safe_to_define___extensions__=yes
else
ac_cv_safe_to_define___extensions__=no
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_define___extensions__" >&5
$as_echo "$ac_cv_safe_to_define___extensions__" >&6; }
test $ac_cv_safe_to_define___extensions__ = yes &&
$as_echo "#define __EXTENSIONS__ 1" >>confdefs.h
$as_echo "#define _ALL_SOURCE 1" >>confdefs.h
$as_echo "#define _GNU_SOURCE 1" >>confdefs.h
$as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h
$as_echo "#define _TANDEM_SOURCE 1" >>confdefs.h
if test "$ac_cv_header_minix_config_h" = "yes"; then
$as_echo "#define _NETBSD_SOURCE 1" >>confdefs.h
fi
case "$prefix" in
NONE)
prefix="/usr/local"
;;
esac
case "$exec_prefix" in
NONE)
exec_prefix="$prefix"
;;
esac
# are we on MinGW?
if uname -s 2>&1 | grep MINGW >/dev/null; then on_mingw="yes"
else
if echo $host | grep mingw >/dev/null; then on_mingw="yes"
else on_mingw="no"; fi
fi
#
# Determine configuration file
# the eval is to evaluate shell expansion twice
UNBOUND_SBIN_DIR=`eval echo "${sbindir}"`
UNBOUND_SYSCONF_DIR=`eval echo "${sysconfdir}"`
UNBOUND_LOCALSTATE_DIR=`eval echo "${localstatedir}"`
if test $on_mingw = "no"; then
ub_conf_file=`eval echo "${sysconfdir}/unbound/unbound.conf"`
else
ub_conf_file="C:\\Program Files\\Unbound\\service.conf"
fi
# Check whether --with-conf_file was given.
if test "${with_conf_file+set}" = set; then :
withval=$with_conf_file; ub_conf_file="$withval"
fi
hdr_config="`echo $ub_conf_file | sed -e 's/\\\\/\\\\\\\\/g'`"
cat >>confdefs.h <<_ACEOF
#define CONFIGFILE "$hdr_config"
_ACEOF
ub_conf_dir=`$as_dirname -- "$ub_conf_file" ||
$as_expr X"$ub_conf_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
X"$ub_conf_file" : 'X\(//\)[^/]' \| \
X"$ub_conf_file" : 'X\(//\)$' \| \
X"$ub_conf_file" : 'X\(/\)' \| . 2>/dev/null ||
$as_echo X"$ub_conf_file" |
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
s//\1/
q
}
/^X\(\/\/\)[^/].*/{
s//\1/
q
}
/^X\(\/\/\)$/{
s//\1/
q
}
/^X\(\/\).*/{
s//\1/
q
}
s/.*/./; q'`
# Determine run, chroot directory and pidfile locations
# Check whether --with-run-dir was given.
if test "${with_run_dir+set}" = set; then :
withval=$with_run_dir; UNBOUND_RUN_DIR="$withval"
else
if test $on_mingw = no; then
UNBOUND_RUN_DIR=`dirname "$ub_conf_file"`
else
UNBOUND_RUN_DIR=""
fi
fi
hdr_run="`echo $UNBOUND_RUN_DIR | sed -e 's/\\\\/\\\\\\\\/g'`"
cat >>confdefs.h <<_ACEOF
#define RUN_DIR "$hdr_run"
_ACEOF
# Check whether --with-chroot-dir was given.
if test "${with_chroot_dir+set}" = set; then :
withval=$with_chroot_dir; UNBOUND_CHROOT_DIR="$withval"
else
if test $on_mingw = no; then
UNBOUND_CHROOT_DIR="$UNBOUND_RUN_DIR"
else
UNBOUND_CHROOT_DIR=""
fi
fi
hdr_chroot="`echo $UNBOUND_CHROOT_DIR | sed -e 's/\\\\/\\\\\\\\/g'`"
cat >>confdefs.h <<_ACEOF
#define CHROOT_DIR "$hdr_chroot"
_ACEOF
# Check whether --with-share-dir was given.
if test "${with_share_dir+set}" = set; then :
withval=$with_share_dir; UNBOUND_SHARE_DIR="$withval"
else
UNBOUND_SHARE_DIR="$UNBOUND_RUN_DIR"
fi
cat >>confdefs.h <<_ACEOF
#define SHARE_DIR "$UNBOUND_SHARE_DIR"
_ACEOF
# Check whether --with-pidfile was given.
if test "${with_pidfile+set}" = set; then :
withval=$with_pidfile; UNBOUND_PIDFILE="$withval"
else
if test $on_mingw = no; then
UNBOUND_PIDFILE="$UNBOUND_RUN_DIR/unbound.pid"
else
UNBOUND_PIDFILE=""
fi
fi
hdr_pid="`echo $UNBOUND_PIDFILE | sed -e 's/\\\\/\\\\\\\\/g'`"
cat >>confdefs.h <<_ACEOF
#define PIDFILE "$hdr_pid"
_ACEOF
# Check whether --with-rootkey-file was given.
if test "${with_rootkey_file+set}" = set; then :
withval=$with_rootkey_file; UNBOUND_ROOTKEY_FILE="$withval"
else
if test $on_mingw = no; then
UNBOUND_ROOTKEY_FILE="$UNBOUND_RUN_DIR/root.key"
else
UNBOUND_ROOTKEY_FILE="C:\\Program Files\\Unbound\\root.key"
fi
fi
hdr_rkey="`echo $UNBOUND_ROOTKEY_FILE | sed -e 's/\\\\/\\\\\\\\/g'`"
cat >>confdefs.h <<_ACEOF
#define ROOT_ANCHOR_FILE "$hdr_rkey"
_ACEOF
# Check whether --with-rootcert-file was given.
if test "${with_rootcert_file+set}" = set; then :
withval=$with_rootcert_file; UNBOUND_ROOTCERT_FILE="$withval"
else
if test $on_mingw = no; then
UNBOUND_ROOTCERT_FILE="$UNBOUND_RUN_DIR/icannbundle.pem"
else
UNBOUND_ROOTCERT_FILE="C:\\Program Files\\Unbound\\icannbundle.pem"
fi
fi
hdr_rpem="`echo $UNBOUND_ROOTCERT_FILE | sed -e 's/\\\\/\\\\\\\\/g'`"
cat >>confdefs.h <<_ACEOF
#define ROOT_CERT_FILE "$hdr_rpem"
_ACEOF
# Check whether --with-username was given.
if test "${with_username+set}" = set; then :
withval=$with_username; UNBOUND_USERNAME="$withval"
else
UNBOUND_USERNAME="unbound"
fi
cat >>confdefs.h <<_ACEOF
#define UB_USERNAME "$UNBOUND_USERNAME"
_ACEOF
$as_echo "#define WINVER 0x0502" >>confdefs.h
wnvs=`echo $PACKAGE_VERSION | sed -e 's/^[^0-9]*\([0-9][0-9]*\)[^0-9][^0-9]*\([0-9][0-9]*\)[^0-9][^0-9]*\([0-9][0-9]*\)[^0-9][^0-9]*\([0-9][0-9]*\).*$/\1,\2,\3,\4/' -e 's/^[^0-9]*\([0-9][0-9]*\)[^0-9][^0-9]*\([0-9][0-9]*\)[^0-9][^0-9]*\([0-9][0-9]*\)[^0-9]*$/\1,\2,\3,0/' `
cat >>confdefs.h <<_ACEOF
#define RSRC_PACKAGE_VERSION $wnvs
_ACEOF
# Checks for typedefs, structures, and compiler characteristics.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5
$as_echo_n "checking for an ANSI C-conforming const... " >&6; }
if ${ac_cv_c_const+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
#ifndef __cplusplus
/* Ultrix mips cc rejects this sort of thing. */
typedef int charset[2];
const charset cs = { 0, 0 };
/* SunOS 4.1.1 cc rejects this. */
char const *const *pcpcc;
char **ppc;
/* NEC SVR4.0.2 mips cc rejects this. */
struct point {int x, y;};
static struct point const zero = {0,0};
/* AIX XL C 1.02.0.0 rejects this.
It does not let you subtract one const X* pointer from another in
an arm of an if-expression whose if-part is not a constant
expression */
const char *g = "string";
pcpcc = &g + (g ? g-g : 0);
/* HPUX 7.0 cc rejects these. */
++pcpcc;
ppc = (char**) pcpcc;
pcpcc = (char const *const *) ppc;
{ /* SCO 3.2v4 cc rejects this sort of thing. */
char tx;
char *t = &tx;
char const *s = 0 ? (char *) 0 : (char const *) 0;
*t++ = 0;
if (s) return 0;
}
{ /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */
int x[] = {25, 17};
const int *foo = &x[0];
++foo;
}
{ /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */
typedef const int *iptr;
iptr p = 0;
++p;
}
{ /* AIX XL C 1.02.0.0 rejects this sort of thing, saying
"k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */
struct s { int j; const int *ap[3]; } bx;
struct s *b = &bx; b->j = 5;
}
{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */
const int foo = 10;
if (!foo) return 0;
}
return !cs[0] && !zero.x;
#endif
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
ac_cv_c_const=yes
else
ac_cv_c_const=no
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_const" >&5
$as_echo "$ac_cv_c_const" >&6; }
if test $ac_cv_c_const = no; then
$as_echo "#define const /**/" >>confdefs.h
fi
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
# allow user to override the -g -O2 flags.
default_cflags=no
if test "x$CFLAGS" = "x" ; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -g" >&5
$as_echo_n "checking whether $CC supports -g... " >&6; }
cache=`echo g | sed 'y%.=/+-%___p_%'`
if eval \${cv_prog_cc_flag_$cache+:} false; then :
$as_echo_n "(cached) " >&6
else
echo 'void f(void){}' >conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS -g -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_$cache=yes"
else
eval "cv_prog_cc_flag_$cache=no"
fi
rm -f conftest conftest.o conftest.c
fi
if eval "test \"`echo '$cv_prog_cc_flag_'$cache`\" = yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
:
CFLAGS="$CFLAGS -g"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
:
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -O2" >&5
$as_echo_n "checking whether $CC supports -O2... " >&6; }
cache=`echo O2 | sed 'y%.=/+-%___p_%'`
if eval \${cv_prog_cc_flag_$cache+:} false; then :
$as_echo_n "(cached) " >&6
else
echo 'void f(void){}' >conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS -O2 -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_$cache=yes"
else
eval "cv_prog_cc_flag_$cache=no"
fi
rm -f conftest conftest.o conftest.c
fi
if eval "test \"`echo '$cv_prog_cc_flag_'$cache`\" = yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
:
CFLAGS="$CFLAGS -O2"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
:
fi
default_cflags=yes
fi
-ac_ext=c
-ac_cpp='$CPP $CPPFLAGS'
-ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_c_compiler_gnu
-if test -n "$ac_tool_prefix"; then
- # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
-set dummy ${ac_tool_prefix}gcc; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_CC+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- if test -n "$CC"; then
- ac_cv_prog_CC="$CC" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
- IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
- for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
- ac_cv_prog_CC="${ac_tool_prefix}gcc"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
- break 2
- fi
-done
- done
-IFS=$as_save_IFS
-
-fi
-fi
-CC=$ac_cv_prog_CC
-if test -n "$CC"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
-$as_echo "$CC" >&6; }
-else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-fi
-
-
-fi
-if test -z "$ac_cv_prog_CC"; then
- ac_ct_CC=$CC
- # Extract the first word of "gcc", so it can be a program name with args.
-set dummy gcc; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_ac_ct_CC+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- if test -n "$ac_ct_CC"; then
- ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
- IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
- for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
- ac_cv_prog_ac_ct_CC="gcc"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
- break 2
- fi
-done
- done
-IFS=$as_save_IFS
-
-fi
-fi
-ac_ct_CC=$ac_cv_prog_ac_ct_CC
-if test -n "$ac_ct_CC"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
-$as_echo "$ac_ct_CC" >&6; }
-else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-fi
-
- if test "x$ac_ct_CC" = x; then
- CC=""
- else
- case $cross_compiling:$ac_tool_warned in
-yes:)
-{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
-$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
-ac_tool_warned=yes ;;
-esac
- CC=$ac_ct_CC
- fi
-else
- CC="$ac_cv_prog_CC"
-fi
-
-if test -z "$CC"; then
- if test -n "$ac_tool_prefix"; then
- # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
-set dummy ${ac_tool_prefix}cc; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_CC+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- if test -n "$CC"; then
- ac_cv_prog_CC="$CC" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
- IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
- for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
- ac_cv_prog_CC="${ac_tool_prefix}cc"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
- break 2
- fi
-done
- done
-IFS=$as_save_IFS
-
-fi
-fi
-CC=$ac_cv_prog_CC
-if test -n "$CC"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
-$as_echo "$CC" >&6; }
-else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-fi
-
-
- fi
-fi
-if test -z "$CC"; then
- # Extract the first word of "cc", so it can be a program name with args.
-set dummy cc; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_CC+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- if test -n "$CC"; then
- ac_cv_prog_CC="$CC" # Let the user override the test.
-else
- ac_prog_rejected=no
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
- IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
- for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
- if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
- ac_prog_rejected=yes
- continue
- fi
- ac_cv_prog_CC="cc"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
- break 2
- fi
-done
- done
-IFS=$as_save_IFS
-
-if test $ac_prog_rejected = yes; then
- # We found a bogon in the path, so make sure we never use it.
- set dummy $ac_cv_prog_CC
- shift
- if test $# != 0; then
- # We chose a different compiler from the bogus one.
- # However, it has the same basename, so the bogon will be chosen
- # first if we set CC to just the basename; use the full file name.
- shift
- ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
- fi
-fi
-fi
-fi
-CC=$ac_cv_prog_CC
-if test -n "$CC"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
-$as_echo "$CC" >&6; }
-else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-fi
-
-
-fi
-if test -z "$CC"; then
- if test -n "$ac_tool_prefix"; then
- for ac_prog in cl.exe
- do
- # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
-set dummy $ac_tool_prefix$ac_prog; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_CC+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- if test -n "$CC"; then
- ac_cv_prog_CC="$CC" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
- IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
- for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
- ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
- break 2
- fi
-done
- done
-IFS=$as_save_IFS
-
-fi
-fi
-CC=$ac_cv_prog_CC
-if test -n "$CC"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
-$as_echo "$CC" >&6; }
-else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-fi
-
-
- test -n "$CC" && break
- done
-fi
-if test -z "$CC"; then
- ac_ct_CC=$CC
- for ac_prog in cl.exe
-do
- # Extract the first word of "$ac_prog", so it can be a program name with args.
-set dummy $ac_prog; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_ac_ct_CC+:} false; then :
+ case $ac_cv_prog_cc_stdc in #(
+ no) :
+ ac_cv_prog_cc_c99=no; ac_cv_prog_cc_c89=no ;; #(
+ *) :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C99" >&5
+$as_echo_n "checking for $CC option to accept ISO C99... " >&6; }
+if ${ac_cv_prog_cc_c99+:} false; then :
$as_echo_n "(cached) " >&6
else
- if test -n "$ac_ct_CC"; then
- ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
- IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
- for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
- ac_cv_prog_ac_ct_CC="$ac_prog"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
- break 2
- fi
-done
- done
-IFS=$as_save_IFS
+ ac_cv_prog_cc_c99=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <wchar.h>
+#include <stdio.h>
-fi
-fi
-ac_ct_CC=$ac_cv_prog_ac_ct_CC
-if test -n "$ac_ct_CC"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
-$as_echo "$ac_ct_CC" >&6; }
-else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-fi
+// Check varargs macros. These examples are taken from C99 6.10.3.5.
+#define debug(...) fprintf (stderr, __VA_ARGS__)
+#define showlist(...) puts (#__VA_ARGS__)
+#define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__))
+static void
+test_varargs_macros (void)
+{
+ int x = 1234;
+ int y = 5678;
+ debug ("Flag");
+ debug ("X = %d\n", x);
+ showlist (The first, second, and third items.);
+ report (x>y, "x is %d but y is %d", x, y);
+}
+// Check long long types.
+#define BIG64 18446744073709551615ull
+#define BIG32 4294967295ul
+#define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0)
+#if !BIG_OK
+ your preprocessor is broken;
+#endif
+#if BIG_OK
+#else
+ your preprocessor is broken;
+#endif
+static long long int bignum = -9223372036854775807LL;
+static unsigned long long int ubignum = BIG64;
- test -n "$ac_ct_CC" && break
-done
+struct incomplete_array
+{
+ int datasize;
+ double data[];
+};
- if test "x$ac_ct_CC" = x; then
- CC=""
- else
- case $cross_compiling:$ac_tool_warned in
-yes:)
-{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
-$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
-ac_tool_warned=yes ;;
-esac
- CC=$ac_ct_CC
- fi
-fi
+struct named_init {
+ int number;
+ const wchar_t *name;
+ double average;
+};
-fi
+typedef const char *ccp;
+static inline int
+test_restrict (ccp restrict text)
+{
+ // See if C++-style comments work.
+ // Iterate through items via the restricted pointer.
+ // Also check for declarations in for loops.
+ for (unsigned int i = 0; *(text+i) != '\0'; ++i)
+ continue;
+ return 0;
+}
-test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error $? "no acceptable C compiler found in \$PATH
-See \`config.log' for more details" "$LINENO" 5; }
+// Check varargs and va_copy.
+static void
+test_varargs (const char *format, ...)
+{
+ va_list args;
+ va_start (args, format);
+ va_list args_copy;
+ va_copy (args_copy, args);
-# Provide some information about the compiler.
-$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
-set X $ac_compile
-ac_compiler=$2
-for ac_option in --version -v -V -qversion; do
- { { ac_try="$ac_compiler $ac_option >&5"
-case "(($ac_try" in
- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
- *) ac_try_echo=$ac_try;;
-esac
-eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-$as_echo "$ac_try_echo"; } >&5
- (eval "$ac_compiler $ac_option >&5") 2>conftest.err
- ac_status=$?
- if test -s conftest.err; then
- sed '10a\
-... rest of stderr output deleted ...
- 10q' conftest.err >conftest.er1
- cat conftest.er1 >&5
- fi
- rm -f conftest.er1 conftest.err
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
- test $ac_status = 0; }
-done
+ const char *str;
+ int number;
+ float fnumber;
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
-$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
-if ${ac_cv_c_compiler_gnu+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
+ while (*format)
+ {
+ switch (*format++)
+ {
+ case 's': // string
+ str = va_arg (args_copy, const char *);
+ break;
+ case 'd': // int
+ number = va_arg (args_copy, int);
+ break;
+ case 'f': // float
+ fnumber = va_arg (args_copy, double);
+ break;
+ default:
+ break;
+ }
+ }
+ va_end (args_copy);
+ va_end (args);
+}
int
main ()
{
-#ifndef __GNUC__
- choke me
-#endif
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
- ac_compiler_gnu=yes
-else
- ac_compiler_gnu=no
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-ac_cv_c_compiler_gnu=$ac_compiler_gnu
+ // Check bool.
+ _Bool success = false;
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
-$as_echo "$ac_cv_c_compiler_gnu" >&6; }
-if test $ac_compiler_gnu = yes; then
- GCC=yes
-else
- GCC=
-fi
-ac_test_CFLAGS=${CFLAGS+set}
-ac_save_CFLAGS=$CFLAGS
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
-$as_echo_n "checking whether $CC accepts -g... " >&6; }
-if ${ac_cv_prog_cc_g+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- ac_save_c_werror_flag=$ac_c_werror_flag
- ac_c_werror_flag=yes
- ac_cv_prog_cc_g=no
- CFLAGS="-g"
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
+ // Check restrict.
+ if (test_restrict ("String literal") == 0)
+ success = true;
+ char *restrict newvar = "Another string";
-int
-main ()
-{
+ // Check varargs.
+ test_varargs ("s, d' f .", "string", 65, 34.234);
+ test_varargs_macros ();
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
- ac_cv_prog_cc_g=yes
-else
- CFLAGS=""
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
+ // Check flexible array members.
+ struct incomplete_array *ia =
+ malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10));
+ ia->datasize = 10;
+ for (int i = 0; i < ia->datasize; ++i)
+ ia->data[i] = i * 1.234;
-int
-main ()
-{
+ // Check named initializers.
+ struct named_init ni = {
+ .number = 34,
+ .name = L"Test wide string",
+ .average = 543.34343,
+ };
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+ ni.number = 58;
-else
- ac_c_werror_flag=$ac_save_c_werror_flag
- CFLAGS="-g"
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
+ int dynamic_array[ni.number];
+ dynamic_array[ni.number - 1] = 543;
-int
-main ()
-{
+ // work around unused variable warnings
+ return (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == 'x'
+ || dynamic_array[ni.number - 1] != 543);
;
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
- ac_cv_prog_cc_g=yes
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+for ac_arg in '' -std=gnu99 -std=c99 -c99 -AC99 -D_STDC_C99= -qlanglvl=extc99
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_c99=$ac_arg
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
- ac_c_werror_flag=$ac_save_c_werror_flag
+rm -f core conftest.err conftest.$ac_objext
+ test "x$ac_cv_prog_cc_c99" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
-$as_echo "$ac_cv_prog_cc_g" >&6; }
-if test "$ac_test_CFLAGS" = set; then
- CFLAGS=$ac_save_CFLAGS
-elif test $ac_cv_prog_cc_g = yes; then
- if test "$GCC" = yes; then
- CFLAGS="-g -O2"
- else
- CFLAGS="-g"
- fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c99" in
+ x)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+ xno)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+ *)
+ CC="$CC $ac_cv_prog_cc_c99"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5
+$as_echo "$ac_cv_prog_cc_c99" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c99" != xno; then :
+ ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99
else
- if test "$GCC" = yes; then
- CFLAGS="-O2"
- else
- CFLAGS=
- fi
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
if ${ac_cv_prog_cc_c89+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_prog_cc_c89=no
ac_save_CC=$CC
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdarg.h>
#include <stdio.h>
struct stat;
/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
struct buf { int x; };
FILE * (*rcsopen) (struct buf *, struct stat *, int);
static char *e (p, i)
char **p;
int i;
{
return p[i];
}
static char *f (char * (*g) (char **, int), char **p, ...)
{
char *s;
va_list v;
va_start (v,p);
s = g (p, va_arg (v,int));
va_end (v);
return s;
}
/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
function prototypes and stuff, but not '\xHH' hex character constants.
These don't provoke an error unfortunately, instead are silently treated
as 'x'. The following induces an error, until -std is added to get
proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
array size at least. It's necessary to write '\x00'==0 to get something
that's true only with -std. */
int osf4_cc_array ['\x00' == 0 ? 1 : -1];
/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
inside strings and character constants. */
#define FOO(x) 'x'
int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
int test (int i, double x);
struct s1 {int (*f) (int a);};
struct s2 {int (*f) (double a);};
int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
int argc;
char **argv;
int
main ()
{
return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
;
return 0;
}
_ACEOF
for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
-Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
do
CC="$ac_save_CC $ac_arg"
if ac_fn_c_try_compile "$LINENO"; then :
ac_cv_prog_cc_c89=$ac_arg
fi
rm -f core conftest.err conftest.$ac_objext
test "x$ac_cv_prog_cc_c89" != "xno" && break
done
rm -f conftest.$ac_ext
CC=$ac_save_CC
fi
# AC_CACHE_VAL
case "x$ac_cv_prog_cc_c89" in
x)
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
$as_echo "none needed" >&6; } ;;
xno)
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
$as_echo "unsupported" >&6; } ;;
*)
CC="$CC $ac_cv_prog_cc_c89"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
esac
if test "x$ac_cv_prog_cc_c89" != xno; then :
+ ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89
+else
+ ac_cv_prog_cc_stdc=no
+fi
+fi
+ ;;
+esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO Standard C" >&5
+$as_echo_n "checking for $CC option to accept ISO Standard C... " >&6; }
+ if ${ac_cv_prog_cc_stdc+:} false; then :
+ $as_echo_n "(cached) " >&6
fi
-ac_ext=c
-ac_cpp='$CPP $CPPFLAGS'
-ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_c_compiler_gnu
+ case $ac_cv_prog_cc_stdc in #(
+ no) :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;; #(
+ '') :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;; #(
+ *) :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_stdc" >&5
+$as_echo "$ac_cv_prog_cc_stdc" >&6; } ;;
+esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $CC dependency flag" >&5
$as_echo_n "checking $CC dependency flag... " >&6; }
echo 'void f(void){}' >conftest.c
if test "`$CC -MM conftest.c 2>&1`" = "conftest.o: conftest.c"; then
DEPFLAG="-MM"
else
if test "`$CC -xM1 conftest.c 2>&1`" = "conftest.o: conftest.c"; then
DEPFLAG="-xM1"
else
DEPFLAG="-MM" # dunno do something
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $DEPFLAG" >&5
$as_echo "$DEPFLAG" >&6; }
rm -f conftest.c
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Werror" >&5
$as_echo_n "checking whether $CC supports -Werror... " >&6; }
cache=`echo Werror | sed 'y%.=/+-%___p_%'`
if eval \${cv_prog_cc_flag_$cache+:} false; then :
$as_echo_n "(cached) " >&6
else
echo 'void f(void){}' >conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS -Werror -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_$cache=yes"
else
eval "cv_prog_cc_flag_$cache=no"
fi
rm -f conftest conftest.o conftest.c
fi
if eval "test \"`echo '$cv_prog_cc_flag_'$cache`\" = yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
:
ERRFLAG="-Werror"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
:
ERRFLAG="-errwarn"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wall" >&5
$as_echo_n "checking whether $CC supports -Wall... " >&6; }
cache=`echo Wall | sed 'y%.=/+-%___p_%'`
if eval \${cv_prog_cc_flag_$cache+:} false; then :
$as_echo_n "(cached) " >&6
else
echo 'void f(void){}' >conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS -Wall -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_$cache=yes"
else
eval "cv_prog_cc_flag_$cache=no"
fi
rm -f conftest conftest.o conftest.c
fi
if eval "test \"`echo '$cv_prog_cc_flag_'$cache`\" = yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
:
ERRFLAG="$ERRFLAG -Wall"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
:
ERRFLAG="$ERRFLAG -errfmt"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -std=c99" >&5
$as_echo_n "checking whether $CC supports -std=c99... " >&6; }
cache=`echo std=c99 | sed 'y%.=/+-%___p_%'`
if eval \${cv_prog_cc_flag_$cache+:} false; then :
$as_echo_n "(cached) " >&6
else
echo 'void f(void){}' >conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS -std=c99 -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_$cache=yes"
else
eval "cv_prog_cc_flag_$cache=no"
fi
rm -f conftest conftest.o conftest.c
fi
if eval "test \"`echo '$cv_prog_cc_flag_'$cache`\" = yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
:
C99FLAG="-std=c99"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
:
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -xc99" >&5
$as_echo_n "checking whether $CC supports -xc99... " >&6; }
cache=`echo xc99 | sed 'y%.=/+-%___p_%'`
if eval \${cv_prog_cc_flag_$cache+:} false; then :
$as_echo_n "(cached) " >&6
else
echo 'void f(void){}' >conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS -xc99 -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_$cache=yes"
else
eval "cv_prog_cc_flag_$cache=no"
fi
rm -f conftest conftest.o conftest.c
fi
if eval "test \"`echo '$cv_prog_cc_flag_'$cache`\" = yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
:
C99FLAG="-xc99"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
:
fi
for ac_header in getopt.h time.h
do :
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
"
if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
cat >>confdefs.h <<_ACEOF
#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
_ACEOF
fi
done
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we need $C99FLAG -D__EXTENSIONS__ -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED=1 -D_ALL_SOURCE as a flag for $CC" >&5
$as_echo_n "checking whether we need $C99FLAG -D__EXTENSIONS__ -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED=1 -D_ALL_SOURCE as a flag for $CC... " >&6; }
cache=`$as_echo "$C99FLAG -D__EXTENSIONS__ -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED=1 -D_ALL_SOURCE" | $as_tr_sh`
if eval \${cv_prog_cc_flag_needed_$cache+:} false; then :
$as_echo_n "(cached) " >&6
else
echo '
#include "confdefs.h"
#include <stdlib.h>
#include <ctype.h>
#include <sys/time.h>
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#include <unistd.h>
#include <netdb.h>
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
int test(void) {
int a;
char **opts = NULL;
struct timeval tv;
char *t;
time_t time = 0;
char *buf = NULL;
const char* str = NULL;
struct msghdr msg;
msg.msg_control = 0;
t = ctime_r(&time, buf);
tv.tv_usec = 10;
srandom(32);
a = getopt(2, opts, "a");
a = isascii(32);
str = gai_strerror(0);
if(str && t && tv.tv_usec && msg.msg_control)
a = 0;
return a;
}
' > 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
if test -z "`$CC $CPPFLAGS $CFLAGS $C99FLAG -D__EXTENSIONS__ -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED=1 -D_ALL_SOURCE $ERRFLAG -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_needed_$cache=yes"
else
eval "cv_prog_cc_flag_needed_$cache=fail"
#echo 'Test with flag fails too!'
#cat conftest.c
#echo "$CC $CPPFLAGS $CFLAGS $C99FLAG -D__EXTENSIONS__ -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED=1 -D_ALL_SOURCE $ERRFLAG -c conftest.c 2>&1"
#echo `$CC $CPPFLAGS $CFLAGS $C99FLAG -D__EXTENSIONS__ -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED=1 -D_ALL_SOURCE $ERRFLAG -c conftest.c 2>&1`
#exit 1
fi
fi
rm -f conftest conftest.c conftest.o
fi
if eval "test \"`echo '$cv_prog_cc_flag_needed_'$cache`\" = yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
:
CFLAGS="$CFLAGS $C99FLAG -D__EXTENSIONS__ -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED=1 -D_ALL_SOURCE"
else
if eval "test \"`echo '$cv_prog_cc_flag_needed_'$cache`\" = no"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
#echo 'Test with flag is no!'
#cat conftest.c
#echo "$CC $CPPFLAGS $CFLAGS $C99FLAG -D__EXTENSIONS__ -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED=1 -D_ALL_SOURCE $ERRFLAG -c conftest.c 2>&1"
#echo `$CC $CPPFLAGS $CFLAGS $C99FLAG -D__EXTENSIONS__ -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED=1 -D_ALL_SOURCE $ERRFLAG -c conftest.c 2>&1`
#exit 1
:
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5
$as_echo "failed" >&6; }
:
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we need $C99FLAG -D__EXTENSIONS__ -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_ALL_SOURCE as a flag for $CC" >&5
$as_echo_n "checking whether we need $C99FLAG -D__EXTENSIONS__ -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_ALL_SOURCE as a flag for $CC... " >&6; }
cache=`$as_echo "$C99FLAG -D__EXTENSIONS__ -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_ALL_SOURCE" | $as_tr_sh`
if eval \${cv_prog_cc_flag_needed_$cache+:} false; then :
$as_echo_n "(cached) " >&6
else
echo '
#include "confdefs.h"
#include <stdlib.h>
#include <ctype.h>
#include <sys/time.h>
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#include <unistd.h>
#include <netdb.h>
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
int test(void) {
int a;
char **opts = NULL;
struct timeval tv;
char *t;
time_t time = 0;
char *buf = NULL;
const char* str = NULL;
struct msghdr msg;
msg.msg_control = 0;
t = ctime_r(&time, buf);
tv.tv_usec = 10;
srandom(32);
a = getopt(2, opts, "a");
a = isascii(32);
str = gai_strerror(0);
if(str && t && tv.tv_usec && msg.msg_control)
a = 0;
return a;
}
' > 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
if test -z "`$CC $CPPFLAGS $CFLAGS $C99FLAG -D__EXTENSIONS__ -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_ALL_SOURCE $ERRFLAG -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_needed_$cache=yes"
else
eval "cv_prog_cc_flag_needed_$cache=fail"
#echo 'Test with flag fails too!'
#cat conftest.c
#echo "$CC $CPPFLAGS $CFLAGS $C99FLAG -D__EXTENSIONS__ -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_ALL_SOURCE $ERRFLAG -c conftest.c 2>&1"
#echo `$CC $CPPFLAGS $CFLAGS $C99FLAG -D__EXTENSIONS__ -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_ALL_SOURCE $ERRFLAG -c conftest.c 2>&1`
#exit 1
fi
fi
rm -f conftest conftest.c conftest.o
fi
if eval "test \"`echo '$cv_prog_cc_flag_needed_'$cache`\" = yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
:
CFLAGS="$CFLAGS $C99FLAG -D__EXTENSIONS__ -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_ALL_SOURCE"
else
if eval "test \"`echo '$cv_prog_cc_flag_needed_'$cache`\" = no"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
#echo 'Test with flag is no!'
#cat conftest.c
#echo "$CC $CPPFLAGS $CFLAGS $C99FLAG -D__EXTENSIONS__ -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_ALL_SOURCE $ERRFLAG -c conftest.c 2>&1"
#echo `$CC $CPPFLAGS $CFLAGS $C99FLAG -D__EXTENSIONS__ -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_ALL_SOURCE $ERRFLAG -c conftest.c 2>&1`
#exit 1
:
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5
$as_echo "failed" >&6; }
:
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we need $C99FLAG as a flag for $CC" >&5
$as_echo_n "checking whether we need $C99FLAG as a flag for $CC... " >&6; }
cache=`$as_echo "$C99FLAG" | $as_tr_sh`
if eval \${cv_prog_cc_flag_needed_$cache+:} false; then :
$as_echo_n "(cached) " >&6
else
echo '
#include <stdbool.h>
#include <ctype.h>
int test(void) {
int a = 0;
return a;
}
' > 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
if test -z "`$CC $CPPFLAGS $CFLAGS $C99FLAG $ERRFLAG -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_needed_$cache=yes"
else
eval "cv_prog_cc_flag_needed_$cache=fail"
#echo 'Test with flag fails too!'
#cat conftest.c
#echo "$CC $CPPFLAGS $CFLAGS $C99FLAG $ERRFLAG -c conftest.c 2>&1"
#echo `$CC $CPPFLAGS $CFLAGS $C99FLAG $ERRFLAG -c conftest.c 2>&1`
#exit 1
fi
fi
rm -f conftest conftest.c conftest.o
fi
if eval "test \"`echo '$cv_prog_cc_flag_needed_'$cache`\" = yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
:
CFLAGS="$CFLAGS $C99FLAG"
else
if eval "test \"`echo '$cv_prog_cc_flag_needed_'$cache`\" = no"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
#echo 'Test with flag is no!'
#cat conftest.c
#echo "$CC $CPPFLAGS $CFLAGS $C99FLAG $ERRFLAG -c conftest.c 2>&1"
#echo `$CC $CPPFLAGS $CFLAGS $C99FLAG $ERRFLAG -c conftest.c 2>&1`
#exit 1
:
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5
$as_echo "failed" >&6; }
:
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we need -D_BSD_SOURCE -D_DEFAULT_SOURCE as a flag for $CC" >&5
$as_echo_n "checking whether we need -D_BSD_SOURCE -D_DEFAULT_SOURCE as a flag for $CC... " >&6; }
cache=_D_BSD_SOURCE__D_DEFAULT_SOURCE
if eval \${cv_prog_cc_flag_needed_$cache+:} false; then :
$as_echo_n "(cached) " >&6
else
echo '
#include <ctype.h>
int test(void) {
int a;
a = isascii(32);
return a;
}
' > 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
if test -z "`$CC $CPPFLAGS $CFLAGS -D_BSD_SOURCE -D_DEFAULT_SOURCE $ERRFLAG -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_needed_$cache=yes"
else
eval "cv_prog_cc_flag_needed_$cache=fail"
#echo 'Test with flag fails too!'
#cat conftest.c
#echo "$CC $CPPFLAGS $CFLAGS -D_BSD_SOURCE -D_DEFAULT_SOURCE $ERRFLAG -c conftest.c 2>&1"
#echo `$CC $CPPFLAGS $CFLAGS -D_BSD_SOURCE -D_DEFAULT_SOURCE $ERRFLAG -c conftest.c 2>&1`
#exit 1
fi
fi
rm -f conftest conftest.c conftest.o
fi
if eval "test \"`echo '$cv_prog_cc_flag_needed_'$cache`\" = yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
:
CFLAGS="$CFLAGS -D_BSD_SOURCE -D_DEFAULT_SOURCE"
else
if eval "test \"`echo '$cv_prog_cc_flag_needed_'$cache`\" = no"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
#echo 'Test with flag is no!'
#cat conftest.c
#echo "$CC $CPPFLAGS $CFLAGS -D_BSD_SOURCE -D_DEFAULT_SOURCE $ERRFLAG -c conftest.c 2>&1"
#echo `$CC $CPPFLAGS $CFLAGS -D_BSD_SOURCE -D_DEFAULT_SOURCE $ERRFLAG -c conftest.c 2>&1`
#exit 1
:
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5
$as_echo "failed" >&6; }
:
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we need -D_GNU_SOURCE as a flag for $CC" >&5
$as_echo_n "checking whether we need -D_GNU_SOURCE as a flag for $CC... " >&6; }
cache=_D_GNU_SOURCE
if eval \${cv_prog_cc_flag_needed_$cache+:} false; then :
$as_echo_n "(cached) " >&6
else
echo '
#include <netinet/in.h>
int test(void) {
struct in6_pktinfo inf;
int a = (int)sizeof(inf);
return a;
}
' > 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
if test -z "`$CC $CPPFLAGS $CFLAGS -D_GNU_SOURCE $ERRFLAG -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_needed_$cache=yes"
else
eval "cv_prog_cc_flag_needed_$cache=fail"
#echo 'Test with flag fails too!'
#cat conftest.c
#echo "$CC $CPPFLAGS $CFLAGS -D_GNU_SOURCE $ERRFLAG -c conftest.c 2>&1"
#echo `$CC $CPPFLAGS $CFLAGS -D_GNU_SOURCE $ERRFLAG -c conftest.c 2>&1`
#exit 1
fi
fi
rm -f conftest conftest.c conftest.o
fi
if eval "test \"`echo '$cv_prog_cc_flag_needed_'$cache`\" = yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
:
CFLAGS="$CFLAGS -D_GNU_SOURCE"
else
if eval "test \"`echo '$cv_prog_cc_flag_needed_'$cache`\" = no"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
#echo 'Test with flag is no!'
#cat conftest.c
#echo "$CC $CPPFLAGS $CFLAGS -D_GNU_SOURCE $ERRFLAG -c conftest.c 2>&1"
#echo `$CC $CPPFLAGS $CFLAGS -D_GNU_SOURCE $ERRFLAG -c conftest.c 2>&1`
#exit 1
:
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5
$as_echo "failed" >&6; }
:
fi
fi
# check again for GNU_SOURCE for setresgid. May fail if setresgid
# is not available at all. -D_FRSRESGID is to make this check unique.
# otherwise we would get the previous cached result.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we need -D_GNU_SOURCE -D_FRSRESGID as a flag for $CC" >&5
$as_echo_n "checking whether we need -D_GNU_SOURCE -D_FRSRESGID as a flag for $CC... " >&6; }
cache=_D_GNU_SOURCE__D_FRSRESGID
if eval \${cv_prog_cc_flag_needed_$cache+:} false; then :
$as_echo_n "(cached) " >&6
else
echo '
#include <unistd.h>
int test(void) {
int a = setresgid(0,0,0);
a = setresuid(0,0,0);
return a;
}
' > 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
if test -z "`$CC $CPPFLAGS $CFLAGS -D_GNU_SOURCE -D_FRSRESGID $ERRFLAG -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_needed_$cache=yes"
else
eval "cv_prog_cc_flag_needed_$cache=fail"
#echo 'Test with flag fails too!'
#cat conftest.c
#echo "$CC $CPPFLAGS $CFLAGS -D_GNU_SOURCE -D_FRSRESGID $ERRFLAG -c conftest.c 2>&1"
#echo `$CC $CPPFLAGS $CFLAGS -D_GNU_SOURCE -D_FRSRESGID $ERRFLAG -c conftest.c 2>&1`
#exit 1
fi
fi
rm -f conftest conftest.c conftest.o
fi
if eval "test \"`echo '$cv_prog_cc_flag_needed_'$cache`\" = yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
:
CFLAGS="$CFLAGS -D_GNU_SOURCE"
else
if eval "test \"`echo '$cv_prog_cc_flag_needed_'$cache`\" = no"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
#echo 'Test with flag is no!'
#cat conftest.c
#echo "$CC $CPPFLAGS $CFLAGS -D_GNU_SOURCE -D_FRSRESGID $ERRFLAG -c conftest.c 2>&1"
#echo `$CC $CPPFLAGS $CFLAGS -D_GNU_SOURCE -D_FRSRESGID $ERRFLAG -c conftest.c 2>&1`
#exit 1
:
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5
$as_echo "failed" >&6; }
:
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we need -D_POSIX_C_SOURCE=200112 as a flag for $CC" >&5
$as_echo_n "checking whether we need -D_POSIX_C_SOURCE=200112 as a flag for $CC... " >&6; }
cache=_D_POSIX_C_SOURCE_200112
if eval \${cv_prog_cc_flag_needed_$cache+:} false; then :
$as_echo_n "(cached) " >&6
else
echo '
#include "confdefs.h"
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#include <netdb.h>
int test(void) {
int a = 0;
char *t;
time_t time = 0;
char *buf = NULL;
const char* str = NULL;
t = ctime_r(&time, buf);
str = gai_strerror(0);
if(t && str)
a = 0;
return a;
}
' > 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
if test -z "`$CC $CPPFLAGS $CFLAGS -D_POSIX_C_SOURCE=200112 $ERRFLAG -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_needed_$cache=yes"
else
eval "cv_prog_cc_flag_needed_$cache=fail"
#echo 'Test with flag fails too!'
#cat conftest.c
#echo "$CC $CPPFLAGS $CFLAGS -D_POSIX_C_SOURCE=200112 $ERRFLAG -c conftest.c 2>&1"
#echo `$CC $CPPFLAGS $CFLAGS -D_POSIX_C_SOURCE=200112 $ERRFLAG -c conftest.c 2>&1`
#exit 1
fi
fi
rm -f conftest conftest.c conftest.o
fi
if eval "test \"`echo '$cv_prog_cc_flag_needed_'$cache`\" = yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
:
CFLAGS="$CFLAGS -D_POSIX_C_SOURCE=200112"
else
if eval "test \"`echo '$cv_prog_cc_flag_needed_'$cache`\" = no"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
#echo 'Test with flag is no!'
#cat conftest.c
#echo "$CC $CPPFLAGS $CFLAGS -D_POSIX_C_SOURCE=200112 $ERRFLAG -c conftest.c 2>&1"
#echo `$CC $CPPFLAGS $CFLAGS -D_POSIX_C_SOURCE=200112 $ERRFLAG -c conftest.c 2>&1`
#exit 1
:
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5
$as_echo "failed" >&6; }
:
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we need -D__EXTENSIONS__ as a flag for $CC" >&5
$as_echo_n "checking whether we need -D__EXTENSIONS__ as a flag for $CC... " >&6; }
cache=_D__EXTENSIONS__
if eval \${cv_prog_cc_flag_needed_$cache+:} false; then :
$as_echo_n "(cached) " >&6
else
echo '
#include "confdefs.h"
#include <stdlib.h>
#include <ctype.h>
#include <sys/time.h>
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#include <unistd.h>
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
int test(void) {
int a;
char **opts = NULL;
struct timeval tv;
tv.tv_usec = 10;
srandom(32);
a = getopt(2, opts, "a");
a = isascii(32);
if(tv.tv_usec)
a = 0;
return a;
}
' > 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
if test -z "`$CC $CPPFLAGS $CFLAGS -D__EXTENSIONS__ $ERRFLAG -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_needed_$cache=yes"
else
eval "cv_prog_cc_flag_needed_$cache=fail"
#echo 'Test with flag fails too!'
#cat conftest.c
#echo "$CC $CPPFLAGS $CFLAGS -D__EXTENSIONS__ $ERRFLAG -c conftest.c 2>&1"
#echo `$CC $CPPFLAGS $CFLAGS -D__EXTENSIONS__ $ERRFLAG -c conftest.c 2>&1`
#exit 1
fi
fi
rm -f conftest conftest.c conftest.o
fi
if eval "test \"`echo '$cv_prog_cc_flag_needed_'$cache`\" = yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
:
CFLAGS="$CFLAGS -D__EXTENSIONS__"
else
if eval "test \"`echo '$cv_prog_cc_flag_needed_'$cache`\" = no"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
#echo 'Test with flag is no!'
#cat conftest.c
#echo "$CC $CPPFLAGS $CFLAGS -D__EXTENSIONS__ $ERRFLAG -c conftest.c 2>&1"
#echo `$CC $CPPFLAGS $CFLAGS -D__EXTENSIONS__ $ERRFLAG -c conftest.c 2>&1`
#exit 1
:
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5
$as_echo "failed" >&6; }
:
fi
fi
# debug mode flags warnings
# Check whether --enable-checking was given.
if test "${enable_checking+set}" = set; then :
enableval=$enable_checking;
fi
# Check whether --enable-debug was given.
if test "${enable_debug+set}" = set; then :
enableval=$enable_debug;
fi
if test "$enable_debug" = "yes"; then debug_enabled="$enable_debug";
else debug_enabled="$enable_checking"; fi
case "$debug_enabled" in
yes)
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -W" >&5
$as_echo_n "checking whether $CC supports -W... " >&6; }
cache=`echo W | sed 'y%.=/+-%___p_%'`
if eval \${cv_prog_cc_flag_$cache+:} false; then :
$as_echo_n "(cached) " >&6
else
echo 'void f(void){}' >conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS -W -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_$cache=yes"
else
eval "cv_prog_cc_flag_$cache=no"
fi
rm -f conftest conftest.o conftest.c
fi
if eval "test \"`echo '$cv_prog_cc_flag_'$cache`\" = yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
:
CFLAGS="$CFLAGS -W"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
:
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wall" >&5
$as_echo_n "checking whether $CC supports -Wall... " >&6; }
cache=`echo Wall | sed 'y%.=/+-%___p_%'`
if eval \${cv_prog_cc_flag_$cache+:} false; then :
$as_echo_n "(cached) " >&6
else
echo 'void f(void){}' >conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS -Wall -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_$cache=yes"
else
eval "cv_prog_cc_flag_$cache=no"
fi
rm -f conftest conftest.o conftest.c
fi
if eval "test \"`echo '$cv_prog_cc_flag_'$cache`\" = yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
:
CFLAGS="$CFLAGS -Wall"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
:
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wextra" >&5
$as_echo_n "checking whether $CC supports -Wextra... " >&6; }
cache=`echo Wextra | sed 'y%.=/+-%___p_%'`
if eval \${cv_prog_cc_flag_$cache+:} false; then :
$as_echo_n "(cached) " >&6
else
echo 'void f(void){}' >conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS -Wextra -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_$cache=yes"
else
eval "cv_prog_cc_flag_$cache=no"
fi
rm -f conftest conftest.o conftest.c
fi
if eval "test \"`echo '$cv_prog_cc_flag_'$cache`\" = yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
:
CFLAGS="$CFLAGS -Wextra"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
:
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -Wdeclaration-after-statement" >&5
$as_echo_n "checking whether $CC supports -Wdeclaration-after-statement... " >&6; }
cache=`echo Wdeclaration-after-statement | sed 'y%.=/+-%___p_%'`
if eval \${cv_prog_cc_flag_$cache+:} false; then :
$as_echo_n "(cached) " >&6
else
echo 'void f(void){}' >conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS -Wdeclaration-after-statement -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_$cache=yes"
else
eval "cv_prog_cc_flag_$cache=no"
fi
rm -f conftest conftest.o conftest.c
fi
if eval "test \"`echo '$cv_prog_cc_flag_'$cache`\" = yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
:
CFLAGS="$CFLAGS -Wdeclaration-after-statement"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
:
fi
$as_echo "#define UNBOUND_DEBUG /**/" >>confdefs.h
;;
no|*)
# nothing to do.
;;
esac
if test "$default_cflags" = "yes"; then
# only when CFLAGS was "" at the start, if the users wants to
# override we shouldn't add default cflags, because they wouldn't
# be able to turn off these options and set the CFLAGS wanted.
# Check whether --enable-flto was given.
if test "${enable_flto+set}" = set; then :
enableval=$enable_flto;
fi
if test "x$enable_flto" != "xno"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC supports -flto" >&5
$as_echo_n "checking if $CC supports -flto... " >&6; }
BAKCFLAGS="$CFLAGS"
CFLAGS="$CFLAGS -flto"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
if $CC $CFLAGS -o conftest conftest.c 2>&1 | $GREP -e "warning: no debug symbols in executable" -e "warning: object" >/dev/null; then
CFLAGS="$BAKCFLAGS"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
fi
rm -f conftest conftest.c conftest.o
else
CFLAGS="$BAKCFLAGS" ; { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
fi
# Check whether --enable-pie was given.
if test "${enable_pie+set}" = set; then :
enableval=$enable_pie;
fi
if test "x$enable_pie" = "xyes"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC supports PIE" >&5
$as_echo_n "checking if $CC supports PIE... " >&6; }
BAKLDFLAGS="$LDFLAGS"
BAKCFLAGS="$CFLAGS"
LDFLAGS="$LDFLAGS -pie"
CFLAGS="$CFLAGS -fPIE"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
if $CC $CFLAGS $LDFLAGS -o conftest conftest.c 2>&1 | grep "warning: no debug symbols in executable" >/dev/null; then
LDFLAGS="$BAKLDFLAGS"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
fi
rm -f conftest conftest.c conftest.o
else
LDFLAGS="$BAKLDFLAGS" ; CFLAGS="$BAKCFLAGS" ; { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
fi
# Check whether --enable-relro_now was given.
if test "${enable_relro_now+set}" = set; then :
enableval=$enable_relro_now;
fi
if test "x$enable_relro_now" = "xyes"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC supports -Wl,-z,relro,-z,now" >&5
$as_echo_n "checking if $CC supports -Wl,-z,relro,-z,now... " >&6; }
BAKLDFLAGS="$LDFLAGS"
LDFLAGS="$LDFLAGS -Wl,-z,relro,-z,now"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
if $CC $CFLAGS $LDFLAGS -o conftest conftest.c 2>&1 | grep "warning: no debug symbols in executable" >/dev/null; then
LDFLAGS="$BAKLDFLAGS"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
fi
rm -f conftest conftest.c conftest.o
else
LDFLAGS="$BAKLDFLAGS" ; { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5
$as_echo_n "checking for inline... " >&6; }
if ${ac_cv_c_inline+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_c_inline=no
for ac_kw in inline __inline__ __inline; do
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#ifndef __cplusplus
typedef int foo_t;
static $ac_kw foo_t static_foo () {return 0; }
$ac_kw foo_t foo () {return 0; }
#endif
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
ac_cv_c_inline=$ac_kw
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
test "$ac_cv_c_inline" != no && break
done
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5
$as_echo "$ac_cv_c_inline" >&6; }
case $ac_cv_c_inline in
inline | yes) ;;
*)
case $ac_cv_c_inline in
no) ac_val=;;
*) ac_val=$ac_cv_c_inline;;
esac
cat >>confdefs.h <<_ACEOF
#ifndef __cplusplus
#define inline $ac_val
#endif
_ACEOF
;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler (${CC-cc}) accepts the \"format\" attribute" >&5
$as_echo_n "checking whether the C compiler (${CC-cc}) accepts the \"format\" attribute... " >&6; }
if ${ac_cv_c_format_attribute+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_c_format_attribute=no
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdio.h>
void f (char *format, ...) __attribute__ ((format (printf, 1, 2)));
void (*pf) (char *format, ...) __attribute__ ((format (printf, 1, 2)));
int
main ()
{
f ("%s", "str");
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
ac_cv_c_format_attribute="yes"
else
ac_cv_c_format_attribute="no"
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_format_attribute" >&5
$as_echo "$ac_cv_c_format_attribute" >&6; }
if test $ac_cv_c_format_attribute = yes; then
$as_echo "#define HAVE_ATTR_FORMAT 1" >>confdefs.h
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler (${CC-cc}) accepts the \"unused\" attribute" >&5
$as_echo_n "checking whether the C compiler (${CC-cc}) accepts the \"unused\" attribute... " >&6; }
if ${ac_cv_c_unused_attribute+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_c_unused_attribute=no
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdio.h>
void f (char *u __attribute__((unused)));
int
main ()
{
f ("x");
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
ac_cv_c_unused_attribute="yes"
else
ac_cv_c_unused_attribute="no"
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_unused_attribute" >&5
$as_echo "$ac_cv_c_unused_attribute" >&6; }
if test $ac_cv_c_unused_attribute = yes; then
$as_echo "#define HAVE_ATTR_UNUSED 1" >>confdefs.h
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler (${CC-cc}) accepts the \"weak\" attribute" >&5
$as_echo_n "checking whether the C compiler (${CC-cc}) accepts the \"weak\" attribute... " >&6; }
if ${ac_cv_c_weak_attribute+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_c_weak_attribute=no
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdio.h>
__attribute__((weak)) void f(int x) { printf("%d", x); }
int
main ()
{
f(1);
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
ac_cv_c_weak_attribute="yes"
else
ac_cv_c_weak_attribute="no"
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_weak_attribute" >&5
$as_echo "$ac_cv_c_weak_attribute" >&6; }
if test $ac_cv_c_weak_attribute = yes; then
$as_echo "#define HAVE_ATTR_WEAK 1" >>confdefs.h
$as_echo "#define ATTR_WEAK __attribute__((weak))" >>confdefs.h
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler (${CC-cc}) accepts the \"noreturn\" attribute" >&5
$as_echo_n "checking whether the C compiler (${CC-cc}) accepts the \"noreturn\" attribute... " >&6; }
if ${ac_cv_c_noreturn_attribute+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_c_noreturn_attribute=no
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdio.h>
__attribute__((noreturn)) void f(int x) { printf("%d", x); }
int
main ()
{
f(1);
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
ac_cv_c_noreturn_attribute="yes"
else
ac_cv_c_noreturn_attribute="no"
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_noreturn_attribute" >&5
$as_echo "$ac_cv_c_noreturn_attribute" >&6; }
if test $ac_cv_c_noreturn_attribute = yes; then
$as_echo "#define HAVE_ATTR_NORETURN 1" >>confdefs.h
$as_echo "#define ATTR_NORETURN __attribute__((__noreturn__))" >>confdefs.h
fi
if test "$srcdir" != "."; then
CPPFLAGS="$CPPFLAGS -I$srcdir"
fi
for ac_prog in flex lex
do
# Extract the first word of "$ac_prog", so it can be a program name with args.
set dummy $ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_LEX+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$LEX"; then
ac_cv_prog_LEX="$LEX" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_LEX="$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
LEX=$ac_cv_prog_LEX
if test -n "$LEX"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $LEX" >&5
$as_echo "$LEX" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
test -n "$LEX" && break
done
test -n "$LEX" || LEX=":"
if test "x$LEX" != "x:"; then
cat >conftest.l <<_ACEOF
%%
a { ECHO; }
b { REJECT; }
c { yymore (); }
d { yyless (1); }
e { /* IRIX 6.5 flex 2.5.4 underquotes its yyless argument. */
yyless ((input () != 0)); }
f { unput (yytext[0]); }
. { BEGIN INITIAL; }
%%
#ifdef YYTEXT_POINTER
extern char *yytext;
#endif
int
main (void)
{
return ! yylex () + ! yywrap ();
}
_ACEOF
{ { ac_try="$LEX conftest.l"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
(eval "$LEX conftest.l") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking lex output file root" >&5
$as_echo_n "checking lex output file root... " >&6; }
if ${ac_cv_prog_lex_root+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -f lex.yy.c; then
ac_cv_prog_lex_root=lex.yy
elif test -f lexyy.c; then
ac_cv_prog_lex_root=lexyy
else
as_fn_error $? "cannot find output from $LEX; giving up" "$LINENO" 5
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_lex_root" >&5
$as_echo "$ac_cv_prog_lex_root" >&6; }
LEX_OUTPUT_ROOT=$ac_cv_prog_lex_root
if test -z "${LEXLIB+set}"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking lex library" >&5
$as_echo_n "checking lex library... " >&6; }
if ${ac_cv_lib_lex+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_save_LIBS=$LIBS
ac_cv_lib_lex='none needed'
for ac_lib in '' -lfl -ll; do
LIBS="$ac_lib $ac_save_LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
`cat $LEX_OUTPUT_ROOT.c`
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_lex=$ac_lib
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
test "$ac_cv_lib_lex" != 'none needed' && break
done
LIBS=$ac_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lex" >&5
$as_echo "$ac_cv_lib_lex" >&6; }
test "$ac_cv_lib_lex" != 'none needed' && LEXLIB=$ac_cv_lib_lex
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether yytext is a pointer" >&5
$as_echo_n "checking whether yytext is a pointer... " >&6; }
if ${ac_cv_prog_lex_yytext_pointer+:} false; then :
$as_echo_n "(cached) " >&6
else
# POSIX says lex can declare yytext either as a pointer or an array; the
# default is implementation-dependent. Figure out which it is, since
# not all implementations provide the %pointer and %array declarations.
ac_cv_prog_lex_yytext_pointer=no
ac_save_LIBS=$LIBS
LIBS="$LEXLIB $ac_save_LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#define YYTEXT_POINTER 1
`cat $LEX_OUTPUT_ROOT.c`
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_prog_lex_yytext_pointer=yes
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_lex_yytext_pointer" >&5
$as_echo "$ac_cv_prog_lex_yytext_pointer" >&6; }
if test $ac_cv_prog_lex_yytext_pointer = yes; then
$as_echo "#define YYTEXT_POINTER 1" >>confdefs.h
fi
rm -f conftest.l $LEX_OUTPUT_ROOT.c
fi
if test "$LEX" != "" -a "$LEX" != ":"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for yylex_destroy" >&5
$as_echo_n "checking for yylex_destroy... " >&6; }
if echo %% | $LEX -t 2>&1 | grep yylex_destroy >/dev/null 2>&1; then
$as_echo "#define LEX_HAS_YYLEX_DESTROY 1" >>confdefs.h
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; };
LEX=":"
fi
fi
if test "$LEX" != "" -a "$LEX" != ":"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for lex %option" >&5
$as_echo_n "checking for lex %option... " >&6; }
if cat <<EOF | $LEX -t 2>&1 | grep yy_delete_buffer >/dev/null 2>&1; then
%option nounput
%%
EOF
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; };
LEX=":"
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.
set dummy $ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_YACC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$YACC"; then
ac_cv_prog_YACC="$YACC" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_YACC="$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
YACC=$ac_cv_prog_YACC
if test -n "$YACC"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $YACC" >&5
$as_echo "$YACC" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
test -n "$YACC" && break
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
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_doxygen+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$doxygen"; then
ac_cv_prog_doxygen="$doxygen" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_doxygen="doxygen"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
doxygen=$ac_cv_prog_doxygen
if test -n "$doxygen"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $doxygen" >&5
$as_echo "$doxygen" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
set dummy ${ac_tool_prefix}strip; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_STRIP+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$STRIP"; then
ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_STRIP="${ac_tool_prefix}strip"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
STRIP=$ac_cv_prog_STRIP
if test -n "$STRIP"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
$as_echo "$STRIP" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
if test -z "$ac_cv_prog_STRIP"; then
ac_ct_STRIP=$STRIP
# Extract the first word of "strip", so it can be a program name with args.
set dummy strip; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ac_ct_STRIP+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_STRIP"; then
ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_STRIP="strip"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
if test -n "$ac_ct_STRIP"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5
$as_echo "$ac_ct_STRIP" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test "x$ac_ct_STRIP" = x; then
STRIP=""
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
STRIP=$ac_ct_STRIP
fi
else
STRIP="$ac_cv_prog_STRIP"
fi
ac_aux_dir=
for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
if test -f "$ac_dir/install-sh"; then
ac_aux_dir=$ac_dir
ac_install_sh="$ac_aux_dir/install-sh -c"
break
elif test -f "$ac_dir/install.sh"; then
ac_aux_dir=$ac_dir
ac_install_sh="$ac_aux_dir/install.sh -c"
break
elif test -f "$ac_dir/shtool"; then
ac_aux_dir=$ac_dir
ac_install_sh="$ac_aux_dir/shtool install -c"
break
fi
done
if test -z "$ac_aux_dir"; then
as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5
fi
# These three variables are undocumented and unsupported,
# and are intended to be withdrawn in a future Autoconf release.
# They can cause serious problems if a builder's source tree is in a directory
# whose full name contains unusual characters.
ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var.
ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var.
ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
# Make sure we can run config.sub.
$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5
$as_echo_n "checking build system type... " >&6; }
if ${ac_cv_build+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_build_alias=$build_alias
test "x$ac_build_alias" = x &&
ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`
test "x$ac_build_alias" = x &&
as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5
ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` ||
as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5
$as_echo "$ac_cv_build" >&6; }
case $ac_cv_build in
*-*-*) ;;
*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;;
esac
build=$ac_cv_build
ac_save_IFS=$IFS; IFS='-'
set x $ac_cv_build
shift
build_cpu=$1
build_vendor=$2
shift; shift
# Remember, the first character of IFS is used to create $*,
# except with old shells:
build_os=$*
IFS=$ac_save_IFS
case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5
$as_echo_n "checking host system type... " >&6; }
if ${ac_cv_host+:} false; then :
$as_echo_n "(cached) " >&6
else
if test "x$host_alias" = x; then
ac_cv_host=$ac_cv_build
else
ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` ||
as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5
$as_echo "$ac_cv_host" >&6; }
case $ac_cv_host in
*-*-*) ;;
*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;;
esac
host=$ac_cv_host
ac_save_IFS=$IFS; IFS='-'
set x $ac_cv_host
shift
host_cpu=$1
host_vendor=$2
shift; shift
# Remember, the first character of IFS is used to create $*,
# except with old shells:
host_os=$*
IFS=$ac_save_IFS
case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
# skip these tests, we do not need them.
# always use ./libtool unless override from commandline (libtool=mylibtool)
if test -z "$libtool"; then
libtool="./libtool"
fi
# avoid libtool max commandline length test on systems that fork slowly.
if echo "$host_os" | grep "sunos4" >/dev/null; then
lt_cv_sys_max_cmd_len=32750;
fi
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args.
set dummy ${ac_tool_prefix}ar; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_path_AR+:} false; then :
$as_echo_n "(cached) " >&6
else
case $AR in
[\\/]* | ?:[\\/]*)
ac_cv_path_AR="$AR" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_path_AR="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
;;
esac
fi
AR=$ac_cv_path_AR
if test -n "$AR"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5
$as_echo "$AR" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
if test -z "$ac_cv_path_AR"; then
ac_pt_AR=$AR
# Extract the first word of "ar", so it can be a program name with args.
set dummy ar; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_path_ac_pt_AR+:} false; then :
$as_echo_n "(cached) " >&6
else
case $ac_pt_AR in
[\\/]* | ?:[\\/]*)
ac_cv_path_ac_pt_AR="$ac_pt_AR" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_path_ac_pt_AR="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
;;
esac
fi
ac_pt_AR=$ac_cv_path_ac_pt_AR
if test -n "$ac_pt_AR"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_AR" >&5
$as_echo "$ac_pt_AR" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test "x$ac_pt_AR" = x; then
AR="false"
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
AR=$ac_pt_AR
fi
else
AR="$ac_cv_path_AR"
fi
if test $AR = false; then
as_fn_error $? "Cannot find 'ar', please extend PATH to include it" "$LINENO" 5
fi
case `pwd` in
*\ * | *\ *)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5
$as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;;
esac
macro_version='2.4.6'
macro_revision='2.4.6'
ltmain=$ac_aux_dir/ltmain.sh
# Backslashify metacharacters that are still active within
# double-quoted strings.
sed_quote_subst='s/\(["`$\\]\)/\\\1/g'
# Same as above, but do not quote variable references.
double_quote_subst='s/\(["`\\]\)/\\\1/g'
# Sed substitution to delay expansion of an escaped shell variable in a
# double_quote_subst'ed string.
delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
# Sed substitution to delay expansion of an escaped single quote.
delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g'
# Sed substitution to avoid accidental globbing in evaled expressions
no_glob_subst='s/\*/\\\*/g'
ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO
ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to print strings" >&5
$as_echo_n "checking how to print strings... " >&6; }
# Test print first, because it will be a builtin if present.
if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \
test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then
ECHO='print -r --'
elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then
ECHO='printf %s\n'
else
# Use this function as a fallback that always works.
func_fallback_echo ()
{
eval 'cat <<_LTECHO_EOF
$1
_LTECHO_EOF'
}
ECHO='func_fallback_echo'
fi
# func_echo_all arg...
# Invoke $ECHO with all args, space-separated.
func_echo_all ()
{
$ECHO ""
}
case $ECHO in
printf*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: printf" >&5
$as_echo "printf" >&6; } ;;
print*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: print -r" >&5
$as_echo "print -r" >&6; } ;;
*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: cat" >&5
$as_echo "cat" >&6; } ;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5
$as_echo_n "checking for a sed that does not truncate output... " >&6; }
if ${ac_cv_path_SED+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
for ac_i in 1 2 3 4 5 6 7; do
ac_script="$ac_script$as_nl$ac_script"
done
echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed
{ ac_script=; unset ac_script;}
if test -z "$SED"; then
ac_path_SED_found=false
# Loop through the user's path and test for each of PROGNAME-LIST
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_prog in sed gsed; do
for ac_exec_ext in '' $ac_executable_extensions; do
ac_path_SED="$as_dir/$ac_prog$ac_exec_ext"
as_fn_executable_p "$ac_path_SED" || continue
# Check for GNU ac_path_SED and select it if it is found.
# Check for GNU $ac_path_SED
case `"$ac_path_SED" --version 2>&1` in
*GNU*)
ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;;
*)
ac_count=0
$as_echo_n 0123456789 >"conftest.in"
while :
do
cat "conftest.in" "conftest.in" >"conftest.tmp"
mv "conftest.tmp" "conftest.in"
cp "conftest.in" "conftest.nl"
$as_echo '' >> "conftest.nl"
"$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break
diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
as_fn_arith $ac_count + 1 && ac_count=$as_val
if test $ac_count -gt ${ac_path_SED_max-0}; then
# Best one so far, save it but keep looking for a better one
ac_cv_path_SED="$ac_path_SED"
ac_path_SED_max=$ac_count
fi
# 10*(2^10) chars as input seems more than enough
test $ac_count -gt 10 && break
done
rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
esac
$ac_path_SED_found && break 3
done
done
done
IFS=$as_save_IFS
if test -z "$ac_cv_path_SED"; then
as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5
fi
else
ac_cv_path_SED=$SED
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5
$as_echo "$ac_cv_path_SED" >&6; }
SED="$ac_cv_path_SED"
rm -f conftest.sed
test -z "$SED" && SED=sed
Xsed="$SED -e 1s/^X//"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5
$as_echo_n "checking for fgrep... " >&6; }
if ${ac_cv_path_FGREP+:} false; then :
$as_echo_n "(cached) " >&6
else
if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1
then ac_cv_path_FGREP="$GREP -F"
else
if test -z "$FGREP"; then
ac_path_FGREP_found=false
# Loop through the user's path and test for each of PROGNAME-LIST
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_prog in fgrep; do
for ac_exec_ext in '' $ac_executable_extensions; do
ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext"
as_fn_executable_p "$ac_path_FGREP" || continue
# Check for GNU ac_path_FGREP and select it if it is found.
# Check for GNU $ac_path_FGREP
case `"$ac_path_FGREP" --version 2>&1` in
*GNU*)
ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;;
*)
ac_count=0
$as_echo_n 0123456789 >"conftest.in"
while :
do
cat "conftest.in" "conftest.in" >"conftest.tmp"
mv "conftest.tmp" "conftest.in"
cp "conftest.in" "conftest.nl"
$as_echo 'FGREP' >> "conftest.nl"
"$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break
diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
as_fn_arith $ac_count + 1 && ac_count=$as_val
if test $ac_count -gt ${ac_path_FGREP_max-0}; then
# Best one so far, save it but keep looking for a better one
ac_cv_path_FGREP="$ac_path_FGREP"
ac_path_FGREP_max=$ac_count
fi
# 10*(2^10) chars as input seems more than enough
test $ac_count -gt 10 && break
done
rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
esac
$ac_path_FGREP_found && break 3
done
done
done
IFS=$as_save_IFS
if test -z "$ac_cv_path_FGREP"; then
as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
fi
else
ac_cv_path_FGREP=$FGREP
fi
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5
$as_echo "$ac_cv_path_FGREP" >&6; }
FGREP="$ac_cv_path_FGREP"
test -z "$GREP" && GREP=grep
# Check whether --with-gnu-ld was given.
if test "${with_gnu_ld+set}" = set; then :
withval=$with_gnu_ld; test no = "$withval" || with_gnu_ld=yes
else
with_gnu_ld=no
fi
ac_prog=ld
if test yes = "$GCC"; then
# Check if gcc -print-prog-name=ld gives a path.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5
$as_echo_n "checking for ld used by $CC... " >&6; }
case $host in
*-*-mingw*)
# gcc leaves a trailing carriage return, which upsets mingw
ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
*)
ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
esac
case $ac_prog in
# Accept absolute paths.
[\\/]* | ?:[\\/]*)
re_direlt='/[^/][^/]*/\.\./'
# Canonicalize the pathname of ld
ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'`
while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
done
test -z "$LD" && LD=$ac_prog
;;
"")
# If it fails, then pretend we aren't using GCC.
ac_prog=ld
;;
*)
# If it is relative, then search for the first ld in PATH.
with_gnu_ld=unknown
;;
esac
elif test yes = "$with_gnu_ld"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5
$as_echo_n "checking for GNU ld... " >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5
$as_echo_n "checking for non-GNU ld... " >&6; }
fi
if ${lt_cv_path_LD+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -z "$LD"; then
lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
for ac_dir in $PATH; do
IFS=$lt_save_ifs
test -z "$ac_dir" && ac_dir=.
if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
lt_cv_path_LD=$ac_dir/$ac_prog
# Check to see if the program is GNU ld. I'd rather use --version,
# but apparently some variants of GNU ld only accept -v.
# Break only if it was the GNU/non-GNU ld that we prefer.
case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
*GNU* | *'with BFD'*)
test no != "$with_gnu_ld" && break
;;
*)
test yes != "$with_gnu_ld" && break
;;
esac
fi
done
IFS=$lt_save_ifs
else
lt_cv_path_LD=$LD # Let the user override the test with a path.
fi
fi
LD=$lt_cv_path_LD
if test -n "$LD"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $LD" >&5
$as_echo "$LD" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5
$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; }
if ${lt_cv_prog_gnu_ld+:} false; then :
$as_echo_n "(cached) " >&6
else
# I'd rather use --version here, but apparently some GNU lds only accept -v.
case `$LD -v 2>&1 </dev/null` in
*GNU* | *'with BFD'*)
lt_cv_prog_gnu_ld=yes
;;
*)
lt_cv_prog_gnu_ld=no
;;
esac
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_gnu_ld" >&5
$as_echo "$lt_cv_prog_gnu_ld" >&6; }
with_gnu_ld=$lt_cv_prog_gnu_ld
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5
$as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; }
if ${lt_cv_path_NM+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$NM"; then
# Let the user override the test.
lt_cv_path_NM=$NM
else
lt_nm_to_check=${ac_tool_prefix}nm
if test -n "$ac_tool_prefix" && test "$build" = "$host"; then
lt_nm_to_check="$lt_nm_to_check nm"
fi
for lt_tmp_nm in $lt_nm_to_check; do
lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do
IFS=$lt_save_ifs
test -z "$ac_dir" && ac_dir=.
tmp_nm=$ac_dir/$lt_tmp_nm
if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then
# Check to see if the nm accepts a BSD-compat flag.
# Adding the 'sed 1q' prevents false positives on HP-UX, which says:
# nm: unknown option "B" ignored
# Tru64's nm complains that /dev/null is an invalid object file
# MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty
case $build_os in
mingw*) lt_bad_file=conftest.nm/nofile ;;
*) lt_bad_file=/dev/null ;;
esac
case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in
*$lt_bad_file* | *'Invalid file or object type'*)
lt_cv_path_NM="$tmp_nm -B"
break 2
;;
*)
case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
*/dev/null*)
lt_cv_path_NM="$tmp_nm -p"
break 2
;;
*)
lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
continue # so that we can try to find one that supports BSD flags
;;
esac
;;
esac
fi
done
IFS=$lt_save_ifs
done
: ${lt_cv_path_NM=no}
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5
$as_echo "$lt_cv_path_NM" >&6; }
if test no != "$lt_cv_path_NM"; then
NM=$lt_cv_path_NM
else
# Didn't find any BSD compatible name lister, look for dumpbin.
if test -n "$DUMPBIN"; then :
# Let the user override the test.
else
if test -n "$ac_tool_prefix"; then
for ac_prog in dumpbin "link -dump"
do
# Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
set dummy $ac_tool_prefix$ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_DUMPBIN+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$DUMPBIN"; then
ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
DUMPBIN=$ac_cv_prog_DUMPBIN
if test -n "$DUMPBIN"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5
$as_echo "$DUMPBIN" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
test -n "$DUMPBIN" && break
done
fi
if test -z "$DUMPBIN"; then
ac_ct_DUMPBIN=$DUMPBIN
for ac_prog in dumpbin "link -dump"
do
# Extract the first word of "$ac_prog", so it can be a program name with args.
set dummy $ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ac_ct_DUMPBIN+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_DUMPBIN"; then
ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_DUMPBIN="$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN
if test -n "$ac_ct_DUMPBIN"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5
$as_echo "$ac_ct_DUMPBIN" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
test -n "$ac_ct_DUMPBIN" && break
done
if test "x$ac_ct_DUMPBIN" = x; then
DUMPBIN=":"
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
DUMPBIN=$ac_ct_DUMPBIN
fi
fi
case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in
*COFF*)
DUMPBIN="$DUMPBIN -symbols -headers"
;;
*)
DUMPBIN=:
;;
esac
fi
if test : != "$DUMPBIN"; then
NM=$DUMPBIN
fi
fi
test -z "$NM" && NM=nm
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5
$as_echo_n "checking the name lister ($NM) interface... " >&6; }
if ${lt_cv_nm_interface+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_nm_interface="BSD nm"
echo "int some_variable = 0;" > conftest.$ac_ext
(eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&5)
(eval "$ac_compile" 2>conftest.err)
cat conftest.err >&5
(eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
(eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
cat conftest.err >&5
(eval echo "\"\$as_me:$LINENO: output\"" >&5)
cat conftest.out >&5
if $GREP 'External.*some_variable' conftest.out > /dev/null; then
lt_cv_nm_interface="MS dumpbin"
fi
rm -f conftest*
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5
$as_echo "$lt_cv_nm_interface" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5
$as_echo_n "checking whether ln -s works... " >&6; }
LN_S=$as_ln_s
if test "$LN_S" = "ln -s"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5
$as_echo "no, using $LN_S" >&6; }
fi
# find the maximum length of command line arguments
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5
$as_echo_n "checking the maximum length of command line arguments... " >&6; }
if ${lt_cv_sys_max_cmd_len+:} false; then :
$as_echo_n "(cached) " >&6
else
i=0
teststring=ABCD
case $build_os in
msdosdjgpp*)
# On DJGPP, this test can blow up pretty badly due to problems in libc
# (any single argument exceeding 2000 bytes causes a buffer overrun
# during glob expansion). Even if it were fixed, the result of this
# check would be larger than it should be.
lt_cv_sys_max_cmd_len=12288; # 12K is about right
;;
gnu*)
# Under GNU Hurd, this test is not required because there is
# no limit to the length of command line arguments.
# Libtool will interpret -1 as no limit whatsoever
lt_cv_sys_max_cmd_len=-1;
;;
cygwin* | mingw* | cegcc*)
# On Win9x/ME, this test blows up -- it succeeds, but takes
# about 5 minutes as the teststring grows exponentially.
# Worse, since 9x/ME are not pre-emptively multitasking,
# you end up with a "frozen" computer, even though with patience
# the test eventually succeeds (with a max line length of 256k).
# Instead, let's just punt: use the minimum linelength reported by
# all of the supported platforms: 8192 (on NT/2K/XP).
lt_cv_sys_max_cmd_len=8192;
;;
mint*)
# On MiNT this can take a long time and run out of memory.
lt_cv_sys_max_cmd_len=8192;
;;
amigaos*)
# On AmigaOS with pdksh, this test takes hours, literally.
# So we just punt and use a minimum line length of 8192.
lt_cv_sys_max_cmd_len=8192;
;;
bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*)
# This has been around since 386BSD, at least. Likely further.
if test -x /sbin/sysctl; then
lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax`
elif test -x /usr/sbin/sysctl; then
lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax`
else
lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs
fi
# And add a safety zone
lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
;;
interix*)
# We know the value 262144 and hardcode it with a safety zone (like BSD)
lt_cv_sys_max_cmd_len=196608
;;
os2*)
# The test takes a long time on OS/2.
lt_cv_sys_max_cmd_len=8192
;;
osf*)
# Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure
# due to this test when exec_disable_arg_limit is 1 on Tru64. It is not
# nice to cause kernel panics so lets avoid the loop below.
# First set a reasonable default.
lt_cv_sys_max_cmd_len=16384
#
if test -x /sbin/sysconfig; then
case `/sbin/sysconfig -q proc exec_disable_arg_limit` in
*1*) lt_cv_sys_max_cmd_len=-1 ;;
esac
fi
;;
sco3.2v5*)
lt_cv_sys_max_cmd_len=102400
;;
sysv5* | sco5v6* | sysv4.2uw2*)
kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null`
if test -n "$kargmax"; then
lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'`
else
lt_cv_sys_max_cmd_len=32768
fi
;;
*)
lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null`
if test -n "$lt_cv_sys_max_cmd_len" && \
test undefined != "$lt_cv_sys_max_cmd_len"; then
lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
else
# Make teststring a little bigger before we do anything with it.
# a 1K string should be a reasonable start.
for i in 1 2 3 4 5 6 7 8; do
teststring=$teststring$teststring
done
SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}}
# If test is not a shell built-in, we'll probably end up computing a
# maximum length that is only half of the actual maximum length, but
# we can't tell.
while { test X`env echo "$teststring$teststring" 2>/dev/null` \
= "X$teststring$teststring"; } >/dev/null 2>&1 &&
test 17 != "$i" # 1/2 MB should be enough
do
i=`expr $i + 1`
teststring=$teststring$teststring
done
# Only check the string length outside the loop.
lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1`
teststring=
# Add a significant safety factor because C++ compilers can tack on
# massive amounts of additional arguments before passing them to the
# linker. It appears as though 1/2 is a usable value.
lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2`
fi
;;
esac
fi
if test -n "$lt_cv_sys_max_cmd_len"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5
$as_echo "$lt_cv_sys_max_cmd_len" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5
$as_echo "none" >&6; }
fi
max_cmd_len=$lt_cv_sys_max_cmd_len
: ${CP="cp -f"}
: ${MV="mv -f"}
: ${RM="rm -f"}
if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
lt_unset=unset
else
lt_unset=false
fi
# test EBCDIC or ASCII
case `echo X|tr X '\101'` in
A) # ASCII based system
# \n is not interpreted correctly by Solaris 8 /usr/ucb/tr
lt_SP2NL='tr \040 \012'
lt_NL2SP='tr \015\012 \040\040'
;;
*) # EBCDIC based system
lt_SP2NL='tr \100 \n'
lt_NL2SP='tr \r\n \100\100'
;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to $host format" >&5
$as_echo_n "checking how to convert $build file names to $host format... " >&6; }
if ${lt_cv_to_host_file_cmd+:} false; then :
$as_echo_n "(cached) " >&6
else
case $host in
*-*-mingw* )
case $build in
*-*-mingw* ) # actually msys
lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32
;;
*-*-cygwin* )
lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32
;;
* ) # otherwise, assume *nix
lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32
;;
esac
;;
*-*-cygwin* )
case $build in
*-*-mingw* ) # actually msys
lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin
;;
*-*-cygwin* )
lt_cv_to_host_file_cmd=func_convert_file_noop
;;
* ) # otherwise, assume *nix
lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin
;;
esac
;;
* ) # unhandled hosts (and "normal" native builds)
lt_cv_to_host_file_cmd=func_convert_file_noop
;;
esac
fi
to_host_file_cmd=$lt_cv_to_host_file_cmd
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_host_file_cmd" >&5
$as_echo "$lt_cv_to_host_file_cmd" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to toolchain format" >&5
$as_echo_n "checking how to convert $build file names to toolchain format... " >&6; }
if ${lt_cv_to_tool_file_cmd+:} false; then :
$as_echo_n "(cached) " >&6
else
#assume ordinary cross tools, or native build.
lt_cv_to_tool_file_cmd=func_convert_file_noop
case $host in
*-*-mingw* )
case $build in
*-*-mingw* ) # actually msys
lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32
;;
esac
;;
esac
fi
to_tool_file_cmd=$lt_cv_to_tool_file_cmd
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_tool_file_cmd" >&5
$as_echo "$lt_cv_to_tool_file_cmd" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5
$as_echo_n "checking for $LD option to reload object files... " >&6; }
if ${lt_cv_ld_reload_flag+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_ld_reload_flag='-r'
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5
$as_echo "$lt_cv_ld_reload_flag" >&6; }
reload_flag=$lt_cv_ld_reload_flag
case $reload_flag in
"" | " "*) ;;
*) reload_flag=" $reload_flag" ;;
esac
reload_cmds='$LD$reload_flag -o $output$reload_objs'
case $host_os in
cygwin* | mingw* | pw32* | cegcc*)
if test yes != "$GCC"; then
reload_cmds=false
fi
;;
darwin*)
if test yes = "$GCC"; then
reload_cmds='$LTCC $LTCFLAGS -nostdlib $wl-r -o $output$reload_objs'
else
reload_cmds='$LD$reload_flag -o $output$reload_objs'
fi
;;
esac
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args.
set dummy ${ac_tool_prefix}objdump; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_OBJDUMP+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$OBJDUMP"; then
ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
OBJDUMP=$ac_cv_prog_OBJDUMP
if test -n "$OBJDUMP"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5
$as_echo "$OBJDUMP" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
if test -z "$ac_cv_prog_OBJDUMP"; then
ac_ct_OBJDUMP=$OBJDUMP
# Extract the first word of "objdump", so it can be a program name with args.
set dummy objdump; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_OBJDUMP"; then
ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_OBJDUMP="objdump"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP
if test -n "$ac_ct_OBJDUMP"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5
$as_echo "$ac_ct_OBJDUMP" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test "x$ac_ct_OBJDUMP" = x; then
OBJDUMP="false"
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
OBJDUMP=$ac_ct_OBJDUMP
fi
else
OBJDUMP="$ac_cv_prog_OBJDUMP"
fi
test -z "$OBJDUMP" && OBJDUMP=objdump
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5
$as_echo_n "checking how to recognize dependent libraries... " >&6; }
if ${lt_cv_deplibs_check_method+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_file_magic_cmd='$MAGIC_CMD'
lt_cv_file_magic_test_file=
lt_cv_deplibs_check_method='unknown'
# Need to set the preceding variable on all platforms that support
# interlibrary dependencies.
# 'none' -- dependencies not supported.
# 'unknown' -- same as none, but documents that we really don't know.
# 'pass_all' -- all dependencies passed with no checks.
# 'test_compile' -- check by making test program.
# 'file_magic [[regex]]' -- check by looking for files in library path
# that responds to the $file_magic_cmd with a given extended regex.
# If you have 'file' or equivalent on your system and you're not sure
# whether 'pass_all' will *always* work, you probably want this one.
case $host_os in
aix[4-9]*)
lt_cv_deplibs_check_method=pass_all
;;
beos*)
lt_cv_deplibs_check_method=pass_all
;;
bsdi[45]*)
lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)'
lt_cv_file_magic_cmd='/usr/bin/file -L'
lt_cv_file_magic_test_file=/shlib/libc.so
;;
cygwin*)
# func_win32_libid is a shell function defined in ltmain.sh
lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
lt_cv_file_magic_cmd='func_win32_libid'
;;
mingw* | pw32*)
# Base MSYS/MinGW do not provide the 'file' command needed by
# func_win32_libid shell function, so use a weaker test based on 'objdump',
# unless we find 'file', for example because we are cross-compiling.
if ( file / ) >/dev/null 2>&1; then
lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
lt_cv_file_magic_cmd='func_win32_libid'
else
# Keep this pattern in sync with the one in func_win32_libid.
lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)'
lt_cv_file_magic_cmd='$OBJDUMP -f'
fi
;;
cegcc*)
# use the weaker test based on 'objdump'. See mingw*.
lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?'
lt_cv_file_magic_cmd='$OBJDUMP -f'
;;
darwin* | rhapsody*)
lt_cv_deplibs_check_method=pass_all
;;
freebsd* | dragonfly*)
if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
case $host_cpu in
i*86 )
# Not sure whether the presence of OpenBSD here was a mistake.
# Let's accept both of them until this is cleared up.
lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library'
lt_cv_file_magic_cmd=/usr/bin/file
lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
;;
esac
else
lt_cv_deplibs_check_method=pass_all
fi
;;
haiku*)
lt_cv_deplibs_check_method=pass_all
;;
hpux10.20* | hpux11*)
lt_cv_file_magic_cmd=/usr/bin/file
case $host_cpu in
ia64*)
lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64'
lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so
;;
hppa*64*)
lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]'
lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl
;;
*)
lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9]\.[0-9]) shared library'
lt_cv_file_magic_test_file=/usr/lib/libc.sl
;;
esac
;;
interix[3-9]*)
# PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here
lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$'
;;
irix5* | irix6* | nonstopux*)
case $LD in
*-32|*"-32 ") libmagic=32-bit;;
*-n32|*"-n32 ") libmagic=N32;;
*-64|*"-64 ") libmagic=64-bit;;
*) libmagic=never-match;;
esac
lt_cv_deplibs_check_method=pass_all
;;
# This must be glibc/ELF.
linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
lt_cv_deplibs_check_method=pass_all
;;
netbsd*)
if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
else
lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$'
fi
;;
newos6*)
lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)'
lt_cv_file_magic_cmd=/usr/bin/file
lt_cv_file_magic_test_file=/usr/lib/libnls.so
;;
*nto* | *qnx*)
lt_cv_deplibs_check_method=pass_all
;;
openbsd* | bitrig*)
if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$'
else
lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
fi
;;
osf3* | osf4* | osf5*)
lt_cv_deplibs_check_method=pass_all
;;
rdos*)
lt_cv_deplibs_check_method=pass_all
;;
solaris*)
lt_cv_deplibs_check_method=pass_all
;;
sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
lt_cv_deplibs_check_method=pass_all
;;
sysv4 | sysv4.3*)
case $host_vendor in
motorola)
lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]'
lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`
;;
ncr)
lt_cv_deplibs_check_method=pass_all
;;
sequent)
lt_cv_file_magic_cmd='/bin/file'
lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )'
;;
sni)
lt_cv_file_magic_cmd='/bin/file'
lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib"
lt_cv_file_magic_test_file=/lib/libc.so
;;
siemens)
lt_cv_deplibs_check_method=pass_all
;;
pc)
lt_cv_deplibs_check_method=pass_all
;;
esac
;;
tpf*)
lt_cv_deplibs_check_method=pass_all
;;
os2*)
lt_cv_deplibs_check_method=pass_all
;;
esac
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5
$as_echo "$lt_cv_deplibs_check_method" >&6; }
file_magic_glob=
want_nocaseglob=no
if test "$build" = "$host"; then
case $host_os in
mingw* | pw32*)
if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then
want_nocaseglob=yes
else
file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[\1]\/[\1]\/g;/g"`
fi
;;
esac
fi
file_magic_cmd=$lt_cv_file_magic_cmd
deplibs_check_method=$lt_cv_deplibs_check_method
test -z "$deplibs_check_method" && deplibs_check_method=unknown
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args.
set dummy ${ac_tool_prefix}dlltool; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_DLLTOOL+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$DLLTOOL"; then
ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
DLLTOOL=$ac_cv_prog_DLLTOOL
if test -n "$DLLTOOL"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5
$as_echo "$DLLTOOL" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
if test -z "$ac_cv_prog_DLLTOOL"; then
ac_ct_DLLTOOL=$DLLTOOL
# Extract the first word of "dlltool", so it can be a program name with args.
set dummy dlltool; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ac_ct_DLLTOOL+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_DLLTOOL"; then
ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_DLLTOOL="dlltool"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL
if test -n "$ac_ct_DLLTOOL"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5
$as_echo "$ac_ct_DLLTOOL" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test "x$ac_ct_DLLTOOL" = x; then
DLLTOOL="false"
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
DLLTOOL=$ac_ct_DLLTOOL
fi
else
DLLTOOL="$ac_cv_prog_DLLTOOL"
fi
test -z "$DLLTOOL" && DLLTOOL=dlltool
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to associate runtime and link libraries" >&5
$as_echo_n "checking how to associate runtime and link libraries... " >&6; }
if ${lt_cv_sharedlib_from_linklib_cmd+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_sharedlib_from_linklib_cmd='unknown'
case $host_os in
cygwin* | mingw* | pw32* | cegcc*)
# two different shell functions defined in ltmain.sh;
# decide which one to use based on capabilities of $DLLTOOL
case `$DLLTOOL --help 2>&1` in
*--identify-strict*)
lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib
;;
*)
lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback
;;
esac
;;
*)
# fallback: assume linklib IS sharedlib
lt_cv_sharedlib_from_linklib_cmd=$ECHO
;;
esac
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sharedlib_from_linklib_cmd" >&5
$as_echo "$lt_cv_sharedlib_from_linklib_cmd" >&6; }
sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd
test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO
if test -n "$ac_tool_prefix"; then
for ac_prog in ar
do
# Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
set dummy $ac_tool_prefix$ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_AR+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$AR"; then
ac_cv_prog_AR="$AR" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_AR="$ac_tool_prefix$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
AR=$ac_cv_prog_AR
if test -n "$AR"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5
$as_echo "$AR" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
test -n "$AR" && break
done
fi
if test -z "$AR"; then
ac_ct_AR=$AR
for ac_prog in ar
do
# Extract the first word of "$ac_prog", so it can be a program name with args.
set dummy $ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ac_ct_AR+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_AR"; then
ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_AR="$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
ac_ct_AR=$ac_cv_prog_ac_ct_AR
if test -n "$ac_ct_AR"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5
$as_echo "$ac_ct_AR" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
test -n "$ac_ct_AR" && break
done
if test "x$ac_ct_AR" = x; then
AR="false"
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
AR=$ac_ct_AR
fi
fi
: ${AR=ar}
: ${AR_FLAGS=cru}
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for archiver @FILE support" >&5
$as_echo_n "checking for archiver @FILE support... " >&6; }
if ${lt_cv_ar_at_file+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_ar_at_file=no
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
echo conftest.$ac_objext > conftest.lst
lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&5'
{ { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5
(eval $lt_ar_try) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }
if test 0 -eq "$ac_status"; then
# Ensure the archiver fails upon bogus file names.
rm -f conftest.$ac_objext libconftest.a
{ { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5
(eval $lt_ar_try) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }
if test 0 -ne "$ac_status"; then
lt_cv_ar_at_file=@
fi
fi
rm -f conftest.* libconftest.a
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ar_at_file" >&5
$as_echo "$lt_cv_ar_at_file" >&6; }
if test no = "$lt_cv_ar_at_file"; then
archiver_list_spec=
else
archiver_list_spec=$lt_cv_ar_at_file
fi
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
set dummy ${ac_tool_prefix}strip; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_STRIP+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$STRIP"; then
ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_STRIP="${ac_tool_prefix}strip"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
STRIP=$ac_cv_prog_STRIP
if test -n "$STRIP"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
$as_echo "$STRIP" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
if test -z "$ac_cv_prog_STRIP"; then
ac_ct_STRIP=$STRIP
# Extract the first word of "strip", so it can be a program name with args.
set dummy strip; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ac_ct_STRIP+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_STRIP"; then
ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_STRIP="strip"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
if test -n "$ac_ct_STRIP"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5
$as_echo "$ac_ct_STRIP" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test "x$ac_ct_STRIP" = x; then
STRIP=":"
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
STRIP=$ac_ct_STRIP
fi
else
STRIP="$ac_cv_prog_STRIP"
fi
test -z "$STRIP" && STRIP=:
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
set dummy ${ac_tool_prefix}ranlib; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_RANLIB+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$RANLIB"; then
ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
RANLIB=$ac_cv_prog_RANLIB
if test -n "$RANLIB"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5
$as_echo "$RANLIB" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
if test -z "$ac_cv_prog_RANLIB"; then
ac_ct_RANLIB=$RANLIB
# Extract the first word of "ranlib", so it can be a program name with args.
set dummy ranlib; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ac_ct_RANLIB+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_RANLIB"; then
ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_RANLIB="ranlib"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
if test -n "$ac_ct_RANLIB"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5
$as_echo "$ac_ct_RANLIB" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test "x$ac_ct_RANLIB" = x; then
RANLIB=":"
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
RANLIB=$ac_ct_RANLIB
fi
else
RANLIB="$ac_cv_prog_RANLIB"
fi
test -z "$RANLIB" && RANLIB=:
# Determine commands to create old-style static archives.
old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs'
old_postinstall_cmds='chmod 644 $oldlib'
old_postuninstall_cmds=
if test -n "$RANLIB"; then
case $host_os in
bitrig* | openbsd*)
old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib"
;;
*)
old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib"
;;
esac
old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib"
fi
case $host_os in
darwin*)
lock_old_archive_extraction=yes ;;
*)
lock_old_archive_extraction=no ;;
esac
for ac_prog in gawk mawk nawk awk
do
# Extract the first word of "$ac_prog", so it can be a program name with args.
set dummy $ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_AWK+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$AWK"; then
ac_cv_prog_AWK="$AWK" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_AWK="$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
AWK=$ac_cv_prog_AWK
if test -n "$AWK"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5
$as_echo "$AWK" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
test -n "$AWK" && break
done
# If no C compiler was specified, use CC.
LTCC=${LTCC-"$CC"}
# If no C compiler flags were specified, use CFLAGS.
LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
# Allow CC to be a program name with arguments.
compiler=$CC
# Check for command to grab the raw symbol name followed by C symbol from nm.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5
$as_echo_n "checking command to parse $NM output from $compiler object... " >&6; }
if ${lt_cv_sys_global_symbol_pipe+:} false; then :
$as_echo_n "(cached) " >&6
else
# These are sane defaults that work on at least a few old systems.
# [They come from Ultrix. What could be older than Ultrix?!! ;)]
# Character class describing NM global symbol codes.
symcode='[BCDEGRST]'
# Regexp to match symbols that can be accessed directly from C.
sympat='\([_A-Za-z][_A-Za-z0-9]*\)'
# Define system-specific variables.
case $host_os in
aix*)
symcode='[BCDT]'
;;
cygwin* | mingw* | pw32* | cegcc*)
symcode='[ABCDGISTW]'
;;
hpux*)
if test ia64 = "$host_cpu"; then
symcode='[ABCDEGRST]'
fi
;;
irix* | nonstopux*)
symcode='[BCDEGRST]'
;;
osf*)
symcode='[BCDEGQRST]'
;;
solaris*)
symcode='[BDRT]'
;;
sco3.2v5*)
symcode='[DT]'
;;
sysv4.2uw2*)
symcode='[DT]'
;;
sysv5* | sco5v6* | unixware* | OpenUNIX*)
symcode='[ABDT]'
;;
sysv4)
symcode='[DFNSTU]'
;;
esac
# If we're using GNU nm, then use its standard symbol codes.
case `$NM -V 2>&1` in
*GNU* | *'with BFD'*)
symcode='[ABCDGIRSTW]' ;;
esac
if test "$lt_cv_nm_interface" = "MS dumpbin"; then
# Gets list of data symbols to import.
lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'"
# Adjust the below global symbol transforms to fixup imported variables.
lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'"
lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'"
lt_c_name_lib_hook="\
-e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\
-e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'"
else
# Disable hooks by default.
lt_cv_sys_global_symbol_to_import=
lt_cdecl_hook=
lt_c_name_hook=
lt_c_name_lib_hook=
fi
# Transform an extracted symbol line into a proper C declaration.
# Some systems (esp. on ia64) link data and code symbols differently,
# so use this general approach.
lt_cv_sys_global_symbol_to_cdecl="sed -n"\
$lt_cdecl_hook\
" -e 's/^T .* \(.*\)$/extern int \1();/p'"\
" -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'"
# Transform an extracted symbol line into symbol name and symbol address
lt_cv_sys_global_symbol_to_c_name_address="sed -n"\
$lt_c_name_hook\
" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\
" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'"
# Transform an extracted symbol line into symbol name with lib prefix and
# symbol address.
lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\
$lt_c_name_lib_hook\
" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\
" -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\
" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'"
# Handle CRLF in mingw tool chain
opt_cr=
case $build_os in
mingw*)
opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp
;;
esac
# Try without a prefix underscore, then with it.
for ac_symprfx in "" "_"; do
# Transform symcode, sympat, and symprfx into a raw symbol and a C symbol.
symxfrm="\\1 $ac_symprfx\\2 \\2"
# Write the raw and C identifiers.
if test "$lt_cv_nm_interface" = "MS dumpbin"; then
# Fake it for dumpbin and say T for any non-static function,
# D for any global variable and I for any imported variable.
# Also find C++ and __fastcall symbols from MSVC++,
# which start with @ or ?.
lt_cv_sys_global_symbol_pipe="$AWK '"\
" {last_section=section; section=\$ 3};"\
" /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\
" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\
" /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\
" /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\
" /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\
" \$ 0!~/External *\|/{next};"\
" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\
" {if(hide[section]) next};"\
" {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\
" {split(\$ 0,a,/\||\r/); split(a[2],s)};"\
" s[1]~/^[@?]/{print f,s[1],s[1]; next};"\
" s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\
" ' prfx=^$ac_symprfx"
else
lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'"
fi
lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'"
# Check to see that the pipe works correctly.
pipe_works=no
rm -f conftest*
cat > conftest.$ac_ext <<_LT_EOF
#ifdef __cplusplus
extern "C" {
#endif
char nm_test_var;
void nm_test_func(void);
void nm_test_func(void){}
#ifdef __cplusplus
}
#endif
int main(){nm_test_var='a';nm_test_func();return(0);}
_LT_EOF
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
(eval $ac_compile) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
# Now try to grab the symbols.
nlist=conftest.nm
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist\""; } >&5
(eval $NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; } && test -s "$nlist"; then
# Try sorting and uniquifying the output.
if sort "$nlist" | uniq > "$nlist"T; then
mv -f "$nlist"T "$nlist"
else
rm -f "$nlist"T
fi
# Make sure that we snagged all the symbols we need.
if $GREP ' nm_test_var$' "$nlist" >/dev/null; then
if $GREP ' nm_test_func$' "$nlist" >/dev/null; then
cat <<_LT_EOF > conftest.$ac_ext
/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */
#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE
/* DATA imports from DLLs on WIN32 can't be const, because runtime
relocations are performed -- see ld's documentation on pseudo-relocs. */
# define LT_DLSYM_CONST
#elif defined __osf__
/* This system does not cope well with relocations in const data. */
# define LT_DLSYM_CONST
#else
# define LT_DLSYM_CONST const
#endif
#ifdef __cplusplus
extern "C" {
#endif
_LT_EOF
# Now generate the symbol file.
eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext'
cat <<_LT_EOF >> conftest.$ac_ext
/* The mapping between symbol names and symbols. */
LT_DLSYM_CONST struct {
const char *name;
void *address;
}
lt__PROGRAM__LTX_preloaded_symbols[] =
{
{ "@PROGRAM@", (void *) 0 },
_LT_EOF
$SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext
cat <<\_LT_EOF >> conftest.$ac_ext
{0, (void *) 0}
};
/* This works around a problem in FreeBSD linker */
#ifdef FREEBSD_WORKAROUND
static const void *lt_preloaded_setup() {
return lt__PROGRAM__LTX_preloaded_symbols;
}
#endif
#ifdef __cplusplus
}
#endif
_LT_EOF
# Now try linking the two files.
mv conftest.$ac_objext conftstm.$ac_objext
lt_globsym_save_LIBS=$LIBS
lt_globsym_save_CFLAGS=$CFLAGS
LIBS=conftstm.$ac_objext
CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag"
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
(eval $ac_link) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; } && test -s conftest$ac_exeext; then
pipe_works=yes
fi
LIBS=$lt_globsym_save_LIBS
CFLAGS=$lt_globsym_save_CFLAGS
else
echo "cannot find nm_test_func in $nlist" >&5
fi
else
echo "cannot find nm_test_var in $nlist" >&5
fi
else
echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5
fi
else
echo "$progname: failed program was:" >&5
cat conftest.$ac_ext >&5
fi
rm -rf conftest* conftst*
# Do not use the global_symbol_pipe unless it works.
if test yes = "$pipe_works"; then
break
else
lt_cv_sys_global_symbol_pipe=
fi
done
fi
if test -z "$lt_cv_sys_global_symbol_pipe"; then
lt_cv_sys_global_symbol_to_cdecl=
fi
if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5
$as_echo "failed" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5
$as_echo "ok" >&6; }
fi
# Response file support.
if test "$lt_cv_nm_interface" = "MS dumpbin"; then
nm_file_list_spec='@'
elif $NM --help 2>/dev/null | grep '[@]FILE' >/dev/null; then
nm_file_list_spec='@'
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysroot" >&5
$as_echo_n "checking for sysroot... " >&6; }
# Check whether --with-sysroot was given.
if test "${with_sysroot+set}" = set; then :
withval=$with_sysroot;
else
with_sysroot=no
fi
lt_sysroot=
case $with_sysroot in #(
yes)
if test yes = "$GCC"; then
lt_sysroot=`$CC --print-sysroot 2>/dev/null`
fi
;; #(
/*)
lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"`
;; #(
no|'')
;; #(
*)
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_sysroot" >&5
$as_echo "$with_sysroot" >&6; }
as_fn_error $? "The sysroot must be an absolute path." "$LINENO" 5
;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${lt_sysroot:-no}" >&5
$as_echo "${lt_sysroot:-no}" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a working dd" >&5
$as_echo_n "checking for a working dd... " >&6; }
if ${ac_cv_path_lt_DD+:} false; then :
$as_echo_n "(cached) " >&6
else
printf 0123456789abcdef0123456789abcdef >conftest.i
cat conftest.i conftest.i >conftest2.i
: ${lt_DD:=$DD}
if test -z "$lt_DD"; then
ac_path_lt_DD_found=false
# Loop through the user's path and test for each of PROGNAME-LIST
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_prog in dd; do
for ac_exec_ext in '' $ac_executable_extensions; do
ac_path_lt_DD="$as_dir/$ac_prog$ac_exec_ext"
as_fn_executable_p "$ac_path_lt_DD" || continue
if "$ac_path_lt_DD" bs=32 count=1 <conftest2.i >conftest.out 2>/dev/null; then
cmp -s conftest.i conftest.out \
&& ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=:
fi
$ac_path_lt_DD_found && break 3
done
done
done
IFS=$as_save_IFS
if test -z "$ac_cv_path_lt_DD"; then
:
fi
else
ac_cv_path_lt_DD=$lt_DD
fi
rm -f conftest.i conftest2.i conftest.out
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_lt_DD" >&5
$as_echo "$ac_cv_path_lt_DD" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to truncate binary pipes" >&5
$as_echo_n "checking how to truncate binary pipes... " >&6; }
if ${lt_cv_truncate_bin+:} false; then :
$as_echo_n "(cached) " >&6
else
printf 0123456789abcdef0123456789abcdef >conftest.i
cat conftest.i conftest.i >conftest2.i
lt_cv_truncate_bin=
if "$ac_cv_path_lt_DD" bs=32 count=1 <conftest2.i >conftest.out 2>/dev/null; then
cmp -s conftest.i conftest.out \
&& lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1"
fi
rm -f conftest.i conftest2.i conftest.out
test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_truncate_bin" >&5
$as_echo "$lt_cv_truncate_bin" >&6; }
# Calculate cc_basename. Skip known compiler wrappers and cross-prefix.
func_cc_basename ()
{
for cc_temp in $*""; do
case $cc_temp in
compile | *[\\/]compile | ccache | *[\\/]ccache ) ;;
distcc | *[\\/]distcc | purify | *[\\/]purify ) ;;
\-*) ;;
*) break;;
esac
done
func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"`
}
# Check whether --enable-libtool-lock was given.
if test "${enable_libtool_lock+set}" = set; then :
enableval=$enable_libtool_lock;
fi
test no = "$enable_libtool_lock" || enable_libtool_lock=yes
# Some flags need to be propagated to the compiler or linker for good
# libtool support.
case $host in
ia64-*-hpux*)
# Find out what ABI is being produced by ac_compile, and set mode
# options accordingly.
echo 'int i;' > conftest.$ac_ext
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
(eval $ac_compile) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
case `/usr/bin/file conftest.$ac_objext` in
*ELF-32*)
HPUX_IA64_MODE=32
;;
*ELF-64*)
HPUX_IA64_MODE=64
;;
esac
fi
rm -rf conftest*
;;
*-*-irix6*)
# Find out what ABI is being produced by ac_compile, and set linker
# options accordingly.
echo '#line '$LINENO' "configure"' > conftest.$ac_ext
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
(eval $ac_compile) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
if test yes = "$lt_cv_prog_gnu_ld"; then
case `/usr/bin/file conftest.$ac_objext` in
*32-bit*)
LD="${LD-ld} -melf32bsmip"
;;
*N32*)
LD="${LD-ld} -melf32bmipn32"
;;
*64-bit*)
LD="${LD-ld} -melf64bmip"
;;
esac
else
case `/usr/bin/file conftest.$ac_objext` in
*32-bit*)
LD="${LD-ld} -32"
;;
*N32*)
LD="${LD-ld} -n32"
;;
*64-bit*)
LD="${LD-ld} -64"
;;
esac
fi
fi
rm -rf conftest*
;;
mips64*-*linux*)
# Find out what ABI is being produced by ac_compile, and set linker
# options accordingly.
echo '#line '$LINENO' "configure"' > conftest.$ac_ext
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
(eval $ac_compile) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
emul=elf
case `/usr/bin/file conftest.$ac_objext` in
*32-bit*)
emul="${emul}32"
;;
*64-bit*)
emul="${emul}64"
;;
esac
case `/usr/bin/file conftest.$ac_objext` in
*MSB*)
emul="${emul}btsmip"
;;
*LSB*)
emul="${emul}ltsmip"
;;
esac
case `/usr/bin/file conftest.$ac_objext` in
*N32*)
emul="${emul}n32"
;;
esac
LD="${LD-ld} -m $emul"
fi
rm -rf conftest*
;;
x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \
s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
# Find out what ABI is being produced by ac_compile, and set linker
# options accordingly. Note that the listed cases only cover the
# situations where additional linker options are needed (such as when
# doing 32-bit compilation for a host where ld defaults to 64-bit, or
# vice versa); the common cases where no linker options are needed do
# not appear in the list.
echo 'int i;' > conftest.$ac_ext
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
(eval $ac_compile) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
case `/usr/bin/file conftest.o` in
*32-bit*)
case $host in
x86_64-*kfreebsd*-gnu)
LD="${LD-ld} -m elf_i386_fbsd"
;;
x86_64-*linux*)
case `/usr/bin/file conftest.o` in
*x86-64*)
LD="${LD-ld} -m elf32_x86_64"
;;
*)
LD="${LD-ld} -m elf_i386"
;;
esac
;;
powerpc64le-*linux*)
LD="${LD-ld} -m elf32lppclinux"
;;
powerpc64-*linux*)
LD="${LD-ld} -m elf32ppclinux"
;;
s390x-*linux*)
LD="${LD-ld} -m elf_s390"
;;
sparc64-*linux*)
LD="${LD-ld} -m elf32_sparc"
;;
esac
;;
*64-bit*)
case $host in
x86_64-*kfreebsd*-gnu)
LD="${LD-ld} -m elf_x86_64_fbsd"
;;
x86_64-*linux*)
LD="${LD-ld} -m elf_x86_64"
;;
powerpcle-*linux*)
LD="${LD-ld} -m elf64lppc"
;;
powerpc-*linux*)
LD="${LD-ld} -m elf64ppc"
;;
s390*-*linux*|s390*-*tpf*)
LD="${LD-ld} -m elf64_s390"
;;
sparc*-*linux*)
LD="${LD-ld} -m elf64_sparc"
;;
esac
;;
esac
fi
rm -rf conftest*
;;
*-*-sco3.2v5*)
# On SCO OpenServer 5, we need -belf to get full-featured binaries.
SAVE_CFLAGS=$CFLAGS
CFLAGS="$CFLAGS -belf"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5
$as_echo_n "checking whether the C compiler needs -belf... " >&6; }
if ${lt_cv_cc_needs_belf+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
lt_cv_cc_needs_belf=yes
else
lt_cv_cc_needs_belf=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5
$as_echo "$lt_cv_cc_needs_belf" >&6; }
if test yes != "$lt_cv_cc_needs_belf"; then
# this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
CFLAGS=$SAVE_CFLAGS
fi
;;
*-*solaris*)
# Find out what ABI is being produced by ac_compile, and set linker
# options accordingly.
echo 'int i;' > conftest.$ac_ext
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
(eval $ac_compile) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
case `/usr/bin/file conftest.o` in
*64-bit*)
case $lt_cv_prog_gnu_ld in
yes*)
case $host in
i?86-*-solaris*|x86_64-*-solaris*)
LD="${LD-ld} -m elf_x86_64"
;;
sparc*-*-solaris*)
LD="${LD-ld} -m elf64_sparc"
;;
esac
# GNU ld 2.21 introduced _sol2 emulations. Use them if available.
if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then
LD=${LD-ld}_sol2
fi
;;
*)
if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then
LD="${LD-ld} -64"
fi
;;
esac
;;
esac
fi
rm -rf conftest*
;;
esac
need_locks=$enable_libtool_lock
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}mt", so it can be a program name with args.
set dummy ${ac_tool_prefix}mt; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_MANIFEST_TOOL+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$MANIFEST_TOOL"; then
ac_cv_prog_MANIFEST_TOOL="$MANIFEST_TOOL" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_MANIFEST_TOOL="${ac_tool_prefix}mt"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
MANIFEST_TOOL=$ac_cv_prog_MANIFEST_TOOL
if test -n "$MANIFEST_TOOL"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MANIFEST_TOOL" >&5
$as_echo "$MANIFEST_TOOL" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
if test -z "$ac_cv_prog_MANIFEST_TOOL"; then
ac_ct_MANIFEST_TOOL=$MANIFEST_TOOL
# Extract the first word of "mt", so it can be a program name with args.
set dummy mt; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ac_ct_MANIFEST_TOOL+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_MANIFEST_TOOL"; then
ac_cv_prog_ac_ct_MANIFEST_TOOL="$ac_ct_MANIFEST_TOOL" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_MANIFEST_TOOL="mt"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
ac_ct_MANIFEST_TOOL=$ac_cv_prog_ac_ct_MANIFEST_TOOL
if test -n "$ac_ct_MANIFEST_TOOL"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_MANIFEST_TOOL" >&5
$as_echo "$ac_ct_MANIFEST_TOOL" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test "x$ac_ct_MANIFEST_TOOL" = x; then
MANIFEST_TOOL=":"
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
MANIFEST_TOOL=$ac_ct_MANIFEST_TOOL
fi
else
MANIFEST_TOOL="$ac_cv_prog_MANIFEST_TOOL"
fi
test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $MANIFEST_TOOL is a manifest tool" >&5
$as_echo_n "checking if $MANIFEST_TOOL is a manifest tool... " >&6; }
if ${lt_cv_path_mainfest_tool+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_path_mainfest_tool=no
echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&5
$MANIFEST_TOOL '-?' 2>conftest.err > conftest.out
cat conftest.err >&5
if $GREP 'Manifest Tool' conftest.out > /dev/null; then
lt_cv_path_mainfest_tool=yes
fi
rm -f conftest*
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_mainfest_tool" >&5
$as_echo "$lt_cv_path_mainfest_tool" >&6; }
if test yes != "$lt_cv_path_mainfest_tool"; then
MANIFEST_TOOL=:
fi
case $host_os in
rhapsody* | darwin*)
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args.
set dummy ${ac_tool_prefix}dsymutil; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_DSYMUTIL+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$DSYMUTIL"; then
ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
DSYMUTIL=$ac_cv_prog_DSYMUTIL
if test -n "$DSYMUTIL"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5
$as_echo "$DSYMUTIL" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
if test -z "$ac_cv_prog_DSYMUTIL"; then
ac_ct_DSYMUTIL=$DSYMUTIL
# Extract the first word of "dsymutil", so it can be a program name with args.
set dummy dsymutil; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ac_ct_DSYMUTIL+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_DSYMUTIL"; then
ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_DSYMUTIL="dsymutil"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL
if test -n "$ac_ct_DSYMUTIL"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5
$as_echo "$ac_ct_DSYMUTIL" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test "x$ac_ct_DSYMUTIL" = x; then
DSYMUTIL=":"
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
DSYMUTIL=$ac_ct_DSYMUTIL
fi
else
DSYMUTIL="$ac_cv_prog_DSYMUTIL"
fi
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args.
set dummy ${ac_tool_prefix}nmedit; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_NMEDIT+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$NMEDIT"; then
ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
NMEDIT=$ac_cv_prog_NMEDIT
if test -n "$NMEDIT"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5
$as_echo "$NMEDIT" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
if test -z "$ac_cv_prog_NMEDIT"; then
ac_ct_NMEDIT=$NMEDIT
# Extract the first word of "nmedit", so it can be a program name with args.
set dummy nmedit; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ac_ct_NMEDIT+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_NMEDIT"; then
ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_NMEDIT="nmedit"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT
if test -n "$ac_ct_NMEDIT"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5
$as_echo "$ac_ct_NMEDIT" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test "x$ac_ct_NMEDIT" = x; then
NMEDIT=":"
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
NMEDIT=$ac_ct_NMEDIT
fi
else
NMEDIT="$ac_cv_prog_NMEDIT"
fi
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args.
set dummy ${ac_tool_prefix}lipo; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_LIPO+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$LIPO"; then
ac_cv_prog_LIPO="$LIPO" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_LIPO="${ac_tool_prefix}lipo"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
LIPO=$ac_cv_prog_LIPO
if test -n "$LIPO"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5
$as_echo "$LIPO" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
if test -z "$ac_cv_prog_LIPO"; then
ac_ct_LIPO=$LIPO
# Extract the first word of "lipo", so it can be a program name with args.
set dummy lipo; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ac_ct_LIPO+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_LIPO"; then
ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_LIPO="lipo"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO
if test -n "$ac_ct_LIPO"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5
$as_echo "$ac_ct_LIPO" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test "x$ac_ct_LIPO" = x; then
LIPO=":"
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
LIPO=$ac_ct_LIPO
fi
else
LIPO="$ac_cv_prog_LIPO"
fi
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args.
set dummy ${ac_tool_prefix}otool; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_OTOOL+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$OTOOL"; then
ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_OTOOL="${ac_tool_prefix}otool"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
OTOOL=$ac_cv_prog_OTOOL
if test -n "$OTOOL"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5
$as_echo "$OTOOL" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
if test -z "$ac_cv_prog_OTOOL"; then
ac_ct_OTOOL=$OTOOL
# Extract the first word of "otool", so it can be a program name with args.
set dummy otool; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ac_ct_OTOOL+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_OTOOL"; then
ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_OTOOL="otool"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL
if test -n "$ac_ct_OTOOL"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5
$as_echo "$ac_ct_OTOOL" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test "x$ac_ct_OTOOL" = x; then
OTOOL=":"
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
OTOOL=$ac_ct_OTOOL
fi
else
OTOOL="$ac_cv_prog_OTOOL"
fi
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args.
set dummy ${ac_tool_prefix}otool64; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_OTOOL64+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$OTOOL64"; then
ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
OTOOL64=$ac_cv_prog_OTOOL64
if test -n "$OTOOL64"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5
$as_echo "$OTOOL64" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
if test -z "$ac_cv_prog_OTOOL64"; then
ac_ct_OTOOL64=$OTOOL64
# Extract the first word of "otool64", so it can be a program name with args.
set dummy otool64; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ac_ct_OTOOL64+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_OTOOL64"; then
ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_OTOOL64="otool64"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64
if test -n "$ac_ct_OTOOL64"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5
$as_echo "$ac_ct_OTOOL64" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test "x$ac_ct_OTOOL64" = x; then
OTOOL64=":"
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
OTOOL64=$ac_ct_OTOOL64
fi
else
OTOOL64="$ac_cv_prog_OTOOL64"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5
$as_echo_n "checking for -single_module linker flag... " >&6; }
if ${lt_cv_apple_cc_single_mod+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_apple_cc_single_mod=no
if test -z "$LT_MULTI_MODULE"; then
# By default we will add the -single_module flag. You can override
# by either setting the environment variable LT_MULTI_MODULE
# non-empty at configure time, or by adding -multi_module to the
# link flags.
rm -rf libconftest.dylib*
echo "int foo(void){return 1;}" > conftest.c
echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
-dynamiclib -Wl,-single_module conftest.c" >&5
$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
-dynamiclib -Wl,-single_module conftest.c 2>conftest.err
_lt_result=$?
# If there is a non-empty error log, and "single_module"
# appears in it, assume the flag caused a linker warning
if test -s conftest.err && $GREP single_module conftest.err; then
cat conftest.err >&5
# Otherwise, if the output was created with a 0 exit code from
# the compiler, it worked.
elif test -f libconftest.dylib && test 0 = "$_lt_result"; then
lt_cv_apple_cc_single_mod=yes
else
cat conftest.err >&5
fi
rm -rf libconftest.dylib*
rm -f conftest.*
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5
$as_echo "$lt_cv_apple_cc_single_mod" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5
$as_echo_n "checking for -exported_symbols_list linker flag... " >&6; }
if ${lt_cv_ld_exported_symbols_list+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_ld_exported_symbols_list=no
save_LDFLAGS=$LDFLAGS
echo "_main" > conftest.sym
LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
lt_cv_ld_exported_symbols_list=yes
else
lt_cv_ld_exported_symbols_list=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LDFLAGS=$save_LDFLAGS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5
$as_echo "$lt_cv_ld_exported_symbols_list" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for -force_load linker flag" >&5
$as_echo_n "checking for -force_load linker flag... " >&6; }
if ${lt_cv_ld_force_load+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_ld_force_load=no
cat > conftest.c << _LT_EOF
int forced_loaded() { return 2;}
_LT_EOF
echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5
$LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5
echo "$AR cru libconftest.a conftest.o" >&5
$AR cru libconftest.a conftest.o 2>&5
echo "$RANLIB libconftest.a" >&5
$RANLIB libconftest.a 2>&5
cat > conftest.c << _LT_EOF
int main() { return 0;}
_LT_EOF
echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&5
$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err
_lt_result=$?
if test -s conftest.err && $GREP force_load conftest.err; then
cat conftest.err >&5
elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then
lt_cv_ld_force_load=yes
else
cat conftest.err >&5
fi
rm -f conftest.err libconftest.a conftest conftest.c
rm -rf conftest.dSYM
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5
$as_echo "$lt_cv_ld_force_load" >&6; }
case $host_os in
rhapsody* | darwin1.[012])
_lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;;
darwin1.*)
_lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
darwin*) # darwin 5.x on
# if running on 10.5 or later, the deployment target defaults
# to the OS version, if on x86, and 10.4, the deployment
# target defaults to 10.4. Don't you love it?
case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
10.0,*86*-darwin8*|10.0,*-darwin[91]*)
_lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
10.[012][,.]*)
_lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
10.*)
_lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
esac
;;
esac
if test yes = "$lt_cv_apple_cc_single_mod"; then
_lt_dar_single_mod='$single_module'
fi
if test yes = "$lt_cv_ld_exported_symbols_list"; then
_lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym'
else
_lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib'
fi
if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then
_lt_dsymutil='~$DSYMUTIL $lib || :'
else
_lt_dsymutil=
fi
;;
esac
# func_munge_path_list VARIABLE PATH
# -----------------------------------
# VARIABLE is name of variable containing _space_ separated list of
# directories to be munged by the contents of PATH, which is string
# having a format:
# "DIR[:DIR]:"
# string "DIR[ DIR]" will be prepended to VARIABLE
# ":DIR[:DIR]"
# string "DIR[ DIR]" will be appended to VARIABLE
# "DIRP[:DIRP]::[DIRA:]DIRA"
# string "DIRP[ DIRP]" will be prepended to VARIABLE and string
# "DIRA[ DIRA]" will be appended to VARIABLE
# "DIR[:DIR]"
# VARIABLE will be replaced by "DIR[ DIR]"
func_munge_path_list ()
{
case x$2 in
x)
;;
*:)
eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\"
;;
x:*)
eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\"
;;
*::*)
eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\"
eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\"
;;
*)
eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\"
;;
esac
}
for ac_header in dlfcn.h
do :
ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default
"
if test "x$ac_cv_header_dlfcn_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_DLFCN_H 1
_ACEOF
fi
done
# Set options
enable_dlopen=no
enable_win32_dll=no
# Check whether --enable-shared was given.
if test "${enable_shared+set}" = set; then :
enableval=$enable_shared; p=${PACKAGE-default}
case $enableval in
yes) enable_shared=yes ;;
no) enable_shared=no ;;
*)
enable_shared=no
# Look at the argument we got. We use all the common list separators.
lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
for pkg in $enableval; do
IFS=$lt_save_ifs
if test "X$pkg" = "X$p"; then
enable_shared=yes
fi
done
IFS=$lt_save_ifs
;;
esac
else
enable_shared=yes
fi
# Check whether --enable-static was given.
if test "${enable_static+set}" = set; then :
enableval=$enable_static; p=${PACKAGE-default}
case $enableval in
yes) enable_static=yes ;;
no) enable_static=no ;;
*)
enable_static=no
# Look at the argument we got. We use all the common list separators.
lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
for pkg in $enableval; do
IFS=$lt_save_ifs
if test "X$pkg" = "X$p"; then
enable_static=yes
fi
done
IFS=$lt_save_ifs
;;
esac
else
enable_static=yes
fi
# Check whether --with-pic was given.
if test "${with_pic+set}" = set; then :
withval=$with_pic; lt_p=${PACKAGE-default}
case $withval in
yes|no) pic_mode=$withval ;;
*)
pic_mode=default
# Look at the argument we got. We use all the common list separators.
lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
for lt_pkg in $withval; do
IFS=$lt_save_ifs
if test "X$lt_pkg" = "X$lt_p"; then
pic_mode=yes
fi
done
IFS=$lt_save_ifs
;;
esac
else
pic_mode=default
fi
# Check whether --enable-fast-install was given.
if test "${enable_fast_install+set}" = set; then :
enableval=$enable_fast_install; p=${PACKAGE-default}
case $enableval in
yes) enable_fast_install=yes ;;
no) enable_fast_install=no ;;
*)
enable_fast_install=no
# Look at the argument we got. We use all the common list separators.
lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
for pkg in $enableval; do
IFS=$lt_save_ifs
if test "X$pkg" = "X$p"; then
enable_fast_install=yes
fi
done
IFS=$lt_save_ifs
;;
esac
else
enable_fast_install=yes
fi
shared_archive_member_spec=
case $host,$enable_shared in
power*-*-aix[5-9]*,yes)
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking which variant of shared library versioning to provide" >&5
$as_echo_n "checking which variant of shared library versioning to provide... " >&6; }
# Check whether --with-aix-soname was given.
if test "${with_aix_soname+set}" = set; then :
withval=$with_aix_soname; case $withval in
aix|svr4|both)
;;
*)
as_fn_error $? "Unknown argument to --with-aix-soname" "$LINENO" 5
;;
esac
lt_cv_with_aix_soname=$with_aix_soname
else
if ${lt_cv_with_aix_soname+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_with_aix_soname=aix
fi
with_aix_soname=$lt_cv_with_aix_soname
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_aix_soname" >&5
$as_echo "$with_aix_soname" >&6; }
if test aix != "$with_aix_soname"; then
# For the AIX way of multilib, we name the shared archive member
# based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o',
# and 'shr.imp' or 'shr_64.imp', respectively, for the Import File.
# Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag,
# the AIX toolchain works better with OBJECT_MODE set (default 32).
if test 64 = "${OBJECT_MODE-32}"; then
shared_archive_member_spec=shr_64
else
shared_archive_member_spec=shr
fi
fi
;;
*)
with_aix_soname=aix
;;
esac
# This can be used to rebuild libtool when needed
LIBTOOL_DEPS=$ltmain
# Always use our own libtool.
LIBTOOL='$(SHELL) $(top_builddir)/libtool'
test -z "$LN_S" && LN_S="ln -s"
if test -n "${ZSH_VERSION+set}"; then
setopt NO_GLOB_SUBST
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5
$as_echo_n "checking for objdir... " >&6; }
if ${lt_cv_objdir+:} false; then :
$as_echo_n "(cached) " >&6
else
rm -f .libs 2>/dev/null
mkdir .libs 2>/dev/null
if test -d .libs; then
lt_cv_objdir=.libs
else
# MS-DOS does not allow filenames that begin with a dot.
lt_cv_objdir=_libs
fi
rmdir .libs 2>/dev/null
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5
$as_echo "$lt_cv_objdir" >&6; }
objdir=$lt_cv_objdir
cat >>confdefs.h <<_ACEOF
#define LT_OBJDIR "$lt_cv_objdir/"
_ACEOF
case $host_os in
aix3*)
# AIX sometimes has problems with the GCC collect2 program. For some
# reason, if we set the COLLECT_NAMES environment variable, the problems
# vanish in a puff of smoke.
if test set != "${COLLECT_NAMES+set}"; then
COLLECT_NAMES=
export COLLECT_NAMES
fi
;;
esac
# Global variables:
ofile=libtool
can_build_shared=yes
# All known linkers require a '.a' archive for static linking (except MSVC,
# which needs '.lib').
libext=a
with_gnu_ld=$lt_cv_prog_gnu_ld
old_CC=$CC
old_CFLAGS=$CFLAGS
# Set sane defaults for various variables
test -z "$CC" && CC=cc
test -z "$LTCC" && LTCC=$CC
test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS
test -z "$LD" && LD=ld
test -z "$ac_objext" && ac_objext=o
func_cc_basename $compiler
cc_basename=$func_cc_basename_result
# Only perform the check for file, if the check method requires it
test -z "$MAGIC_CMD" && MAGIC_CMD=file
case $deplibs_check_method in
file_magic*)
if test "$file_magic_cmd" = '$MAGIC_CMD'; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5
$as_echo_n "checking for ${ac_tool_prefix}file... " >&6; }
if ${lt_cv_path_MAGIC_CMD+:} false; then :
$as_echo_n "(cached) " >&6
else
case $MAGIC_CMD in
[\\/*] | ?:[\\/]*)
lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path.
;;
*)
lt_save_MAGIC_CMD=$MAGIC_CMD
lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
for ac_dir in $ac_dummy; do
IFS=$lt_save_ifs
test -z "$ac_dir" && ac_dir=.
if test -f "$ac_dir/${ac_tool_prefix}file"; then
lt_cv_path_MAGIC_CMD=$ac_dir/"${ac_tool_prefix}file"
if test -n "$file_magic_test_file"; then
case $deplibs_check_method in
"file_magic "*)
file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
MAGIC_CMD=$lt_cv_path_MAGIC_CMD
if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
$EGREP "$file_magic_regex" > /dev/null; then
:
else
cat <<_LT_EOF 1>&2
*** Warning: the command libtool uses to detect shared libraries,
*** $file_magic_cmd, produces output that libtool cannot recognize.
*** The result is that libtool may fail to recognize shared libraries
*** as such. This will affect the creation of libtool libraries that
*** depend on shared libraries, but programs linked with such libtool
*** libraries will work regardless of this problem. Nevertheless, you
*** may want to report the problem to your system manager and/or to
*** bug-libtool@gnu.org
_LT_EOF
fi ;;
esac
fi
break
fi
done
IFS=$lt_save_ifs
MAGIC_CMD=$lt_save_MAGIC_CMD
;;
esac
fi
MAGIC_CMD=$lt_cv_path_MAGIC_CMD
if test -n "$MAGIC_CMD"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5
$as_echo "$MAGIC_CMD" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test -z "$lt_cv_path_MAGIC_CMD"; then
if test -n "$ac_tool_prefix"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5
$as_echo_n "checking for file... " >&6; }
if ${lt_cv_path_MAGIC_CMD+:} false; then :
$as_echo_n "(cached) " >&6
else
case $MAGIC_CMD in
[\\/*] | ?:[\\/]*)
lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path.
;;
*)
lt_save_MAGIC_CMD=$MAGIC_CMD
lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
for ac_dir in $ac_dummy; do
IFS=$lt_save_ifs
test -z "$ac_dir" && ac_dir=.
if test -f "$ac_dir/file"; then
lt_cv_path_MAGIC_CMD=$ac_dir/"file"
if test -n "$file_magic_test_file"; then
case $deplibs_check_method in
"file_magic "*)
file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
MAGIC_CMD=$lt_cv_path_MAGIC_CMD
if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
$EGREP "$file_magic_regex" > /dev/null; then
:
else
cat <<_LT_EOF 1>&2
*** Warning: the command libtool uses to detect shared libraries,
*** $file_magic_cmd, produces output that libtool cannot recognize.
*** The result is that libtool may fail to recognize shared libraries
*** as such. This will affect the creation of libtool libraries that
*** depend on shared libraries, but programs linked with such libtool
*** libraries will work regardless of this problem. Nevertheless, you
*** may want to report the problem to your system manager and/or to
*** bug-libtool@gnu.org
_LT_EOF
fi ;;
esac
fi
break
fi
done
IFS=$lt_save_ifs
MAGIC_CMD=$lt_save_MAGIC_CMD
;;
esac
fi
MAGIC_CMD=$lt_cv_path_MAGIC_CMD
if test -n "$MAGIC_CMD"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5
$as_echo "$MAGIC_CMD" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
else
MAGIC_CMD=:
fi
fi
fi
;;
esac
# Use C for the default configuration in the libtool script
lt_save_CC=$CC
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
# Source file extension for C test sources.
ac_ext=c
# Object file extension for compiled C test sources.
objext=o
objext=$objext
# Code to be used in simple compile tests
lt_simple_compile_test_code="int some_variable = 0;"
# Code to be used in simple link tests
lt_simple_link_test_code='int main(){return(0);}'
# If no C compiler was specified, use CC.
LTCC=${LTCC-"$CC"}
# If no C compiler flags were specified, use CFLAGS.
LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
# Allow CC to be a program name with arguments.
compiler=$CC
# Save the default compiler, since it gets overwritten when the other
# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP.
compiler_DEFAULT=$CC
# save warnings/boilerplate of simple test code
ac_outfile=conftest.$ac_objext
echo "$lt_simple_compile_test_code" >conftest.$ac_ext
eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
_lt_compiler_boilerplate=`cat conftest.err`
$RM conftest*
ac_outfile=conftest.$ac_objext
echo "$lt_simple_link_test_code" >conftest.$ac_ext
eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
_lt_linker_boilerplate=`cat conftest.err`
$RM -r conftest*
if test -n "$compiler"; then
lt_prog_compiler_no_builtin_flag=
if test yes = "$GCC"; then
case $cc_basename in
nvcc*)
lt_prog_compiler_no_builtin_flag=' -Xcompiler -fno-builtin' ;;
*)
lt_prog_compiler_no_builtin_flag=' -fno-builtin' ;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5
$as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; }
if ${lt_cv_prog_compiler_rtti_exceptions+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_prog_compiler_rtti_exceptions=no
ac_outfile=conftest.$ac_objext
echo "$lt_simple_compile_test_code" > conftest.$ac_ext
lt_compiler_flag="-fno-rtti -fno-exceptions" ## exclude from sc_useless_quotes_in_assignment
# Insert the option either (1) after the last *FLAGS variable, or
# (2) before a word containing "conftest.", or (3) at the end.
# Note that $ac_compile itself does not contain backslashes and begins
# with a dollar sign (not a hyphen), so the echo should work correctly.
# The option is referenced via a variable to avoid confusing sed.
lt_compile=`echo "$ac_compile" | $SED \
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
$ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp
$SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
lt_cv_prog_compiler_rtti_exceptions=yes
fi
fi
$RM conftest*
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5
$as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; }
if test yes = "$lt_cv_prog_compiler_rtti_exceptions"; then
lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions"
else
:
fi
fi
lt_prog_compiler_wl=
lt_prog_compiler_pic=
lt_prog_compiler_static=
if test yes = "$GCC"; then
lt_prog_compiler_wl='-Wl,'
lt_prog_compiler_static='-static'
case $host_os in
aix*)
# All AIX code is PIC.
if test ia64 = "$host_cpu"; then
# AIX 5 now supports IA64 processor
lt_prog_compiler_static='-Bstatic'
fi
lt_prog_compiler_pic='-fPIC'
;;
amigaos*)
case $host_cpu in
powerpc)
# see comment about AmigaOS4 .so support
lt_prog_compiler_pic='-fPIC'
;;
m68k)
# FIXME: we need at least 68020 code to build shared libraries, but
# adding the '-m68020' flag to GCC prevents building anything better,
# like '-m68040'.
lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4'
;;
esac
;;
beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
# PIC is the default for these OSes.
;;
mingw* | cygwin* | pw32* | os2* | cegcc*)
# This hack is so that the source file can tell whether it is being
# built for inclusion in a dll (and should export symbols for example).
# Although the cygwin gcc ignores -fPIC, still need this for old-style
# (--disable-auto-import) libraries
lt_prog_compiler_pic='-DDLL_EXPORT'
case $host_os in
os2*)
lt_prog_compiler_static='$wl-static'
;;
esac
;;
darwin* | rhapsody*)
# PIC is the default on this platform
# Common symbols not allowed in MH_DYLIB files
lt_prog_compiler_pic='-fno-common'
;;
haiku*)
# PIC is the default for Haiku.
# The "-static" flag exists, but is broken.
lt_prog_compiler_static=
;;
hpux*)
# PIC is the default for 64-bit PA HP-UX, but not for 32-bit
# PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag
# sets the default TLS model and affects inlining.
case $host_cpu in
hppa*64*)
# +Z the default
;;
*)
lt_prog_compiler_pic='-fPIC'
;;
esac
;;
interix[3-9]*)
# Interix 3.x gcc -fpic/-fPIC options generate broken code.
# Instead, we relocate shared libraries at runtime.
;;
msdosdjgpp*)
# Just because we use GCC doesn't mean we suddenly get shared libraries
# on systems that don't support them.
lt_prog_compiler_can_build_shared=no
enable_shared=no
;;
*nto* | *qnx*)
# QNX uses GNU C++, but need to define -shared option too, otherwise
# it will coredump.
lt_prog_compiler_pic='-fPIC -shared'
;;
sysv4*MP*)
if test -d /usr/nec; then
lt_prog_compiler_pic=-Kconform_pic
fi
;;
*)
lt_prog_compiler_pic='-fPIC'
;;
esac
case $cc_basename in
nvcc*) # Cuda Compiler Driver 2.2
lt_prog_compiler_wl='-Xlinker '
if test -n "$lt_prog_compiler_pic"; then
lt_prog_compiler_pic="-Xcompiler $lt_prog_compiler_pic"
fi
;;
esac
else
# PORTME Check for flag to pass linker flags through the system compiler.
case $host_os in
aix*)
lt_prog_compiler_wl='-Wl,'
if test ia64 = "$host_cpu"; then
# AIX 5 now supports IA64 processor
lt_prog_compiler_static='-Bstatic'
else
lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp'
fi
;;
darwin* | rhapsody*)
# PIC is the default on this platform
# Common symbols not allowed in MH_DYLIB files
lt_prog_compiler_pic='-fno-common'
case $cc_basename in
nagfor*)
# NAG Fortran compiler
lt_prog_compiler_wl='-Wl,-Wl,,'
lt_prog_compiler_pic='-PIC'
lt_prog_compiler_static='-Bstatic'
;;
esac
;;
mingw* | cygwin* | pw32* | os2* | cegcc*)
# This hack is so that the source file can tell whether it is being
# built for inclusion in a dll (and should export symbols for example).
lt_prog_compiler_pic='-DDLL_EXPORT'
case $host_os in
os2*)
lt_prog_compiler_static='$wl-static'
;;
esac
;;
hpux9* | hpux10* | hpux11*)
lt_prog_compiler_wl='-Wl,'
# PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
# not for PA HP-UX.
case $host_cpu in
hppa*64*|ia64*)
# +Z the default
;;
*)
lt_prog_compiler_pic='+Z'
;;
esac
# Is there a better lt_prog_compiler_static that works with the bundled CC?
lt_prog_compiler_static='$wl-a ${wl}archive'
;;
irix5* | irix6* | nonstopux*)
lt_prog_compiler_wl='-Wl,'
# PIC (with -KPIC) is the default.
lt_prog_compiler_static='-non_shared'
;;
linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
case $cc_basename in
# old Intel for x86_64, which still supported -KPIC.
ecc*)
lt_prog_compiler_wl='-Wl,'
lt_prog_compiler_pic='-KPIC'
lt_prog_compiler_static='-static'
;;
# icc used to be incompatible with GCC.
# ICC 10 doesn't accept -KPIC any more.
icc* | ifort*)
lt_prog_compiler_wl='-Wl,'
lt_prog_compiler_pic='-fPIC'
lt_prog_compiler_static='-static'
;;
# Lahey Fortran 8.1.
lf95*)
lt_prog_compiler_wl='-Wl,'
lt_prog_compiler_pic='--shared'
lt_prog_compiler_static='--static'
;;
nagfor*)
# NAG Fortran compiler
lt_prog_compiler_wl='-Wl,-Wl,,'
lt_prog_compiler_pic='-PIC'
lt_prog_compiler_static='-Bstatic'
;;
tcc*)
# Fabrice Bellard et al's Tiny C Compiler
lt_prog_compiler_wl='-Wl,'
lt_prog_compiler_pic='-fPIC'
lt_prog_compiler_static='-static'
;;
pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*)
# Portland Group compilers (*not* the Pentium gcc compiler,
# which looks to be a dead project)
lt_prog_compiler_wl='-Wl,'
lt_prog_compiler_pic='-fpic'
lt_prog_compiler_static='-Bstatic'
;;
ccc*)
lt_prog_compiler_wl='-Wl,'
# All Alpha code is PIC.
lt_prog_compiler_static='-non_shared'
;;
xl* | bgxl* | bgf* | mpixl*)
# IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene
lt_prog_compiler_wl='-Wl,'
lt_prog_compiler_pic='-qpic'
lt_prog_compiler_static='-qstaticlink'
;;
*)
case `$CC -V 2>&1 | sed 5q` in
*Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [1-7].* | *Sun*Fortran*\ 8.[0-3]*)
# Sun Fortran 8.3 passes all unrecognized flags to the linker
lt_prog_compiler_pic='-KPIC'
lt_prog_compiler_static='-Bstatic'
lt_prog_compiler_wl=''
;;
*Sun\ F* | *Sun*Fortran*)
lt_prog_compiler_pic='-KPIC'
lt_prog_compiler_static='-Bstatic'
lt_prog_compiler_wl='-Qoption ld '
;;
*Sun\ C*)
# Sun C 5.9
lt_prog_compiler_pic='-KPIC'
lt_prog_compiler_static='-Bstatic'
lt_prog_compiler_wl='-Wl,'
;;
*Intel*\ [CF]*Compiler*)
lt_prog_compiler_wl='-Wl,'
lt_prog_compiler_pic='-fPIC'
lt_prog_compiler_static='-static'
;;
*Portland\ Group*)
lt_prog_compiler_wl='-Wl,'
lt_prog_compiler_pic='-fpic'
lt_prog_compiler_static='-Bstatic'
;;
esac
;;
esac
;;
newsos6)
lt_prog_compiler_pic='-KPIC'
lt_prog_compiler_static='-Bstatic'
;;
*nto* | *qnx*)
# QNX uses GNU C++, but need to define -shared option too, otherwise
# it will coredump.
lt_prog_compiler_pic='-fPIC -shared'
;;
osf3* | osf4* | osf5*)
lt_prog_compiler_wl='-Wl,'
# All OSF/1 code is PIC.
lt_prog_compiler_static='-non_shared'
;;
rdos*)
lt_prog_compiler_static='-non_shared'
;;
solaris*)
lt_prog_compiler_pic='-KPIC'
lt_prog_compiler_static='-Bstatic'
case $cc_basename in
f77* | f90* | f95* | sunf77* | sunf90* | sunf95*)
lt_prog_compiler_wl='-Qoption ld ';;
*)
lt_prog_compiler_wl='-Wl,';;
esac
;;
sunos4*)
lt_prog_compiler_wl='-Qoption ld '
lt_prog_compiler_pic='-PIC'
lt_prog_compiler_static='-Bstatic'
;;
sysv4 | sysv4.2uw2* | sysv4.3*)
lt_prog_compiler_wl='-Wl,'
lt_prog_compiler_pic='-KPIC'
lt_prog_compiler_static='-Bstatic'
;;
sysv4*MP*)
if test -d /usr/nec; then
lt_prog_compiler_pic='-Kconform_pic'
lt_prog_compiler_static='-Bstatic'
fi
;;
sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
lt_prog_compiler_wl='-Wl,'
lt_prog_compiler_pic='-KPIC'
lt_prog_compiler_static='-Bstatic'
;;
unicos*)
lt_prog_compiler_wl='-Wl,'
lt_prog_compiler_can_build_shared=no
;;
uts4*)
lt_prog_compiler_pic='-pic'
lt_prog_compiler_static='-Bstatic'
;;
*)
lt_prog_compiler_can_build_shared=no
;;
esac
fi
case $host_os in
# For platforms that do not support PIC, -DPIC is meaningless:
*djgpp*)
lt_prog_compiler_pic=
;;
*)
lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC"
;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5
$as_echo_n "checking for $compiler option to produce PIC... " >&6; }
if ${lt_cv_prog_compiler_pic+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_prog_compiler_pic=$lt_prog_compiler_pic
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic" >&5
$as_echo "$lt_cv_prog_compiler_pic" >&6; }
lt_prog_compiler_pic=$lt_cv_prog_compiler_pic
#
# Check to make sure the PIC flag actually works.
#
if test -n "$lt_prog_compiler_pic"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5
$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; }
if ${lt_cv_prog_compiler_pic_works+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_prog_compiler_pic_works=no
ac_outfile=conftest.$ac_objext
echo "$lt_simple_compile_test_code" > conftest.$ac_ext
lt_compiler_flag="$lt_prog_compiler_pic -DPIC" ## exclude from sc_useless_quotes_in_assignment
# Insert the option either (1) after the last *FLAGS variable, or
# (2) before a word containing "conftest.", or (3) at the end.
# Note that $ac_compile itself does not contain backslashes and begins
# with a dollar sign (not a hyphen), so the echo should work correctly.
# The option is referenced via a variable to avoid confusing sed.
lt_compile=`echo "$ac_compile" | $SED \
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
$ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp
$SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
lt_cv_prog_compiler_pic_works=yes
fi
fi
$RM conftest*
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5
$as_echo "$lt_cv_prog_compiler_pic_works" >&6; }
if test yes = "$lt_cv_prog_compiler_pic_works"; then
case $lt_prog_compiler_pic in
"" | " "*) ;;
*) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;;
esac
else
lt_prog_compiler_pic=
lt_prog_compiler_can_build_shared=no
fi
fi
#
# Check to make sure the static flag actually works.
#
wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5
$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; }
if ${lt_cv_prog_compiler_static_works+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_prog_compiler_static_works=no
save_LDFLAGS=$LDFLAGS
LDFLAGS="$LDFLAGS $lt_tmp_static_flag"
echo "$lt_simple_link_test_code" > conftest.$ac_ext
if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
# The linker can only warn and ignore the option if not recognized
# So say no if there are warnings
if test -s conftest.err; then
# Append any errors to the config.log.
cat conftest.err 1>&5
$ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp
$SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
if diff conftest.exp conftest.er2 >/dev/null; then
lt_cv_prog_compiler_static_works=yes
fi
else
lt_cv_prog_compiler_static_works=yes
fi
fi
$RM -r conftest*
LDFLAGS=$save_LDFLAGS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5
$as_echo "$lt_cv_prog_compiler_static_works" >&6; }
if test yes = "$lt_cv_prog_compiler_static_works"; then
:
else
lt_prog_compiler_static=
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
if ${lt_cv_prog_compiler_c_o+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_prog_compiler_c_o=no
$RM -r conftest 2>/dev/null
mkdir conftest
cd conftest
mkdir out
echo "$lt_simple_compile_test_code" > conftest.$ac_ext
lt_compiler_flag="-o out/conftest2.$ac_objext"
# Insert the option either (1) after the last *FLAGS variable, or
# (2) before a word containing "conftest.", or (3) at the end.
# Note that $ac_compile itself does not contain backslashes and begins
# with a dollar sign (not a hyphen), so the echo should work correctly.
lt_compile=`echo "$ac_compile" | $SED \
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings
$ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp
$SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
lt_cv_prog_compiler_c_o=yes
fi
fi
chmod u+w . 2>&5
$RM conftest*
# SGI C++ compiler will create directory out/ii_files/ for
# template instantiation
test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
$RM out/* && rmdir out
cd ..
$RM -r conftest
$RM conftest*
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5
$as_echo "$lt_cv_prog_compiler_c_o" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
if ${lt_cv_prog_compiler_c_o+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_prog_compiler_c_o=no
$RM -r conftest 2>/dev/null
mkdir conftest
cd conftest
mkdir out
echo "$lt_simple_compile_test_code" > conftest.$ac_ext
lt_compiler_flag="-o out/conftest2.$ac_objext"
# Insert the option either (1) after the last *FLAGS variable, or
# (2) before a word containing "conftest.", or (3) at the end.
# Note that $ac_compile itself does not contain backslashes and begins
# with a dollar sign (not a hyphen), so the echo should work correctly.
lt_compile=`echo "$ac_compile" | $SED \
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings
$ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp
$SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
lt_cv_prog_compiler_c_o=yes
fi
fi
chmod u+w . 2>&5
$RM conftest*
# SGI C++ compiler will create directory out/ii_files/ for
# template instantiation
test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
$RM out/* && rmdir out
cd ..
$RM -r conftest
$RM conftest*
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5
$as_echo "$lt_cv_prog_compiler_c_o" >&6; }
hard_links=nottested
if test no = "$lt_cv_prog_compiler_c_o" && test no != "$need_locks"; then
# do not overwrite the value of need_locks provided by the user
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5
$as_echo_n "checking if we can lock with hard links... " >&6; }
hard_links=yes
$RM conftest*
ln conftest.a conftest.b 2>/dev/null && hard_links=no
touch conftest.a
ln conftest.a conftest.b 2>&5 || hard_links=no
ln conftest.a conftest.b 2>/dev/null && hard_links=no
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5
$as_echo "$hard_links" >&6; }
if test no = "$hard_links"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&5
$as_echo "$as_me: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&2;}
need_locks=warn
fi
else
need_locks=no
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5
$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; }
runpath_var=
allow_undefined_flag=
always_export_symbols=no
archive_cmds=
archive_expsym_cmds=
compiler_needs_object=no
enable_shared_with_static_runtimes=no
export_dynamic_flag_spec=
export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
hardcode_automatic=no
hardcode_direct=no
hardcode_direct_absolute=no
hardcode_libdir_flag_spec=
hardcode_libdir_separator=
hardcode_minus_L=no
hardcode_shlibpath_var=unsupported
inherit_rpath=no
link_all_deplibs=unknown
module_cmds=
module_expsym_cmds=
old_archive_from_new_cmds=
old_archive_from_expsyms_cmds=
thread_safe_flag_spec=
whole_archive_flag_spec=
# include_expsyms should be a list of space-separated symbols to be *always*
# included in the symbol list
include_expsyms=
# exclude_expsyms can be an extended regexp of symbols to exclude
# it will be wrapped by ' (' and ')$', so one must not match beginning or
# end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc',
# as well as any symbol that contains 'd'.
exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'
# Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
# platforms (ab)use it in PIC code, but their linkers get confused if
# the symbol is explicitly referenced. Since portable code cannot
# rely on this symbol name, it's probably fine to never include it in
# preloaded symbol tables.
# Exclude shared library initialization/finalization symbols.
extract_expsyms_cmds=
case $host_os in
cygwin* | mingw* | pw32* | cegcc*)
# FIXME: the MSVC++ port hasn't been tested in a loooong time
# When not using gcc, we currently assume that we are using
# Microsoft Visual C++.
if test yes != "$GCC"; then
with_gnu_ld=no
fi
;;
interix*)
# we just hope/assume this is gcc and not c89 (= MSVC++)
with_gnu_ld=yes
;;
openbsd* | bitrig*)
with_gnu_ld=no
;;
esac
ld_shlibs=yes
# On some targets, GNU ld is compatible enough with the native linker
# that we're better off using the native interface for both.
lt_use_gnu_ld_interface=no
if test yes = "$with_gnu_ld"; then
case $host_os in
aix*)
# The AIX port of GNU ld has always aspired to compatibility
# with the native linker. However, as the warning in the GNU ld
# block says, versions before 2.19.5* couldn't really create working
# shared libraries, regardless of the interface used.
case `$LD -v 2>&1` in
*\ \(GNU\ Binutils\)\ 2.19.5*) ;;
*\ \(GNU\ Binutils\)\ 2.[2-9]*) ;;
*\ \(GNU\ Binutils\)\ [3-9]*) ;;
*)
lt_use_gnu_ld_interface=yes
;;
esac
;;
*)
lt_use_gnu_ld_interface=yes
;;
esac
fi
if test yes = "$lt_use_gnu_ld_interface"; then
# If archive_cmds runs LD, not CC, wlarc should be empty
wlarc='$wl'
# Set some defaults for GNU ld with shared library support. These
# are reset later if shared libraries are not supported. Putting them
# here allows them to be overridden if necessary.
runpath_var=LD_RUN_PATH
hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
export_dynamic_flag_spec='$wl--export-dynamic'
# ancient GNU ld didn't support --whole-archive et. al.
if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then
whole_archive_flag_spec=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive'
else
whole_archive_flag_spec=
fi
supports_anon_versioning=no
case `$LD -v | $SED -e 's/(^)\+)\s\+//' 2>&1` in
*GNU\ gold*) supports_anon_versioning=yes ;;
*\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11
*\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
*\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
*\ 2.11.*) ;; # other 2.11 versions
*) supports_anon_versioning=yes ;;
esac
# See if GNU ld supports shared libraries.
case $host_os in
aix[3-9]*)
# On AIX/PPC, the GNU linker is very broken
if test ia64 != "$host_cpu"; then
ld_shlibs=no
cat <<_LT_EOF 1>&2
*** Warning: the GNU linker, at least up to release 2.19, is reported
*** to be unable to reliably create shared libraries on AIX.
*** Therefore, libtool is disabling shared libraries support. If you
*** really care for shared libraries, you may want to install binutils
*** 2.20 or above, or modify your PATH so that a non-GNU linker is found.
*** You will then need to restart the configuration process.
_LT_EOF
fi
;;
amigaos*)
case $host_cpu in
powerpc)
# see comment about AmigaOS4 .so support
archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
archive_expsym_cmds=''
;;
m68k)
archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
hardcode_libdir_flag_spec='-L$libdir'
hardcode_minus_L=yes
;;
esac
;;
beos*)
if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
allow_undefined_flag=unsupported
# Joseph Beckenbach <jrb3@best.com> says some releases of gcc
# support --undefined. This deserves some investigation. FIXME
archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
else
ld_shlibs=no
fi
;;
cygwin* | mingw* | pw32* | cegcc*)
# _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless,
# as there is no search path for DLLs.
hardcode_libdir_flag_spec='-L$libdir'
export_dynamic_flag_spec='$wl--export-all-symbols'
allow_undefined_flag=unsupported
always_export_symbols=no
enable_shared_with_static_runtimes=yes
export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols'
exclude_expsyms='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'
if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
# If the export-symbols file already is a .def file, use it as
# is; otherwise, prepend EXPORTS...
archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then
cp $export_symbols $output_objdir/$soname.def;
else
echo EXPORTS > $output_objdir/$soname.def;
cat $export_symbols >> $output_objdir/$soname.def;
fi~
$CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
else
ld_shlibs=no
fi
;;
haiku*)
archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
link_all_deplibs=yes
;;
os2*)
hardcode_libdir_flag_spec='-L$libdir'
hardcode_minus_L=yes
allow_undefined_flag=unsupported
shrext_cmds=.dll
archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
$ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
$ECHO EXPORTS >> $output_objdir/$libname.def~
emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~
$CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
emximp -o $lib $output_objdir/$libname.def'
archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
$ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
$ECHO EXPORTS >> $output_objdir/$libname.def~
prefix_cmds="$SED"~
if test EXPORTS = "`$SED 1q $export_symbols`"; then
prefix_cmds="$prefix_cmds -e 1d";
fi~
prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~
cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~
$CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
emximp -o $lib $output_objdir/$libname.def'
old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def'
enable_shared_with_static_runtimes=yes
;;
interix[3-9]*)
hardcode_direct=no
hardcode_shlibpath_var=no
hardcode_libdir_flag_spec='$wl-rpath,$libdir'
export_dynamic_flag_spec='$wl-E'
# Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
# Instead, shared libraries are loaded at an image base (0x10000000 by
# default) and relocated if they conflict, which is a slow very memory
# consuming and fragmenting process. To avoid this, we pick a random,
# 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
# time. Moving up from 0x10000000 also allows more sbrk(2) space.
archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
archive_expsym_cmds='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
;;
gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu)
tmp_diet=no
if test linux-dietlibc = "$host_os"; then
case $cc_basename in
diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn)
esac
fi
if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \
&& test no = "$tmp_diet"
then
tmp_addflag=' $pic_flag'
tmp_sharedflag='-shared'
case $cc_basename,$host_cpu in
pgcc*) # Portland Group C compiler
whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
tmp_addflag=' $pic_flag'
;;
pgf77* | pgf90* | pgf95* | pgfortran*)
# Portland Group f77 and f90 compilers
whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
tmp_addflag=' $pic_flag -Mnomain' ;;
ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64
tmp_addflag=' -i_dynamic' ;;
efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64
tmp_addflag=' -i_dynamic -nofor_main' ;;
ifc* | ifort*) # Intel Fortran compiler
tmp_addflag=' -nofor_main' ;;
lf95*) # Lahey Fortran 8.1
whole_archive_flag_spec=
tmp_sharedflag='--shared' ;;
nagfor*) # NAGFOR 5.3
tmp_sharedflag='-Wl,-shared' ;;
xl[cC]* | bgxl[cC]* | mpixl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below)
tmp_sharedflag='-qmkshrobj'
tmp_addflag= ;;
nvcc*) # Cuda Compiler Driver 2.2
whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
compiler_needs_object=yes
;;
esac
case `$CC -V 2>&1 | sed 5q` in
*Sun\ C*) # Sun C 5.9
whole_archive_flag_spec='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
compiler_needs_object=yes
tmp_sharedflag='-G' ;;
*Sun\ F*) # Sun Fortran 8.3
tmp_sharedflag='-G' ;;
esac
archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
if test yes = "$supports_anon_versioning"; then
archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
echo "local: *; };" >> $output_objdir/$libname.ver~
$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib'
fi
case $cc_basename in
tcc*)
export_dynamic_flag_spec='-rdynamic'
;;
xlf* | bgf* | bgxlf* | mpixlf*)
# IBM XL Fortran 10.1 on PPC cannot create shared libs itself
whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive'
hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib'
if test yes = "$supports_anon_versioning"; then
archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
echo "local: *; };" >> $output_objdir/$libname.ver~
$LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib'
fi
;;
esac
else
ld_shlibs=no
fi
;;
netbsd*)
if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
wlarc=
else
archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
fi
;;
solaris*)
if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then
ld_shlibs=no
cat <<_LT_EOF 1>&2
*** Warning: The releases 2.8.* of the GNU linker cannot reliably
*** create shared libraries on Solaris systems. Therefore, libtool
*** is disabling shared libraries support. We urge you to upgrade GNU
*** binutils to release 2.9.1 or newer. Another option is to modify
*** your PATH or compiler configuration so that the native linker is
*** used, and then restart.
_LT_EOF
elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
else
ld_shlibs=no
fi
;;
sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*)
case `$LD -v 2>&1` in
*\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*)
ld_shlibs=no
cat <<_LT_EOF 1>&2
*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot
*** reliably create shared libraries on SCO systems. Therefore, libtool
*** is disabling shared libraries support. We urge you to upgrade GNU
*** binutils to release 2.16.91.0.3 or newer. Another option is to modify
*** your PATH or compiler configuration so that the native linker is
*** used, and then restart.
_LT_EOF
;;
*)
# For security reasons, it is highly recommended that you always
# use absolute paths for naming shared libraries, and exclude the
# DT_RUNPATH tag from executables and libraries. But doing so
# requires that you compile everything twice, which is a pain.
if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
else
ld_shlibs=no
fi
;;
esac
;;
sunos4*)
archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
wlarc=
hardcode_direct=yes
hardcode_shlibpath_var=no
;;
*)
if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
else
ld_shlibs=no
fi
;;
esac
if test no = "$ld_shlibs"; then
runpath_var=
hardcode_libdir_flag_spec=
export_dynamic_flag_spec=
whole_archive_flag_spec=
fi
else
# PORTME fill in a description of your system's linker (not GNU ld)
case $host_os in
aix3*)
allow_undefined_flag=unsupported
always_export_symbols=yes
archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
# Note: this linker hardcodes the directories in LIBPATH if there
# are no directories specified by -L.
hardcode_minus_L=yes
if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then
# Neither direct hardcoding nor static linking is supported with a
# broken collect2.
hardcode_direct=unsupported
fi
;;
aix[4-9]*)
if test ia64 = "$host_cpu"; then
# On IA64, the linker does run time linking by default, so we don't
# have to do anything special.
aix_use_runtimelinking=no
exp_sym_flag='-Bexport'
no_entry_flag=
else
# If we're using GNU nm, then we don't want the "-C" option.
# -C means demangle to GNU nm, but means don't demangle to AIX nm.
# Without the "-l" option, or with the "-B" option, AIX nm treats
# weak defined symbols like other global defined symbols, whereas
# GNU nm marks them as "W".
# While the 'weak' keyword is ignored in the Export File, we need
# it in the Import File for the 'aix-soname' feature, so we have
# to replace the "-B" option with "-P" for AIX nm.
if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols'
else
export_symbols_cmds='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols'
fi
aix_use_runtimelinking=no
# Test if we are trying to use run time linking or normal
# AIX style linking. If -brtl is somewhere in LDFLAGS, we
# have runtime linking enabled, and use it for executables.
# For shared libraries, we enable/disable runtime linking
# depending on the kind of the shared library created -
# when "with_aix_soname,aix_use_runtimelinking" is:
# "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables
# "aix,yes" lib.so shared, rtl:yes, for executables
# lib.a static archive
# "both,no" lib.so.V(shr.o) shared, rtl:yes
# lib.a(lib.so.V) shared, rtl:no, for executables
# "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables
# lib.a(lib.so.V) shared, rtl:no
# "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables
# lib.a static archive
case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*)
for ld_flag in $LDFLAGS; do
if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then
aix_use_runtimelinking=yes
break
fi
done
if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then
# With aix-soname=svr4, we create the lib.so.V shared archives only,
# so we don't have lib.a shared libs to link our executables.
# We have to force runtime linking in this case.
aix_use_runtimelinking=yes
LDFLAGS="$LDFLAGS -Wl,-brtl"
fi
;;
esac
exp_sym_flag='-bexport'
no_entry_flag='-bnoentry'
fi
# When large executables or shared objects are built, AIX ld can
# have problems creating the table of contents. If linking a library
# or program results in "error TOC overflow" add -mminimal-toc to
# CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not
# enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
archive_cmds=''
hardcode_direct=yes
hardcode_direct_absolute=yes
hardcode_libdir_separator=':'
link_all_deplibs=yes
file_list_spec='$wl-f,'
case $with_aix_soname,$aix_use_runtimelinking in
aix,*) ;; # traditional, no import file
svr4,* | *,yes) # use import file
# The Import File defines what to hardcode.
hardcode_direct=no
hardcode_direct_absolute=no
;;
esac
if test yes = "$GCC"; then
case $host_os in aix4.[012]|aix4.[012].*)
# We only want to do this on AIX 4.2 and lower, the check
# below for broken collect2 doesn't work under 4.3+
collect2name=`$CC -print-prog-name=collect2`
if test -f "$collect2name" &&
strings "$collect2name" | $GREP resolve_lib_name >/dev/null
then
# We have reworked collect2
:
else
# We have old collect2
hardcode_direct=unsupported
# It fails to find uninstalled libraries when the uninstalled
# path is not listed in the libpath. Setting hardcode_minus_L
# to unsupported forces relinking
hardcode_minus_L=yes
hardcode_libdir_flag_spec='-L$libdir'
hardcode_libdir_separator=
fi
;;
esac
shared_flag='-shared'
if test yes = "$aix_use_runtimelinking"; then
shared_flag="$shared_flag "'$wl-G'
fi
# Need to ensure runtime linking is disabled for the traditional
# shared library, or the linker may eventually find shared libraries
# /with/ Import File - we do not want to mix them.
shared_flag_aix='-shared'
shared_flag_svr4='-shared $wl-G'
else
# not using gcc
if test ia64 = "$host_cpu"; then
# VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
# chokes on -Wl,-G. The following line is correct:
shared_flag='-G'
else
if test yes = "$aix_use_runtimelinking"; then
shared_flag='$wl-G'
else
shared_flag='$wl-bM:SRE'
fi
shared_flag_aix='$wl-bM:SRE'
shared_flag_svr4='$wl-G'
fi
fi
export_dynamic_flag_spec='$wl-bexpall'
# It seems that -bexpall does not export symbols beginning with
# underscore (_), so it is better to generate a list of symbols to export.
always_export_symbols=yes
if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then
# Warning - without using the other runtime loading flags (-brtl),
# -berok will link without error, but may produce a broken library.
allow_undefined_flag='-berok'
# Determine the default libpath from the value encoded in an
# empty executable.
if test set = "${lt_cv_aix_libpath+set}"; then
aix_libpath=$lt_cv_aix_libpath
else
if ${lt_cv_aix_libpath_+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
lt_aix_libpath_sed='
/Import File Strings/,/^$/ {
/^0/ {
s/^0 *\([^ ]*\) *$/\1/
p
}
}'
lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
# Check for a 64-bit object if we didn't find anything.
if test -z "$lt_cv_aix_libpath_"; then
lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
fi
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
if test -z "$lt_cv_aix_libpath_"; then
lt_cv_aix_libpath_=/usr/lib:/lib
fi
fi
aix_libpath=$lt_cv_aix_libpath_
fi
hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath"
archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag
else
if test ia64 = "$host_cpu"; then
hardcode_libdir_flag_spec='$wl-R $libdir:/usr/lib:/lib'
allow_undefined_flag="-z nodefs"
archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols"
else
# Determine the default libpath from the value encoded in an
# empty executable.
if test set = "${lt_cv_aix_libpath+set}"; then
aix_libpath=$lt_cv_aix_libpath
else
if ${lt_cv_aix_libpath_+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
lt_aix_libpath_sed='
/Import File Strings/,/^$/ {
/^0/ {
s/^0 *\([^ ]*\) *$/\1/
p
}
}'
lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
# Check for a 64-bit object if we didn't find anything.
if test -z "$lt_cv_aix_libpath_"; then
lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
fi
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
if test -z "$lt_cv_aix_libpath_"; then
lt_cv_aix_libpath_=/usr/lib:/lib
fi
fi
aix_libpath=$lt_cv_aix_libpath_
fi
hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath"
# Warning - without using the other run time loading flags,
# -berok will link without error, but may produce a broken library.
no_undefined_flag=' $wl-bernotok'
allow_undefined_flag=' $wl-berok'
if test yes = "$with_gnu_ld"; then
# We only use this code for GNU lds that support --whole-archive.
whole_archive_flag_spec='$wl--whole-archive$convenience $wl--no-whole-archive'
else
# Exported symbols can be pulled into shared objects from archives
whole_archive_flag_spec='$convenience'
fi
archive_cmds_need_lc=yes
archive_expsym_cmds='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d'
# -brtl affects multiple linker settings, -berok does not and is overridden later
compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([, ]\\)%-berok\\1%g"`'
if test svr4 != "$with_aix_soname"; then
# This is similar to how AIX traditionally builds its shared libraries.
archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname'
fi
if test aix != "$with_aix_soname"; then
archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp'
else
# used by -dlpreopen to get the symbols
archive_expsym_cmds="$archive_expsym_cmds"'~$MV $output_objdir/$realname.d/$soname $output_objdir'
fi
archive_expsym_cmds="$archive_expsym_cmds"'~$RM -r $output_objdir/$realname.d'
fi
fi
;;
amigaos*)
case $host_cpu in
powerpc)
# see comment about AmigaOS4 .so support
archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
archive_expsym_cmds=''
;;
m68k)
archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
hardcode_libdir_flag_spec='-L$libdir'
hardcode_minus_L=yes
;;
esac
;;
bsdi[45]*)
export_dynamic_flag_spec=-rdynamic
;;
cygwin* | mingw* | pw32* | cegcc*)
# When not using gcc, we currently assume that we are using
# Microsoft Visual C++.
# hardcode_libdir_flag_spec is actually meaningless, as there is
# no search path for DLLs.
case $cc_basename in
cl*)
# Native MSVC
hardcode_libdir_flag_spec=' '
allow_undefined_flag=unsupported
always_export_symbols=yes
file_list_spec='@'
# Tell ltmain to make .lib files, not .a files.
libext=lib
# Tell ltmain to make .dll files, not .so files.
shrext_cmds=.dll
# FIXME: Setting linknames here is a bad hack.
archive_cmds='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames='
archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then
cp "$export_symbols" "$output_objdir/$soname.def";
echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp";
else
$SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp;
fi~
$CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
linknames='
# The linker will not automatically build a static lib if we build a DLL.
# _LT_TAGVAR(old_archive_from_new_cmds, )='true'
enable_shared_with_static_runtimes=yes
exclude_expsyms='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*'
export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1,DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols'
# Don't use ranlib
old_postinstall_cmds='chmod 644 $oldlib'
postlink_cmds='lt_outputfile="@OUTPUT@"~
lt_tool_outputfile="@TOOL_OUTPUT@"~
case $lt_outputfile in
*.exe|*.EXE) ;;
*)
lt_outputfile=$lt_outputfile.exe
lt_tool_outputfile=$lt_tool_outputfile.exe
;;
esac~
if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then
$MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1;
$RM "$lt_outputfile.manifest";
fi'
;;
*)
# Assume MSVC wrapper
hardcode_libdir_flag_spec=' '
allow_undefined_flag=unsupported
# Tell ltmain to make .lib files, not .a files.
libext=lib
# Tell ltmain to make .dll files, not .so files.
shrext_cmds=.dll
# FIXME: Setting linknames here is a bad hack.
archive_cmds='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames='
# The linker will automatically build a .lib file if we build a DLL.
old_archive_from_new_cmds='true'
# FIXME: Should let the user specify the lib program.
old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs'
enable_shared_with_static_runtimes=yes
;;
esac
;;
darwin* | rhapsody*)
archive_cmds_need_lc=no
hardcode_direct=no
hardcode_automatic=yes
hardcode_shlibpath_var=unsupported
if test yes = "$lt_cv_ld_force_load"; then
whole_archive_flag_spec='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`'
else
whole_archive_flag_spec=''
fi
link_all_deplibs=yes
allow_undefined_flag=$_lt_dar_allow_undefined
case $cc_basename in
ifort*|nagfor*) _lt_dar_can_shared=yes ;;
*) _lt_dar_can_shared=$GCC ;;
esac
if test yes = "$_lt_dar_can_shared"; then
output_verbose_link_cmd=func_echo_all
archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil"
module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil"
archive_expsym_cmds="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil"
module_expsym_cmds="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil"
else
ld_shlibs=no
fi
;;
dgux*)
archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
hardcode_libdir_flag_spec='-L$libdir'
hardcode_shlibpath_var=no
;;
# FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
# support. Future versions do this automatically, but an explicit c++rt0.o
# does not break anything, and helps significantly (at the cost of a little
# extra space).
freebsd2.2*)
archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
hardcode_libdir_flag_spec='-R$libdir'
hardcode_direct=yes
hardcode_shlibpath_var=no
;;
# Unfortunately, older versions of FreeBSD 2 do not have this feature.
freebsd2.*)
archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
hardcode_direct=yes
hardcode_minus_L=yes
hardcode_shlibpath_var=no
;;
# FreeBSD 3 and greater uses gcc -shared to do shared libraries.
freebsd* | dragonfly*)
archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
hardcode_libdir_flag_spec='-R$libdir'
hardcode_direct=yes
hardcode_shlibpath_var=no
;;
hpux9*)
if test yes = "$GCC"; then
archive_cmds='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
else
archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
fi
hardcode_libdir_flag_spec='$wl+b $wl$libdir'
hardcode_libdir_separator=:
hardcode_direct=yes
# hardcode_minus_L: Not really in the search PATH,
# but as the default location of the library.
hardcode_minus_L=yes
export_dynamic_flag_spec='$wl-E'
;;
hpux10*)
if test yes,no = "$GCC,$with_gnu_ld"; then
archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
else
archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
fi
if test no = "$with_gnu_ld"; then
hardcode_libdir_flag_spec='$wl+b $wl$libdir'
hardcode_libdir_separator=:
hardcode_direct=yes
hardcode_direct_absolute=yes
export_dynamic_flag_spec='$wl-E'
# hardcode_minus_L: Not really in the search PATH,
# but as the default location of the library.
hardcode_minus_L=yes
fi
;;
hpux11*)
if test yes,no = "$GCC,$with_gnu_ld"; then
case $host_cpu in
hppa*64*)
archive_cmds='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
;;
ia64*)
archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
;;
*)
archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
;;
esac
else
case $host_cpu in
hppa*64*)
archive_cmds='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
;;
ia64*)
archive_cmds='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
;;
*)
# Older versions of the 11.00 compiler do not understand -b yet
# (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does)
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC understands -b" >&5
$as_echo_n "checking if $CC understands -b... " >&6; }
if ${lt_cv_prog_compiler__b+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_prog_compiler__b=no
save_LDFLAGS=$LDFLAGS
LDFLAGS="$LDFLAGS -b"
echo "$lt_simple_link_test_code" > conftest.$ac_ext
if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
# The linker can only warn and ignore the option if not recognized
# So say no if there are warnings
if test -s conftest.err; then
# Append any errors to the config.log.
cat conftest.err 1>&5
$ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp
$SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
if diff conftest.exp conftest.er2 >/dev/null; then
lt_cv_prog_compiler__b=yes
fi
else
lt_cv_prog_compiler__b=yes
fi
fi
$RM -r conftest*
LDFLAGS=$save_LDFLAGS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b" >&5
$as_echo "$lt_cv_prog_compiler__b" >&6; }
if test yes = "$lt_cv_prog_compiler__b"; then
archive_cmds='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
else
archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
fi
;;
esac
fi
if test no = "$with_gnu_ld"; then
hardcode_libdir_flag_spec='$wl+b $wl$libdir'
hardcode_libdir_separator=:
case $host_cpu in
hppa*64*|ia64*)
hardcode_direct=no
hardcode_shlibpath_var=no
;;
*)
hardcode_direct=yes
hardcode_direct_absolute=yes
export_dynamic_flag_spec='$wl-E'
# hardcode_minus_L: Not really in the search PATH,
# but as the default location of the library.
hardcode_minus_L=yes
;;
esac
fi
;;
irix5* | irix6* | nonstopux*)
if test yes = "$GCC"; then
archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
# Try to use the -exported_symbol ld option, if it does not
# work, assume that -exports_file does not work either and
# implicitly export all symbols.
# This should be the same for all languages, so no per-tag cache variable.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $host_os linker accepts -exported_symbol" >&5
$as_echo_n "checking whether the $host_os linker accepts -exported_symbol... " >&6; }
if ${lt_cv_irix_exported_symbol+:} false; then :
$as_echo_n "(cached) " >&6
else
save_LDFLAGS=$LDFLAGS
LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int foo (void) { return 0; }
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
lt_cv_irix_exported_symbol=yes
else
lt_cv_irix_exported_symbol=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LDFLAGS=$save_LDFLAGS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_irix_exported_symbol" >&5
$as_echo "$lt_cv_irix_exported_symbol" >&6; }
if test yes = "$lt_cv_irix_exported_symbol"; then
archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib'
fi
else
archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib'
fi
archive_cmds_need_lc='no'
hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
hardcode_libdir_separator=:
inherit_rpath=yes
link_all_deplibs=yes
;;
linux*)
case $cc_basename in
tcc*)
# Fabrice Bellard et al's Tiny C Compiler
ld_shlibs=yes
archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
;;
esac
;;
netbsd*)
if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out
else
archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF
fi
hardcode_libdir_flag_spec='-R$libdir'
hardcode_direct=yes
hardcode_shlibpath_var=no
;;
newsos6)
archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
hardcode_direct=yes
hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
hardcode_libdir_separator=:
hardcode_shlibpath_var=no
;;
*nto* | *qnx*)
;;
openbsd* | bitrig*)
if test -f /usr/libexec/ld.so; then
hardcode_direct=yes
hardcode_shlibpath_var=no
hardcode_direct_absolute=yes
if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols'
hardcode_libdir_flag_spec='$wl-rpath,$libdir'
export_dynamic_flag_spec='$wl-E'
else
archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
hardcode_libdir_flag_spec='$wl-rpath,$libdir'
fi
else
ld_shlibs=no
fi
;;
os2*)
hardcode_libdir_flag_spec='-L$libdir'
hardcode_minus_L=yes
allow_undefined_flag=unsupported
shrext_cmds=.dll
archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
$ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
$ECHO EXPORTS >> $output_objdir/$libname.def~
emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~
$CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
emximp -o $lib $output_objdir/$libname.def'
archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
$ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
$ECHO EXPORTS >> $output_objdir/$libname.def~
prefix_cmds="$SED"~
if test EXPORTS = "`$SED 1q $export_symbols`"; then
prefix_cmds="$prefix_cmds -e 1d";
fi~
prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~
cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~
$CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
emximp -o $lib $output_objdir/$libname.def'
old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def'
enable_shared_with_static_runtimes=yes
;;
osf3*)
if test yes = "$GCC"; then
allow_undefined_flag=' $wl-expect_unresolved $wl\*'
archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
else
allow_undefined_flag=' -expect_unresolved \*'
archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
fi
archive_cmds_need_lc='no'
hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
hardcode_libdir_separator=:
;;
osf4* | osf5*) # as osf3* with the addition of -msym flag
if test yes = "$GCC"; then
allow_undefined_flag=' $wl-expect_unresolved $wl\*'
archive_cmds='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
else
allow_undefined_flag=' -expect_unresolved \*'
archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~
$CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp'
# Both c and cxx compiler support -rpath directly
hardcode_libdir_flag_spec='-rpath $libdir'
fi
archive_cmds_need_lc='no'
hardcode_libdir_separator=:
;;
solaris*)
no_undefined_flag=' -z defs'
if test yes = "$GCC"; then
wlarc='$wl'
archive_cmds='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
$CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
else
case `$CC -V 2>&1` in
*"Compilers 5.0"*)
wlarc=''
archive_cmds='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags'
archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
$LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp'
;;
*)
wlarc='$wl'
archive_cmds='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags'
archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
$CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
;;
esac
fi
hardcode_libdir_flag_spec='-R$libdir'
hardcode_shlibpath_var=no
case $host_os in
solaris2.[0-5] | solaris2.[0-5].*) ;;
*)
# The compiler driver will combine and reorder linker options,
# but understands '-z linker_flag'. GCC discards it without '$wl',
# but is careful enough not to reorder.
# Supported since Solaris 2.6 (maybe 2.5.1?)
if test yes = "$GCC"; then
whole_archive_flag_spec='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract'
else
whole_archive_flag_spec='-z allextract$convenience -z defaultextract'
fi
;;
esac
link_all_deplibs=yes
;;
sunos4*)
if test sequent = "$host_vendor"; then
# Use $CC to link under sequent, because it throws in some extra .o
# files that make .init and .fini sections work.
archive_cmds='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags'
else
archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
fi
hardcode_libdir_flag_spec='-L$libdir'
hardcode_direct=yes
hardcode_minus_L=yes
hardcode_shlibpath_var=no
;;
sysv4)
case $host_vendor in
sni)
archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
hardcode_direct=yes # is this really true???
;;
siemens)
## LD is ld it makes a PLAMLIB
## CC just makes a GrossModule.
archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags'
reload_cmds='$CC -r -o $output$reload_objs'
hardcode_direct=no
;;
motorola)
archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
hardcode_direct=no #Motorola manual says yes, but my tests say they lie
;;
esac
runpath_var='LD_RUN_PATH'
hardcode_shlibpath_var=no
;;
sysv4.3*)
archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
hardcode_shlibpath_var=no
export_dynamic_flag_spec='-Bexport'
;;
sysv4*MP*)
if test -d /usr/nec; then
archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
hardcode_shlibpath_var=no
runpath_var=LD_RUN_PATH
hardcode_runpath_var=yes
ld_shlibs=yes
fi
;;
sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*)
no_undefined_flag='$wl-z,text'
archive_cmds_need_lc=no
hardcode_shlibpath_var=no
runpath_var='LD_RUN_PATH'
if test yes = "$GCC"; then
archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
else
archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
fi
;;
sysv5* | sco3.2v5* | sco5v6*)
# Note: We CANNOT use -z defs as we might desire, because we do not
# link with -lc, and that would cause any symbols used from libc to
# always be unresolved, which means just about no library would
# ever link correctly. If we're not using GNU ld we use -z text
# though, which does catch some bad symbols but isn't as heavy-handed
# as -z defs.
no_undefined_flag='$wl-z,text'
allow_undefined_flag='$wl-z,nodefs'
archive_cmds_need_lc=no
hardcode_shlibpath_var=no
hardcode_libdir_flag_spec='$wl-R,$libdir'
hardcode_libdir_separator=':'
link_all_deplibs=yes
export_dynamic_flag_spec='$wl-Bexport'
runpath_var='LD_RUN_PATH'
if test yes = "$GCC"; then
archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
else
archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
fi
;;
uts4*)
archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
hardcode_libdir_flag_spec='-L$libdir'
hardcode_shlibpath_var=no
;;
*)
ld_shlibs=no
;;
esac
if test sni = "$host_vendor"; then
case $host in
sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
export_dynamic_flag_spec='$wl-Blargedynsym'
;;
esac
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5
$as_echo "$ld_shlibs" >&6; }
test no = "$ld_shlibs" && can_build_shared=no
with_gnu_ld=$with_gnu_ld
#
# Do we need to explicitly link libc?
#
case "x$archive_cmds_need_lc" in
x|xyes)
# Assume -lc should be added
archive_cmds_need_lc=yes
if test yes,yes = "$GCC,$enable_shared"; then
case $archive_cmds in
*'~'*)
# FIXME: we may have to deal with multi-command sequences.
;;
'$CC '*)
# Test whether the compiler implicitly links with -lc since on some
# systems, -lgcc has to come before -lc. If gcc already passes -lc
# to ld, don't add -lc before -lgcc.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5
$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; }
if ${lt_cv_archive_cmds_need_lc+:} false; then :
$as_echo_n "(cached) " >&6
else
$RM conftest*
echo "$lt_simple_compile_test_code" > conftest.$ac_ext
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
(eval $ac_compile) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; } 2>conftest.err; then
soname=conftest
lib=conftest
libobjs=conftest.$ac_objext
deplibs=
wl=$lt_prog_compiler_wl
pic_flag=$lt_prog_compiler_pic
compiler_flags=-v
linker_flags=-v
verstring=
output_objdir=.
libname=conftest
lt_save_allow_undefined_flag=$allow_undefined_flag
allow_undefined_flag=
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5
(eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }
then
lt_cv_archive_cmds_need_lc=no
else
lt_cv_archive_cmds_need_lc=yes
fi
allow_undefined_flag=$lt_save_allow_undefined_flag
else
cat conftest.err 1>&5
fi
$RM conftest*
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc" >&5
$as_echo "$lt_cv_archive_cmds_need_lc" >&6; }
archive_cmds_need_lc=$lt_cv_archive_cmds_need_lc
;;
esac
fi
;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5
$as_echo_n "checking dynamic linker characteristics... " >&6; }
if test yes = "$GCC"; then
case $host_os in
darwin*) lt_awk_arg='/^libraries:/,/LR/' ;;
*) lt_awk_arg='/^libraries:/' ;;
esac
case $host_os in
mingw* | cegcc*) lt_sed_strip_eq='s|=\([A-Za-z]:\)|\1|g' ;;
*) lt_sed_strip_eq='s|=/|/|g' ;;
esac
lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq`
case $lt_search_path_spec in
*\;*)
# if the path contains ";" then we assume it to be the separator
# otherwise default to the standard path separator (i.e. ":") - it is
# assumed that no part of a normal pathname contains ";" but that should
# okay in the real world where ";" in dirpaths is itself problematic.
lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'`
;;
*)
lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"`
;;
esac
# Ok, now we have the path, separated by spaces, we can step through it
# and add multilib dir if necessary...
lt_tmp_lt_search_path_spec=
lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`
# ...but if some path component already ends with the multilib dir we assume
# that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer).
case "$lt_multi_os_dir; $lt_search_path_spec " in
"/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*)
lt_multi_os_dir=
;;
esac
for lt_sys_path in $lt_search_path_spec; do
if test -d "$lt_sys_path$lt_multi_os_dir"; then
lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir"
elif test -n "$lt_multi_os_dir"; then
test -d "$lt_sys_path" && \
lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path"
fi
done
lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk '
BEGIN {RS = " "; FS = "/|\n";} {
lt_foo = "";
lt_count = 0;
for (lt_i = NF; lt_i > 0; lt_i--) {
if ($lt_i != "" && $lt_i != ".") {
if ($lt_i == "..") {
lt_count++;
} else {
if (lt_count == 0) {
lt_foo = "/" $lt_i lt_foo;
} else {
lt_count--;
}
}
}
}
if (lt_foo != "") { lt_freq[lt_foo]++; }
if (lt_freq[lt_foo] == 1) { print lt_foo; }
}'`
# AWK program above erroneously prepends '/' to C:/dos/paths
# for these hosts.
case $host_os in
mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\
$SED 's|/\([A-Za-z]:\)|\1|g'` ;;
esac
sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP`
else
sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
fi
library_names_spec=
libname_spec='lib$name'
soname_spec=
shrext_cmds=.so
postinstall_cmds=
postuninstall_cmds=
finish_cmds=
finish_eval=
shlibpath_var=
shlibpath_overrides_runpath=unknown
version_type=none
dynamic_linker="$host_os ld.so"
sys_lib_dlsearch_path_spec="/lib /usr/lib"
need_lib_prefix=unknown
hardcode_into_libs=no
# when you set need_version to no, make sure it does not cause -set_version
# flags to be left without arguments
need_version=unknown
case $host_os in
aix3*)
version_type=linux # correct to gnu/linux during the next big refactor
library_names_spec='$libname$release$shared_ext$versuffix $libname.a'
shlibpath_var=LIBPATH
# AIX 3 has no versioning support, so we append a major version to the name.
soname_spec='$libname$release$shared_ext$major'
;;
aix[4-9]*)
version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
hardcode_into_libs=yes
if test ia64 = "$host_cpu"; then
# AIX 5 supports IA64
library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext'
shlibpath_var=LD_LIBRARY_PATH
else
# With GCC up to 2.95.x, collect2 would create an import file
# for dependence libraries. The import file would start with
# the line '#! .'. This would cause the generated library to
# depend on '.', always an invalid library. This was fixed in
# development snapshots of GCC prior to 3.0.
case $host_os in
aix4 | aix4.[01] | aix4.[01].*)
if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
echo ' yes '
echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then
:
else
can_build_shared=no
fi
;;
esac
# Using Import Files as archive members, it is possible to support
# filename-based versioning of shared library archives on AIX. While
# this would work for both with and without runtime linking, it will
# prevent static linking of such archives. So we do filename-based
# shared library versioning with .so extension only, which is used
# when both runtime linking and shared linking is enabled.
# Unfortunately, runtime linking may impact performance, so we do
# not want this to be the default eventually. Also, we use the
# versioned .so libs for executables only if there is the -brtl
# linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only.
# To allow for filename-based versioning support, we need to create
# libNAME.so.V as an archive file, containing:
# *) an Import File, referring to the versioned filename of the
# archive as well as the shared archive member, telling the
# bitwidth (32 or 64) of that shared object, and providing the
# list of exported symbols of that shared object, eventually
# decorated with the 'weak' keyword
# *) the shared object with the F_LOADONLY flag set, to really avoid
# it being seen by the linker.
# At run time we better use the real file rather than another symlink,
# but for link time we create the symlink libNAME.so -> libNAME.so.V
case $with_aix_soname,$aix_use_runtimelinking in
# AIX (on Power*) has no versioning support, so currently we cannot hardcode correct
# soname into executable. Probably we can add versioning support to
# collect2, so additional links can be useful in future.
aix,yes) # traditional libtool
dynamic_linker='AIX unversionable lib.so'
# If using run time linking (on AIX 4.2 or later) use lib<name>.so
# instead of lib<name>.a to let people know that these are not
# typical AIX shared libraries.
library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
;;
aix,no) # traditional AIX only
dynamic_linker='AIX lib.a(lib.so.V)'
# We preserve .a as extension for shared libraries through AIX4.2
# and later when we are not doing run time linking.
library_names_spec='$libname$release.a $libname.a'
soname_spec='$libname$release$shared_ext$major'
;;
svr4,*) # full svr4 only
dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o)"
library_names_spec='$libname$release$shared_ext$major $libname$shared_ext'
# We do not specify a path in Import Files, so LIBPATH fires.
shlibpath_overrides_runpath=yes
;;
*,yes) # both, prefer svr4
dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o), lib.a(lib.so.V)"
library_names_spec='$libname$release$shared_ext$major $libname$shared_ext'
# unpreferred sharedlib libNAME.a needs extra handling
postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"'
postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"'
# We do not specify a path in Import Files, so LIBPATH fires.
shlibpath_overrides_runpath=yes
;;
*,no) # both, prefer aix
dynamic_linker="AIX lib.a(lib.so.V), lib.so.V($shared_archive_member_spec.o)"
library_names_spec='$libname$release.a $libname.a'
soname_spec='$libname$release$shared_ext$major'
# unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling
postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)'
postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"'
;;
esac
shlibpath_var=LIBPATH
fi
;;
amigaos*)
case $host_cpu in
powerpc)
# Since July 2007 AmigaOS4 officially supports .so libraries.
# When compiling the executable, add -use-dynld -Lsobjs: to the compileline.
library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
;;
m68k)
library_names_spec='$libname.ixlibrary $libname.a'
# Create ${libname}_ixlibrary.a entries in /sys/libs.
finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
;;
esac
;;
beos*)
library_names_spec='$libname$shared_ext'
dynamic_linker="$host_os ld.so"
shlibpath_var=LIBRARY_PATH
;;
bsdi[45]*)
version_type=linux # correct to gnu/linux during the next big refactor
need_version=no
library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
soname_spec='$libname$release$shared_ext$major'
finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
shlibpath_var=LD_LIBRARY_PATH
sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
# the default ld.so.conf also contains /usr/contrib/lib and
# /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
# libtool to hard-code these into programs
;;
cygwin* | mingw* | pw32* | cegcc*)
version_type=windows
shrext_cmds=.dll
need_version=no
need_lib_prefix=no
case $GCC,$cc_basename in
yes,*)
# gcc
library_names_spec='$libname.dll.a'
# DLL is installed to $(libdir)/../bin by postinstall_cmds
postinstall_cmds='base_file=`basename \$file`~
dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~
dldir=$destdir/`dirname \$dlpath`~
test -d \$dldir || mkdir -p \$dldir~
$install_prog $dir/$dlname \$dldir/$dlname~
chmod a+x \$dldir/$dlname~
if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
fi'
postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
dlpath=$dir/\$dldll~
$RM \$dlpath'
shlibpath_overrides_runpath=yes
case $host_os in
cygwin*)
# Cygwin DLLs use 'cyg' prefix rather than 'lib'
soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"
;;
mingw* | cegcc*)
# MinGW DLLs use traditional 'lib' prefix
soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
;;
pw32*)
# pw32 DLLs use 'pw' prefix rather than 'lib'
library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
;;
esac
dynamic_linker='Win32 ld.exe'
;;
*,cl*)
# Native MSVC
libname_spec='$name'
soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
library_names_spec='$libname.dll.lib'
case $build_os in
mingw*)
sys_lib_search_path_spec=
lt_save_ifs=$IFS
IFS=';'
for lt_path in $LIB
do
IFS=$lt_save_ifs
# Let DOS variable expansion print the short 8.3 style file name.
lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"`
sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path"
done
IFS=$lt_save_ifs
# Convert to MSYS style.
sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'`
;;
cygwin*)
# Convert to unix form, then to dos form, then back to unix form
# but this time dos style (no spaces!) so that the unix form looks
# like /cygdrive/c/PROGRA~1:/cygdr...
sys_lib_search_path_spec=`cygpath --path --unix "$LIB"`
sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null`
sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
;;
*)
sys_lib_search_path_spec=$LIB
if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then
# It is most probably a Windows format PATH.
sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
else
sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
fi
# FIXME: find the short name or the path components, as spaces are
# common. (e.g. "Program Files" -> "PROGRA~1")
;;
esac
# DLL is installed to $(libdir)/../bin by postinstall_cmds
postinstall_cmds='base_file=`basename \$file`~
dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~
dldir=$destdir/`dirname \$dlpath`~
test -d \$dldir || mkdir -p \$dldir~
$install_prog $dir/$dlname \$dldir/$dlname'
postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
dlpath=$dir/\$dldll~
$RM \$dlpath'
shlibpath_overrides_runpath=yes
dynamic_linker='Win32 link.exe'
;;
*)
# Assume MSVC wrapper
library_names_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext $libname.lib'
dynamic_linker='Win32 ld.exe'
;;
esac
# FIXME: first we should search . and the directory the executable is in
shlibpath_var=PATH
;;
darwin* | rhapsody*)
dynamic_linker="$host_os dyld"
version_type=darwin
need_lib_prefix=no
need_version=no
library_names_spec='$libname$release$major$shared_ext $libname$shared_ext'
soname_spec='$libname$release$major$shared_ext'
shlibpath_overrides_runpath=yes
shlibpath_var=DYLD_LIBRARY_PATH
shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'
sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"
sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
;;
dgux*)
version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LD_LIBRARY_PATH
;;
freebsd* | dragonfly*)
# DragonFly does not have aout. When/if they implement a new
# versioning mechanism, adjust this.
if test -x /usr/bin/objformat; then
objformat=`/usr/bin/objformat`
else
case $host_os in
freebsd[23].*) objformat=aout ;;
*) objformat=elf ;;
esac
fi
version_type=freebsd-$objformat
case $version_type in
freebsd-elf*)
library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
soname_spec='$libname$release$shared_ext$major'
need_version=no
need_lib_prefix=no
;;
freebsd-*)
library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
need_version=yes
;;
esac
shlibpath_var=LD_LIBRARY_PATH
case $host_os in
freebsd2.*)
shlibpath_overrides_runpath=yes
;;
freebsd3.[01]* | freebsdelf3.[01]*)
shlibpath_overrides_runpath=yes
hardcode_into_libs=yes
;;
freebsd3.[2-9]* | freebsdelf3.[2-9]* | \
freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1)
shlibpath_overrides_runpath=no
hardcode_into_libs=yes
;;
*) # from 4.6 on, and DragonFly
shlibpath_overrides_runpath=yes
hardcode_into_libs=yes
;;
esac
;;
haiku*)
version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
dynamic_linker="$host_os runtime_loader"
library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LIBRARY_PATH
shlibpath_overrides_runpath=no
sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib'
hardcode_into_libs=yes
;;
hpux9* | hpux10* | hpux11*)
# Give a soname corresponding to the major version so that dld.sl refuses to
# link against other versions.
version_type=sunos
need_lib_prefix=no
need_version=no
case $host_cpu in
ia64*)
shrext_cmds='.so'
hardcode_into_libs=yes
dynamic_linker="$host_os dld.so"
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
soname_spec='$libname$release$shared_ext$major'
if test 32 = "$HPUX_IA64_MODE"; then
sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
sys_lib_dlsearch_path_spec=/usr/lib/hpux32
else
sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
sys_lib_dlsearch_path_spec=/usr/lib/hpux64
fi
;;
hppa*64*)
shrext_cmds='.sl'
hardcode_into_libs=yes
dynamic_linker="$host_os dld.sl"
shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
soname_spec='$libname$release$shared_ext$major'
sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
;;
*)
shrext_cmds='.sl'
dynamic_linker="$host_os dld.sl"
shlibpath_var=SHLIB_PATH
shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
soname_spec='$libname$release$shared_ext$major'
;;
esac
# HP-UX runs *really* slowly unless shared libraries are mode 555, ...
postinstall_cmds='chmod 555 $lib'
# or fails outright, so override atomically:
install_override_mode=555
;;
interix[3-9]*)
version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
soname_spec='$libname$release$shared_ext$major'
dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=no
hardcode_into_libs=yes
;;
irix5* | irix6* | nonstopux*)
case $host_os in
nonstopux*) version_type=nonstopux ;;
*)
if test yes = "$lt_cv_prog_gnu_ld"; then
version_type=linux # correct to gnu/linux during the next big refactor
else
version_type=irix
fi ;;
esac
need_lib_prefix=no
need_version=no
soname_spec='$libname$release$shared_ext$major'
library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext'
case $host_os in
irix5* | nonstopux*)
libsuff= shlibsuff=
;;
*)
case $LD in # libtool.m4 will add one of these switches to LD
*-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
libsuff= shlibsuff= libmagic=32-bit;;
*-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
libsuff=32 shlibsuff=N32 libmagic=N32;;
*-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
libsuff=64 shlibsuff=64 libmagic=64-bit;;
*) libsuff= shlibsuff= libmagic=never-match;;
esac
;;
esac
shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
shlibpath_overrides_runpath=no
sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff"
sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff"
hardcode_into_libs=yes
;;
# No shared lib support for Linux oldld, aout, or coff.
linux*oldld* | linux*aout* | linux*coff*)
dynamic_linker=no
;;
linux*android*)
version_type=none # Android doesn't support versioned libraries.
need_lib_prefix=no
need_version=no
library_names_spec='$libname$release$shared_ext'
soname_spec='$libname$release$shared_ext'
finish_cmds=
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=yes
# This implies no fast_install, which is unacceptable.
# Some rework will be needed to allow for fast_install
# before this can be enabled.
hardcode_into_libs=yes
dynamic_linker='Android linker'
# Don't embed -rpath directories since the linker doesn't support them.
hardcode_libdir_flag_spec='-L$libdir'
;;
# This must be glibc/ELF.
linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
soname_spec='$libname$release$shared_ext$major'
finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=no
# Some binutils ld are patched to set DT_RUNPATH
if ${lt_cv_shlibpath_overrides_runpath+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_shlibpath_overrides_runpath=no
save_LDFLAGS=$LDFLAGS
save_libdir=$libdir
eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \
LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\""
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then :
lt_cv_shlibpath_overrides_runpath=yes
fi
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LDFLAGS=$save_LDFLAGS
libdir=$save_libdir
fi
shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath
# This implies no fast_install, which is unacceptable.
# Some rework will be needed to allow for fast_install
# before this can be enabled.
hardcode_into_libs=yes
# Add ABI-specific directories to the system library path.
sys_lib_dlsearch_path_spec="/lib64 /usr/lib64 /lib /usr/lib"
# Ideally, we could use ldconfig to report *all* directores which are
# searched for libraries, however this is still not possible. Aside from not
# being certain /sbin/ldconfig is available, command
# 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64,
# even though it is searched at run-time. Try to do the best guess by
# appending ld.so.conf contents (and includes) to the search path.
if test -f /etc/ld.so.conf; then
lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '`
sys_lib_dlsearch_path_spec="$sys_lib_dlsearch_path_spec $lt_ld_extra"
fi
# We used to test for /lib/ld.so.1 and disable shared libraries on
# powerpc, because MkLinux only supported shared libraries with the
# GNU dynamic linker. Since this was broken with cross compilers,
# most powerpc-linux boxes support dynamic linking these days and
# people can always --disable-shared, the test was removed, and we
# assume the GNU/Linux dynamic linker is in use.
dynamic_linker='GNU/Linux ld.so'
;;
netbsd*)
version_type=sunos
need_lib_prefix=no
need_version=no
if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
dynamic_linker='NetBSD (a.out) ld.so'
else
library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
soname_spec='$libname$release$shared_ext$major'
dynamic_linker='NetBSD ld.elf_so'
fi
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=yes
hardcode_into_libs=yes
;;
newsos6)
version_type=linux # correct to gnu/linux during the next big refactor
library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=yes
;;
*nto* | *qnx*)
version_type=qnx
need_lib_prefix=no
need_version=no
library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=no
hardcode_into_libs=yes
dynamic_linker='ldqnx.so'
;;
openbsd* | bitrig*)
version_type=sunos
sys_lib_dlsearch_path_spec=/usr/lib
need_lib_prefix=no
if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
need_version=no
else
need_version=yes
fi
library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=yes
;;
os2*)
libname_spec='$name'
version_type=windows
shrext_cmds=.dll
need_version=no
need_lib_prefix=no
# OS/2 can only load a DLL with a base name of 8 characters or less.
soname_spec='`test -n "$os2dllname" && libname="$os2dllname";
v=$($ECHO $release$versuffix | tr -d .-);
n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _);
$ECHO $n$v`$shared_ext'
library_names_spec='${libname}_dll.$libext'
dynamic_linker='OS/2 ld.exe'
shlibpath_var=BEGINLIBPATH
sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
postinstall_cmds='base_file=`basename \$file`~
dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~
dldir=$destdir/`dirname \$dlpath`~
test -d \$dldir || mkdir -p \$dldir~
$install_prog $dir/$dlname \$dldir/$dlname~
chmod a+x \$dldir/$dlname~
if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
fi'
postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~
dlpath=$dir/\$dldll~
$RM \$dlpath'
;;
osf3* | osf4* | osf5*)
version_type=osf
need_lib_prefix=no
need_version=no
soname_spec='$libname$release$shared_ext$major'
library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
shlibpath_var=LD_LIBRARY_PATH
sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
;;
rdos*)
dynamic_linker=no
;;
solaris*)
version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=yes
hardcode_into_libs=yes
# ldd complains unless libraries are executable
postinstall_cmds='chmod +x $lib'
;;
sunos4*)
version_type=sunos
library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=yes
if test yes = "$with_gnu_ld"; then
need_lib_prefix=no
fi
need_version=yes
;;
sysv4 | sysv4.3*)
version_type=linux # correct to gnu/linux during the next big refactor
library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LD_LIBRARY_PATH
case $host_vendor in
sni)
shlibpath_overrides_runpath=no
need_lib_prefix=no
runpath_var=LD_RUN_PATH
;;
siemens)
need_lib_prefix=no
;;
motorola)
need_lib_prefix=no
need_version=no
shlibpath_overrides_runpath=no
sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
;;
esac
;;
sysv4*MP*)
if test -d /usr/nec; then
version_type=linux # correct to gnu/linux during the next big refactor
library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext'
soname_spec='$libname$shared_ext.$major'
shlibpath_var=LD_LIBRARY_PATH
fi
;;
sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
version_type=sco
need_lib_prefix=no
need_version=no
library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext'
soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=yes
hardcode_into_libs=yes
if test yes = "$with_gnu_ld"; then
sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'
else
sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'
case $host_os in
sco3.2v5*)
sys_lib_search_path_spec="$sys_lib_search_path_spec /lib"
;;
esac
fi
sys_lib_dlsearch_path_spec='/usr/lib'
;;
tpf*)
# TPF is a cross-target only. Preferred cross-host = GNU/Linux.
version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=no
hardcode_into_libs=yes
;;
uts4*)
version_type=linux # correct to gnu/linux during the next big refactor
library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LD_LIBRARY_PATH
;;
*)
dynamic_linker=no
;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5
$as_echo "$dynamic_linker" >&6; }
test no = "$dynamic_linker" && can_build_shared=no
variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
if test yes = "$GCC"; then
variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
fi
if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then
sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec
fi
if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then
sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec
fi
# remember unaugmented sys_lib_dlsearch_path content for libtool script decls...
configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec
# ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code
func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH"
# to be used as default LT_SYS_LIBRARY_PATH value in generated libtool
configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5
$as_echo_n "checking how to hardcode library paths into programs... " >&6; }
hardcode_action=
if test -n "$hardcode_libdir_flag_spec" ||
test -n "$runpath_var" ||
test yes = "$hardcode_automatic"; then
# We can hardcode non-existent directories.
if test no != "$hardcode_direct" &&
# If the only mechanism to avoid hardcoding is shlibpath_var, we
# have to relink, otherwise we might link with an installed library
# when we should be linking with a yet-to-be-installed one
## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, )" &&
test no != "$hardcode_minus_L"; then
# Linking always hardcodes the temporary library directory.
hardcode_action=relink
else
# We can link without hardcoding, and we can hardcode nonexisting dirs.
hardcode_action=immediate
fi
else
# We cannot hardcode anything, or else we can only hardcode existing
# directories.
hardcode_action=unsupported
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5
$as_echo "$hardcode_action" >&6; }
if test relink = "$hardcode_action" ||
test yes = "$inherit_rpath"; then
# Fast installation is not supported
enable_fast_install=no
elif test yes = "$shlibpath_overrides_runpath" ||
test no = "$enable_shared"; then
# Fast installation is not necessary
enable_fast_install=needless
fi
if test yes != "$enable_dlopen"; then
enable_dlopen=unknown
enable_dlopen_self=unknown
enable_dlopen_self_static=unknown
else
lt_cv_dlopen=no
lt_cv_dlopen_libs=
case $host_os in
beos*)
lt_cv_dlopen=load_add_on
lt_cv_dlopen_libs=
lt_cv_dlopen_self=yes
;;
mingw* | pw32* | cegcc*)
lt_cv_dlopen=LoadLibrary
lt_cv_dlopen_libs=
;;
cygwin*)
lt_cv_dlopen=dlopen
lt_cv_dlopen_libs=
;;
darwin*)
# if libdl is installed we need to link against it
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
$as_echo_n "checking for dlopen in -ldl... " >&6; }
if ${ac_cv_lib_dl_dlopen+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-ldl $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char dlopen ();
int
main ()
{
return dlopen ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_dl_dlopen=yes
else
ac_cv_lib_dl_dlopen=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl
else
lt_cv_dlopen=dyld
lt_cv_dlopen_libs=
lt_cv_dlopen_self=yes
fi
;;
tpf*)
# Don't try to run any link tests for TPF. We know it's impossible
# because TPF is a cross-compiler, and we know how we open DSOs.
lt_cv_dlopen=dlopen
lt_cv_dlopen_libs=
lt_cv_dlopen_self=no
;;
*)
ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load"
if test "x$ac_cv_func_shl_load" = xyes; then :
lt_cv_dlopen=shl_load
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5
$as_echo_n "checking for shl_load in -ldld... " >&6; }
if ${ac_cv_lib_dld_shl_load+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-ldld $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char shl_load ();
int
main ()
{
return shl_load ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_dld_shl_load=yes
else
ac_cv_lib_dld_shl_load=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5
$as_echo "$ac_cv_lib_dld_shl_load" >&6; }
if test "x$ac_cv_lib_dld_shl_load" = xyes; then :
lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld
else
ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen"
if test "x$ac_cv_func_dlopen" = xyes; then :
lt_cv_dlopen=dlopen
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
$as_echo_n "checking for dlopen in -ldl... " >&6; }
if ${ac_cv_lib_dl_dlopen+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-ldl $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char dlopen ();
int
main ()
{
return dlopen ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_dl_dlopen=yes
else
ac_cv_lib_dl_dlopen=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5
$as_echo_n "checking for dlopen in -lsvld... " >&6; }
if ${ac_cv_lib_svld_dlopen+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lsvld $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char dlopen ();
int
main ()
{
return dlopen ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_svld_dlopen=yes
else
ac_cv_lib_svld_dlopen=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5
$as_echo "$ac_cv_lib_svld_dlopen" >&6; }
if test "x$ac_cv_lib_svld_dlopen" = xyes; then :
lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5
$as_echo_n "checking for dld_link in -ldld... " >&6; }
if ${ac_cv_lib_dld_dld_link+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-ldld $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char dld_link ();
int
main ()
{
return dld_link ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_dld_dld_link=yes
else
ac_cv_lib_dld_dld_link=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5
$as_echo "$ac_cv_lib_dld_dld_link" >&6; }
if test "x$ac_cv_lib_dld_dld_link" = xyes; then :
lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld
fi
fi
fi
fi
fi
fi
;;
esac
if test no = "$lt_cv_dlopen"; then
enable_dlopen=no
else
enable_dlopen=yes
fi
case $lt_cv_dlopen in
dlopen)
save_CPPFLAGS=$CPPFLAGS
test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
save_LDFLAGS=$LDFLAGS
wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
save_LIBS=$LIBS
LIBS="$lt_cv_dlopen_libs $LIBS"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5
$as_echo_n "checking whether a program can dlopen itself... " >&6; }
if ${lt_cv_dlopen_self+:} false; then :
$as_echo_n "(cached) " >&6
else
if test yes = "$cross_compiling"; then :
lt_cv_dlopen_self=cross
else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
#line $LINENO "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
#include <dlfcn.h>
#endif
#include <stdio.h>
#ifdef RTLD_GLOBAL
# define LT_DLGLOBAL RTLD_GLOBAL
#else
# ifdef DL_GLOBAL
# define LT_DLGLOBAL DL_GLOBAL
# else
# define LT_DLGLOBAL 0
# endif
#endif
/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
find out it does not work in some platform. */
#ifndef LT_DLLAZY_OR_NOW
# ifdef RTLD_LAZY
# define LT_DLLAZY_OR_NOW RTLD_LAZY
# else
# ifdef DL_LAZY
# define LT_DLLAZY_OR_NOW DL_LAZY
# else
# ifdef RTLD_NOW
# define LT_DLLAZY_OR_NOW RTLD_NOW
# else
# ifdef DL_NOW
# define LT_DLLAZY_OR_NOW DL_NOW
# else
# define LT_DLLAZY_OR_NOW 0
# endif
# endif
# endif
# endif
#endif
/* When -fvisibility=hidden is used, assume the code has been annotated
correspondingly for the symbols needed. */
#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))
int fnord () __attribute__((visibility("default")));
#endif
int fnord () { return 42; }
int main ()
{
void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
int status = $lt_dlunknown;
if (self)
{
if (dlsym (self,"fnord")) status = $lt_dlno_uscore;
else
{
if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
else puts (dlerror ());
}
/* dlclose (self); */
}
else
puts (dlerror ());
return status;
}
_LT_EOF
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
(eval $ac_link) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then
(./conftest; exit; ) >&5 2>/dev/null
lt_status=$?
case x$lt_status in
x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;;
x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;;
x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;;
esac
else :
# compilation failed
lt_cv_dlopen_self=no
fi
fi
rm -fr conftest*
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5
$as_echo "$lt_cv_dlopen_self" >&6; }
if test yes = "$lt_cv_dlopen_self"; then
wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5
$as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; }
if ${lt_cv_dlopen_self_static+:} false; then :
$as_echo_n "(cached) " >&6
else
if test yes = "$cross_compiling"; then :
lt_cv_dlopen_self_static=cross
else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
#line $LINENO "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
#include <dlfcn.h>
#endif
#include <stdio.h>
#ifdef RTLD_GLOBAL
# define LT_DLGLOBAL RTLD_GLOBAL
#else
# ifdef DL_GLOBAL
# define LT_DLGLOBAL DL_GLOBAL
# else
# define LT_DLGLOBAL 0
# endif
#endif
/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
find out it does not work in some platform. */
#ifndef LT_DLLAZY_OR_NOW
# ifdef RTLD_LAZY
# define LT_DLLAZY_OR_NOW RTLD_LAZY
# else
# ifdef DL_LAZY
# define LT_DLLAZY_OR_NOW DL_LAZY
# else
# ifdef RTLD_NOW
# define LT_DLLAZY_OR_NOW RTLD_NOW
# else
# ifdef DL_NOW
# define LT_DLLAZY_OR_NOW DL_NOW
# else
# define LT_DLLAZY_OR_NOW 0
# endif
# endif
# endif
# endif
#endif
/* When -fvisibility=hidden is used, assume the code has been annotated
correspondingly for the symbols needed. */
#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))
int fnord () __attribute__((visibility("default")));
#endif
int fnord () { return 42; }
int main ()
{
void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
int status = $lt_dlunknown;
if (self)
{
if (dlsym (self,"fnord")) status = $lt_dlno_uscore;
else
{
if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
else puts (dlerror ());
}
/* dlclose (self); */
}
else
puts (dlerror ());
return status;
}
_LT_EOF
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
(eval $ac_link) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then
(./conftest; exit; ) >&5 2>/dev/null
lt_status=$?
case x$lt_status in
x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;;
x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;;
x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;;
esac
else :
# compilation failed
lt_cv_dlopen_self_static=no
fi
fi
rm -fr conftest*
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5
$as_echo "$lt_cv_dlopen_self_static" >&6; }
fi
CPPFLAGS=$save_CPPFLAGS
LDFLAGS=$save_LDFLAGS
LIBS=$save_LIBS
;;
esac
case $lt_cv_dlopen_self in
yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
*) enable_dlopen_self=unknown ;;
esac
case $lt_cv_dlopen_self_static in
yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
*) enable_dlopen_self_static=unknown ;;
esac
fi
striplib=
old_striplib=
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5
$as_echo_n "checking whether stripping libraries is possible... " >&6; }
if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then
test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
test -z "$striplib" && striplib="$STRIP --strip-unneeded"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
# FIXME - insert some real tests, host_os isn't really good enough
case $host_os in
darwin*)
if test -n "$STRIP"; then
striplib="$STRIP -x"
old_striplib="$STRIP -S"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
;;
*)
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
;;
esac
fi
# Report what library types will actually be built
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5
$as_echo_n "checking if libtool supports shared libraries... " >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5
$as_echo "$can_build_shared" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5
$as_echo_n "checking whether to build shared libraries... " >&6; }
test no = "$can_build_shared" && enable_shared=no
# On AIX, shared libraries and static libraries use the same namespace, and
# are all built from PIC.
case $host_os in
aix3*)
test yes = "$enable_shared" && enable_static=no
if test -n "$RANLIB"; then
archive_cmds="$archive_cmds~\$RANLIB \$lib"
postinstall_cmds='$RANLIB $lib'
fi
;;
aix[4-9]*)
if test ia64 != "$host_cpu"; then
case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in
yes,aix,yes) ;; # shared object as lib.so file only
yes,svr4,*) ;; # shared object as lib.so archive member only
yes,*) enable_static=no ;; # shared object in lib.a archive as well
esac
fi
;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5
$as_echo "$enable_shared" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5
$as_echo_n "checking whether to build static libraries... " >&6; }
# Make sure either enable_shared or enable_static is yes.
test yes = "$enable_shared" || enable_static=yes
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5
$as_echo "$enable_static" >&6; }
fi
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
CC=$lt_save_CC
ac_config_commands="$ac_config_commands libtool"
# Only expand once:
# pkg-config is only needed for these options, do not require it otherwise
if test "$enable_systemd" = "yes" -o "$with_pyunbound" = "yes" -o "$with_pythonmod" = "yes"; then
if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args.
set dummy ${ac_tool_prefix}pkg-config; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_path_PKG_CONFIG+:} false; then :
$as_echo_n "(cached) " >&6
else
case $PKG_CONFIG in
[\\/]* | ?:[\\/]*)
ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
;;
esac
fi
PKG_CONFIG=$ac_cv_path_PKG_CONFIG
if test -n "$PKG_CONFIG"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5
$as_echo "$PKG_CONFIG" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
if test -z "$ac_cv_path_PKG_CONFIG"; then
ac_pt_PKG_CONFIG=$PKG_CONFIG
# Extract the first word of "pkg-config", so it can be a program name with args.
set dummy pkg-config; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then :
$as_echo_n "(cached) " >&6
else
case $ac_pt_PKG_CONFIG in
[\\/]* | ?:[\\/]*)
ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
;;
esac
fi
ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG
if test -n "$ac_pt_PKG_CONFIG"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5
$as_echo "$ac_pt_PKG_CONFIG" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test "x$ac_pt_PKG_CONFIG" = x; then
PKG_CONFIG=""
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
PKG_CONFIG=$ac_pt_PKG_CONFIG
fi
else
PKG_CONFIG="$ac_cv_path_PKG_CONFIG"
fi
fi
if test -n "$PKG_CONFIG"; then
_pkg_min_version=0.9.0
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5
$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; }
if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
PKG_CONFIG=""
fi
fi
fi
# Checks for header files.
for ac_header in stdarg.h stdbool.h netinet/in.h netinet/tcp.h sys/param.h sys/select.h sys/socket.h sys/un.h sys/uio.h sys/resource.h arpa/inet.h syslog.h netdb.h sys/wait.h pwd.h glob.h grp.h login_cap.h winsock2.h ws2tcpip.h endian.h sys/endian.h libkern/OSByteOrder.h sys/ipc.h sys/shm.h ifaddrs.h poll.h
do :
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
"
if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
cat >>confdefs.h <<_ACEOF
#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
_ACEOF
fi
done
# net/if.h portability for Darwin see:
# https://www.gnu.org/software/autoconf/manual/autoconf-2.69/html_node/Header-Portability.html
for ac_header in net/if.h
do :
ac_fn_c_check_header_compile "$LINENO" "net/if.h" "ac_cv_header_net_if_h" "
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
"
if test "x$ac_cv_header_net_if_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_NET_IF_H 1
_ACEOF
fi
done
# Check for Apple header. This uncovers TARGET_OS_IPHONE, TARGET_OS_TV or TARGET_OS_WATCH
for ac_header in TargetConditionals.h
do :
ac_fn_c_check_header_compile "$LINENO" "TargetConditionals.h" "ac_cv_header_TargetConditionals_h" "$ac_includes_default
"
if test "x$ac_cv_header_TargetConditionals_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_TARGETCONDITIONALS_H 1
_ACEOF
fi
done
for ac_header in netioapi.h
do :
ac_fn_c_check_header_compile "$LINENO" "netioapi.h" "ac_cv_header_netioapi_h" "$ac_includes_default
#if HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_SYS_UIO_H
#include <sys/uio.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_WINSOCK2_H
#include <winsock2.h>
#endif
#ifdef HAVE_WS2TCPIP_H
#include <ws2tcpip.h>
#endif
"
if test "x$ac_cv_header_netioapi_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_NETIOAPI_H 1
_ACEOF
fi
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"
if test "x$ac_cv_type_int8_t" = xyes; then :
else
cat >>confdefs.h <<_ACEOF
#define int8_t signed char
_ACEOF
fi
ac_fn_c_check_type "$LINENO" "int16_t" "ac_cv_type_int16_t" "$ac_includes_default"
if test "x$ac_cv_type_int16_t" = xyes; then :
else
cat >>confdefs.h <<_ACEOF
#define int16_t short
_ACEOF
fi
ac_fn_c_check_type "$LINENO" "int32_t" "ac_cv_type_int32_t" "$ac_includes_default"
if test "x$ac_cv_type_int32_t" = xyes; then :
else
cat >>confdefs.h <<_ACEOF
#define int32_t int
_ACEOF
fi
ac_fn_c_check_type "$LINENO" "int64_t" "ac_cv_type_int64_t" "$ac_includes_default"
if test "x$ac_cv_type_int64_t" = xyes; then :
else
cat >>confdefs.h <<_ACEOF
#define int64_t long long
_ACEOF
fi
ac_fn_c_check_type "$LINENO" "uint8_t" "ac_cv_type_uint8_t" "$ac_includes_default"
if test "x$ac_cv_type_uint8_t" = xyes; then :
else
cat >>confdefs.h <<_ACEOF
#define uint8_t unsigned char
_ACEOF
fi
ac_fn_c_check_type "$LINENO" "uint16_t" "ac_cv_type_uint16_t" "$ac_includes_default"
if test "x$ac_cv_type_uint16_t" = xyes; then :
else
cat >>confdefs.h <<_ACEOF
#define uint16_t unsigned short
_ACEOF
fi
ac_fn_c_check_type "$LINENO" "uint32_t" "ac_cv_type_uint32_t" "$ac_includes_default"
if test "x$ac_cv_type_uint32_t" = xyes; then :
else
cat >>confdefs.h <<_ACEOF
#define uint32_t unsigned int
_ACEOF
fi
ac_fn_c_check_type "$LINENO" "uint64_t" "ac_cv_type_uint64_t" "$ac_includes_default"
if test "x$ac_cv_type_uint64_t" = xyes; then :
else
cat >>confdefs.h <<_ACEOF
#define uint64_t unsigned long long
_ACEOF
fi
ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default"
if test "x$ac_cv_type_size_t" = xyes; then :
else
cat >>confdefs.h <<_ACEOF
#define size_t unsigned int
_ACEOF
fi
ac_fn_c_check_type "$LINENO" "ssize_t" "ac_cv_type_ssize_t" "$ac_includes_default"
if test "x$ac_cv_type_ssize_t" = xyes; then :
else
cat >>confdefs.h <<_ACEOF
#define ssize_t int
_ACEOF
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for uid_t in sys/types.h" >&5
$as_echo_n "checking for uid_t in sys/types.h... " >&6; }
if ${ac_cv_type_uid_t+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <sys/types.h>
_ACEOF
if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
$EGREP "uid_t" >/dev/null 2>&1; then :
ac_cv_type_uid_t=yes
else
ac_cv_type_uid_t=no
fi
rm -f conftest*
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uid_t" >&5
$as_echo "$ac_cv_type_uid_t" >&6; }
if test $ac_cv_type_uid_t = no; then
$as_echo "#define uid_t int" >>confdefs.h
$as_echo "#define gid_t int" >>confdefs.h
fi
ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default"
if test "x$ac_cv_type_pid_t" = xyes; then :
else
cat >>confdefs.h <<_ACEOF
#define pid_t int
_ACEOF
fi
ac_fn_c_check_type "$LINENO" "off_t" "ac_cv_type_off_t" "$ac_includes_default"
if test "x$ac_cv_type_off_t" = xyes; then :
else
cat >>confdefs.h <<_ACEOF
#define off_t long int
_ACEOF
fi
ac_fn_c_check_type "$LINENO" "u_char" "ac_cv_type_u_char" "
$ac_includes_default
#ifdef HAVE_WINSOCK2_H
# include <winsock2.h>
#endif
"
if test "x$ac_cv_type_u_char" = xyes; then :
else
$as_echo "#define u_char unsigned char" >>confdefs.h
fi
ac_fn_c_check_type "$LINENO" "rlim_t" "ac_cv_type_rlim_t" "
$ac_includes_default
#ifdef HAVE_SYS_RESOURCE_H
# include <sys/resource.h>
#endif
"
if test "x$ac_cv_type_rlim_t" = xyes; then :
else
$as_echo "#define rlim_t unsigned long" >>confdefs.h
fi
ac_fn_c_check_type "$LINENO" "socklen_t" "ac_cv_type_socklen_t" "
$ac_includes_default
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_WS2TCPIP_H
# include <ws2tcpip.h>
#endif
"
if test "x$ac_cv_type_socklen_t" = xyes; then :
else
$as_echo "#define socklen_t int" >>confdefs.h
fi
ac_fn_c_check_type "$LINENO" "in_addr_t" "ac_cv_type_in_addr_t" "
$ac_includes_default
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
"
if test "x$ac_cv_type_in_addr_t" = xyes; then :
else
$as_echo "#define in_addr_t uint32_t" >>confdefs.h
fi
ac_fn_c_check_type "$LINENO" "in_port_t" "ac_cv_type_in_port_t" "
$ac_includes_default
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
"
if test "x$ac_cv_type_in_port_t" = xyes; then :
else
$as_echo "#define in_port_t uint16_t" >>confdefs.h
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if memcmp compares unsigned" >&5
$as_echo_n "checking if memcmp compares unsigned... " >&6; }
if test "$cross_compiling" = yes; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: cross-compile no" >&5
$as_echo "cross-compile no" >&6; }
$as_echo "#define MEMCMP_IS_BROKEN 1" >>confdefs.h
case " $LIBOBJS " in
*" memcmp.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS memcmp.$ac_objext"
;;
esac
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char a = 255, b = 0;
if(memcmp(&a, &b, 1) < 0)
return 1;
return 0;
}
_ACEOF
if ac_fn_c_try_run "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
$as_echo "#define MEMCMP_IS_BROKEN 1" >>confdefs.h
case " $LIBOBJS " in
*" memcmp.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS memcmp.$ac_objext"
;;
esac
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
# The cast to long int works around a bug in the HP C Compiler
# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
# This bug is HP SR number 8606223364.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of time_t" >&5
$as_echo_n "checking size of time_t... " >&6; }
if ${ac_cv_sizeof_time_t+:} false; then :
$as_echo_n "(cached) " >&6
else
if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (time_t))" "ac_cv_sizeof_time_t" "
$ac_includes_default
#ifdef TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
"; then :
else
if test "$ac_cv_type_time_t" = yes; then
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error 77 "cannot compute sizeof (time_t)
See \`config.log' for more details" "$LINENO" 5; }
else
ac_cv_sizeof_time_t=0
fi
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_time_t" >&5
$as_echo "$ac_cv_sizeof_time_t" >&6; }
cat >>confdefs.h <<_ACEOF
#define SIZEOF_TIME_T $ac_cv_sizeof_time_t
_ACEOF
# The cast to long int works around a bug in the HP C Compiler
# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
# This bug is HP SR number 8606223364.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of size_t" >&5
$as_echo_n "checking size of size_t... " >&6; }
if ${ac_cv_sizeof_size_t+:} false; then :
$as_echo_n "(cached) " >&6
else
if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (size_t))" "ac_cv_sizeof_size_t" "$ac_includes_default"; then :
else
if test "$ac_cv_type_size_t" = yes; then
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error 77 "cannot compute sizeof (size_t)
See \`config.log' for more details" "$LINENO" 5; }
else
ac_cv_sizeof_size_t=0
fi
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_size_t" >&5
$as_echo "$ac_cv_sizeof_size_t" >&6; }
cat >>confdefs.h <<_ACEOF
#define SIZEOF_SIZE_T $ac_cv_sizeof_size_t
_ACEOF
# add option to disable the evil rpath
# Check whether --enable-rpath was given.
if test "${enable_rpath+set}" = set; then :
enableval=$enable_rpath; enable_rpath=$enableval
else
enable_rpath=yes
fi
if test "x$enable_rpath" = xno; then
ac_config_commands="$ac_config_commands disable-rpath"
fi
# check to see if libraries are needed for these functions.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing inet_pton" >&5
$as_echo_n "checking for library containing inet_pton... " >&6; }
if ${ac_cv_search_inet_pton+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char inet_pton ();
int
main ()
{
return inet_pton ();
;
return 0;
}
_ACEOF
for ac_lib in '' nsl; do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_search_inet_pton=$ac_res
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext
if ${ac_cv_search_inet_pton+:} false; then :
break
fi
done
if ${ac_cv_search_inet_pton+:} false; then :
else
ac_cv_search_inet_pton=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_inet_pton" >&5
$as_echo "$ac_cv_search_inet_pton" >&6; }
ac_res=$ac_cv_search_inet_pton
if test "$ac_res" != no; then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing socket" >&5
$as_echo_n "checking for library containing socket... " >&6; }
if ${ac_cv_search_socket+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char socket ();
int
main ()
{
return socket ();
;
return 0;
}
_ACEOF
for ac_lib in '' socket; do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_search_socket=$ac_res
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext
if ${ac_cv_search_socket+:} false; then :
break
fi
done
if ${ac_cv_search_socket+:} false; then :
else
ac_cv_search_socket=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_socket" >&5
$as_echo "$ac_cv_search_socket" >&6; }
ac_res=$ac_cv_search_socket
if test "$ac_res" != no; then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
fi
# check whether strptime also works
# check some functions of the OS before linking libs (while still runnable).
for ac_header in unistd.h
do :
ac_fn_c_check_header_mongrel "$LINENO" "unistd.h" "ac_cv_header_unistd_h" "$ac_includes_default"
if test "x$ac_cv_header_unistd_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_UNISTD_H 1
_ACEOF
fi
done
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working chown" >&5
$as_echo_n "checking for working chown... " >&6; }
if ${ac_cv_func_chown_works+:} false; then :
$as_echo_n "(cached) " >&6
else
if test "$cross_compiling" = yes; then :
ac_cv_func_chown_works=no
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$ac_includes_default
#include <fcntl.h>
int
main ()
{
char *f = "conftest.chown";
struct stat before, after;
if (creat (f, 0600) < 0)
return 1;
if (stat (f, &before) < 0)
return 1;
if (chown (f, (uid_t) -1, (gid_t) -1) == -1)
return 1;
if (stat (f, &after) < 0)
return 1;
return ! (before.st_uid == after.st_uid && before.st_gid == after.st_gid);
;
return 0;
}
_ACEOF
if ac_fn_c_try_run "$LINENO"; then :
ac_cv_func_chown_works=yes
else
ac_cv_func_chown_works=no
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
rm -f conftest.chown
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_chown_works" >&5
$as_echo "$ac_cv_func_chown_works" >&6; }
if test $ac_cv_func_chown_works = yes; then
$as_echo "#define HAVE_CHOWN 1" >>confdefs.h
fi
for ac_header in vfork.h
do :
ac_fn_c_check_header_mongrel "$LINENO" "vfork.h" "ac_cv_header_vfork_h" "$ac_includes_default"
if test "x$ac_cv_header_vfork_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_VFORK_H 1
_ACEOF
fi
done
for ac_func in fork vfork
do :
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
cat >>confdefs.h <<_ACEOF
#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
_ACEOF
fi
done
if test "x$ac_cv_func_fork" = xyes; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working fork" >&5
$as_echo_n "checking for working fork... " >&6; }
if ${ac_cv_func_fork_works+:} false; then :
$as_echo_n "(cached) " >&6
else
if test "$cross_compiling" = yes; then :
ac_cv_func_fork_works=cross
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$ac_includes_default
int
main ()
{
/* By Ruediger Kuhlmann. */
return fork () < 0;
;
return 0;
}
_ACEOF
if ac_fn_c_try_run "$LINENO"; then :
ac_cv_func_fork_works=yes
else
ac_cv_func_fork_works=no
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_fork_works" >&5
$as_echo "$ac_cv_func_fork_works" >&6; }
else
ac_cv_func_fork_works=$ac_cv_func_fork
fi
if test "x$ac_cv_func_fork_works" = xcross; then
case $host in
*-*-amigaos* | *-*-msdosdjgpp*)
# Override, as these systems have only a dummy fork() stub
ac_cv_func_fork_works=no
;;
*)
ac_cv_func_fork_works=yes
;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5
$as_echo "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;}
fi
ac_cv_func_vfork_works=$ac_cv_func_vfork
if test "x$ac_cv_func_vfork" = xyes; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working vfork" >&5
$as_echo_n "checking for working vfork... " >&6; }
if ${ac_cv_func_vfork_works+:} false; then :
$as_echo_n "(cached) " >&6
else
if test "$cross_compiling" = yes; then :
ac_cv_func_vfork_works=cross
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Thanks to Paul Eggert for this test. */
$ac_includes_default
#include <sys/wait.h>
#ifdef HAVE_VFORK_H
# include <vfork.h>
#endif
/* On some sparc systems, changes by the child to local and incoming
argument registers are propagated back to the parent. The compiler
is told about this with #include <vfork.h>, but some compilers
(e.g. gcc -O) don't grok <vfork.h>. Test for this by using a
static variable whose address is put into a register that is
clobbered by the vfork. */
static void
#ifdef __cplusplus
sparc_address_test (int arg)
# else
sparc_address_test (arg) int arg;
#endif
{
static pid_t child;
if (!child) {
child = vfork ();
if (child < 0) {
perror ("vfork");
_exit(2);
}
if (!child) {
arg = getpid();
write(-1, "", 0);
_exit (arg);
}
}
}
int
main ()
{
pid_t parent = getpid ();
pid_t child;
sparc_address_test (0);
child = vfork ();
if (child == 0) {
/* Here is another test for sparc vfork register problems. This
test uses lots of local variables, at least as many local
variables as main has allocated so far including compiler
temporaries. 4 locals are enough for gcc 1.40.3 on a Solaris
4.1.3 sparc, but we use 8 to be safe. A buggy compiler should
reuse the register of parent for one of the local variables,
since it will think that parent can't possibly be used any more
in this routine. Assigning to the local variable will thus
munge parent in the parent process. */
pid_t
p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(),
p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid();
/* Convince the compiler that p..p7 are live; otherwise, it might
use the same hardware register for all 8 local variables. */
if (p != p1 || p != p2 || p != p3 || p != p4
|| p != p5 || p != p6 || p != p7)
_exit(1);
/* On some systems (e.g. IRIX 3.3), vfork doesn't separate parent
from child file descriptors. If the child closes a descriptor
before it execs or exits, this munges the parent's descriptor
as well. Test for this by closing stdout in the child. */
_exit(close(fileno(stdout)) != 0);
} else {
int status;
struct stat st;
while (wait(&status) != child)
;
return (
/* Was there some problem with vforking? */
child < 0
/* Did the child fail? (This shouldn't happen.) */
|| status
/* Did the vfork/compiler bug occur? */
|| parent != getpid()
/* Did the file descriptor bug occur? */
|| fstat(fileno(stdout), &st) != 0
);
}
}
_ACEOF
if ac_fn_c_try_run "$LINENO"; then :
ac_cv_func_vfork_works=yes
else
ac_cv_func_vfork_works=no
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_vfork_works" >&5
$as_echo "$ac_cv_func_vfork_works" >&6; }
fi;
if test "x$ac_cv_func_fork_works" = xcross; then
ac_cv_func_vfork_works=$ac_cv_func_vfork
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5
$as_echo "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;}
fi
if test "x$ac_cv_func_vfork_works" = xyes; then
$as_echo "#define HAVE_WORKING_VFORK 1" >>confdefs.h
else
$as_echo "#define vfork fork" >>confdefs.h
fi
if test "x$ac_cv_func_fork_works" = xyes; then
$as_echo "#define HAVE_WORKING_FORK 1" >>confdefs.h
fi
$as_echo "#define RETSIGTYPE void" >>confdefs.h
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGEFILE_SOURCE value needed for large files" >&5
$as_echo_n "checking for _LARGEFILE_SOURCE value needed for large files... " >&6; }
if ${ac_cv_sys_largefile_source+:} false; then :
$as_echo_n "(cached) " >&6
else
while :; do
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <sys/types.h> /* for off_t */
#include <stdio.h>
int
main ()
{
int (*fp) (FILE *, off_t, int) = fseeko;
return fseeko (stdin, 0, 0) && fp (stdin, 0, 0);
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_sys_largefile_source=no; break
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#define _LARGEFILE_SOURCE 1
#include <sys/types.h> /* for off_t */
#include <stdio.h>
int
main ()
{
int (*fp) (FILE *, off_t, int) = fseeko;
return fseeko (stdin, 0, 0) && fp (stdin, 0, 0);
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_sys_largefile_source=1; break
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_cv_sys_largefile_source=unknown
break
done
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_source" >&5
$as_echo "$ac_cv_sys_largefile_source" >&6; }
case $ac_cv_sys_largefile_source in #(
no | unknown) ;;
*)
cat >>confdefs.h <<_ACEOF
#define _LARGEFILE_SOURCE $ac_cv_sys_largefile_source
_ACEOF
;;
esac
rm -rf conftest*
# We used to try defining _XOPEN_SOURCE=500 too, to work around a bug
# in glibc 2.1.3, but that breaks too many other things.
# If you want fseeko and ftello with glibc, upgrade to a fixed glibc.
if test $ac_cv_sys_largefile_source != unknown; then
$as_echo "#define HAVE_FSEEKO 1" >>confdefs.h
fi
# Check whether --enable-largefile was given.
if test "${enable_largefile+set}" = set; then :
enableval=$enable_largefile;
fi
if test "$enable_largefile" != no; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for special C compiler options needed for large files" >&5
$as_echo_n "checking for special C compiler options needed for large files... " >&6; }
if ${ac_cv_sys_largefile_CC+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_sys_largefile_CC=no
if test "$GCC" != yes; then
ac_save_CC=$CC
while :; do
# IRIX 6.2 and later do not support large files by default,
# so use the C compiler's -n32 option if that helps.
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <sys/types.h>
/* Check that off_t can represent 2**63 - 1 correctly.
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
break
fi
rm -f core conftest.err conftest.$ac_objext
CC="$CC -n32"
if ac_fn_c_try_compile "$LINENO"; then :
ac_cv_sys_largefile_CC=' -n32'; break
fi
rm -f core conftest.err conftest.$ac_objext
break
done
CC=$ac_save_CC
rm -f conftest.$ac_ext
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_CC" >&5
$as_echo "$ac_cv_sys_largefile_CC" >&6; }
if test "$ac_cv_sys_largefile_CC" != no; then
CC=$CC$ac_cv_sys_largefile_CC
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for _FILE_OFFSET_BITS value needed for large files" >&5
$as_echo_n "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; }
if ${ac_cv_sys_file_offset_bits+:} false; then :
$as_echo_n "(cached) " >&6
else
while :; do
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <sys/types.h>
/* Check that off_t can represent 2**63 - 1 correctly.
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
ac_cv_sys_file_offset_bits=no; break
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#define _FILE_OFFSET_BITS 64
#include <sys/types.h>
/* Check that off_t can represent 2**63 - 1 correctly.
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
ac_cv_sys_file_offset_bits=64; break
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
ac_cv_sys_file_offset_bits=unknown
break
done
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_file_offset_bits" >&5
$as_echo "$ac_cv_sys_file_offset_bits" >&6; }
case $ac_cv_sys_file_offset_bits in #(
no | unknown) ;;
*)
cat >>confdefs.h <<_ACEOF
#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits
_ACEOF
;;
esac
rm -rf conftest*
if test $ac_cv_sys_file_offset_bits = unknown; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGE_FILES value needed for large files" >&5
$as_echo_n "checking for _LARGE_FILES value needed for large files... " >&6; }
if ${ac_cv_sys_large_files+:} false; then :
$as_echo_n "(cached) " >&6
else
while :; do
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <sys/types.h>
/* Check that off_t can represent 2**63 - 1 correctly.
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
ac_cv_sys_large_files=no; break
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#define _LARGE_FILES 1
#include <sys/types.h>
/* Check that off_t can represent 2**63 - 1 correctly.
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
ac_cv_sys_large_files=1; break
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
ac_cv_sys_large_files=unknown
break
done
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_large_files" >&5
$as_echo "$ac_cv_sys_large_files" >&6; }
case $ac_cv_sys_large_files in #(
no | unknown) ;;
*)
cat >>confdefs.h <<_ACEOF
#define _LARGE_FILES $ac_cv_sys_large_files
_ACEOF
;;
esac
rm -rf conftest*
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we need -D_LARGEFILE_SOURCE=1 as a flag for $CC" >&5
$as_echo_n "checking whether we need -D_LARGEFILE_SOURCE=1 as a flag for $CC... " >&6; }
cache=_D_LARGEFILE_SOURCE_1
if eval \${cv_prog_cc_flag_needed_$cache+:} false; then :
$as_echo_n "(cached) " >&6
else
echo '
#include <stdio.h>
int test(void) {
int a = fseeko(stdin, 0, 0);
return a;
}
' > 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
if test -z "`$CC $CPPFLAGS $CFLAGS -D_LARGEFILE_SOURCE=1 $ERRFLAG -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_needed_$cache=yes"
else
eval "cv_prog_cc_flag_needed_$cache=fail"
#echo 'Test with flag fails too!'
#cat conftest.c
#echo "$CC $CPPFLAGS $CFLAGS -D_LARGEFILE_SOURCE=1 $ERRFLAG -c conftest.c 2>&1"
#echo `$CC $CPPFLAGS $CFLAGS -D_LARGEFILE_SOURCE=1 $ERRFLAG -c conftest.c 2>&1`
#exit 1
fi
fi
rm -f conftest conftest.c conftest.o
fi
if eval "test \"`echo '$cv_prog_cc_flag_needed_'$cache`\" = yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
:
CFLAGS="$CFLAGS -D_LARGEFILE_SOURCE=1"
else
if eval "test \"`echo '$cv_prog_cc_flag_needed_'$cache`\" = no"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
#echo 'Test with flag is no!'
#cat conftest.c
#echo "$CC $CPPFLAGS $CFLAGS -D_LARGEFILE_SOURCE=1 $ERRFLAG -c conftest.c 2>&1"
#echo `$CC $CPPFLAGS $CFLAGS -D_LARGEFILE_SOURCE=1 $ERRFLAG -c conftest.c 2>&1`
#exit 1
:
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5
$as_echo "failed" >&6; }
:
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if nonblocking sockets work" >&5
$as_echo_n "checking if nonblocking sockets work... " >&6; }
if echo $host | grep mingw >/dev/null; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no (windows)" >&5
$as_echo "no (windows)" >&6; }
$as_echo "#define NONBLOCKING_IS_BROKEN 1" >>confdefs.h
else
if test "$cross_compiling" = yes; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: crosscompile(yes)" >&5
$as_echo "crosscompile(yes)" >&6; }
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_TIME_H
#include <time.h>
#endif
int main(void)
{
int port;
int sfd, cfd;
int num = 10;
int i, p;
struct sockaddr_in a;
/* test if select and nonblocking reads work well together */
/* open port.
fork child to send 10 messages.
select to read.
then try to nonblocking read the 10 messages
then, nonblocking read must give EAGAIN
*/
port = 12345 + (time(0)%32);
sfd = socket(PF_INET, SOCK_DGRAM, 0);
if(sfd == -1) {
perror("socket");
return 1;
}
memset(&a, 0, sizeof(a));
a.sin_family = AF_INET;
a.sin_port = htons(port);
a.sin_addr.s_addr = inet_addr("127.0.0.1");
if(bind(sfd, (struct sockaddr*)&a, sizeof(a)) < 0) {
perror("bind");
return 1;
}
if(fcntl(sfd, F_SETFL, O_NONBLOCK) == -1) {
perror("fcntl");
return 1;
}
cfd = socket(PF_INET, SOCK_DGRAM, 0);
if(cfd == -1) {
perror("client socket");
return 1;
}
a.sin_port = 0;
if(bind(cfd, (struct sockaddr*)&a, sizeof(a)) < 0) {
perror("client bind");
return 1;
}
a.sin_port = htons(port);
/* no handler, causes exit in 10 seconds */
alarm(10);
/* send and receive on the socket */
if((p=fork()) == 0) {
for(i=0; i<num; i++) {
if(sendto(cfd, &i, sizeof(i), 0,
(struct sockaddr*)&a, sizeof(a)) < 0) {
perror("sendto");
return 1;
}
}
} else {
/* parent */
fd_set rset;
int x;
if(p == -1) {
perror("fork");
return 1;
}
FD_ZERO(&rset);
FD_SET(sfd, &rset);
if(select(sfd+1, &rset, NULL, NULL, NULL) < 1) {
perror("select");
return 1;
}
i = 0;
while(i < num) {
if(recv(sfd, &x, sizeof(x), 0) != sizeof(x)) {
if(errno == EAGAIN)
continue;
perror("recv");
return 1;
}
i++;
}
/* now we want to get EAGAIN: nonblocking goodness */
errno = 0;
recv(sfd, &x, sizeof(x), 0);
if(errno != EAGAIN) {
perror("trying to recv again");
return 1;
}
/* EAGAIN encountered */
}
close(sfd);
close(cfd);
return 0;
}
_ACEOF
if ac_fn_c_try_run "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
$as_echo "#define NONBLOCKING_IS_BROKEN 1" >>confdefs.h
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether mkdir has one arg" >&5
$as_echo_n "checking whether mkdir has one arg... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdio.h>
#include <unistd.h>
#ifdef HAVE_WINSOCK2_H
#include <winsock2.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
int
main ()
{
(void)mkdir("directory");
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
$as_echo "#define MKDIR_HAS_ONE_ARG 1" >>confdefs.h
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
for ac_func in strptime
do :
ac_fn_c_check_func "$LINENO" "strptime" "ac_cv_func_strptime"
if test "x$ac_cv_func_strptime" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_STRPTIME 1
_ACEOF
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether strptime works" >&5
$as_echo_n "checking whether strptime works... " >&6; }
if test c${cross_compiling} = cno; then
if test "$cross_compiling" = yes; then :
- { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error $? "cannot run test program while cross compiling
-See \`config.log' for more details" "$LINENO" 5; }
+ eval "ac_cv_c_strptime_works=maybe"
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#define _XOPEN_SOURCE 600
#include <time.h>
int main(void) { struct tm tm; char *res;
res = strptime("2010-07-15T00:00:00+00:00", "%t%Y%t-%t%m%t-%t%d%tT%t%H%t:%t%M%t:%t%S%t", &tm);
if (!res) return 2;
res = strptime("20070207111842", "%Y%m%d%H%M%S", &tm);
if (!res) return 1; return 0; }
_ACEOF
if ac_fn_c_try_run "$LINENO"; then :
eval "ac_cv_c_strptime_works=yes"
else
eval "ac_cv_c_strptime_works=no"
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
else
eval "ac_cv_c_strptime_works=maybe"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_strptime_works" >&5
$as_echo "$ac_cv_c_strptime_works" >&6; }
if test $ac_cv_c_strptime_works = no; then
case " $LIBOBJS " in
*" strptime.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS strptime.$ac_objext"
;;
esac
else
cat >>confdefs.h <<_ACEOF
#define STRPTIME_WORKS 1
_ACEOF
fi
else
case " $LIBOBJS " in
*" strptime.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS strptime.$ac_objext"
;;
esac
fi
done
# check if we can use SO_REUSEPORT
reuseport_default=0
if echo "$host" | $GREP -i -e linux >/dev/null; then reuseport_default=1; fi
if echo "$host" | $GREP -i -e dragonfly >/dev/null; then reuseport_default=1; fi
if test "$reuseport_default" = 1; then
$as_echo "#define REUSEPORT_DEFAULT 1" >>confdefs.h
else
$as_echo "#define REUSEPORT_DEFAULT 0" >>confdefs.h
fi
# Include systemd.m4 - begin
# macros for configuring systemd
# Copyright 2015, Sami Kerola, CloudFlare.
# BSD licensed.
# Check whether --enable-systemd was given.
if test "${enable_systemd+set}" = set; then :
enableval=$enable_systemd;
else
enable_systemd=no
fi
have_systemd=no
if test "x$enable_systemd" != xno; then :
pkg_failed=no
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for SYSTEMD" >&5
$as_echo_n "checking for SYSTEMD... " >&6; }
if test -n "$SYSTEMD_CFLAGS"; then
pkg_cv_SYSTEMD_CFLAGS="$SYSTEMD_CFLAGS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsystemd\""; } >&5
($PKG_CONFIG --exists --print-errors "libsystemd") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
pkg_cv_SYSTEMD_CFLAGS=`$PKG_CONFIG --cflags "libsystemd" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
fi
else
pkg_failed=untried
fi
if test -n "$SYSTEMD_LIBS"; then
pkg_cv_SYSTEMD_LIBS="$SYSTEMD_LIBS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsystemd\""; } >&5
($PKG_CONFIG --exists --print-errors "libsystemd") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
pkg_cv_SYSTEMD_LIBS=`$PKG_CONFIG --libs "libsystemd" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
fi
else
pkg_failed=untried
fi
if test $pkg_failed = yes; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
_pkg_short_errors_supported=yes
else
_pkg_short_errors_supported=no
fi
if test $_pkg_short_errors_supported = yes; then
SYSTEMD_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libsystemd" 2>&1`
else
SYSTEMD_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libsystemd" 2>&1`
fi
# Put the nasty error message in config.log where it belongs
echo "$SYSTEMD_PKG_ERRORS" >&5
have_systemd=no
elif test $pkg_failed = untried; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
have_systemd=no
else
SYSTEMD_CFLAGS=$pkg_cv_SYSTEMD_CFLAGS
SYSTEMD_LIBS=$pkg_cv_SYSTEMD_LIBS
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
have_systemd=yes
fi
if test "x$have_systemd" != "xyes"; then :
pkg_failed=no
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for SYSTEMD_DAEMON" >&5
$as_echo_n "checking for SYSTEMD_DAEMON... " >&6; }
if test -n "$SYSTEMD_DAEMON_CFLAGS"; then
pkg_cv_SYSTEMD_DAEMON_CFLAGS="$SYSTEMD_DAEMON_CFLAGS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsystemd-daemon\""; } >&5
($PKG_CONFIG --exists --print-errors "libsystemd-daemon") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
pkg_cv_SYSTEMD_DAEMON_CFLAGS=`$PKG_CONFIG --cflags "libsystemd-daemon" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
fi
else
pkg_failed=untried
fi
if test -n "$SYSTEMD_DAEMON_LIBS"; then
pkg_cv_SYSTEMD_DAEMON_LIBS="$SYSTEMD_DAEMON_LIBS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsystemd-daemon\""; } >&5
($PKG_CONFIG --exists --print-errors "libsystemd-daemon") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
pkg_cv_SYSTEMD_DAEMON_LIBS=`$PKG_CONFIG --libs "libsystemd-daemon" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
fi
else
pkg_failed=untried
fi
if test $pkg_failed = yes; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
_pkg_short_errors_supported=yes
else
_pkg_short_errors_supported=no
fi
if test $_pkg_short_errors_supported = yes; then
SYSTEMD_DAEMON_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libsystemd-daemon" 2>&1`
else
SYSTEMD_DAEMON_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libsystemd-daemon" 2>&1`
fi
# Put the nasty error message in config.log where it belongs
echo "$SYSTEMD_DAEMON_PKG_ERRORS" >&5
have_systemd_daemon=no
elif test $pkg_failed = untried; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
have_systemd_daemon=no
else
SYSTEMD_DAEMON_CFLAGS=$pkg_cv_SYSTEMD_DAEMON_CFLAGS
SYSTEMD_DAEMON_LIBS=$pkg_cv_SYSTEMD_DAEMON_LIBS
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
have_systemd_daemon=yes
fi
if test "x$have_systemd_daemon" = "xyes"; then :
have_systemd=yes
fi
fi
case $enable_systemd:$have_systemd in #(
yes:no) :
as_fn_error $? "systemd enabled but libsystemd not found" "$LINENO" 5 ;; #(
*:yes) :
$as_echo "#define HAVE_SYSTEMD 1" >>confdefs.h
LIBS="$LIBS $SYSTEMD_LIBS"
;; #(
*) :
;;
esac
fi
if test "x$have_systemd" = xyes; then
USE_SYSTEMD_TRUE=
USE_SYSTEMD_FALSE='#'
else
USE_SYSTEMD_TRUE='#'
USE_SYSTEMD_FALSE=
fi
# Include systemd.m4 - end
# set memory allocation checking if requested
# Check whether --enable-alloc-checks was given.
if test "${enable_alloc_checks+set}" = set; then :
enableval=$enable_alloc_checks;
fi
# Check whether --enable-alloc-lite was given.
if test "${enable_alloc_lite+set}" = set; then :
enableval=$enable_alloc_lite;
fi
# Check whether --enable-alloc-nonregional was given.
if test "${enable_alloc_nonregional+set}" = set; then :
enableval=$enable_alloc_nonregional;
fi
if test x_$enable_alloc_nonregional = x_yes; then
$as_echo "#define UNBOUND_ALLOC_NONREGIONAL 1" >>confdefs.h
fi
if test x_$enable_alloc_checks = x_yes; then
$as_echo "#define UNBOUND_ALLOC_STATS 1" >>confdefs.h
SLDNS_ALLOCCHECK_EXTRA_OBJ="alloc.lo log.lo"
ASYNCLOOK_ALLOCCHECK_EXTRA_OBJ="alloc.lo"
else
if test x_$enable_alloc_lite = x_yes; then
$as_echo "#define UNBOUND_ALLOC_LITE 1" >>confdefs.h
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU libc compatible malloc" >&5
$as_echo_n "checking for GNU libc compatible malloc... " >&6; }
if test "$cross_compiling" = yes; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no (crosscompile)" >&5
$as_echo "no (crosscompile)" >&6; }
case " $LIBOBJS " in
*" malloc.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS malloc.$ac_objext"
;;
esac
cat >>confdefs.h <<_ACEOF
#define malloc rpl_malloc_unbound
_ACEOF
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#if defined STDC_HEADERS || defined HAVE_STDLIB_H
#include <stdlib.h>
#else
char *malloc ();
#endif
int
main ()
{
if(malloc(0) != 0) return 1;
;
return 0;
}
_ACEOF
if ac_fn_c_try_run "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
case " $LIBOBJS " in
*" malloc.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS malloc.$ac_objext"
;;
esac
cat >>confdefs.h <<_ACEOF
#define malloc rpl_malloc_unbound
_ACEOF
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
$as_echo "#define HAVE_MALLOC 1" >>confdefs.h
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
fi
fi
# check windows threads (we use them, not pthreads, on windows).
if test "$on_mingw" = "yes"; then
# check windows threads
for ac_header in windows.h
do :
ac_fn_c_check_header_compile "$LINENO" "windows.h" "ac_cv_header_windows_h" "$ac_includes_default
"
if test "x$ac_cv_header_windows_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_WINDOWS_H 1
_ACEOF
fi
done
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for CreateThread" >&5
$as_echo_n "checking for CreateThread... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif
int
main ()
{
HANDLE t = CreateThread(NULL, 0, NULL, NULL, 0, NULL);
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
$as_echo "#define HAVE_WINDOWS_THREADS 1" >>confdefs.h
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
else
# not on mingw, check thread libraries.
# check for thread library.
# 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.
# Check whether --with-pthreads was given.
if test "${with_pthreads+set}" = set; then :
withval=$with_pthreads;
else
withval="yes"
fi
ub_have_pthreads=no
if test x_$withval != x_no; then
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
ax_pthread_ok=no
# We used to check for pthread.h first, but this fails if pthread.h
# requires special compiler flags (e.g. on True64 or Sequent).
# It gets checked for in the link test anyway.
# First of all, check if the user has set any of the PTHREAD_LIBS,
# etcetera environment variables, and if threads linking works using
# them:
if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
save_LIBS="$LIBS"
LIBS="$PTHREAD_LIBS $LIBS"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS" >&5
$as_echo_n "checking for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char pthread_join ();
int
main ()
{
return pthread_join ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ax_pthread_ok=yes
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_ok" >&5
$as_echo "$ax_pthread_ok" >&6; }
if test x"$ax_pthread_ok" = xno; then
PTHREAD_LIBS=""
PTHREAD_CFLAGS=""
fi
LIBS="$save_LIBS"
CFLAGS="$save_CFLAGS"
fi
# We must check for the threads library under a number of different
# names; the ordering is very important because some systems
# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
# libraries is broken (non-POSIX).
# Create a list of thread flags to try. Items starting with a "-" are
# C compiler flags, and other items are library names, except for "none"
# which indicates that we try without any flags at all, and "pthread-config"
# which is a program returning the flags for the Pth emulation library.
ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
# The ordering *is* (sometimes) important. Some notes on the
# individual items follow:
# pthreads: AIX (must check this before -lpthread)
# none: in case threads are in libc; should be tried before -Kthread and
# other compiler flags to prevent continual compiler warnings
# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
# -pthreads: Solaris/gcc
# -mthreads: Mingw32/gcc, Lynx/gcc
# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
# doesn't hurt to check since this sometimes defines pthreads too;
# also defines -D_REENTRANT)
# ... -mt is also the pthreads flag for HP/aCC
# pthread: Linux, etcetera
# --thread-safe: KAI C++
# pthread-config: use pthread-config program (for GNU Pth library)
case ${host_os} in
solaris*)
# On Solaris (at least, for some versions), libc contains stubbed
# (non-functional) versions of the pthreads routines, so link-based
# tests will erroneously succeed. (We need to link with -pthreads/-mt/
# -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
# a function called by this macro, so we could check for that, but
# who knows whether they'll stub that too in a future libc.) So,
# we'll just look for -pthreads and -lpthread first:
ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags"
;;
darwin*)
ax_pthread_flags="-pthread $ax_pthread_flags"
;;
esac
# Clang doesn't consider unrecognized options an error unless we specify
# -Werror. We throw in some extra Clang-specific options to ensure that
# this doesn't happen for GCC, which also accepts -Werror.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if compiler needs -Werror to reject unknown flags" >&5
$as_echo_n "checking if compiler needs -Werror to reject unknown flags... " >&6; }
save_CFLAGS="$CFLAGS"
ax_pthread_extra_flags="-Werror"
CFLAGS="$CFLAGS $ax_pthread_extra_flags -Wunknown-warning-option -Wsizeof-array-argument"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int foo(void);
int
main ()
{
foo()
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
ax_pthread_extra_flags=
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
CFLAGS="$save_CFLAGS"
if test x"$ax_pthread_ok" = xno; then
for flag in $ax_pthread_flags; do
case $flag in
none)
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work without any flags" >&5
$as_echo_n "checking whether pthreads work without any flags... " >&6; }
;;
-*)
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work with $flag" >&5
$as_echo_n "checking whether pthreads work with $flag... " >&6; }
PTHREAD_CFLAGS="$flag"
;;
pthread-config)
# Extract the first word of "pthread-config", so it can be a program name with args.
set dummy pthread-config; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ax_pthread_config+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ax_pthread_config"; then
ac_cv_prog_ax_pthread_config="$ax_pthread_config" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ax_pthread_config="yes"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
test -z "$ac_cv_prog_ax_pthread_config" && ac_cv_prog_ax_pthread_config="no"
fi
fi
ax_pthread_config=$ac_cv_prog_ax_pthread_config
if test -n "$ax_pthread_config"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_config" >&5
$as_echo "$ax_pthread_config" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test x"$ax_pthread_config" = xno; then continue; fi
PTHREAD_CFLAGS="`pthread-config --cflags`"
PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
;;
*)
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for the pthreads library -l$flag" >&5
$as_echo_n "checking for the pthreads library -l$flag... " >&6; }
PTHREAD_LIBS="-l$flag"
;;
esac
save_LIBS="$LIBS"
save_CFLAGS="$CFLAGS"
LIBS="$PTHREAD_LIBS $LIBS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS $ax_pthread_extra_flags"
# Check for various functions. We must include pthread.h,
# since some functions may be macros. (On the Sequent, we
# need a special flag -Kthread to make this header compile.)
# We check for pthread_join because it is in -lpthread on IRIX
# while pthread_create is in libc. We check for pthread_attr_init
# due to DEC craziness with -lpthreads. We check for
# pthread_cleanup_push because it is one of the few pthread
# functions on Solaris that doesn't have a non-functional libc stub.
# We try pthread_create on general principles.
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <pthread.h>
static void routine(void *a) { *((int*)a) = 0; }
static void *start_routine(void *a) { return a; }
int
main ()
{
pthread_t th; pthread_attr_t attr;
pthread_create(&th, 0, start_routine, 0);
pthread_join(th, 0);
pthread_attr_init(&attr);
pthread_cleanup_push(routine, 0);
pthread_cleanup_pop(0) /* ; */
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ax_pthread_ok=yes
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS="$save_LIBS"
CFLAGS="$save_CFLAGS"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_ok" >&5
$as_echo "$ax_pthread_ok" >&6; }
if test "x$ax_pthread_ok" = xyes; then
break;
fi
PTHREAD_LIBS=""
PTHREAD_CFLAGS=""
done
fi
# Various other checks:
if test "x$ax_pthread_ok" = xyes; then
save_LIBS="$LIBS"
LIBS="$PTHREAD_LIBS $LIBS"
save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
# Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for joinable pthread attribute" >&5
$as_echo_n "checking for joinable pthread attribute... " >&6; }
attr_name=unknown
for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <pthread.h>
int
main ()
{
int attr = $attr; return attr /* ; */
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
attr_name=$attr; break
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
done
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $attr_name" >&5
$as_echo "$attr_name" >&6; }
if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
cat >>confdefs.h <<_ACEOF
#define PTHREAD_CREATE_JOINABLE $attr_name
_ACEOF
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if more special flags are required for pthreads" >&5
$as_echo_n "checking if more special flags are required for pthreads... " >&6; }
flag=no
case ${host_os} in
aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";;
osf* | hpux*) flag="-D_REENTRANT";;
solaris*)
if test "$GCC" = "yes"; then
flag="-D_REENTRANT"
else
# TODO: What about Clang on Solaris?
flag="-mt -D_REENTRANT"
fi
;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $flag" >&5
$as_echo "$flag" >&6; }
if test "x$flag" != xno; then
PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for PTHREAD_PRIO_INHERIT" >&5
$as_echo_n "checking for PTHREAD_PRIO_INHERIT... " >&6; }
if ${ax_cv_PTHREAD_PRIO_INHERIT+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <pthread.h>
int
main ()
{
int i = PTHREAD_PRIO_INHERIT;
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ax_cv_PTHREAD_PRIO_INHERIT=yes
else
ax_cv_PTHREAD_PRIO_INHERIT=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_PRIO_INHERIT" >&5
$as_echo "$ax_cv_PTHREAD_PRIO_INHERIT" >&6; }
if test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"; then :
$as_echo "#define HAVE_PTHREAD_PRIO_INHERIT 1" >>confdefs.h
fi
LIBS="$save_LIBS"
CFLAGS="$save_CFLAGS"
# More AIX lossage: compile with *_r variant
if test "x$GCC" != xyes; then
case $host_os in
aix*)
case "x/$CC" in #(
x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6) :
#handle absolute path differently from PATH based program lookup
case "x$CC" in #(
x/*) :
if as_fn_executable_p ${CC}_r; then :
PTHREAD_CC="${CC}_r"
fi ;; #(
*) :
for ac_prog in ${CC}_r
do
# Extract the first word of "$ac_prog", so it can be a program name with args.
set dummy $ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_PTHREAD_CC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$PTHREAD_CC"; then
ac_cv_prog_PTHREAD_CC="$PTHREAD_CC" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_PTHREAD_CC="$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
PTHREAD_CC=$ac_cv_prog_PTHREAD_CC
if test -n "$PTHREAD_CC"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $PTHREAD_CC" >&5
$as_echo "$PTHREAD_CC" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
test -n "$PTHREAD_CC" && break
done
test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
;;
esac ;; #(
*) :
;;
esac
;;
esac
fi
fi
test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
if test x"$ax_pthread_ok" = xyes; then
$as_echo "#define HAVE_PTHREAD 1" >>confdefs.h
if test -n "$PTHREAD_LIBS"; then
LIBS="$PTHREAD_LIBS $LIBS"
fi
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
CC="$PTHREAD_CC"
ub_have_pthreads=yes
ac_fn_c_check_type "$LINENO" "pthread_spinlock_t" "ac_cv_type_pthread_spinlock_t" "#include <pthread.h>
"
if test "x$ac_cv_type_pthread_spinlock_t" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_PTHREAD_SPINLOCK_T 1
_ACEOF
fi
ac_fn_c_check_type "$LINENO" "pthread_rwlock_t" "ac_cv_type_pthread_rwlock_t" "#include <pthread.h>
"
if test "x$ac_cv_type_pthread_rwlock_t" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_PTHREAD_RWLOCK_T 1
_ACEOF
fi
# The cast to long int works around a bug in the HP C Compiler
# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
# This bug is HP SR number 8606223364.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned long" >&5
$as_echo_n "checking size of unsigned long... " >&6; }
if ${ac_cv_sizeof_unsigned_long+:} false; then :
$as_echo_n "(cached) " >&6
else
if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned long))" "ac_cv_sizeof_unsigned_long" "$ac_includes_default"; then :
else
if test "$ac_cv_type_unsigned_long" = yes; then
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error 77 "cannot compute sizeof (unsigned long)
See \`config.log' for more details" "$LINENO" 5; }
else
ac_cv_sizeof_unsigned_long=0
fi
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_long" >&5
$as_echo "$ac_cv_sizeof_unsigned_long" >&6; }
cat >>confdefs.h <<_ACEOF
#define SIZEOF_UNSIGNED_LONG $ac_cv_sizeof_unsigned_long
_ACEOF
# The cast to long int works around a bug in the HP C Compiler
# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
# This bug is HP SR number 8606223364.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of pthread_t" >&5
$as_echo_n "checking size of pthread_t... " >&6; }
if ${ac_cv_sizeof_pthread_t+:} false; then :
$as_echo_n "(cached) " >&6
else
if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (pthread_t))" "ac_cv_sizeof_pthread_t" "$ac_includes_default"; then :
else
if test "$ac_cv_type_pthread_t" = yes; then
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error 77 "cannot compute sizeof (pthread_t)
See \`config.log' for more details" "$LINENO" 5; }
else
ac_cv_sizeof_pthread_t=0
fi
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_pthread_t" >&5
$as_echo "$ac_cv_sizeof_pthread_t" >&6; }
cat >>confdefs.h <<_ACEOF
#define SIZEOF_PTHREAD_T $ac_cv_sizeof_pthread_t
_ACEOF
if echo "$CFLAGS" | $GREP -e "-pthread" >/dev/null; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if -pthread unused during linking" >&5
$as_echo_n "checking if -pthread unused during linking... " >&6; }
# catch clang warning 'argument unused during compilation'
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$ac_includes_default
int main(void) {return 0;}
_ACEOF
pthread_unused="yes"
# first compile
echo "$CC $CFLAGS -c conftest.c -o conftest.o" >&5
$CC $CFLAGS -c conftest.c -o conftest.o 2>&5 >&5
if test $? = 0; then
# then link
echo "$CC $CFLAGS -Werror $LDFLAGS $LIBS -o conftest contest.o" >&5
$CC $CFLAGS -Werror $LDFLAGS $LIBS -o conftest conftest.o 2>&5 >&5
if test $? -ne 0; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
CFLAGS=`echo "$CFLAGS" | sed -e 's/-pthread//'`
PTHREAD_CFLAGS_ONLY="-pthread"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi # endif cc successful
rm -f conftest conftest.c conftest.o
fi # endif -pthread in CFLAGS
:
else
ax_pthread_ok=no
fi
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
fi
# check solaris thread library
# Check whether --with-solaris-threads was given.
if test "${with_solaris_threads+set}" = set; then :
withval=$with_solaris_threads;
else
withval="no"
fi
ub_have_sol_threads=no
if test x_$withval != x_no; then
if test x_$ub_have_pthreads != x_no; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Have pthreads already, ignoring --with-solaris-threads" >&5
$as_echo "$as_me: WARNING: Have pthreads already, ignoring --with-solaris-threads" >&2;}
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing thr_create" >&5
$as_echo_n "checking for library containing thr_create... " >&6; }
if ${ac_cv_search_thr_create+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char thr_create ();
int
main ()
{
return thr_create ();
;
return 0;
}
_ACEOF
for ac_lib in '' thread; do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_search_thr_create=$ac_res
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext
if ${ac_cv_search_thr_create+:} false; then :
break
fi
done
if ${ac_cv_search_thr_create+:} false; then :
else
ac_cv_search_thr_create=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_thr_create" >&5
$as_echo "$ac_cv_search_thr_create" >&6; }
ac_res=$ac_cv_search_thr_create
if test "$ac_res" != no; then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
$as_echo "#define HAVE_SOLARIS_THREADS 1" >>confdefs.h
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC supports -mt" >&5
$as_echo_n "checking whether $CC supports -mt... " >&6; }
cache=`echo mt | sed 'y%.=/+-%___p_%'`
if eval \${cv_prog_cc_flag_$cache+:} false; then :
$as_echo_n "(cached) " >&6
else
echo 'void f(void){}' >conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS -mt -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_$cache=yes"
else
eval "cv_prog_cc_flag_$cache=no"
fi
rm -f conftest conftest.o conftest.c
fi
if eval "test \"`echo '$cv_prog_cc_flag_'$cache`\" = yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
:
CFLAGS="$CFLAGS -mt"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
:
CFLAGS="$CFLAGS -D_REENTRANT"
fi
ub_have_sol_threads=yes
else
as_fn_error $? "no solaris threads found." "$LINENO" 5
fi
fi
fi
fi # end of non-mingw check of thread libraries
# Check for SYSLOG_FACILITY
# Check whether --with-syslog-facility was given.
if test "${with_syslog_facility+set}" = set; then :
withval=$with_syslog_facility; UNBOUND_SYSLOG_FACILITY="$withval"
fi
case "${UNBOUND_SYSLOG_FACILITY}" in
LOCAL[0-7]) UNBOUND_SYSLOG_FACILITY="LOG_${UNBOUND_SYSLOG_FACILITY}" ;;
*) UNBOUND_SYSLOG_FACILITY="LOG_DAEMON" ;;
esac
cat >>confdefs.h <<_ACEOF
#define UB_SYSLOG_FACILITY ${UNBOUND_SYSLOG_FACILITY}
_ACEOF
# Check for dynamic library module
# Check whether --with-dynlibmodule was given.
if test "${with_dynlibmodule+set}" = set; then :
withval=$with_dynlibmodule;
else
withval="no"
fi
if test x_$withval != x_no; then
$as_echo "#define WITH_DYNLIBMODULE 1" >>confdefs.h
WITH_DYNLIBMODULE=yes
DYNLIBMOD_OBJ="dynlibmod.lo"
DYNLIBMOD_HEADER='$(srcdir)/dynlibmod/dynlibmod.h'
if test $on_mingw = "no"; then
# link with -ldl if not already there, for all executables because
# dlopen call is in the dynlib module. For unbound executable, also
# export symbols.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing dlopen" >&5
$as_echo_n "checking for library containing dlopen... " >&6; }
if ${ac_cv_search_dlopen+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char dlopen ();
int
main ()
{
return dlopen ();
;
return 0;
}
_ACEOF
for ac_lib in '' dl; do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_search_dlopen=$ac_res
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext
if ${ac_cv_search_dlopen+:} false; then :
break
fi
done
if ${ac_cv_search_dlopen+:} false; then :
else
ac_cv_search_dlopen=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_dlopen" >&5
$as_echo "$ac_cv_search_dlopen" >&6; }
ac_res=$ac_cv_search_dlopen
if test "$ac_res" != no; then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
fi
DYNLIBMOD_EXTRALIBS="-export-dynamic"
else
DYNLIBMOD_EXTRALIBS="-Wl,--export-all-symbols,--out-implib,libunbound.dll.a"
fi
fi
# Check for PyUnbound
# Check whether --with-pyunbound was given.
if test "${with_pyunbound+set}" = set; then :
withval=$with_pyunbound;
else
withval="no"
fi
ub_test_python=no
ub_with_pyunbound=no
if test x_$withval != x_no; then
ub_with_pyunbound=yes
ub_test_python=yes
fi
# Check for Python module
# Check whether --with-pythonmodule was given.
if test "${with_pythonmodule+set}" = set; then :
withval=$with_pythonmodule;
else
withval="no"
fi
ub_with_pythonmod=no
if test x_$withval != x_no; then
ub_with_pythonmod=yes
ub_test_python=yes
fi
# Check for Python & SWIG only on PyUnbound or PyModule
if test x_$ub_test_python != x_no; then
# Check for Python
ub_have_python=no
ac_save_LIBS="$LIBS"
#
# Allow the use of a (user set) custom python version
#
# Extract the first word of "python[$PYTHON_VERSION]", so it can be a program name with args.
set dummy python$PYTHON_VERSION; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_path_PYTHON+:} false; then :
$as_echo_n "(cached) " >&6
else
case $PYTHON in
[\\/]* | ?:[\\/]*)
ac_cv_path_PYTHON="$PYTHON" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_path_PYTHON="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
;;
esac
fi
PYTHON=$ac_cv_path_PYTHON
if test -n "$PYTHON"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON" >&5
$as_echo "$PYTHON" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test -z "$PYTHON"; then
as_fn_error $? "Cannot find python$PYTHON_VERSION in your system path" "$LINENO" 5
PYTHON_VERSION=""
fi
if test -z "$PYTHON_VERSION"; then
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 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="$m"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
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 \"$m\", or \"$m2\".
Please check your Python installation. The errors are:
$m
$ac_modulecheck_result1
$m2
$ac_modulecheck_result2" "$LINENO" 5
PYTHON_VERSION=""
fi
fi
if test "$sysconfig_module" = "distutils"; then sysconfig_module="distutils.sysconfig"; fi
#
# Check for Python include path
#
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for Python include path" >&5
$as_echo_n "checking for Python include path... " >&6; }
if test -z "$PYTHON_CPPFLAGS"; then
if test "$sysconfig_module" = "sysconfig"; then
python_path=`$PYTHON -c 'import sysconfig; \
print(sysconfig.get_path("include"));'`
else
python_path=`$PYTHON -c "import distutils.sysconfig; \
print(distutils.sysconfig.get_python_inc());"`
fi
if test -n "${python_path}"; then
python_path="-I$python_path"
fi
PYTHON_CPPFLAGS=$python_path
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON_CPPFLAGS" >&5
$as_echo "$PYTHON_CPPFLAGS" >&6; }
#
# Check for Python library path
#
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for Python library path" >&5
$as_echo_n "checking for Python library path... " >&6; }
if test -z "$PYTHON_LDFLAGS"; then
PYTHON_LDFLAGS=`$PYTHON -c "from $sysconfig_module import *; \
print('-L'+get_config_var('LIBDIR')+' -L'+get_config_var('LIBDEST')+' '+get_config_var('BLDLIBRARY'));"`
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON_LDFLAGS" >&5
$as_echo "$PYTHON_LDFLAGS" >&6; }
if test -z "$PYTHON_LIBDIR"; then
PYTHON_LIBDIR=`$PYTHON -c "from $sysconfig_module import *; \
print(get_config_var('LIBDIR'));"`
fi
#
# Check for site packages
#
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for Python site-packages path" >&5
$as_echo_n "checking for Python site-packages path... " >&6; }
if test -z "$PYTHON_SITE_PKG"; then
if test "$sysconfig_module" = "sysconfig"; then
PYTHON_SITE_PKG=`$PYTHON -c 'import sysconfig; \
print(sysconfig.get_path("platlib"));'`
else
PYTHON_SITE_PKG=`$PYTHON -c "import distutils.sysconfig; \
print(distutils.sysconfig.get_python_lib(1,0));"`
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON_SITE_PKG" >&5
$as_echo "$PYTHON_SITE_PKG" >&6; }
#
# final check to see if everything compiles alright
#
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking consistency of all components of python development environment" >&5
$as_echo_n "checking consistency of all components of python development environment... " >&6; }
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
# save current global flags
ac_save_LIBS="$LIBS"
ac_save_CPPFLAGS="$CPPFLAGS"
LIBS="$LIBS $PYTHON_LDFLAGS"
CPPFLAGS="$CPPFLAGS $PYTHON_CPPFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <Python.h>
int
main ()
{
Py_Initialize();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
pythonexists=yes
else
pythonexists=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pythonexists" >&5
$as_echo "$pythonexists" >&6; }
if test ! "$pythonexists" = "yes"; then
as_fn_error $? "
Could not link test program to Python. Maybe the main Python library has been
installed in some non-standard library path. If so, pass it to configure,
via the LDFLAGS environment variable.
Example: ./configure LDFLAGS=\"-L/usr/non-standard-path/python/lib\"
============================================================================
ERROR!
You probably have to install the development version of the Python package
for your distribution. The exact name of this package varies among them.
============================================================================
" "$LINENO" 5
PYTHON_VERSION=""
fi
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
# turn back to default flags
CPPFLAGS="$ac_save_CPPFLAGS"
LIBS="$ac_save_LIBS"
#
# all done!
#
if test ! -z "$PYTHON_VERSION"; 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
PY_MAJOR_VERSION="`$PYTHON -c \"import sys; print(sys.version_info[0])\"`"
# Have Python
$as_echo "#define HAVE_PYTHON 1" >>confdefs.h
if test x_$ub_with_pythonmod != x_no; then
if test -n "$LIBS"; then
LIBS="$PYTHON_LDFLAGS $LIBS"
else
LIBS="$PYTHON_LDFLAGS"
fi
fi
PYTHON_LIBS="$PYTHON_LDFLAGS"
if test -n "$CPPFLAGS"; then
CPPFLAGS="$CPPFLAGS $PYTHON_CPPFLAGS"
else
CPPFLAGS="$PYTHON_CPPFLAGS"
fi
if test "$PYTHON_LIBDIR" != "/usr/lib" -a "$PYTHON_LIBDIR" != "" -a "$PYTHON_LIBDIR" != "/usr/lib64"; then
if test "x$enable_rpath" = xyes; then
if echo "$PYTHON_LIBDIR" | grep "^/" >/dev/null; then
RUNTIME_PATH="$RUNTIME_PATH -R$PYTHON_LIBDIR"
fi
fi
fi
ub_have_python=yes
if test -n "$PKG_CONFIG" && \
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\"python\${PY_MAJOR_VERSION}\"\""; } >&5
($PKG_CONFIG --exists --print-errors ""python${PY_MAJOR_VERSION}"") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
PC_PY_DEPENDENCY="python${PY_MAJOR_VERSION}"
else
PC_PY_DEPENDENCY="python"
fi
# Check for SWIG
ub_have_swig=no
# Check whether --enable-swig-version-check was given.
if test "${enable_swig_version_check+set}" = set; then :
enableval=$enable_swig_version_check;
fi
if test "$enable_swig_version_check" = "yes"; then
# Extract the first word of "swig", so it can be a program name with args.
set dummy swig; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_path_SWIG+:} false; then :
$as_echo_n "(cached) " >&6
else
case $SWIG in
[\\/]* | ?:[\\/]*)
ac_cv_path_SWIG="$SWIG" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_path_SWIG="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
;;
esac
fi
SWIG=$ac_cv_path_SWIG
if test -n "$SWIG"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $SWIG" >&5
$as_echo "$SWIG" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test -z "$SWIG" ; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cannot find 'swig' program. You should look at http://www.swig.org" >&5
$as_echo "$as_me: WARNING: cannot find 'swig' program. You should look at http://www.swig.org" >&2;}
SWIG='echo "Error: SWIG is not installed. You should look at http://www.swig.org" ; false'
elif test -n "2.0.1" ; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for SWIG version" >&5
$as_echo_n "checking for SWIG version... " >&6; }
swig_version=`$SWIG -version 2>&1 | grep 'SWIG Version' | sed 's/.*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*/\1/g'`
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $swig_version" >&5
$as_echo "$swig_version" >&6; }
if test -n "$swig_version" ; then
# Calculate the required version number components
required=2.0.1
required_major=`echo $required | sed 's/[^0-9].*//'`
if test -z "$required_major" ; then
required_major=0
fi
required=`echo $required | sed 's/[0-9]*[^0-9]//'`
required_minor=`echo $required | sed 's/[^0-9].*//'`
if test -z "$required_minor" ; then
required_minor=0
fi
required=`echo $required | sed 's/[0-9]*[^0-9]//'`
required_patch=`echo $required | sed 's/[^0-9].*//'`
if test -z "$required_patch" ; then
required_patch=0
fi
# Calculate the available version number components
available=$swig_version
available_major=`echo $available | sed 's/[^0-9].*//'`
if test -z "$available_major" ; then
available_major=0
fi
available=`echo $available | sed 's/[0-9]*[^0-9]//'`
available_minor=`echo $available | sed 's/[^0-9].*//'`
if test -z "$available_minor" ; then
available_minor=0
fi
available=`echo $available | sed 's/[0-9]*[^0-9]//'`
available_patch=`echo $available | sed 's/[^0-9].*//'`
if test -z "$available_patch" ; then
available_patch=0
fi
badversion=0
if test $available_major -lt $required_major ; then
badversion=1
fi
if test $available_major -eq $required_major \
-a $available_minor -lt $required_minor ; then
badversion=1
fi
if test $available_major -eq $required_major \
-a $available_minor -eq $required_minor \
-a $available_patch -lt $required_patch ; then
badversion=1
fi
if test $badversion -eq 1 ; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: SWIG version >= 2.0.1 is required. You have $swig_version. You should look at http://www.swig.org" >&5
$as_echo "$as_me: WARNING: SWIG version >= 2.0.1 is required. You have $swig_version. You should look at http://www.swig.org" >&2;}
SWIG='echo "Error: SWIG version >= 2.0.1 is required. You have '"$swig_version"'. You should look at http://www.swig.org" ; false'
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: SWIG executable is '$SWIG'" >&5
$as_echo "$as_me: SWIG executable is '$SWIG'" >&6;}
SWIG_LIB=`$SWIG -swiglib`
{ $as_echo "$as_me:${as_lineno-$LINENO}: SWIG library directory is '$SWIG_LIB'" >&5
$as_echo "$as_me: SWIG library directory is '$SWIG_LIB'" >&6;}
fi
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cannot determine SWIG version" >&5
$as_echo "$as_me: WARNING: cannot determine SWIG version" >&2;}
SWIG='echo "Error: Cannot determine SWIG version. You should look at http://www.swig.org" ; false'
fi
fi
else
# Extract the first word of "swig", so it can be a program name with args.
set dummy swig; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_path_SWIG+:} false; then :
$as_echo_n "(cached) " >&6
else
case $SWIG in
[\\/]* | ?:[\\/]*)
ac_cv_path_SWIG="$SWIG" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_path_SWIG="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
;;
esac
fi
SWIG=$ac_cv_path_SWIG
if test -n "$SWIG"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $SWIG" >&5
$as_echo "$SWIG" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test -z "$SWIG" ; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cannot find 'swig' program. You should look at http://www.swig.org" >&5
$as_echo "$as_me: WARNING: cannot find 'swig' program. You should look at http://www.swig.org" >&2;}
SWIG='echo "Error: SWIG is not installed. You should look at http://www.swig.org" ; false'
elif test -n "" ; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for SWIG version" >&5
$as_echo_n "checking for SWIG version... " >&6; }
swig_version=`$SWIG -version 2>&1 | grep 'SWIG Version' | sed 's/.*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*/\1/g'`
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $swig_version" >&5
$as_echo "$swig_version" >&6; }
if test -n "$swig_version" ; then
# Calculate the required version number components
required=
required_major=`echo $required | sed 's/[^0-9].*//'`
if test -z "$required_major" ; then
required_major=0
fi
required=`echo $required | sed 's/[0-9]*[^0-9]//'`
required_minor=`echo $required | sed 's/[^0-9].*//'`
if test -z "$required_minor" ; then
required_minor=0
fi
required=`echo $required | sed 's/[0-9]*[^0-9]//'`
required_patch=`echo $required | sed 's/[^0-9].*//'`
if test -z "$required_patch" ; then
required_patch=0
fi
# Calculate the available version number components
available=$swig_version
available_major=`echo $available | sed 's/[^0-9].*//'`
if test -z "$available_major" ; then
available_major=0
fi
available=`echo $available | sed 's/[0-9]*[^0-9]//'`
available_minor=`echo $available | sed 's/[^0-9].*//'`
if test -z "$available_minor" ; then
available_minor=0
fi
available=`echo $available | sed 's/[0-9]*[^0-9]//'`
available_patch=`echo $available | sed 's/[^0-9].*//'`
if test -z "$available_patch" ; then
available_patch=0
fi
badversion=0
if test $available_major -lt $required_major ; then
badversion=1
fi
if test $available_major -eq $required_major \
-a $available_minor -lt $required_minor ; then
badversion=1
fi
if test $available_major -eq $required_major \
-a $available_minor -eq $required_minor \
-a $available_patch -lt $required_patch ; then
badversion=1
fi
if test $badversion -eq 1 ; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: SWIG version >= is required. You have $swig_version. You should look at http://www.swig.org" >&5
$as_echo "$as_me: WARNING: SWIG version >= is required. You have $swig_version. You should look at http://www.swig.org" >&2;}
SWIG='echo "Error: SWIG version >= is required. You have '"$swig_version"'. You should look at http://www.swig.org" ; false'
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: SWIG executable is '$SWIG'" >&5
$as_echo "$as_me: SWIG executable is '$SWIG'" >&6;}
SWIG_LIB=`$SWIG -swiglib`
{ $as_echo "$as_me:${as_lineno-$LINENO}: SWIG library directory is '$SWIG_LIB'" >&5
$as_echo "$as_me: SWIG library directory is '$SWIG_LIB'" >&6;}
fi
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cannot determine SWIG version" >&5
$as_echo "$as_me: WARNING: cannot determine SWIG version" >&2;}
SWIG='echo "Error: Cannot determine SWIG version. You should look at http://www.swig.org" ; false'
fi
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking SWIG" >&5
$as_echo_n "checking SWIG... " >&6; }
if test ! -x "$SWIG"; then
as_fn_error $? "failed to find swig tool, install it, or do not build Python module and PyUnbound" "$LINENO" 5
else
$as_echo "#define HAVE_SWIG 1" >>confdefs.h
swig="$SWIG"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: present" >&5
$as_echo "present" >&6; }
# If have Python & SWIG
# Declare PythonMod
if test x_$ub_with_pythonmod != x_no; then
$as_echo "#define WITH_PYTHONMODULE 1" >>confdefs.h
WITH_PYTHONMODULE=yes
PYTHONMOD_OBJ="pythonmod.lo pythonmod_utils.lo"
PYTHONMOD_HEADER='$(srcdir)/pythonmod/pythonmod.h'
PYTHONMOD_INSTALL=pythonmod-install
PYTHONMOD_UNINSTALL=pythonmod-uninstall
fi
# Declare PyUnbound
if test x_$ub_with_pyunbound != x_no; then
$as_echo "#define WITH_PYUNBOUND 1" >>confdefs.h
WITH_PYUNBOUND=yes
PYUNBOUND_OBJ="libunbound_wrap.lo"
PYUNBOUND_TARGET="_unbound.la"
PYUNBOUND_INSTALL=pyunbound-install
PYUNBOUND_UNINSTALL=pyunbound-uninstall
fi
fi
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: *** Python libraries not found, won't build PythonMod or PyUnbound ***" >&5
$as_echo "*** Python libraries not found, won't build PythonMod or PyUnbound ***" >&6; }
ub_with_pyunbound=no
ub_with_pythonmod=no
fi
fi
if test "`uname`" = "NetBSD"; then
NETBSD_LINTFLAGS='"-D__RENAME(x)=" -D_NETINET_IN_H_'
fi
if test "`uname`" = "Linux"; then
# splint cannot parse modern c99 header files
GCC_DOCKER_LINTFLAGS='-syntax'
fi
CONFIG_DATE=`date +%Y%m%d`
# Checks for libraries.
# libnss
USE_NSS="no"
# Check whether --with-nss was given.
if test "${with_nss+set}" = set; then :
withval=$with_nss;
USE_NSS="yes"
$as_echo "#define HAVE_NSS 1" >>confdefs.h
if test "$withval" != "" -a "$withval" != "yes"; then
CPPFLAGS="$CPPFLAGS -I$withval/include/nss3"
LDFLAGS="$LDFLAGS -L$withval/lib"
if test "x$enable_rpath" = xyes; then
if echo "$withval/lib" | grep "^/" >/dev/null; then
RUNTIME_PATH="$RUNTIME_PATH -R$withval/lib"
fi
fi
CPPFLAGS="-I$withval/include/nspr4 $CPPFLAGS"
else
CPPFLAGS="$CPPFLAGS -I/usr/include/nss3"
CPPFLAGS="-I/usr/include/nspr4 $CPPFLAGS"
fi
LIBS="$LIBS -lnss3 -lnspr4"
SSLLIB=""
PC_CRYPTO_DEPENDENCY="nss nspr"
fi
# libnettle
USE_NETTLE="no"
# Check whether --with-nettle was given.
if test "${with_nettle+set}" = set; then :
withval=$with_nettle;
USE_NETTLE="yes"
$as_echo "#define HAVE_NETTLE 1" >>confdefs.h
for ac_header in nettle/dsa-compat.h
do :
ac_fn_c_check_header_compile "$LINENO" "nettle/dsa-compat.h" "ac_cv_header_nettle_dsa_compat_h" "$ac_includes_default
"
if test "x$ac_cv_header_nettle_dsa_compat_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_NETTLE_DSA_COMPAT_H 1
_ACEOF
fi
done
if test "$withval" != "" -a "$withval" != "yes"; then
CPPFLAGS="$CPPFLAGS -I$withval/include/nettle"
LDFLAGS="$LDFLAGS -L$withval/lib"
if test "x$enable_rpath" = xyes; then
if echo "$withval/lib" | grep "^/" >/dev/null; then
RUNTIME_PATH="$RUNTIME_PATH -R$withval/lib"
fi
fi
else
CPPFLAGS="$CPPFLAGS -I/usr/include/nettle"
fi
LIBS="$LIBS -lhogweed -lnettle -lgmp"
SSLLIB=""
PC_CRYPTO_DEPENDENCY="hogweed nettle"
fi
# openssl
if test $USE_NSS = "no" -a $USE_NETTLE = "no"; then
# Check whether --with-ssl was given.
if test "${with_ssl+set}" = set; then :
withval=$with_ssl;
else
withval="yes"
fi
if test x_$withval = x_no; then
as_fn_error $? "Need SSL library to do digital signature cryptography" "$LINENO" 5
fi
withval=$withval
if test x_$withval != x_no; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for SSL" >&5
$as_echo_n "checking for SSL... " >&6; }
if test -n "$withval"; then
if test ! -f "$withval/include/openssl/ssl.h" -a -f "$withval/openssl/ssl.h"; then
ssldir="$withval"
found_ssl="yes"
withval=""
ssldir_include="$ssldir"
ssldir_lib=`echo $ssldir | sed -e 's/include/lib/'`
if test -f "$ssldir_lib/libssl.a" -o -f "$ssldir_lib/libssl.so"; then
: # found here
else
ssldir_lib=`echo $ssldir | sed -e 's/include/lib64/'`
if test -f "$ssldir_lib/libssl.a" -o -f "$ssldir_lib/libssl.so"; then
: # found here
else
as_fn_error $? "Could not find openssl lib file, $ssldir_lib/libssl.so,a, pass like \"/usr/local\" or \"/usr/include/openssl11\"" "$LINENO" 5
fi
fi
fi
fi
if test x_$withval = x_ -o x_$withval = x_yes; then
withval="/usr/local/ssl /usr/lib/ssl /usr/ssl /usr/pkg /usr/local /opt/local /usr/sfw /usr"
fi
for dir in $withval; do
ssldir="$dir"
if test -f "$dir/include/openssl/ssl.h"; then
found_ssl="yes"
ssldir_include="$ssldir/include"
if test ! -d "$ssldir/lib" -a -d "$ssldir/lib64"; then
ssldir_lib="$ssldir/lib64"
else
ssldir_lib="$ssldir/lib"
fi
break;
fi
done
if test x_$found_ssl != x_yes; then
as_fn_error $? "Cannot find the SSL libraries in $withval" "$LINENO" 5
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: found in $ssldir" >&5
$as_echo "found in $ssldir" >&6; }
cat >>confdefs.h <<_ACEOF
#define HAVE_SSL /**/
_ACEOF
HAVE_SSL=yes
if test "$ssldir" != "/usr"; then
CPPFLAGS="$CPPFLAGS -I$ssldir_include"
LIBSSL_CPPFLAGS="$LIBSSL_CPPFLAGS -I$ssldir_include"
LDFLAGS="$LDFLAGS -L$ssldir_lib"
LIBSSL_LDFLAGS="$LIBSSL_LDFLAGS -L$ssldir_lib"
if test "x$enable_rpath" = xyes; then
if echo "$ssldir_lib" | grep "^/" >/dev/null; then
RUNTIME_PATH="$RUNTIME_PATH -R$ssldir_lib"
fi
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for EVP_sha256 in -lcrypto" >&5
$as_echo_n "checking for EVP_sha256 in -lcrypto... " >&6; }
LIBS="$LIBS -lcrypto"
LIBSSL_LIBS="$LIBSSL_LIBS -lcrypto"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
int EVP_sha256(void);
(void)EVP_sha256();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
$as_echo "#define HAVE_EVP_SHA256 1" >>confdefs.h
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
# check if -lwsock32 or -lgdi32 are needed.
BAKLIBS="$LIBS"
BAKSSLLIBS="$LIBSSL_LIBS"
LIBS="$LIBS -lgdi32 -lws2_32"
LIBSSL_LIBS="$LIBSSL_LIBS -lgdi32 -lws2_32"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if -lcrypto needs -lgdi32" >&5
$as_echo_n "checking if -lcrypto needs -lgdi32... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
int EVP_sha256(void);
(void)EVP_sha256();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
$as_echo "#define HAVE_EVP_SHA256 1" >>confdefs.h
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
LIBS="$BAKLIBS"
LIBSSL_LIBS="$BAKSSLLIBS"
LIBS="$LIBS -ldl"
LIBSSL_LIBS="$LIBSSL_LIBS -ldl"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if -lcrypto needs -ldl" >&5
$as_echo_n "checking if -lcrypto needs -ldl... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
int EVP_sha256(void);
(void)EVP_sha256();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
$as_echo "#define HAVE_EVP_SHA256 1" >>confdefs.h
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
LIBS="$BAKLIBS"
LIBSSL_LIBS="$BAKSSLLIBS"
LIBS="$LIBS -ldl -pthread"
LIBSSL_LIBS="$LIBSSL_LIBS -ldl -pthread"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if -lcrypto needs -ldl -pthread" >&5
$as_echo_n "checking if -lcrypto needs -ldl -pthread... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
int EVP_sha256(void);
(void)EVP_sha256();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
$as_echo "#define HAVE_EVP_SHA256 1" >>confdefs.h
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
as_fn_error $? "OpenSSL found in $ssldir, but version 0.9.7 or higher is required" "$LINENO" 5
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
fi
fi
for ac_header in openssl/ssl.h
do :
ac_fn_c_check_header_compile "$LINENO" "openssl/ssl.h" "ac_cv_header_openssl_ssl_h" "$ac_includes_default
"
if test "x$ac_cv_header_openssl_ssl_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_OPENSSL_SSL_H 1
_ACEOF
fi
done
for ac_header in openssl/err.h
do :
ac_fn_c_check_header_compile "$LINENO" "openssl/err.h" "ac_cv_header_openssl_err_h" "$ac_includes_default
"
if test "x$ac_cv_header_openssl_err_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_OPENSSL_ERR_H 1
_ACEOF
fi
done
for ac_header in openssl/rand.h
do :
ac_fn_c_check_header_compile "$LINENO" "openssl/rand.h" "ac_cv_header_openssl_rand_h" "$ac_includes_default
"
if test "x$ac_cv_header_openssl_rand_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_OPENSSL_RAND_H 1
_ACEOF
fi
done
# check if libssl needs libdl
BAKLIBS="$LIBS"
LIBS="-lssl $LIBS"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if libssl needs libdl" >&5
$as_echo_n "checking if libssl needs libdl... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char SSL_CTX_new ();
int
main ()
{
return SSL_CTX_new ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
LIBS="$BAKLIBS"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
LIBS="$BAKLIBS"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing dlopen" >&5
$as_echo_n "checking for library containing dlopen... " >&6; }
if ${ac_cv_search_dlopen+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char dlopen ();
int
main ()
{
return dlopen ();
;
return 0;
}
_ACEOF
for ac_lib in '' dl; do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_search_dlopen=$ac_res
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext
if ${ac_cv_search_dlopen+:} false; then :
break
fi
done
if ${ac_cv_search_dlopen+:} false; then :
else
ac_cv_search_dlopen=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_dlopen" >&5
$as_echo "$ac_cv_search_dlopen" >&6; }
ac_res=$ac_cv_search_dlopen
if test "$ac_res" != no; then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
fi
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
SSLLIB="-lssl"
PC_CRYPTO_DEPENDENCY=""
# check if -lcrypt32 is needed because CAPIENG needs that. (on windows)
BAKLIBS="$LIBS"
LIBS="-lssl $LIBS"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if libssl needs -lcrypt32" >&5
$as_echo_n "checking if libssl needs -lcrypt32... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
int EVP_sha256(void);
(void)EVP_sha256();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
LIBS="$BAKLIBS"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
LIBS="$BAKLIBS"
LIBS="$LIBS -lcrypt32"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for LibreSSL" >&5
$as_echo_n "checking for LibreSSL... " >&6; }
if grep VERSION_TEXT $ssldir_include/openssl/opensslv.h | grep "LibreSSL" >/dev/null; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
$as_echo "#define HAVE_LIBRESSL 1" >>confdefs.h
# libressl provides these compat functions, but they may also be
# declared by the OS in libc. See if they have been declared.
ac_fn_c_check_decl "$LINENO" "strlcpy" "ac_cv_have_decl_strlcpy" "$ac_includes_default"
if test "x$ac_cv_have_decl_strlcpy" = xyes; then :
ac_have_decl=1
else
ac_have_decl=0
fi
cat >>confdefs.h <<_ACEOF
#define HAVE_DECL_STRLCPY $ac_have_decl
_ACEOF
ac_fn_c_check_decl "$LINENO" "strlcat" "ac_cv_have_decl_strlcat" "$ac_includes_default"
if test "x$ac_cv_have_decl_strlcat" = xyes; then :
ac_have_decl=1
else
ac_have_decl=0
fi
cat >>confdefs.h <<_ACEOF
#define HAVE_DECL_STRLCAT $ac_have_decl
_ACEOF
ac_fn_c_check_decl "$LINENO" "arc4random" "ac_cv_have_decl_arc4random" "$ac_includes_default"
if test "x$ac_cv_have_decl_arc4random" = xyes; then :
ac_have_decl=1
else
ac_have_decl=0
fi
cat >>confdefs.h <<_ACEOF
#define HAVE_DECL_ARC4RANDOM $ac_have_decl
_ACEOF
ac_fn_c_check_decl "$LINENO" "arc4random_uniform" "ac_cv_have_decl_arc4random_uniform" "$ac_includes_default"
if test "x$ac_cv_have_decl_arc4random_uniform" = xyes; then :
ac_have_decl=1
else
ac_have_decl=0
fi
cat >>confdefs.h <<_ACEOF
#define HAVE_DECL_ARC4RANDOM_UNIFORM $ac_have_decl
_ACEOF
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
for ac_header in openssl/conf.h openssl/engine.h openssl/bn.h openssl/dh.h openssl/dsa.h openssl/rsa.h openssl/core_names.h openssl/param_build.h
do :
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
"
if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
cat >>confdefs.h <<_ACEOF
#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
_ACEOF
fi
done
for ac_func in OPENSSL_config EVP_sha1 EVP_sha256 EVP_sha512 FIPS_mode EVP_default_properties_is_fips_enabled EVP_MD_CTX_new OpenSSL_add_all_digests OPENSSL_init_crypto EVP_cleanup ENGINE_cleanup ERR_load_crypto_strings CRYPTO_cleanup_all_ex_data ERR_free_strings RAND_cleanup DSA_SIG_set0 EVP_dss1 EVP_DigestVerify EVP_aes_256_cbc EVP_EncryptInit_ex HMAC_Init_ex CRYPTO_THREADID_set_callback EVP_MAC_CTX_set_params OSSL_PARAM_BLD_new BIO_set_callback_ex
do :
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
cat >>confdefs.h <<_ACEOF
#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
_ACEOF
fi
done
# these check_funcs need -lssl
BAKLIBS="$LIBS"
LIBS="-lssl $LIBS"
for ac_func in OPENSSL_init_ssl SSL_CTX_set_security_level SSL_set1_host SSL_get0_peername X509_VERIFY_PARAM_set1_host SSL_CTX_set_ciphersuites SSL_CTX_set_tlsext_ticket_key_evp_cb SSL_CTX_set_alpn_select_cb SSL_get0_alpn_selected SSL_CTX_set_alpn_protos SSL_get1_peer_certificate
do :
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
cat >>confdefs.h <<_ACEOF
#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
_ACEOF
fi
done
LIBS="$BAKLIBS"
ac_fn_c_check_decl "$LINENO" "SSL_COMP_get_compression_methods" "ac_cv_have_decl_SSL_COMP_get_compression_methods" "
$ac_includes_default
#ifdef HAVE_OPENSSL_ERR_H
#include <openssl/err.h>
#endif
#ifdef HAVE_OPENSSL_RAND_H
#include <openssl/rand.h>
#endif
#ifdef HAVE_OPENSSL_CONF_H
#include <openssl/conf.h>
#endif
#ifdef HAVE_OPENSSL_ENGINE_H
#include <openssl/engine.h>
#endif
#include <openssl/ssl.h>
#include <openssl/evp.h>
"
if test "x$ac_cv_have_decl_SSL_COMP_get_compression_methods" = xyes; then :
ac_have_decl=1
else
ac_have_decl=0
fi
cat >>confdefs.h <<_ACEOF
#define HAVE_DECL_SSL_COMP_GET_COMPRESSION_METHODS $ac_have_decl
_ACEOF
ac_fn_c_check_decl "$LINENO" "sk_SSL_COMP_pop_free" "ac_cv_have_decl_sk_SSL_COMP_pop_free" "
$ac_includes_default
#ifdef HAVE_OPENSSL_ERR_H
#include <openssl/err.h>
#endif
#ifdef HAVE_OPENSSL_RAND_H
#include <openssl/rand.h>
#endif
#ifdef HAVE_OPENSSL_CONF_H
#include <openssl/conf.h>
#endif
#ifdef HAVE_OPENSSL_ENGINE_H
#include <openssl/engine.h>
#endif
#include <openssl/ssl.h>
#include <openssl/evp.h>
"
if test "x$ac_cv_have_decl_sk_SSL_COMP_pop_free" = xyes; then :
ac_have_decl=1
else
ac_have_decl=0
fi
cat >>confdefs.h <<_ACEOF
#define HAVE_DECL_SK_SSL_COMP_POP_FREE $ac_have_decl
_ACEOF
ac_fn_c_check_decl "$LINENO" "SSL_CTX_set_ecdh_auto" "ac_cv_have_decl_SSL_CTX_set_ecdh_auto" "
$ac_includes_default
#ifdef HAVE_OPENSSL_ERR_H
#include <openssl/err.h>
#endif
#ifdef HAVE_OPENSSL_RAND_H
#include <openssl/rand.h>
#endif
#ifdef HAVE_OPENSSL_CONF_H
#include <openssl/conf.h>
#endif
#ifdef HAVE_OPENSSL_ENGINE_H
#include <openssl/engine.h>
#endif
#include <openssl/ssl.h>
#include <openssl/evp.h>
"
if test "x$ac_cv_have_decl_SSL_CTX_set_ecdh_auto" = xyes; then :
ac_have_decl=1
else
ac_have_decl=0
fi
cat >>confdefs.h <<_ACEOF
#define HAVE_DECL_SSL_CTX_SET_ECDH_AUTO $ac_have_decl
_ACEOF
if test "$ac_cv_func_HMAC_Init_ex" = "yes"; then
# check function return type.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the return type of HMAC_Init_ex" >&5
$as_echo_n "checking the return type of HMAC_Init_ex... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#ifdef HAVE_OPENSSL_ERR_H
#include <openssl/err.h>
#endif
#ifdef HAVE_OPENSSL_RAND_H
#include <openssl/rand.h>
#endif
#ifdef HAVE_OPENSSL_CONF_H
#include <openssl/conf.h>
#endif
#ifdef HAVE_OPENSSL_ENGINE_H
#include <openssl/engine.h>
#endif
#include <openssl/ssl.h>
#include <openssl/evp.h>
int
main ()
{
HMAC_CTX* hmac_ctx = NULL;
void* hmac_key = NULL;
const EVP_MD* digest = NULL;
int x = HMAC_Init_ex(hmac_ctx, hmac_key, 32, digest, NULL);
(void)x;
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: int" >&5
$as_echo "int" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: void" >&5
$as_echo "void" >&6; }
$as_echo "#define HMAC_INIT_EX_RETURNS_VOID 1" >>confdefs.h
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
fi
# libbsd
# Check whether --with-libbsd was given.
if test "${with_libbsd+set}" = set; then :
withval=$with_libbsd;
for ac_header in bsd/string.h bsd/stdlib.h
do :
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
"
if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
cat >>confdefs.h <<_ACEOF
#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
_ACEOF
fi
done
if test "x$ac_cv_header_bsd_string_h" = xyes -a "x$ac_cv_header_bsd_stdlib_h" = xyes; then
for func in strlcpy strlcat arc4random arc4random_uniform reallocarray; do
as_ac_Search=`$as_echo "ac_cv_search_$func" | $as_tr_sh`
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing $func" >&5
$as_echo_n "checking for library containing $func... " >&6; }
if eval \${$as_ac_Search+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char $func ();
int
main ()
{
return $func ();
;
return 0;
}
_ACEOF
for ac_lib in '' bsd; do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
if ac_fn_c_try_link "$LINENO"; then :
eval "$as_ac_Search=\$ac_res"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext
if eval \${$as_ac_Search+:} false; then :
break
fi
done
if eval \${$as_ac_Search+:} false; then :
else
eval "$as_ac_Search=no"
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
eval ac_res=\$$as_ac_Search
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
eval ac_res=\$$as_ac_Search
if test "$ac_res" != no; then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
$as_echo "#define HAVE_LIBBSD 1" >>confdefs.h
PC_LIBBSD_DEPENDENCY=libbsd
fi
done
fi
fi
# Check whether --enable-sha1 was given.
if test "${enable_sha1+set}" = set; then :
enableval=$enable_sha1;
fi
case "$enable_sha1" in
no)
;;
yes|*)
$as_echo "#define USE_SHA1 1" >>confdefs.h
;;
esac
# Check whether --enable-sha2 was given.
if test "${enable_sha2+set}" = set; then :
enableval=$enable_sha2;
fi
case "$enable_sha2" in
no)
;;
yes|*)
$as_echo "#define USE_SHA2 1" >>confdefs.h
;;
esac
# Check whether --enable-subnet was given.
if test "${enable_subnet+set}" = set; then :
enableval=$enable_subnet;
fi
case "$enable_subnet" in
yes)
$as_echo "#define CLIENT_SUBNET 1" >>confdefs.h
SUBNET_OBJ="edns-subnet.lo subnetmod.lo addrtree.lo subnet-whitelist.lo"
SUBNET_HEADER='$(srcdir)/edns-subnet/subnetmod.h $(srcdir)/edns-subnet/edns-subnet.h $(srcdir)/edns-subnet/subnet-whitelist.h $(srcdir)/edns-subnet/addrtree.h'
;;
no|*)
;;
esac
# check whether gost also works
# Check whether --enable-gost was given.
if test "${enable_gost+set}" = set; then :
enableval=$enable_gost;
fi
use_gost="no"
if test $USE_NSS = "no" -a $USE_NETTLE = "no"; then
case "$enable_gost" in
no)
;;
*)
ac_fn_c_check_func "$LINENO" "EVP_PKEY_set_type_str" "ac_cv_func_EVP_PKEY_set_type_str"
if test "x$ac_cv_func_EVP_PKEY_set_type_str" = xyes; then :
:
else
as_fn_error $? "OpenSSL 1.0.0 is needed for GOST support" "$LINENO" 5
fi
ac_fn_c_check_func "$LINENO" "EC_KEY_new" "ac_cv_func_EC_KEY_new"
if test "x$ac_cv_func_EC_KEY_new" = xyes; then :
else
as_fn_error $? "OpenSSL does not support ECC, needed for GOST support" "$LINENO" 5
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if GOST works" >&5
$as_echo_n "checking if GOST works... " >&6; }
if test c${cross_compiling} = cno; then
BAKCFLAGS="$CFLAGS"
if test -n "$ssldir"; then
CFLAGS="$CFLAGS -Wl,-rpath,$ssldir_lib"
fi
if test "$cross_compiling" = yes; then :
- { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error $? "cannot run test program while cross compiling
-See \`config.log' for more details" "$LINENO" 5; }
+ eval "ac_cv_c_gost_works=maybe"
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <string.h>
#include <openssl/ssl.h>
#include <openssl/evp.h>
#include <openssl/engine.h>
#include <openssl/conf.h>
/* routine to load gost (from sldns) */
int load_gost_id(void)
{
static int gost_id = 0;
const EVP_PKEY_ASN1_METHOD* meth;
ENGINE* e;
if(gost_id) return gost_id;
/* see if configuration loaded gost implementation from other engine*/
meth = EVP_PKEY_asn1_find_str(NULL, "gost2001", -1);
if(meth) {
EVP_PKEY_asn1_get0_info(&gost_id, NULL, NULL, NULL, NULL, meth);
return gost_id;
}
/* see if engine can be loaded already */
e = ENGINE_by_id("gost");
if(!e) {
/* load it ourself, in case statically linked */
ENGINE_load_builtin_engines();
ENGINE_load_dynamic();
e = ENGINE_by_id("gost");
}
if(!e) {
/* no gost engine in openssl */
return 0;
}
if(!ENGINE_set_default(e, ENGINE_METHOD_ALL)) {
ENGINE_finish(e);
ENGINE_free(e);
return 0;
}
meth = EVP_PKEY_asn1_find_str(&e, "gost2001", -1);
if(!meth) {
/* algo not found */
ENGINE_finish(e);
ENGINE_free(e);
return 0;
}
EVP_PKEY_asn1_get0_info(&gost_id, NULL, NULL, NULL, NULL, meth);
return gost_id;
}
int main(void) {
EVP_MD_CTX* ctx;
const EVP_MD* md;
unsigned char digest[64]; /* its a 256-bit digest, so uses 32 bytes */
const char* str = "Hello world";
const unsigned char check[] = {
0x40 , 0xed , 0xf8 , 0x56 , 0x5a , 0xc5 , 0x36 , 0xe1 ,
0x33 , 0x7c , 0x7e , 0x87 , 0x62 , 0x1c , 0x42 , 0xe0 ,
0x17 , 0x1b , 0x5e , 0xce , 0xa8 , 0x46 , 0x65 , 0x4d ,
0x8d , 0x3e , 0x22 , 0x9b , 0xe1 , 0x30 , 0x19 , 0x9d
};
OPENSSL_config(NULL);
(void)load_gost_id();
md = EVP_get_digestbyname("md_gost94");
if(!md) return 1;
memset(digest, 0, sizeof(digest));
ctx = EVP_MD_CTX_create();
if(!ctx) return 2;
if(!EVP_DigestInit_ex(ctx, md, NULL)) return 3;
if(!EVP_DigestUpdate(ctx, str, 10)) return 4;
if(!EVP_DigestFinal_ex(ctx, digest, NULL)) return 5;
/* uncomment to see the hash calculated.
{int i;
for(i=0; i<32; i++)
printf(" %2.2x", (int)digest[i]);
printf("\n");}
*/
if(memcmp(digest, check, sizeof(check)) != 0)
return 6;
return 0;
}
_ACEOF
if ac_fn_c_try_run "$LINENO"; then :
eval "ac_cv_c_gost_works=yes"
else
eval "ac_cv_c_gost_works=no"
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
CFLAGS="$BAKCFLAGS"
else
eval "ac_cv_c_gost_works=maybe"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_gost_works" >&5
$as_echo "$ac_cv_c_gost_works" >&6; }
if test "$ac_cv_c_gost_works" != no; then
use_gost="yes"
$as_echo "#define USE_GOST 1" >>confdefs.h
fi
;;
esac
fi
# Check whether --enable-ecdsa was given.
if test "${enable_ecdsa+set}" = set; then :
enableval=$enable_ecdsa;
fi
use_ecdsa="no"
case "$enable_ecdsa" in
no)
;;
*)
if test $USE_NSS = "no" -a $USE_NETTLE = "no"; then
ac_fn_c_check_func "$LINENO" "ECDSA_sign" "ac_cv_func_ECDSA_sign"
if test "x$ac_cv_func_ECDSA_sign" = xyes; then :
else
as_fn_error $? "OpenSSL does not support ECDSA: please upgrade or rerun with --disable-ecdsa" "$LINENO" 5
fi
ac_fn_c_check_func "$LINENO" "SHA384_Init" "ac_cv_func_SHA384_Init"
if test "x$ac_cv_func_SHA384_Init" = xyes; then :
else
as_fn_error $? "OpenSSL does not support SHA384: please upgrade or rerun with --disable-ecdsa" "$LINENO" 5
fi
ac_fn_c_check_decl "$LINENO" "NID_X9_62_prime256v1" "ac_cv_have_decl_NID_X9_62_prime256v1" "$ac_includes_default
#include <openssl/evp.h>
"
if test "x$ac_cv_have_decl_NID_X9_62_prime256v1" = xyes; then :
ac_have_decl=1
else
ac_have_decl=0
fi
cat >>confdefs.h <<_ACEOF
#define HAVE_DECL_NID_X9_62_PRIME256V1 $ac_have_decl
_ACEOF
if test $ac_have_decl = 1; then :
else
as_fn_error $? "OpenSSL does not support the ECDSA curves: please upgrade or rerun with --disable-ecdsa" "$LINENO" 5
fi
ac_fn_c_check_decl "$LINENO" "NID_secp384r1" "ac_cv_have_decl_NID_secp384r1" "$ac_includes_default
#include <openssl/evp.h>
"
if test "x$ac_cv_have_decl_NID_secp384r1" = xyes; then :
ac_have_decl=1
else
ac_have_decl=0
fi
cat >>confdefs.h <<_ACEOF
#define HAVE_DECL_NID_SECP384R1 $ac_have_decl
_ACEOF
if test $ac_have_decl = 1; then :
else
as_fn_error $? "OpenSSL does not support the ECDSA curves: please upgrade or rerun with --disable-ecdsa" "$LINENO" 5
fi
# see if OPENSSL 1.0.0 or later (has EVP MD and Verify independency)
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if openssl supports SHA2 and ECDSA with EVP" >&5
$as_echo_n "checking if openssl supports SHA2 and ECDSA with EVP... " >&6; }
if grep OPENSSL_VERSION_TEXT $ssldir_include/openssl/opensslv.h | grep "OpenSSL" >/dev/null; then
if grep OPENSSL_VERSION_NUMBER $ssldir_include/openssl/opensslv.h | grep 0x0 >/dev/null; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
cat >>confdefs.h <<_ACEOF
#define USE_ECDSA_EVP_WORKAROUND 1
_ACEOF
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
fi
else
# not OpenSSL, thus likely LibreSSL, which supports it
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
fi
fi
# we now know we have ECDSA and the required curves.
cat >>confdefs.h <<_ACEOF
#define USE_ECDSA 1
_ACEOF
use_ecdsa="yes"
;;
esac
# Check whether --enable-dsa was given.
if test "${enable_dsa+set}" = set; then :
enableval=$enable_dsa;
fi
use_dsa="no"
case "$enable_dsa" in
yes)
# detect if DSA is supported, and turn it off if not.
if test $USE_NSS = "no" -a $USE_NETTLE = "no"; then
ac_fn_c_check_func "$LINENO" "DSA_SIG_new" "ac_cv_func_DSA_SIG_new"
if test "x$ac_cv_func_DSA_SIG_new" = xyes; then :
as_ac_Type=`$as_echo "ac_cv_type_DSA_SIG*" | $as_tr_sh`
ac_fn_c_check_type "$LINENO" "DSA_SIG*" "$as_ac_Type" "
$ac_includes_default
#ifdef HAVE_OPENSSL_ERR_H
#include <openssl/err.h>
#endif
#ifdef HAVE_OPENSSL_RAND_H
#include <openssl/rand.h>
#endif
#ifdef HAVE_OPENSSL_CONF_H
#include <openssl/conf.h>
#endif
#ifdef HAVE_OPENSSL_ENGINE_H
#include <openssl/engine.h>
#endif
"
if eval test \"x\$"$as_ac_Type"\" = x"yes"; then :
cat >>confdefs.h <<_ACEOF
#define USE_DSA 1
_ACEOF
else
if test "x$enable_dsa" = "xyes"; then as_fn_error $? "OpenSSL does not support DSA and you used --enable-dsa." "$LINENO" 5
fi
fi
else
if test "x$enable_dsa" = "xyes"; then as_fn_error $? "OpenSSL does not support DSA and you used --enable-dsa." "$LINENO" 5
fi
fi
else
cat >>confdefs.h <<_ACEOF
#define USE_DSA 1
_ACEOF
fi
;;
*)
# disable dsa by default, RFC 8624 section 3.1, validators MUST NOT
# support DSA for DNSSEC Validation.
;;
esac
# Check whether --with-deprecate-rsa-1024 was given.
if test "${with_deprecate_rsa_1024+set}" = set; then :
withval=$with_deprecate_rsa_1024;
fi
if test "$with_deprecate_rsa_1024" = "yes"; then
$as_echo "#define DEPRECATE_RSA_1024 1" >>confdefs.h
fi
# Check whether --enable-ed25519 was given.
if test "${enable_ed25519+set}" = set; then :
enableval=$enable_ed25519;
fi
use_ed25519="no"
case "$enable_ed25519" in
no)
;;
*)
if test $USE_NSS = "no" -a $USE_NETTLE = "no"; then
ac_fn_c_check_decl "$LINENO" "NID_ED25519" "ac_cv_have_decl_NID_ED25519" "$ac_includes_default
#include <openssl/evp.h>
"
if test "x$ac_cv_have_decl_NID_ED25519" = xyes; then :
ac_have_decl=1
else
ac_have_decl=0
fi
cat >>confdefs.h <<_ACEOF
#define HAVE_DECL_NID_ED25519 $ac_have_decl
_ACEOF
if test $ac_have_decl = 1; then :
use_ed25519="yes"
else
if test "x$enable_ed25519" = "xyes"; then as_fn_error $? "OpenSSL does not support ED25519 and you used --enable-ed25519." "$LINENO" 5
fi
fi
fi
if test $USE_NETTLE = "yes"; then
for ac_header in nettle/eddsa.h
do :
ac_fn_c_check_header_compile "$LINENO" "nettle/eddsa.h" "ac_cv_header_nettle_eddsa_h" "$ac_includes_default
"
if test "x$ac_cv_header_nettle_eddsa_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_NETTLE_EDDSA_H 1
_ACEOF
use_ed25519="yes"
fi
done
fi
if test $use_ed25519 = "yes"; then
cat >>confdefs.h <<_ACEOF
#define USE_ED25519 1
_ACEOF
fi
;;
esac
# Check whether --enable-ed448 was given.
if test "${enable_ed448+set}" = set; then :
enableval=$enable_ed448;
fi
use_ed448="no"
case "$enable_ed448" in
no)
;;
*)
if test $USE_NSS = "no" -a $USE_NETTLE = "no"; then
ac_fn_c_check_decl "$LINENO" "NID_ED448" "ac_cv_have_decl_NID_ED448" "$ac_includes_default
#include <openssl/evp.h>
"
if test "x$ac_cv_have_decl_NID_ED448" = xyes; then :
ac_have_decl=1
else
ac_have_decl=0
fi
cat >>confdefs.h <<_ACEOF
#define HAVE_DECL_NID_ED448 $ac_have_decl
_ACEOF
if test $ac_have_decl = 1; then :
use_ed448="yes"
else
if test "x$enable_ed448" = "xyes"; then as_fn_error $? "OpenSSL does not support ED448 and you used --enable-ed448." "$LINENO" 5
fi
fi
fi
if test $use_ed448 = "yes"; then
cat >>confdefs.h <<_ACEOF
#define USE_ED448 1
_ACEOF
fi
;;
esac
# Check whether --enable-event-api was given.
if test "${enable_event_api+set}" = set; then :
enableval=$enable_event_api;
fi
case "$enable_event_api" in
yes)
UNBOUND_EVENT_INSTALL=unbound-event-install
UNBOUND_EVENT_UNINSTALL=unbound-event-uninstall
;;
*)
;;
esac
# Check whether --enable-tfo-client was given.
if test "${enable_tfo_client+set}" = set; then :
enableval=$enable_tfo_client;
fi
case "$enable_tfo_client" in
yes)
case "$host_os" in
linux*) ac_fn_c_check_decl "$LINENO" "MSG_FASTOPEN" "ac_cv_have_decl_MSG_FASTOPEN" "$ac_includes_default
#include <netinet/tcp.h>
"
if test "x$ac_cv_have_decl_MSG_FASTOPEN" = xyes; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Check the platform specific TFO kernel parameters are correctly configured to support client mode TFO" >&5
$as_echo "$as_me: WARNING: Check the platform specific TFO kernel parameters are correctly configured to support client mode TFO" >&2;}
else
as_fn_error $? "TCP Fast Open is not available for client mode: please rerun without --enable-tfo-client" "$LINENO" 5
fi
cat >>confdefs.h <<_ACEOF
#define USE_MSG_FASTOPEN 1
_ACEOF
;;
darwin*) ac_fn_c_check_decl "$LINENO" "CONNECT_RESUME_ON_READ_WRITE" "ac_cv_have_decl_CONNECT_RESUME_ON_READ_WRITE" "$ac_includes_default
#include <sys/socket.h>
"
if test "x$ac_cv_have_decl_CONNECT_RESUME_ON_READ_WRITE" = xyes; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Check the platform specific TFO kernel parameters are correctly configured to support client mode TFO" >&5
$as_echo "$as_me: WARNING: Check the platform specific TFO kernel parameters are correctly configured to support client mode TFO" >&2;}
else
as_fn_error $? "TCP Fast Open is not available for client mode: please rerun without --enable-tfo-client" "$LINENO" 5
fi
cat >>confdefs.h <<_ACEOF
#define USE_OSX_MSG_FASTOPEN 1
_ACEOF
;;
esac
;;
no|*)
;;
esac
# Check whether --enable-tfo-server was given.
if test "${enable_tfo_server+set}" = set; then :
enableval=$enable_tfo_server;
fi
case "$enable_tfo_server" in
yes)
ac_fn_c_check_decl "$LINENO" "TCP_FASTOPEN" "ac_cv_have_decl_TCP_FASTOPEN" "$ac_includes_default
#include <netinet/tcp.h>
"
if test "x$ac_cv_have_decl_TCP_FASTOPEN" = xyes; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Check the platform specific TFO kernel parameters are correctly configured to support server mode TFO" >&5
$as_echo "$as_me: WARNING: Check the platform specific TFO kernel parameters are correctly configured to support server mode TFO" >&2;}
else
as_fn_error $? "TCP Fast Open is not available for server mode: please rerun without --enable-tfo-server" "$LINENO" 5
fi
cat >>confdefs.h <<_ACEOF
#define USE_TCP_FASTOPEN 1
_ACEOF
;;
no|*)
;;
esac
# check for libevent
# Check whether --with-libevent was given.
if test "${with_libevent+set}" = set; then :
withval=$with_libevent;
else
with_libevent="no"
fi
if test "x_$with_libevent" != x_no; then
$as_echo "#define USE_LIBEVENT 1" >>confdefs.h
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libevent" >&5
$as_echo_n "checking for libevent... " >&6; }
if test "x_$with_libevent" = x_ -o "x_$with_libevent" = x_yes; then
with_libevent="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr"
fi
for dir in $with_libevent; do
thedir="$dir"
if test -f "$dir/include/event.h" -o -f "$dir/include/event2/event.h"; then
found_libevent="yes"
if test "$thedir" != "/usr"; then
CPPFLAGS="$CPPFLAGS -I$thedir/include"
fi
break;
fi
done
if test x_$found_libevent != x_yes; then
if test -f "$dir/event.h" -a \( -f "$dir/libevent.la" -o -f "$dir/libev.la" \) ; then
# libevent source directory
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: found in $thedir" >&5
$as_echo "found in $thedir" >&6; }
CPPFLAGS="$CPPFLAGS -I$thedir -I$thedir/include"
BAK_LDFLAGS_SET="1"
BAK_LDFLAGS="$LDFLAGS"
# remove evdns from linking
mkdir build >/dev/null 2>&1
mkdir build/libevent >/dev/null 2>&1
mkdir build/libevent/.libs >/dev/null 2>&1
ev_files_o=`ls $thedir/*.o | grep -v evdns\.o | grep -v bufferevent_openssl\.o`
ev_files_lo=`ls $thedir/*.lo | grep -v evdns\.lo | grep -v bufferevent_openssl\.lo`
ev_files_libso=`ls $thedir/.libs/*.o | grep -v evdns\.o | grep -v bufferevent_openssl\.o`
cp $ev_files_o build/libevent
cp $ev_files_lo build/libevent
cp $ev_files_libso build/libevent/.libs
LATE_LDFLAGS="build/libevent/*.lo -lm"
LDFLAGS="build/libevent/*.o $LDFLAGS -lm"
else
as_fn_error $? "Cannot find the libevent library in $with_libevent
You can restart ./configure --with-libevent=no to use a builtin alternative.
Please note that this alternative is not as capable as libevent when using
large outgoing port ranges. " "$LINENO" 5
fi
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: found in $thedir" >&5
$as_echo "found in $thedir" >&6; }
if test ! -f $thedir/lib/libevent.a -a ! -f $thedir/lib/libevent.so -a -d "$thedir/lib/event2"; then
LDFLAGS="$LDFLAGS -L$thedir/lib/event2"
if test "x$enable_rpath" = xyes; then
if echo "$thedir/lib/event2" | grep "^/" >/dev/null; then
RUNTIME_PATH="$RUNTIME_PATH -R$thedir/lib/event2"
fi
fi
else
if test "$thedir" != "/usr" -a "$thedir" != ""; then
LDFLAGS="$LDFLAGS -L$thedir/lib"
if test "x$enable_rpath" = xyes; then
if echo "$thedir/lib" | grep "^/" >/dev/null; then
RUNTIME_PATH="$RUNTIME_PATH -R$thedir/lib"
fi
fi
fi
fi
fi
# check for library used by libevent after 1.3c
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5
$as_echo_n "checking for library containing clock_gettime... " >&6; }
if ${ac_cv_search_clock_gettime+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char clock_gettime ();
int
main ()
{
return clock_gettime ();
;
return 0;
}
_ACEOF
for ac_lib in '' rt; do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_search_clock_gettime=$ac_res
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext
if ${ac_cv_search_clock_gettime+:} false; then :
break
fi
done
if ${ac_cv_search_clock_gettime+:} false; then :
else
ac_cv_search_clock_gettime=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5
$as_echo "$ac_cv_search_clock_gettime" >&6; }
ac_res=$ac_cv_search_clock_gettime
if test "$ac_res" != no; then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
fi
# is the event.h header libev or libevent?
for ac_header in event.h
do :
ac_fn_c_check_header_compile "$LINENO" "event.h" "ac_cv_header_event_h" "$ac_includes_default
"
if test "x$ac_cv_header_event_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_EVENT_H 1
_ACEOF
fi
done
ac_fn_c_check_decl "$LINENO" "EV_VERSION_MAJOR" "ac_cv_have_decl_EV_VERSION_MAJOR" "$ac_includes_default
#include <event.h>
"
if test "x$ac_cv_have_decl_EV_VERSION_MAJOR" = xyes; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing event_set" >&5
$as_echo_n "checking for library containing event_set... " >&6; }
if ${ac_cv_search_event_set+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char event_set ();
int
main ()
{
return event_set ();
;
return 0;
}
_ACEOF
for ac_lib in '' ev; do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_search_event_set=$ac_res
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext
if ${ac_cv_search_event_set+:} false; then :
break
fi
done
if ${ac_cv_search_event_set+:} false; then :
else
ac_cv_search_event_set=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_event_set" >&5
$as_echo "$ac_cv_search_event_set" >&6; }
ac_res=$ac_cv_search_event_set
if test "$ac_res" != no; then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
fi
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing event_set" >&5
$as_echo_n "checking for library containing event_set... " >&6; }
if ${ac_cv_search_event_set+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char event_set ();
int
main ()
{
return event_set ();
;
return 0;
}
_ACEOF
for ac_lib in '' event; do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_search_event_set=$ac_res
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext
if ${ac_cv_search_event_set+:} false; then :
break
fi
done
if ${ac_cv_search_event_set+:} false; then :
else
ac_cv_search_event_set=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_event_set" >&5
$as_echo "$ac_cv_search_event_set" >&6; }
ac_res=$ac_cv_search_event_set
if test "$ac_res" != no; then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
fi
fi
for ac_func in event_base_free
do :
ac_fn_c_check_func "$LINENO" "event_base_free" "ac_cv_func_event_base_free"
if test "x$ac_cv_func_event_base_free" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_EVENT_BASE_FREE 1
_ACEOF
fi
done
# only in libevent 1.2 and later
for ac_func in event_base_once
do :
ac_fn_c_check_func "$LINENO" "event_base_once" "ac_cv_func_event_base_once"
if test "x$ac_cv_func_event_base_once" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_EVENT_BASE_ONCE 1
_ACEOF
fi
done
# only in libevent 1.4.1 and later
for ac_func in event_base_new
do :
ac_fn_c_check_func "$LINENO" "event_base_new" "ac_cv_func_event_base_new"
if test "x$ac_cv_func_event_base_new" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_EVENT_BASE_NEW 1
_ACEOF
fi
done
# only in libevent 1.4.1 and later
for ac_func in event_base_get_method
do :
ac_fn_c_check_func "$LINENO" "event_base_get_method" "ac_cv_func_event_base_get_method"
if test "x$ac_cv_func_event_base_get_method" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_EVENT_BASE_GET_METHOD 1
_ACEOF
fi
done
# only in libevent 1.4.3 and later
for ac_func in ev_loop
do :
ac_fn_c_check_func "$LINENO" "ev_loop" "ac_cv_func_ev_loop"
if test "x$ac_cv_func_ev_loop" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_EV_LOOP 1
_ACEOF
fi
done
# only in libev. (tested on 3.51)
for ac_func in ev_default_loop
do :
ac_fn_c_check_func "$LINENO" "ev_default_loop" "ac_cv_func_ev_default_loop"
if test "x$ac_cv_func_ev_default_loop" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_EV_DEFAULT_LOOP 1
_ACEOF
fi
done
# only in libev. (tested on 4.00)
for ac_func in event_assign
do :
ac_fn_c_check_func "$LINENO" "event_assign" "ac_cv_func_event_assign"
if test "x$ac_cv_func_event_assign" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_EVENT_ASSIGN 1
_ACEOF
fi
done
# in libevent, for thread-safety
ac_fn_c_check_decl "$LINENO" "evsignal_assign" "ac_cv_have_decl_evsignal_assign" "$ac_includes_default
#ifdef HAVE_EVENT_H
# include <event.h>
#else
# include \"event2/event.h\"
#endif
"
if test "x$ac_cv_have_decl_evsignal_assign" = xyes; then :
ac_have_decl=1
else
ac_have_decl=0
fi
cat >>confdefs.h <<_ACEOF
#define HAVE_DECL_EVSIGNAL_ASSIGN $ac_have_decl
_ACEOF
PC_LIBEVENT_DEPENDENCY="libevent"
if test -n "$BAK_LDFLAGS_SET"; then
LDFLAGS="$BAK_LDFLAGS"
fi
else
$as_echo "#define USE_MINI_EVENT 1" >>confdefs.h
fi
# check for libexpat
# Check whether --with-libexpat was given.
if test "${with_libexpat+set}" = set; then :
withval=$with_libexpat;
else
withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libexpat" >&5
$as_echo_n "checking for libexpat... " >&6; }
found_libexpat="no"
for dir in $withval ; do
if test -f "$dir/include/expat.h"; then
found_libexpat="yes"
if test "$dir" != "/usr"; then
CPPFLAGS="$CPPFLAGS -I$dir/include"
LDFLAGS="$LDFLAGS -L$dir/lib"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: found in $dir" >&5
$as_echo "found in $dir" >&6; }
break;
fi
done
if test x_$found_libexpat != x_yes; then
as_fn_error $? "Could not find libexpat, expat.h" "$LINENO" 5
fi
for ac_header in expat.h
do :
ac_fn_c_check_header_compile "$LINENO" "expat.h" "ac_cv_header_expat_h" "$ac_includes_default
"
if test "x$ac_cv_header_expat_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_EXPAT_H 1
_ACEOF
fi
done
ac_fn_c_check_decl "$LINENO" "XML_StopParser" "ac_cv_have_decl_XML_StopParser" "$ac_includes_default
#include <expat.h>
"
if test "x$ac_cv_have_decl_XML_StopParser" = xyes; then :
ac_have_decl=1
else
ac_have_decl=0
fi
cat >>confdefs.h <<_ACEOF
#define HAVE_DECL_XML_STOPPARSER $ac_have_decl
_ACEOF
# hiredis (redis C client for cachedb)
# Check whether --with-libhiredis was given.
if test "${with_libhiredis+set}" = set; then :
withval=$with_libhiredis;
else
withval="no"
fi
found_libhiredis="no"
if test x_$withval = x_yes -o x_$withval != x_no; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libhiredis" >&5
$as_echo_n "checking for libhiredis... " >&6; }
if test x_$withval = x_ -o x_$withval = x_yes; then
withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr"
fi
for dir in $withval ; do
if test -f "$dir/include/hiredis/hiredis.h"; then
found_libhiredis="yes"
if test "$dir" != "/usr"; then
CPPFLAGS="$CPPFLAGS -I$dir/include"
LDFLAGS="$LDFLAGS -L$dir/lib"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: found in $dir" >&5
$as_echo "found in $dir" >&6; }
$as_echo "#define USE_REDIS 1" >>confdefs.h
LIBS="$LIBS -lhiredis"
break;
fi
done
if test x_$found_libhiredis != x_yes; then
as_fn_error $? "Could not find libhiredis, hiredis.h" "$LINENO" 5
fi
for ac_header in hiredis/hiredis.h
do :
ac_fn_c_check_header_compile "$LINENO" "hiredis/hiredis.h" "ac_cv_header_hiredis_hiredis_h" "$ac_includes_default
"
if test "x$ac_cv_header_hiredis_hiredis_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_HIREDIS_HIREDIS_H 1
_ACEOF
fi
done
ac_fn_c_check_decl "$LINENO" "redisConnect" "ac_cv_have_decl_redisConnect" "$ac_includes_default
#include <hiredis/hiredis.h>
"
if test "x$ac_cv_have_decl_redisConnect" = xyes; then :
ac_have_decl=1
else
ac_have_decl=0
fi
cat >>confdefs.h <<_ACEOF
#define HAVE_DECL_REDISCONNECT $ac_have_decl
_ACEOF
fi
# nghttp2
# Check whether --with-libnghttp2 was given.
if test "${with_libnghttp2+set}" = set; then :
withval=$with_libnghttp2;
else
withval="no"
fi
found_libnghttp2="no"
if test x_$withval = x_yes -o x_$withval != x_no; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libnghttp2" >&5
$as_echo_n "checking for libnghttp2... " >&6; }
if test x_$withval = x_ -o x_$withval = x_yes; then
withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr"
fi
for dir in $withval ; do
if test -f "$dir/include/nghttp2/nghttp2.h"; then
found_libnghttp2="yes"
if test "$dir" != "/usr"; then
CPPFLAGS="$CPPFLAGS -I$dir/include"
LDFLAGS="$LDFLAGS -L$dir/lib"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: found in $dir" >&5
$as_echo "found in $dir" >&6; }
$as_echo "#define HAVE_NGHTTP2 1" >>confdefs.h
LIBS="$LIBS -lnghttp2"
break;
fi
done
if test x_$found_libnghttp2 != x_yes; then
as_fn_error $? "Could not find libnghttp2, nghttp2.h" "$LINENO" 5
fi
for ac_header in nghttp2/nghttp2.h
do :
ac_fn_c_check_header_compile "$LINENO" "nghttp2/nghttp2.h" "ac_cv_header_nghttp2_nghttp2_h" "$ac_includes_default
"
if test "x$ac_cv_header_nghttp2_nghttp2_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_NGHTTP2_NGHTTP2_H 1
_ACEOF
fi
done
ac_fn_c_check_decl "$LINENO" "nghttp2_session_server_new" "ac_cv_have_decl_nghttp2_session_server_new" "$ac_includes_default
#include <nghttp2/nghttp2.h>
"
if test "x$ac_cv_have_decl_nghttp2_session_server_new" = xyes; then :
ac_have_decl=1
else
ac_have_decl=0
fi
cat >>confdefs.h <<_ACEOF
#define HAVE_DECL_NGHTTP2_SESSION_SERVER_NEW $ac_have_decl
_ACEOF
fi
# set static linking for uninstalled libraries if requested
staticexe=""
# Check whether --enable-static-exe was given.
if test "${enable_static_exe+set}" = set; then :
enableval=$enable_static_exe;
fi
if test x_$enable_static_exe = x_yes; then
staticexe="-static"
if test "$on_mingw" = yes; then
staticexe="-all-static"
# for static compile, include gdi32 and zlib here.
if echo $LIBS | grep 'lgdi32' >/dev/null; then
:
else
LIBS="$LIBS -lgdi32"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for compress in -lz" >&5
$as_echo_n "checking for compress in -lz... " >&6; }
if ${ac_cv_lib_z_compress+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lz $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char compress ();
int
main ()
{
return compress ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_z_compress=yes
else
ac_cv_lib_z_compress=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_z_compress" >&5
$as_echo "$ac_cv_lib_z_compress" >&6; }
if test "x$ac_cv_lib_z_compress" = xyes; then :
LIBS="$LIBS -lz"
fi
LIBS="$LIBS -l:libssp.a"
fi
fi
# set full static linking if requested
# Check whether --enable-fully-static was given.
if test "${enable_fully_static+set}" = set; then :
enableval=$enable_fully_static;
fi
if test x_$enable_fully_static = x_yes; then
staticexe="-all-static"
if test "$on_mingw" = yes; then
# for static compile, include gdi32 and zlib here.
if echo $LIBS | grep 'lgdi32' >/dev/null; then
:
else
LIBS="$LIBS -lgdi32"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for compress in -lz" >&5
$as_echo_n "checking for compress in -lz... " >&6; }
if ${ac_cv_lib_z_compress+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lz $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char compress ();
int
main ()
{
return compress ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_z_compress=yes
else
ac_cv_lib_z_compress=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_z_compress" >&5
$as_echo "$ac_cv_lib_z_compress" >&6; }
if test "x$ac_cv_lib_z_compress" = xyes; then :
LIBS="$LIBS -lz"
fi
LIBS="$LIBS -l:libssp.a"
fi
fi
# set lock checking if requested
# Check whether --enable-lock_checks was given.
if test "${enable_lock_checks+set}" = set; then :
enableval=$enable_lock_checks;
fi
if test x_$enable_lock_checks = x_yes; then
$as_echo "#define ENABLE_LOCK_CHECKS 1" >>confdefs.h
CHECKLOCK_OBJ="checklocks.lo"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for getaddrinfo" >&5
$as_echo_n "checking for getaddrinfo... " >&6; }
ac_cv_func_getaddrinfo=no
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#ifdef __cplusplus
extern "C"
{
#endif
char* getaddrinfo();
char* (*f) () = getaddrinfo;
#ifdef __cplusplus
}
#endif
int main(void) {
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_func_getaddrinfo="yes"
if test "$ac_cv_header_windows_h" = "yes"; then
$as_echo "#define USE_WINSOCK 1" >>confdefs.h
USE_WINSOCK="1"
if echo $LIBS | grep 'lws2_32' >/dev/null; then
:
else
LIBS="$LIBS -lws2_32"
fi
fi
else
ORIGLIBS="$LIBS"
LIBS="$LIBS -lws2_32"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#ifdef HAVE_WS2TCPIP_H
#include <ws2tcpip.h>
#endif
int
main ()
{
(void)getaddrinfo(NULL, NULL, NULL, NULL);
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_func_getaddrinfo="yes"
$as_echo "#define USE_WINSOCK 1" >>confdefs.h
USE_WINSOCK="1"
else
ac_cv_func_getaddrinfo="no"
LIBS="$ORIGLIBS"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_getaddrinfo" >&5
$as_echo "$ac_cv_func_getaddrinfo" >&6; }
if test $ac_cv_func_getaddrinfo = yes; then
$as_echo "#define HAVE_GETADDRINFO 1" >>confdefs.h
fi
if test "$USE_WINSOCK" = 1; then
$as_echo "#define UB_ON_WINDOWS 1" >>confdefs.h
for ac_header in iphlpapi.h
do :
ac_fn_c_check_header_compile "$LINENO" "iphlpapi.h" "ac_cv_header_iphlpapi_h" "$ac_includes_default
#include <windows.h>
"
if test "x$ac_cv_header_iphlpapi_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_IPHLPAPI_H 1
_ACEOF
fi
done
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}windres", so it can be a program name with args.
set dummy ${ac_tool_prefix}windres; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_WINDRES+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$WINDRES"; then
ac_cv_prog_WINDRES="$WINDRES" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_WINDRES="${ac_tool_prefix}windres"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
WINDRES=$ac_cv_prog_WINDRES
if test -n "$WINDRES"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $WINDRES" >&5
$as_echo "$WINDRES" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
if test -z "$ac_cv_prog_WINDRES"; then
ac_ct_WINDRES=$WINDRES
# Extract the first word of "windres", so it can be a program name with args.
set dummy windres; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ac_ct_WINDRES+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_WINDRES"; then
ac_cv_prog_ac_ct_WINDRES="$ac_ct_WINDRES" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_WINDRES="windres"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
ac_ct_WINDRES=$ac_cv_prog_ac_ct_WINDRES
if test -n "$ac_ct_WINDRES"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_WINDRES" >&5
$as_echo "$ac_ct_WINDRES" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test "x$ac_ct_WINDRES" = x; then
WINDRES=""
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
WINDRES=$ac_ct_WINDRES
fi
else
WINDRES="$ac_cv_prog_WINDRES"
fi
LIBS="$LIBS -liphlpapi -lcrypt32"
WINAPPS="unbound-service-install.exe unbound-service-remove.exe anchor-update.exe"
WIN_DAEMON_SRC="winrc/win_svc.c winrc/w_inst.c"
WIN_DAEMON_OBJ="win_svc.lo w_inst.lo"
WIN_DAEMON_OBJ_LINK="rsrc_unbound.o"
WIN_HOST_OBJ_LINK="rsrc_unbound_host.o"
WIN_UBANCHOR_OBJ_LINK="rsrc_unbound_anchor.o log.lo locks.lo"
WIN_CONTROL_OBJ_LINK="rsrc_unbound_control.o"
WIN_CHECKCONF_OBJ_LINK="rsrc_unbound_checkconf.o"
$as_echo "#define __USE_MINGW_ANSI_STDIO 1" >>confdefs.h
fi
if test $ac_cv_func_getaddrinfo = no; then
case " $LIBOBJS " in
*" fake-rfc2553.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS fake-rfc2553.$ac_objext"
;;
esac
fi
# check after getaddrinfo for its libraries
# check ioctlsocket
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ioctlsocket" >&5
$as_echo_n "checking for ioctlsocket... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#ifdef HAVE_WINSOCK2_H
#include <winsock2.h>
#endif
int
main ()
{
(void)ioctlsocket(0, 0, NULL);
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
$as_echo "#define HAVE_IOCTLSOCKET 1" >>confdefs.h
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
# see if daemon(3) exists, and if it is deprecated.
for ac_func in daemon
do :
ac_fn_c_check_func "$LINENO" "daemon" "ac_cv_func_daemon"
if test "x$ac_cv_func_daemon" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_DAEMON 1
_ACEOF
fi
done
if test $ac_cv_func_daemon = yes; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if daemon is deprecated" >&5
$as_echo_n "checking if daemon is deprecated... " >&6; }
cache=`echo daemon | sed 'y%.=/+-%___p_%'`
if eval \${cv_cc_deprecated_$cache+:} false; then :
$as_echo_n "(cached) " >&6
else
echo '
#include <stdlib.h>
#include <unistd.h>
' >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
eval "cv_cc_deprecated_$cache=yes"
fi
rm -f conftest conftest.o conftest.c
fi
if eval "test \"`echo '$cv_cc_deprecated_'$cache`\" = yes"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
cat >>confdefs.h <<_ACEOF
#define DEPRECATED_DAEMON 1
_ACEOF
:
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
:
fi
fi
ac_fn_c_check_member "$LINENO" "struct sockaddr_un" "sun_len" "ac_cv_member_struct_sockaddr_un_sun_len" "
$ac_includes_default
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif
"
if test "x$ac_cv_member_struct_sockaddr_un_sun_len" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_STRUCT_SOCKADDR_UN_SUN_LEN 1
_ACEOF
fi
ac_fn_c_check_member "$LINENO" "struct in_pktinfo" "ipi_spec_dst" "ac_cv_member_struct_in_pktinfo_ipi_spec_dst" "
$ac_includes_default
#if HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_SYS_UIO_H
#include <sys/uio.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_WINSOCK2_H
#include <winsock2.h>
#endif
#ifdef HAVE_WS2TCPIP_H
#include <ws2tcpip.h>
#endif
"
if test "x$ac_cv_member_struct_in_pktinfo_ipi_spec_dst" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_STRUCT_IN_PKTINFO_IPI_SPEC_DST 1
_ACEOF
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for htobe64" >&5
$as_echo_n "checking for htobe64... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdio.h>
#ifdef HAVE_ENDIAN_H
# include <endian.h>
#endif
#ifdef HAVE_SYS_ENDIAN_H
# include <sys/endian.h>
#endif
int
main ()
{
unsigned long long x = htobe64(0); printf("%u", (unsigned)x);
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
$as_echo "#define HAVE_HTOBE64 1" >>confdefs.h
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for be64toh" >&5
$as_echo_n "checking for be64toh... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdio.h>
#ifdef HAVE_ENDIAN_H
# include <endian.h>
#endif
#ifdef HAVE_SYS_ENDIAN_H
# include <sys/endian.h>
#endif
int
main ()
{
unsigned long long x = be64toh(0); printf("%u", (unsigned)x);
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
$as_echo "#define HAVE_BE64TOH 1" >>confdefs.h
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing setusercontext" >&5
$as_echo_n "checking for library containing setusercontext... " >&6; }
if ${ac_cv_search_setusercontext+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char setusercontext ();
int
main ()
{
return setusercontext ();
;
return 0;
}
_ACEOF
for ac_lib in '' util; do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_search_setusercontext=$ac_res
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext
if ${ac_cv_search_setusercontext+:} false; then :
break
fi
done
if ${ac_cv_search_setusercontext+:} false; then :
else
ac_cv_search_setusercontext=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_setusercontext" >&5
$as_echo "$ac_cv_search_setusercontext" >&6; }
ac_res=$ac_cv_search_setusercontext
if test "$ac_res" != no; then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
fi
for ac_func in tzset sigprocmask fcntl getpwnam endpwent getrlimit setrlimit setsid chroot kill chown sleep usleep random srandom recvmsg sendmsg writev socketpair glob initgroups strftime localtime_r setusercontext _beginthreadex endservent endprotoent fsync shmget accept4 getifaddrs if_nametoindex poll gettid
do :
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
cat >>confdefs.h <<_ACEOF
#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
_ACEOF
fi
done
for ac_func in setresuid
do :
ac_fn_c_check_func "$LINENO" "setresuid" "ac_cv_func_setresuid"
if test "x$ac_cv_func_setresuid" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_SETRESUID 1
_ACEOF
else
for ac_func in setreuid
do :
ac_fn_c_check_func "$LINENO" "setreuid" "ac_cv_func_setreuid"
if test "x$ac_cv_func_setreuid" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_SETREUID 1
_ACEOF
fi
done
fi
done
for ac_func in setresgid
do :
ac_fn_c_check_func "$LINENO" "setresgid" "ac_cv_func_setresgid"
if test "x$ac_cv_func_setresgid" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_SETRESGID 1
_ACEOF
else
for ac_func in setregid
do :
ac_fn_c_check_func "$LINENO" "setregid" "ac_cv_func_setregid"
if test "x$ac_cv_func_setregid" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_SETREGID 1
_ACEOF
fi
done
fi
done
# check if setreuid en setregid fail, on MacOSX10.4(darwin8).
if echo $host_os | grep darwin8 > /dev/null; then
$as_echo "#define DARWIN_BROKEN_SETREUID 1" >>confdefs.h
fi
ac_fn_c_check_decl "$LINENO" "inet_pton" "ac_cv_have_decl_inet_pton" "
$ac_includes_default
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_WINSOCK2_H
#include <winsock2.h>
#endif
#ifdef HAVE_WS2TCPIP_H
#include <ws2tcpip.h>
#endif
"
if test "x$ac_cv_have_decl_inet_pton" = xyes; then :
ac_have_decl=1
else
ac_have_decl=0
fi
cat >>confdefs.h <<_ACEOF
#define HAVE_DECL_INET_PTON $ac_have_decl
_ACEOF
ac_fn_c_check_decl "$LINENO" "inet_ntop" "ac_cv_have_decl_inet_ntop" "
$ac_includes_default
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_WINSOCK2_H
#include <winsock2.h>
#endif
#ifdef HAVE_WS2TCPIP_H
#include <ws2tcpip.h>
#endif
"
if test "x$ac_cv_have_decl_inet_ntop" = xyes; then :
ac_have_decl=1
else
ac_have_decl=0
fi
cat >>confdefs.h <<_ACEOF
#define HAVE_DECL_INET_NTOP $ac_have_decl
_ACEOF
ac_fn_c_check_func "$LINENO" "inet_aton" "ac_cv_func_inet_aton"
if test "x$ac_cv_func_inet_aton" = xyes; then :
$as_echo "#define HAVE_INET_ATON 1" >>confdefs.h
else
case " $LIBOBJS " in
*" inet_aton.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS inet_aton.$ac_objext"
;;
esac
fi
ac_fn_c_check_func "$LINENO" "inet_pton" "ac_cv_func_inet_pton"
if test "x$ac_cv_func_inet_pton" = xyes; then :
$as_echo "#define HAVE_INET_PTON 1" >>confdefs.h
else
case " $LIBOBJS " in
*" inet_pton.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS inet_pton.$ac_objext"
;;
esac
fi
ac_fn_c_check_func "$LINENO" "inet_ntop" "ac_cv_func_inet_ntop"
if test "x$ac_cv_func_inet_ntop" = xyes; then :
$as_echo "#define HAVE_INET_NTOP 1" >>confdefs.h
else
case " $LIBOBJS " in
*" inet_ntop.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS inet_ntop.$ac_objext"
;;
esac
fi
ac_fn_c_check_func "$LINENO" "snprintf" "ac_cv_func_snprintf"
if test "x$ac_cv_func_snprintf" = xyes; then :
$as_echo "#define HAVE_SNPRINTF 1" >>confdefs.h
else
case " $LIBOBJS " in
*" snprintf.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS snprintf.$ac_objext"
;;
esac
fi
# test if snprintf return the proper length
if test "x$ac_cv_func_snprintf" = xyes; then
if test c${cross_compiling} = cno; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for correct snprintf return value" >&5
$as_echo_n "checking for correct snprintf return value... " >&6; }
if test "$cross_compiling" = yes; then :
- { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error $? "cannot run test program while cross compiling
-See \`config.log' for more details" "$LINENO" 5; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: maybe" >&5
+$as_echo "maybe" >&6; }
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$ac_includes_default
int main(void) { return !(snprintf(NULL, 0, "test") == 4); }
_ACEOF
if ac_fn_c_try_run "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
$as_echo "#define SNPRINTF_RET_BROKEN /**/" >>confdefs.h
case " $LIBOBJS " in
*" snprintf.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS snprintf.$ac_objext"
;;
esac
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
fi
fi
ac_fn_c_check_func "$LINENO" "strlcat" "ac_cv_func_strlcat"
if test "x$ac_cv_func_strlcat" = xyes; then :
$as_echo "#define HAVE_STRLCAT 1" >>confdefs.h
else
case " $LIBOBJS " in
*" strlcat.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS strlcat.$ac_objext"
;;
esac
fi
ac_fn_c_check_func "$LINENO" "strlcpy" "ac_cv_func_strlcpy"
if test "x$ac_cv_func_strlcpy" = xyes; then :
$as_echo "#define HAVE_STRLCPY 1" >>confdefs.h
else
case " $LIBOBJS " in
*" strlcpy.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS strlcpy.$ac_objext"
;;
esac
fi
ac_fn_c_check_func "$LINENO" "memmove" "ac_cv_func_memmove"
if test "x$ac_cv_func_memmove" = xyes; then :
$as_echo "#define HAVE_MEMMOVE 1" >>confdefs.h
else
case " $LIBOBJS " in
*" memmove.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS memmove.$ac_objext"
;;
esac
fi
ac_fn_c_check_func "$LINENO" "gmtime_r" "ac_cv_func_gmtime_r"
if test "x$ac_cv_func_gmtime_r" = xyes; then :
$as_echo "#define HAVE_GMTIME_R 1" >>confdefs.h
else
case " $LIBOBJS " in
*" gmtime_r.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS gmtime_r.$ac_objext"
;;
esac
fi
ac_fn_c_check_func "$LINENO" "isblank" "ac_cv_func_isblank"
if test "x$ac_cv_func_isblank" = xyes; then :
$as_echo "#define HAVE_ISBLANK 1" >>confdefs.h
else
case " $LIBOBJS " in
*" isblank.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS isblank.$ac_objext"
;;
esac
fi
ac_fn_c_check_func "$LINENO" "explicit_bzero" "ac_cv_func_explicit_bzero"
if test "x$ac_cv_func_explicit_bzero" = xyes; then :
$as_echo "#define HAVE_EXPLICIT_BZERO 1" >>confdefs.h
else
case " $LIBOBJS " in
*" explicit_bzero.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS explicit_bzero.$ac_objext"
;;
esac
fi
LIBOBJ_WITHOUT_CTIMEARC4="$LIBOBJS"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for reallocarray" >&5
$as_echo_n "checking for reallocarray... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$ac_includes_default
#ifndef _OPENBSD_SOURCE
#define _OPENBSD_SOURCE 1
#endif
#include <stdlib.h>
int main(void) {
void* p = reallocarray(NULL, 10, 100);
free(p);
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
$as_echo "#define HAVE_REALLOCARRAY 1" >>confdefs.h
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
case " $LIBOBJS " in
*" reallocarray.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS reallocarray.$ac_objext"
;;
esac
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
ac_fn_c_check_decl "$LINENO" "reallocarray" "ac_cv_have_decl_reallocarray" "$ac_includes_default"
if test "x$ac_cv_have_decl_reallocarray" = xyes; then :
ac_have_decl=1
else
ac_have_decl=0
fi
cat >>confdefs.h <<_ACEOF
#define HAVE_DECL_REALLOCARRAY $ac_have_decl
_ACEOF
if test "$USE_NSS" = "no"; then
ac_fn_c_check_func "$LINENO" "arc4random" "ac_cv_func_arc4random"
if test "x$ac_cv_func_arc4random" = xyes; then :
$as_echo "#define HAVE_ARC4RANDOM 1" >>confdefs.h
else
case " $LIBOBJS " in
*" arc4random.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS arc4random.$ac_objext"
;;
esac
fi
ac_fn_c_check_func "$LINENO" "arc4random_uniform" "ac_cv_func_arc4random_uniform"
if test "x$ac_cv_func_arc4random_uniform" = xyes; then :
$as_echo "#define HAVE_ARC4RANDOM_UNIFORM 1" >>confdefs.h
else
case " $LIBOBJS " in
*" arc4random_uniform.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS arc4random_uniform.$ac_objext"
;;
esac
fi
if test "$ac_cv_func_arc4random" = "no"; then
case " $LIBOBJS " in
*" arc4_lock.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS arc4_lock.$ac_objext"
;;
esac
for ac_func in getentropy
do :
ac_fn_c_check_func "$LINENO" "getentropy" "ac_cv_func_getentropy"
if test "x$ac_cv_func_getentropy" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_GETENTROPY 1
_ACEOF
else
if test "$USE_WINSOCK" = 1; then
case " $LIBOBJS " in
*" getentropy_win.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS getentropy_win.$ac_objext"
;;
esac
else
case "$host" in
Darwin|*darwin*)
case " $LIBOBJS " in
*" getentropy_osx.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS getentropy_osx.$ac_objext"
;;
esac
;;
*solaris*|*sunos*|SunOS)
case " $LIBOBJS " in
*" getentropy_solaris.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS getentropy_solaris.$ac_objext"
;;
esac
for ac_header in sys/sha2.h
do :
ac_fn_c_check_header_compile "$LINENO" "sys/sha2.h" "ac_cv_header_sys_sha2_h" "$ac_includes_default
"
if test "x$ac_cv_header_sys_sha2_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_SYS_SHA2_H 1
_ACEOF
else
for ac_func in SHA512_Update
do :
ac_fn_c_check_func "$LINENO" "SHA512_Update" "ac_cv_func_SHA512_Update"
if test "x$ac_cv_func_SHA512_Update" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_SHA512_UPDATE 1
_ACEOF
else
case " $LIBOBJS " in
*" sha512.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS sha512.$ac_objext"
;;
esac
fi
done
fi
done
if test "$ac_cv_header_sys_sha2_h" = "yes"; then
# this lib needed for sha2 on solaris
LIBS="$LIBS -lmd"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5
$as_echo_n "checking for library containing clock_gettime... " >&6; }
if ${ac_cv_search_clock_gettime+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char clock_gettime ();
int
main ()
{
return clock_gettime ();
;
return 0;
}
_ACEOF
for ac_lib in '' rt; do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_search_clock_gettime=$ac_res
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext
if ${ac_cv_search_clock_gettime+:} false; then :
break
fi
done
if ${ac_cv_search_clock_gettime+:} false; then :
else
ac_cv_search_clock_gettime=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5
$as_echo "$ac_cv_search_clock_gettime" >&6; }
ac_res=$ac_cv_search_clock_gettime
if test "$ac_res" != no; then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
fi
;;
*freebsd*|*FreeBSD)
case " $LIBOBJS " in
*" getentropy_freebsd.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS getentropy_freebsd.$ac_objext"
;;
esac
;;
*linux*|Linux|*)
case " $LIBOBJS " in
*" getentropy_linux.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS getentropy_linux.$ac_objext"
;;
esac
for ac_func in SHA512_Update
do :
ac_fn_c_check_func "$LINENO" "SHA512_Update" "ac_cv_func_SHA512_Update"
if test "x$ac_cv_func_SHA512_Update" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_SHA512_UPDATE 1
_ACEOF
else
$as_echo "#define COMPAT_SHA512 1" >>confdefs.h
case " $LIBOBJS " in
*" sha512.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS sha512.$ac_objext"
;;
esac
fi
done
for ac_header in sys/sysctl.h
do :
ac_fn_c_check_header_compile "$LINENO" "sys/sysctl.h" "ac_cv_header_sys_sysctl_h" "$ac_includes_default
"
if test "x$ac_cv_header_sys_sysctl_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_SYS_SYSCTL_H 1
_ACEOF
fi
done
for ac_func in getauxval
do :
ac_fn_c_check_func "$LINENO" "getauxval" "ac_cv_func_getauxval"
if test "x$ac_cv_func_getauxval" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_GETAUXVAL 1
_ACEOF
fi
done
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5
$as_echo_n "checking for library containing clock_gettime... " >&6; }
if ${ac_cv_search_clock_gettime+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char clock_gettime ();
int
main ()
{
return clock_gettime ();
;
return 0;
}
_ACEOF
for ac_lib in '' rt; do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_search_clock_gettime=$ac_res
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext
if ${ac_cv_search_clock_gettime+:} false; then :
break
fi
done
if ${ac_cv_search_clock_gettime+:} false; then :
else
ac_cv_search_clock_gettime=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5
$as_echo "$ac_cv_search_clock_gettime" >&6; }
ac_res=$ac_cv_search_clock_gettime
if test "$ac_res" != no; then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
fi
;;
esac
fi
fi
done
fi
fi
LIBOBJ_WITHOUT_CTIME="$LIBOBJS"
ac_fn_c_check_func "$LINENO" "ctime_r" "ac_cv_func_ctime_r"
if test "x$ac_cv_func_ctime_r" = xyes; then :
$as_echo "#define HAVE_CTIME_R 1" >>confdefs.h
else
case " $LIBOBJS " in
*" ctime_r.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS ctime_r.$ac_objext"
;;
esac
fi
ac_fn_c_check_func "$LINENO" "strsep" "ac_cv_func_strsep"
if test "x$ac_cv_func_strsep" = xyes; then :
$as_echo "#define HAVE_STRSEP 1" >>confdefs.h
else
case " $LIBOBJS " in
*" strsep.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS strsep.$ac_objext"
;;
esac
fi
# Check whether --enable-allsymbols was given.
if test "${enable_allsymbols+set}" = set; then :
enableval=$enable_allsymbols;
fi
case "$enable_allsymbols" in
yes)
COMMON_OBJ_ALL_SYMBOLS=""
UBSYMS=""
EXTRALINK="libunbound.la"
$as_echo "#define EXPORT_ALL_SYMBOLS 1" >>confdefs.h
;;
no|*)
COMMON_OBJ_ALL_SYMBOLS='$(COMMON_OBJ)'
UBSYMS='-export-symbols $(srcdir)/libunbound/ubsyms.def'
EXTRALINK=""
;;
esac
if test x_$enable_lock_checks = x_yes; then
UBSYMS="-export-symbols clubsyms.def"
cp ${srcdir}/libunbound/ubsyms.def clubsyms.def
echo lock_protect >> clubsyms.def
echo lock_unprotect >> clubsyms.def
echo lock_get_mem >> clubsyms.def
echo checklock_start >> clubsyms.def
echo checklock_stop >> clubsyms.def
echo checklock_lock >> clubsyms.def
echo checklock_unlock >> clubsyms.def
echo checklock_init >> clubsyms.def
echo checklock_thrcreate >> clubsyms.def
echo checklock_thrjoin >> clubsyms.def
fi
# check for dnstap if requested
# Check whether --enable-dnstap was given.
if test "${enable_dnstap+set}" = set; then :
enableval=$enable_dnstap; opt_dnstap=$enableval
else
opt_dnstap=no
fi
# Check whether --with-dnstap-socket-path was given.
if test "${with_dnstap_socket_path+set}" = set; then :
withval=$with_dnstap_socket_path; opt_dnstap_socket_path=$withval
else
opt_dnstap_socket_path="$UNBOUND_RUN_DIR/dnstap.sock"
fi
if test "x$opt_dnstap" != "xno"; then
# Extract the first word of "protoc-c", so it can be a program name with args.
set dummy protoc-c; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_path_PROTOC_C+:} false; then :
$as_echo_n "(cached) " >&6
else
case $PROTOC_C in
[\\/]* | ?:[\\/]*)
ac_cv_path_PROTOC_C="$PROTOC_C" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_path_PROTOC_C="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
;;
esac
fi
PROTOC_C=$ac_cv_path_PROTOC_C
if test -n "$PROTOC_C"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $PROTOC_C" >&5
$as_echo "$PROTOC_C" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test -z "$PROTOC_C"; then
as_fn_error $? "The protoc-c program was not found. Please install protobuf-c!" "$LINENO" 5
fi
# Check whether --with-protobuf-c was given.
if test "${with_protobuf_c+set}" = set; then :
withval=$with_protobuf_c;
# workaround for protobuf-c includes at old dir before protobuf-c-1.0.0
if test -f $withval/include/google/protobuf-c/protobuf-c.h; then
CFLAGS="$CFLAGS -I$withval/include/google"
else
CFLAGS="$CFLAGS -I$withval/include"
fi
LDFLAGS="$LDFLAGS -L$withval/lib"
else
# workaround for protobuf-c includes at old dir before protobuf-c-1.0.0
if test -f /usr/include/google/protobuf-c/protobuf-c.h; then
CFLAGS="$CFLAGS -I/usr/include/google"
else
if test -f /usr/local/include/google/protobuf-c/protobuf-c.h; then
CFLAGS="$CFLAGS -I/usr/local/include/google"
LDFLAGS="$LDFLAGS -L/usr/local/lib"
fi
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing protobuf_c_message_pack" >&5
$as_echo_n "checking for library containing protobuf_c_message_pack... " >&6; }
if ${ac_cv_search_protobuf_c_message_pack+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char protobuf_c_message_pack ();
int
main ()
{
return protobuf_c_message_pack ();
;
return 0;
}
_ACEOF
for ac_lib in '' protobuf-c; do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_search_protobuf_c_message_pack=$ac_res
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext
if ${ac_cv_search_protobuf_c_message_pack+:} false; then :
break
fi
done
if ${ac_cv_search_protobuf_c_message_pack+:} false; then :
else
ac_cv_search_protobuf_c_message_pack=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_protobuf_c_message_pack" >&5
$as_echo "$ac_cv_search_protobuf_c_message_pack" >&6; }
ac_res=$ac_cv_search_protobuf_c_message_pack
if test "$ac_res" != no; then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
else
- as_fn_error $? "The protobuf-c library was not found. Please install protobuf-c!" "$LINENO" 5
+ as_fn_error $? "The protobuf-c library was not found. Please install the development libraries for protobuf-c!" "$LINENO" 5
fi
$as_echo "#define USE_DNSTAP 1" >>confdefs.h
ENABLE_DNSTAP=1
hdr_dnstap_socket_path="`echo $opt_dnstap_socket_path | sed -e 's/\\\\/\\\\\\\\/g'`"
cat >>confdefs.h <<_ACEOF
#define DNSTAP_SOCKET_PATH "$hdr_dnstap_socket_path"
_ACEOF
DNSTAP_SOCKET_PATH="$hdr_dnstap_socket_path"
DNSTAP_SOCKET_TESTBIN='unbound-dnstap-socket$(EXEEXT)'
DNSTAP_SRC="dnstap/dnstap.c dnstap/dnstap.pb-c.c dnstap/dnstap_fstrm.c dnstap/dtstream.c"
DNSTAP_OBJ="dnstap.lo dnstap.pb-c.lo dnstap_fstrm.lo dtstream.lo"
else
ENABLE_DNSTAP=0
fi
# check for dnscrypt if requested
# Check whether --enable-dnscrypt was given.
if test "${enable_dnscrypt+set}" = set; then :
enableval=$enable_dnscrypt; opt_dnscrypt=$enableval
else
opt_dnscrypt=no
fi
if test "x$opt_dnscrypt" != "xno"; then
# Check whether --with-libsodium was given.
if test "${with_libsodium+set}" = set; then :
withval=$with_libsodium;
CFLAGS="$CFLAGS -I$withval/include"
LDFLAGS="$LDFLAGS -L$withval/lib"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing sodium_init" >&5
$as_echo_n "checking for library containing sodium_init... " >&6; }
if ${ac_cv_search_sodium_init+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char sodium_init ();
int
main ()
{
return sodium_init ();
;
return 0;
}
_ACEOF
for ac_lib in '' sodium; do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_search_sodium_init=$ac_res
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext
if ${ac_cv_search_sodium_init+:} false; then :
break
fi
done
if ${ac_cv_search_sodium_init+:} false; then :
else
ac_cv_search_sodium_init=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_sodium_init" >&5
$as_echo "$ac_cv_search_sodium_init" >&6; }
ac_res=$ac_cv_search_sodium_init
if test "$ac_res" != no; then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
else
as_fn_error $? "The sodium library was not found. Please install sodium!" "$LINENO" 5
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing crypto_box_curve25519xchacha20poly1305_beforenm" >&5
$as_echo_n "checking for library containing crypto_box_curve25519xchacha20poly1305_beforenm... " >&6; }
if ${ac_cv_search_crypto_box_curve25519xchacha20poly1305_beforenm+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char crypto_box_curve25519xchacha20poly1305_beforenm ();
int
main ()
{
return crypto_box_curve25519xchacha20poly1305_beforenm ();
;
return 0;
}
_ACEOF
for ac_lib in '' sodium; do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_search_crypto_box_curve25519xchacha20poly1305_beforenm=$ac_res
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext
if ${ac_cv_search_crypto_box_curve25519xchacha20poly1305_beforenm+:} false; then :
break
fi
done
if ${ac_cv_search_crypto_box_curve25519xchacha20poly1305_beforenm+:} false; then :
else
ac_cv_search_crypto_box_curve25519xchacha20poly1305_beforenm=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_crypto_box_curve25519xchacha20poly1305_beforenm" >&5
$as_echo "$ac_cv_search_crypto_box_curve25519xchacha20poly1305_beforenm" >&6; }
ac_res=$ac_cv_search_crypto_box_curve25519xchacha20poly1305_beforenm
if test "$ac_res" != no; then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
ENABLE_DNSCRYPT_XCHACHA20=1
$as_echo "#define USE_DNSCRYPT_XCHACHA20 1" >>confdefs.h
else
ENABLE_DNSCRYPT_XCHACHA20=0
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing sodium_set_misuse_handler" >&5
$as_echo_n "checking for library containing sodium_set_misuse_handler... " >&6; }
if ${ac_cv_search_sodium_set_misuse_handler+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char sodium_set_misuse_handler ();
int
main ()
{
return sodium_set_misuse_handler ();
;
return 0;
}
_ACEOF
for ac_lib in '' sodium; do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_search_sodium_set_misuse_handler=$ac_res
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext
if ${ac_cv_search_sodium_set_misuse_handler+:} false; then :
break
fi
done
if ${ac_cv_search_sodium_set_misuse_handler+:} false; then :
else
ac_cv_search_sodium_set_misuse_handler=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_sodium_set_misuse_handler" >&5
$as_echo "$ac_cv_search_sodium_set_misuse_handler" >&6; }
ac_res=$ac_cv_search_sodium_set_misuse_handler
if test "$ac_res" != no; then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
$as_echo "#define SODIUM_MISUSE_HANDLER 1" >>confdefs.h
fi
$as_echo "#define USE_DNSCRYPT 1" >>confdefs.h
ENABLE_DNSCRYPT=1
DNSCRYPT_SRC="dnscrypt/dnscrypt.c"
DNSCRYPT_OBJ="dnscrypt.lo"
else
ENABLE_DNSCRYPT_XCHACHA20=0
ENABLE_DNSCRYPT=0
fi
# check for cachedb if requested
# Check whether --enable-cachedb was given.
if test "${enable_cachedb+set}" = set; then :
enableval=$enable_cachedb;
fi
# turn on cachedb when hiredis support is enabled.
if test "$found_libhiredis" = "yes"; then enable_cachedb="yes"; fi
case "$enable_cachedb" in
yes)
$as_echo "#define USE_CACHEDB 1" >>confdefs.h
CACHEDB_SRC="cachedb/cachedb.c cachedb/redis.c"
CACHEDB_OBJ="cachedb.lo redis.lo"
;;
no|*)
# nothing
;;
esac
# check for ipsecmod if requested
# Check whether --enable-ipsecmod was given.
if test "${enable_ipsecmod+set}" = set; then :
enableval=$enable_ipsecmod;
fi
case "$enable_ipsecmod" in
yes)
$as_echo "#define USE_IPSECMOD 1" >>confdefs.h
IPSECMOD_OBJ="ipsecmod.lo ipsecmod-whitelist.lo"
IPSECMOD_HEADER='$(srcdir)/ipsecmod/ipsecmod.h $(srcdir)/ipsecmod/ipsecmod-whitelist.h'
;;
no|*)
# nothing
;;
esac
# check for ipset if requested
# Check whether --enable-ipset was given.
if test "${enable_ipset+set}" = set; then :
enableval=$enable_ipset;
fi
case "$enable_ipset" in
yes)
$as_echo "#define USE_IPSET 1" >>confdefs.h
IPSET_SRC="ipset/ipset.c"
IPSET_OBJ="ipset.lo"
# mnl
# Check whether --with-libmnl was given.
if test "${with_libmnl+set}" = set; then :
withval=$with_libmnl;
else
withval="yes"
fi
found_libmnl="no"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libmnl" >&5
$as_echo_n "checking for libmnl... " >&6; }
if test x_$withval = x_ -o x_$withval = x_yes; then
withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr"
fi
for dir in $withval ; do
if test -f "$dir/include/libmnl/libmnl.h" -o -f "$dir/include/libmnl/libmnl/libmnl.h"; then
found_libmnl="yes"
extralibmnl=""
if test -f "$dir/include/libmnl/libmnl/libmnl.h"; then
extralibmnl="/libmnl"
fi
if test "$dir" != "/usr" -o -n "$extralibmnl"; then
CPPFLAGS="$CPPFLAGS -I$dir/include$extralibmnl"
fi
if test "$dir" != "/usr"; then
LDFLAGS="$LDFLAGS -L$dir/lib"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: found in $dir" >&5
$as_echo "found in $dir" >&6; }
LIBS="$LIBS -lmnl"
break;
fi
done
if test x_$found_libmnl != x_yes; then
as_fn_error $? "Could not find libmnl, libmnl.h" "$LINENO" 5
fi
;;
no|*)
# nothing
;;
esac
# Check whether --enable-explicit-port-randomisation was given.
if test "${enable_explicit_port_randomisation+set}" = set; then :
enableval=$enable_explicit_port_randomisation;
fi
case "$enable_explicit_port_randomisation" in
no)
$as_echo "#define DISABLE_EXPLICIT_PORT_RANDOMISATION 1" >>confdefs.h
;;
yes|*)
;;
esac
if echo "$host" | $GREP -i -e linux >/dev/null; then
# Check whether --enable-linux-ip-local-port-range was given.
if test "${enable_linux_ip_local_port_range+set}" = set; then :
enableval=$enable_linux_ip_local_port_range;
fi
case "$enable_linux_ip_local_port_range" in
yes)
$as_echo "#define USE_LINUX_IP_LOCAL_PORT_RANGE 1" >>confdefs.h
;;
no|*)
;;
esac
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if ${MAKE:-make} supports $< with implicit rule in scope" >&5
$as_echo_n "checking if ${MAKE:-make} supports $< with implicit rule in scope... " >&6; }
# on openBSD, the implicit rule make $< work.
# on Solaris, it does not work ($? is changed sources, $^ lists dependencies).
# gmake works.
cat >conftest.make <<EOF
all: conftest.lo
conftest.lo foo.lo bla.lo:
if test -f "\$<"; then touch \$@; fi
.SUFFIXES: .lo
.c.lo:
if test -f "\$<"; then touch \$@; fi
conftest.lo: conftest.dir/conftest.c
EOF
mkdir conftest.dir
touch conftest.dir/conftest.c
rm -f conftest.lo conftest.c
${MAKE:-make} -f conftest.make >/dev/null
rm -f conftest.make conftest.c conftest.dir/conftest.c
rm -rf conftest.dir
if test ! -f conftest.lo; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
SOURCEDETERMINE='echo "$^" | awk "-F " "{print \$$1;}" > .source'
SOURCEFILE='`cat .source`'
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
SOURCEDETERMINE=':'
SOURCEFILE='$<'
fi
rm -f conftest.lo
# see if we want to build the library or everything
ALLTARGET="alltargets"
INSTALLTARGET="install-all"
# Check whether --with-libunbound-only was given.
if test "${with_libunbound_only+set}" = set; then :
withval=$with_libunbound_only;
if test "$withval" = "yes"; then
ALLTARGET="lib"
INSTALLTARGET="install-lib"
fi
fi
if test $ALLTARGET = "alltargets"; then
if test $USE_NSS = "yes"; then
as_fn_error $? "--with-nss can only be used in combination with --with-libunbound-only." "$LINENO" 5
fi
if test $USE_NETTLE = "yes"; then
as_fn_error $? "--with-nettle can only be used in combination with --with-libunbound-only." "$LINENO" 5
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: Stripping extension flags..." >&5
$as_echo "$as_me: Stripping extension flags..." >&6;}
if echo $CFLAGS | grep " -D_GNU_SOURCE" >/dev/null 2>&1; then
CFLAGS="`echo $CFLAGS | sed -e 's/ -D_GNU_SOURCE//g'`"
$as_echo "#define OMITTED__D_GNU_SOURCE 1" >>confdefs.h
fi
if echo $CFLAGS | grep " -D_BSD_SOURCE" >/dev/null 2>&1; then
CFLAGS="`echo $CFLAGS | sed -e 's/ -D_BSD_SOURCE//g'`"
$as_echo "#define OMITTED__D_BSD_SOURCE 1" >>confdefs.h
fi
if echo $CFLAGS | grep " -D_DEFAULT_SOURCE" >/dev/null 2>&1; then
CFLAGS="`echo $CFLAGS | sed -e 's/ -D_DEFAULT_SOURCE//g'`"
$as_echo "#define OMITTED__D_DEFAULT_SOURCE 1" >>confdefs.h
fi
if echo $CFLAGS | grep " -D__EXTENSIONS__" >/dev/null 2>&1; then
CFLAGS="`echo $CFLAGS | sed -e 's/ -D__EXTENSIONS__//g'`"
$as_echo "#define OMITTED__D__EXTENSIONS__ 1" >>confdefs.h
fi
if echo $CFLAGS | grep " -D_POSIX_C_SOURCE=200112" >/dev/null 2>&1; then
CFLAGS="`echo $CFLAGS | sed -e 's/ -D_POSIX_C_SOURCE=200112//g'`"
$as_echo "#define OMITTED__D_POSIX_C_SOURCE_200112 1" >>confdefs.h
fi
if echo $CFLAGS | grep " -D_XOPEN_SOURCE=600" >/dev/null 2>&1; then
CFLAGS="`echo $CFLAGS | sed -e 's/ -D_XOPEN_SOURCE=600//g'`"
$as_echo "#define OMITTED__D_XOPEN_SOURCE_600 1" >>confdefs.h
fi
if echo $CFLAGS | grep " -D_XOPEN_SOURCE_EXTENDED=1" >/dev/null 2>&1; then
CFLAGS="`echo $CFLAGS | sed -e 's/ -D_XOPEN_SOURCE_EXTENDED=1//g'`"
$as_echo "#define OMITTED__D_XOPEN_SOURCE_EXTENDED_1 1" >>confdefs.h
fi
if echo $CFLAGS | grep " -D_ALL_SOURCE" >/dev/null 2>&1; then
CFLAGS="`echo $CFLAGS | sed -e 's/ -D_ALL_SOURCE//g'`"
$as_echo "#define OMITTED__D_ALL_SOURCE 1" >>confdefs.h
fi
if echo $CFLAGS | grep " -D_LARGEFILE_SOURCE=1" >/dev/null 2>&1; then
CFLAGS="`echo $CFLAGS | sed -e 's/ -D_LARGEFILE_SOURCE=1//g'`"
$as_echo "#define OMITTED__D_LARGEFILE_SOURCE_1 1" >>confdefs.h
fi
if test -n "$LATE_LDFLAGS"; then
LDFLAGS="$LATE_LDFLAGS $LDFLAGS"
fi
# remove start spaces
LDFLAGS=`echo "$LDFLAGS"|sed -e 's/^ *//'`
LIBS=`echo "$LIBS"|sed -e 's/^ *//'`
cat >>confdefs.h <<_ACEOF
#define MAXSYSLOGMSGLEN 10240
_ACEOF
-version=1.18.0
+version=1.19.0
date=`date +'%b %e, %Y'`
ac_config_files="$ac_config_files Makefile doc/example.conf doc/libunbound.3 doc/unbound.8 doc/unbound-anchor.8 doc/unbound-checkconf.8 doc/unbound.conf.5 doc/unbound-control.8 doc/unbound-host.1 smallapp/unbound-control-setup.sh dnstap/dnstap_config.h dnscrypt/dnscrypt_config.h contrib/libunbound.pc contrib/unbound.socket contrib/unbound.service contrib/unbound_portable.service"
ac_config_headers="$ac_config_headers config.h"
cat >confcache <<\_ACEOF
# This file is a shell script that caches the results of configure
# tests run on this system so they can be shared between configure
# scripts and configure runs, see configure's option --config-cache.
# It is not useful on other systems. If it contains results you don't
# want to keep, you may remove or edit it.
#
# config.status only pays attention to the cache file if you give it
# the --recheck option to rerun configure.
#
# `ac_cv_env_foo' variables (set or unset) will be overridden when
# loading this file, other *unset* `ac_cv_foo' will be assigned the
# following values.
_ACEOF
# The following way of writing the cache mishandles newlines in values,
# but we know of no workaround that is simple, portable, and efficient.
# So, we kill variables containing newlines.
# Ultrix sh set writes to stderr and can't be redirected directly,
# and sets the high bit in the cache file unless we assign to the vars.
(
for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
eval ac_val=\$$ac_var
case $ac_val in #(
*${as_nl}*)
case $ac_var in #(
*_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
esac
case $ac_var in #(
_ | IFS | as_nl) ;; #(
BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
*) { eval $ac_var=; unset $ac_var;} ;;
esac ;;
esac
done
(set) 2>&1 |
case $as_nl`(ac_space=' '; set) 2>&1` in #(
*${as_nl}ac_space=\ *)
# `set' does not quote correctly, so add quotes: double-quote
# substitution turns \\\\ into \\, and sed turns \\ into \.
sed -n \
"s/'/'\\\\''/g;
s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
;; #(
*)
# `set' quotes correctly as required by POSIX, so do not add quotes.
sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
;;
esac |
sort
) |
sed '
/^ac_cv_env_/b end
t clear
:clear
s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
t end
s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
:end' >>confcache
if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
if test -w "$cache_file"; then
if test "x$cache_file" != "x/dev/null"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
$as_echo "$as_me: updating cache $cache_file" >&6;}
if test ! -f "$cache_file" || test -h "$cache_file"; then
cat confcache >"$cache_file"
else
case $cache_file in #(
*/* | ?:*)
mv -f confcache "$cache_file"$$ &&
mv -f "$cache_file"$$ "$cache_file" ;; #(
*)
mv -f confcache "$cache_file" ;;
esac
fi
fi
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
fi
fi
rm -f confcache
test "x$prefix" = xNONE && prefix=$ac_default_prefix
# Let make expand exec_prefix.
test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
DEFS=-DHAVE_CONFIG_H
ac_libobjs=
ac_ltlibobjs=
U=
for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
# 1. Remove the extension, and $U if already installed.
ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
# 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR
# will be set to the directory where LIBOBJS objects are built.
as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
done
LIBOBJS=$ac_libobjs
LTLIBOBJS=$ac_ltlibobjs
if test -z "${USE_SYSTEMD_TRUE}" && test -z "${USE_SYSTEMD_FALSE}"; then
as_fn_error $? "conditional \"USE_SYSTEMD\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
fi
: "${CONFIG_STATUS=./config.status}"
ac_write_fail=0
ac_clean_files_save=$ac_clean_files
ac_clean_files="$ac_clean_files $CONFIG_STATUS"
{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
as_write_fail=0
cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
#! $SHELL
# Generated by $as_me.
# Run this file to recreate the current configuration.
# Compiler output produced by configure, useful for debugging
# configure, is in config.log if it exists.
debug=false
ac_cs_recheck=false
ac_cs_silent=false
SHELL=\${CONFIG_SHELL-$SHELL}
export SHELL
_ASEOF
cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
## -------------------- ##
## M4sh Initialization. ##
## -------------------- ##
# Be more Bourne compatible
DUALCASE=1; export DUALCASE # for MKS sh
if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
emulate sh
NULLCMD=:
# Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
# is contrary to our usage. Disable this feature.
alias -g '${1+"$@"}'='"$@"'
setopt NO_GLOB_SUBST
else
case `(set -o) 2>/dev/null` in #(
*posix*) :
set -o posix ;; #(
*) :
;;
esac
fi
as_nl='
'
export as_nl
# Printing a long string crashes Solaris 7 /usr/bin/printf.
as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
# Prefer a ksh shell builtin over an external printf program on Solaris,
# but without wasting forks for bash or zsh.
if test -z "$BASH_VERSION$ZSH_VERSION" \
&& (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
as_echo='print -r --'
as_echo_n='print -rn --'
elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
as_echo='printf %s\n'
as_echo_n='printf %s'
else
if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
as_echo_n='/usr/ucb/echo -n'
else
as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
as_echo_n_body='eval
arg=$1;
case $arg in #(
*"$as_nl"*)
expr "X$arg" : "X\\(.*\\)$as_nl";
arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
esac;
expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
'
export as_echo_n_body
as_echo_n='sh -c $as_echo_n_body as_echo'
fi
export as_echo_body
as_echo='sh -c $as_echo_body as_echo'
fi
# The user is always right.
if test "${PATH_SEPARATOR+set}" != set; then
PATH_SEPARATOR=:
(PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
(PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
PATH_SEPARATOR=';'
}
fi
# IFS
# We need space, tab and new line, in precisely that order. Quoting is
# there to prevent editors from complaining about space-tab.
# (If _AS_PATH_WALK were called with IFS unset, it would disable word
# splitting by setting IFS to empty value.)
IFS=" "" $as_nl"
# Find who we are. Look in the path if we contain no directory separator.
as_myself=
case $0 in #((
*[\\/]* ) as_myself=$0 ;;
*) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
done
IFS=$as_save_IFS
;;
esac
# We did not find ourselves, most probably we were run as `sh COMMAND'
# in which case we are not to be found in the path.
if test "x$as_myself" = x; then
as_myself=$0
fi
if test ! -f "$as_myself"; then
$as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
exit 1
fi
# Unset variables that we do not need and which cause bugs (e.g. in
# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
# suppresses any "Segmentation fault" message there. '((' could
# trigger a bug in pdksh 5.2.14.
for as_var in BASH_ENV ENV MAIL MAILPATH
do eval test x\${$as_var+set} = xset \
&& ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
done
PS1='$ '
PS2='> '
PS4='+ '
# NLS nuisances.
LC_ALL=C
export LC_ALL
LANGUAGE=C
export LANGUAGE
# CDPATH.
(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
# as_fn_error STATUS ERROR [LINENO LOG_FD]
# ----------------------------------------
# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
# script with STATUS, using 1 if that was 0.
as_fn_error ()
{
as_status=$1; test $as_status -eq 0 && as_status=1
if test "$4"; then
as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
$as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
fi
$as_echo "$as_me: error: $2" >&2
as_fn_exit $as_status
} # as_fn_error
# as_fn_set_status STATUS
# -----------------------
# Set $? to STATUS, without forking.
as_fn_set_status ()
{
return $1
} # as_fn_set_status
# as_fn_exit STATUS
# -----------------
# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
as_fn_exit ()
{
set +e
as_fn_set_status $1
exit $1
} # as_fn_exit
# as_fn_unset VAR
# ---------------
# Portably unset VAR.
as_fn_unset ()
{
{ eval $1=; unset $1;}
}
as_unset=as_fn_unset
# as_fn_append VAR VALUE
# ----------------------
# Append the text in VALUE to the end of the definition contained in VAR. Take
# advantage of any shell optimizations that allow amortized linear growth over
# repeated appends, instead of the typical quadratic growth present in naive
# implementations.
if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
eval 'as_fn_append ()
{
eval $1+=\$2
}'
else
as_fn_append ()
{
eval $1=\$$1\$2
}
fi # as_fn_append
# as_fn_arith ARG...
# ------------------
# Perform arithmetic evaluation on the ARGs, and store the result in the
# global $as_val. Take advantage of shells that can avoid forks. The arguments
# must be portable across $(()) and expr.
if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
eval 'as_fn_arith ()
{
as_val=$(( $* ))
}'
else
as_fn_arith ()
{
as_val=`expr "$@" || test $? -eq 1`
}
fi # as_fn_arith
if expr a : '\(a\)' >/dev/null 2>&1 &&
test "X`expr 00001 : '.*\(...\)'`" = X001; then
as_expr=expr
else
as_expr=false
fi
if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
as_basename=basename
else
as_basename=false
fi
if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
as_dirname=dirname
else
as_dirname=false
fi
as_me=`$as_basename -- "$0" ||
$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
X"$0" : 'X\(//\)$' \| \
X"$0" : 'X\(/\)' \| . 2>/dev/null ||
$as_echo X/"$0" |
sed '/^.*\/\([^/][^/]*\)\/*$/{
s//\1/
q
}
/^X\/\(\/\/\)$/{
s//\1/
q
}
/^X\/\(\/\).*/{
s//\1/
q
}
s/.*/./; q'`
# Avoid depending upon Character Ranges.
as_cr_letters='abcdefghijklmnopqrstuvwxyz'
as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
as_cr_Letters=$as_cr_letters$as_cr_LETTERS
as_cr_digits='0123456789'
as_cr_alnum=$as_cr_Letters$as_cr_digits
ECHO_C= ECHO_N= ECHO_T=
case `echo -n x` in #(((((
-n*)
case `echo 'xy\c'` in
*c*) ECHO_T=' ';; # ECHO_T is single tab character.
xy) ECHO_C='\c';;
*) echo `echo ksh88 bug on AIX 6.1` > /dev/null
ECHO_T=' ';;
esac;;
*)
ECHO_N='-n';;
esac
rm -f conf$$ conf$$.exe conf$$.file
if test -d conf$$.dir; then
rm -f conf$$.dir/conf$$.file
else
rm -f conf$$.dir
mkdir conf$$.dir 2>/dev/null
fi
if (echo >conf$$.file) 2>/dev/null; then
if ln -s conf$$.file conf$$ 2>/dev/null; then
as_ln_s='ln -s'
# ... but there are two gotchas:
# 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
# 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
# In both cases, we have to default to `cp -pR'.
ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
as_ln_s='cp -pR'
elif ln conf$$.file conf$$ 2>/dev/null; then
as_ln_s=ln
else
as_ln_s='cp -pR'
fi
else
as_ln_s='cp -pR'
fi
rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
rmdir conf$$.dir 2>/dev/null
# as_fn_mkdir_p
# -------------
# Create "$as_dir" as a directory, including parents if necessary.
as_fn_mkdir_p ()
{
case $as_dir in #(
-*) as_dir=./$as_dir;;
esac
test -d "$as_dir" || eval $as_mkdir_p || {
as_dirs=
while :; do
case $as_dir in #(
*\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
*) as_qdir=$as_dir;;
esac
as_dirs="'$as_qdir' $as_dirs"
as_dir=`$as_dirname -- "$as_dir" ||
$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
X"$as_dir" : 'X\(//\)[^/]' \| \
X"$as_dir" : 'X\(//\)$' \| \
X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
$as_echo X"$as_dir" |
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
s//\1/
q
}
/^X\(\/\/\)[^/].*/{
s//\1/
q
}
/^X\(\/\/\)$/{
s//\1/
q
}
/^X\(\/\).*/{
s//\1/
q
}
s/.*/./; q'`
test -d "$as_dir" && break
done
test -z "$as_dirs" || eval "mkdir $as_dirs"
} || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
} # as_fn_mkdir_p
if mkdir -p . 2>/dev/null; then
as_mkdir_p='mkdir -p "$as_dir"'
else
test -d ./-p && rmdir ./-p
as_mkdir_p=false
fi
# as_fn_executable_p FILE
# -----------------------
# Test if FILE is an executable regular file.
as_fn_executable_p ()
{
test -f "$1" && test -x "$1"
} # as_fn_executable_p
as_test_x='test -x'
as_executable_p=as_fn_executable_p
# Sed expression to map a string onto a valid CPP name.
as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
# Sed expression to map a string onto a valid variable name.
as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
exec 6>&1
## ----------------------------------- ##
## Main body of $CONFIG_STATUS script. ##
## ----------------------------------- ##
_ASEOF
test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# Save the log message, to keep $0 and so on meaningful, and to
# 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.18.0, which was
+This file was extended by unbound $as_me 1.19.0, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
CONFIG_HEADERS = $CONFIG_HEADERS
CONFIG_LINKS = $CONFIG_LINKS
CONFIG_COMMANDS = $CONFIG_COMMANDS
$ $0 $@
on `(hostname || uname -n) 2>/dev/null | sed 1q`
"
_ACEOF
case $ac_config_files in *"
"*) set x $ac_config_files; shift; ac_config_files=$*;;
esac
case $ac_config_headers in *"
"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
esac
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
# Files that config.status was made for.
config_files="$ac_config_files"
config_headers="$ac_config_headers"
config_commands="$ac_config_commands"
_ACEOF
cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
ac_cs_usage="\
\`$as_me' instantiates files and other configuration actions
from templates according to the current configuration. Unless the files
and actions are specified as TAGs, all are instantiated by default.
Usage: $0 [OPTION]... [TAG]...
-h, --help print this help, then exit
-V, --version print version number and configuration settings, then exit
--config print configuration, then exit
-q, --quiet, --silent
do not print progress messages
-d, --debug don't remove temporary files
--recheck update $as_me by reconfiguring in the same conditions
--file=FILE[:TEMPLATE]
instantiate the configuration file FILE
--header=FILE[:TEMPLATE]
instantiate the configuration header FILE
Configuration files:
$config_files
Configuration headers:
$config_headers
Configuration commands:
$config_commands
Report bugs to <unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues>."
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-unbound config.status 1.18.0
+unbound config.status 1.19.0
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
Copyright (C) 2012 Free Software Foundation, Inc.
This config.status script is free software; the Free Software Foundation
gives unlimited permission to copy, distribute and modify it."
ac_pwd='$ac_pwd'
srcdir='$srcdir'
AWK='$AWK'
test -n "\$AWK" || AWK=awk
_ACEOF
cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# The default lists apply if the user does not specify any file.
ac_need_defaults=:
while test $# != 0
do
case $1 in
--*=?*)
ac_option=`expr "X$1" : 'X\([^=]*\)='`
ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
ac_shift=:
;;
--*=)
ac_option=`expr "X$1" : 'X\([^=]*\)='`
ac_optarg=
ac_shift=:
;;
*)
ac_option=$1
ac_optarg=$2
ac_shift=shift
;;
esac
case $ac_option in
# Handling of the options.
-recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
ac_cs_recheck=: ;;
--version | --versio | --versi | --vers | --ver | --ve | --v | -V )
$as_echo "$ac_cs_version"; exit ;;
--config | --confi | --conf | --con | --co | --c )
$as_echo "$ac_cs_config"; exit ;;
--debug | --debu | --deb | --de | --d | -d )
debug=: ;;
--file | --fil | --fi | --f )
$ac_shift
case $ac_optarg in
*\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
'') as_fn_error $? "missing file argument" ;;
esac
as_fn_append CONFIG_FILES " '$ac_optarg'"
ac_need_defaults=false;;
--header | --heade | --head | --hea )
$ac_shift
case $ac_optarg in
*\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
esac
as_fn_append CONFIG_HEADERS " '$ac_optarg'"
ac_need_defaults=false;;
--he | --h)
# Conflict between --help and --header
as_fn_error $? "ambiguous option: \`$1'
Try \`$0 --help' for more information.";;
--help | --hel | -h )
$as_echo "$ac_cs_usage"; exit ;;
-q | -quiet | --quiet | --quie | --qui | --qu | --q \
| -silent | --silent | --silen | --sile | --sil | --si | --s)
ac_cs_silent=: ;;
# This is an error.
-*) as_fn_error $? "unrecognized option: \`$1'
Try \`$0 --help' for more information." ;;
*) as_fn_append ac_config_targets " $1"
ac_need_defaults=false ;;
esac
shift
done
ac_configure_extra_args=
if $ac_cs_silent; then
exec 6>/dev/null
ac_configure_extra_args="$ac_configure_extra_args --silent"
fi
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
if \$ac_cs_recheck; then
set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
shift
\$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
CONFIG_SHELL='$SHELL'
export CONFIG_SHELL
exec "\$@"
fi
_ACEOF
cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
exec 5>>config.log
{
echo
sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
## Running $as_me. ##
_ASBOX
$as_echo "$ac_log"
} >&5
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
#
# INIT-COMMANDS
#
# The HP-UX ksh and POSIX shell print the target directory to stdout
# if CDPATH is set.
(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
sed_quote_subst='$sed_quote_subst'
double_quote_subst='$double_quote_subst'
delay_variable_subst='$delay_variable_subst'
macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`'
macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`'
enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`'
enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`'
pic_mode='`$ECHO "$pic_mode" | $SED "$delay_single_quote_subst"`'
enable_fast_install='`$ECHO "$enable_fast_install" | $SED "$delay_single_quote_subst"`'
shared_archive_member_spec='`$ECHO "$shared_archive_member_spec" | $SED "$delay_single_quote_subst"`'
SHELL='`$ECHO "$SHELL" | $SED "$delay_single_quote_subst"`'
ECHO='`$ECHO "$ECHO" | $SED "$delay_single_quote_subst"`'
PATH_SEPARATOR='`$ECHO "$PATH_SEPARATOR" | $SED "$delay_single_quote_subst"`'
host_alias='`$ECHO "$host_alias" | $SED "$delay_single_quote_subst"`'
host='`$ECHO "$host" | $SED "$delay_single_quote_subst"`'
host_os='`$ECHO "$host_os" | $SED "$delay_single_quote_subst"`'
build_alias='`$ECHO "$build_alias" | $SED "$delay_single_quote_subst"`'
build='`$ECHO "$build" | $SED "$delay_single_quote_subst"`'
build_os='`$ECHO "$build_os" | $SED "$delay_single_quote_subst"`'
SED='`$ECHO "$SED" | $SED "$delay_single_quote_subst"`'
Xsed='`$ECHO "$Xsed" | $SED "$delay_single_quote_subst"`'
GREP='`$ECHO "$GREP" | $SED "$delay_single_quote_subst"`'
EGREP='`$ECHO "$EGREP" | $SED "$delay_single_quote_subst"`'
FGREP='`$ECHO "$FGREP" | $SED "$delay_single_quote_subst"`'
LD='`$ECHO "$LD" | $SED "$delay_single_quote_subst"`'
NM='`$ECHO "$NM" | $SED "$delay_single_quote_subst"`'
LN_S='`$ECHO "$LN_S" | $SED "$delay_single_quote_subst"`'
max_cmd_len='`$ECHO "$max_cmd_len" | $SED "$delay_single_quote_subst"`'
ac_objext='`$ECHO "$ac_objext" | $SED "$delay_single_quote_subst"`'
exeext='`$ECHO "$exeext" | $SED "$delay_single_quote_subst"`'
lt_unset='`$ECHO "$lt_unset" | $SED "$delay_single_quote_subst"`'
lt_SP2NL='`$ECHO "$lt_SP2NL" | $SED "$delay_single_quote_subst"`'
lt_NL2SP='`$ECHO "$lt_NL2SP" | $SED "$delay_single_quote_subst"`'
lt_cv_to_host_file_cmd='`$ECHO "$lt_cv_to_host_file_cmd" | $SED "$delay_single_quote_subst"`'
lt_cv_to_tool_file_cmd='`$ECHO "$lt_cv_to_tool_file_cmd" | $SED "$delay_single_quote_subst"`'
reload_flag='`$ECHO "$reload_flag" | $SED "$delay_single_quote_subst"`'
reload_cmds='`$ECHO "$reload_cmds" | $SED "$delay_single_quote_subst"`'
OBJDUMP='`$ECHO "$OBJDUMP" | $SED "$delay_single_quote_subst"`'
deplibs_check_method='`$ECHO "$deplibs_check_method" | $SED "$delay_single_quote_subst"`'
file_magic_cmd='`$ECHO "$file_magic_cmd" | $SED "$delay_single_quote_subst"`'
file_magic_glob='`$ECHO "$file_magic_glob" | $SED "$delay_single_quote_subst"`'
want_nocaseglob='`$ECHO "$want_nocaseglob" | $SED "$delay_single_quote_subst"`'
DLLTOOL='`$ECHO "$DLLTOOL" | $SED "$delay_single_quote_subst"`'
sharedlib_from_linklib_cmd='`$ECHO "$sharedlib_from_linklib_cmd" | $SED "$delay_single_quote_subst"`'
AR='`$ECHO "$AR" | $SED "$delay_single_quote_subst"`'
AR_FLAGS='`$ECHO "$AR_FLAGS" | $SED "$delay_single_quote_subst"`'
archiver_list_spec='`$ECHO "$archiver_list_spec" | $SED "$delay_single_quote_subst"`'
STRIP='`$ECHO "$STRIP" | $SED "$delay_single_quote_subst"`'
RANLIB='`$ECHO "$RANLIB" | $SED "$delay_single_quote_subst"`'
old_postinstall_cmds='`$ECHO "$old_postinstall_cmds" | $SED "$delay_single_quote_subst"`'
old_postuninstall_cmds='`$ECHO "$old_postuninstall_cmds" | $SED "$delay_single_quote_subst"`'
old_archive_cmds='`$ECHO "$old_archive_cmds" | $SED "$delay_single_quote_subst"`'
lock_old_archive_extraction='`$ECHO "$lock_old_archive_extraction" | $SED "$delay_single_quote_subst"`'
CC='`$ECHO "$CC" | $SED "$delay_single_quote_subst"`'
CFLAGS='`$ECHO "$CFLAGS" | $SED "$delay_single_quote_subst"`'
compiler='`$ECHO "$compiler" | $SED "$delay_single_quote_subst"`'
GCC='`$ECHO "$GCC" | $SED "$delay_single_quote_subst"`'
lt_cv_sys_global_symbol_pipe='`$ECHO "$lt_cv_sys_global_symbol_pipe" | $SED "$delay_single_quote_subst"`'
lt_cv_sys_global_symbol_to_cdecl='`$ECHO "$lt_cv_sys_global_symbol_to_cdecl" | $SED "$delay_single_quote_subst"`'
lt_cv_sys_global_symbol_to_import='`$ECHO "$lt_cv_sys_global_symbol_to_import" | $SED "$delay_single_quote_subst"`'
lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address" | $SED "$delay_single_quote_subst"`'
lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $SED "$delay_single_quote_subst"`'
lt_cv_nm_interface='`$ECHO "$lt_cv_nm_interface" | $SED "$delay_single_quote_subst"`'
nm_file_list_spec='`$ECHO "$nm_file_list_spec" | $SED "$delay_single_quote_subst"`'
lt_sysroot='`$ECHO "$lt_sysroot" | $SED "$delay_single_quote_subst"`'
lt_cv_truncate_bin='`$ECHO "$lt_cv_truncate_bin" | $SED "$delay_single_quote_subst"`'
objdir='`$ECHO "$objdir" | $SED "$delay_single_quote_subst"`'
MAGIC_CMD='`$ECHO "$MAGIC_CMD" | $SED "$delay_single_quote_subst"`'
lt_prog_compiler_no_builtin_flag='`$ECHO "$lt_prog_compiler_no_builtin_flag" | $SED "$delay_single_quote_subst"`'
lt_prog_compiler_pic='`$ECHO "$lt_prog_compiler_pic" | $SED "$delay_single_quote_subst"`'
lt_prog_compiler_wl='`$ECHO "$lt_prog_compiler_wl" | $SED "$delay_single_quote_subst"`'
lt_prog_compiler_static='`$ECHO "$lt_prog_compiler_static" | $SED "$delay_single_quote_subst"`'
lt_cv_prog_compiler_c_o='`$ECHO "$lt_cv_prog_compiler_c_o" | $SED "$delay_single_quote_subst"`'
need_locks='`$ECHO "$need_locks" | $SED "$delay_single_quote_subst"`'
MANIFEST_TOOL='`$ECHO "$MANIFEST_TOOL" | $SED "$delay_single_quote_subst"`'
DSYMUTIL='`$ECHO "$DSYMUTIL" | $SED "$delay_single_quote_subst"`'
NMEDIT='`$ECHO "$NMEDIT" | $SED "$delay_single_quote_subst"`'
LIPO='`$ECHO "$LIPO" | $SED "$delay_single_quote_subst"`'
OTOOL='`$ECHO "$OTOOL" | $SED "$delay_single_quote_subst"`'
OTOOL64='`$ECHO "$OTOOL64" | $SED "$delay_single_quote_subst"`'
libext='`$ECHO "$libext" | $SED "$delay_single_quote_subst"`'
shrext_cmds='`$ECHO "$shrext_cmds" | $SED "$delay_single_quote_subst"`'
extract_expsyms_cmds='`$ECHO "$extract_expsyms_cmds" | $SED "$delay_single_quote_subst"`'
archive_cmds_need_lc='`$ECHO "$archive_cmds_need_lc" | $SED "$delay_single_quote_subst"`'
enable_shared_with_static_runtimes='`$ECHO "$enable_shared_with_static_runtimes" | $SED "$delay_single_quote_subst"`'
export_dynamic_flag_spec='`$ECHO "$export_dynamic_flag_spec" | $SED "$delay_single_quote_subst"`'
whole_archive_flag_spec='`$ECHO "$whole_archive_flag_spec" | $SED "$delay_single_quote_subst"`'
compiler_needs_object='`$ECHO "$compiler_needs_object" | $SED "$delay_single_quote_subst"`'
old_archive_from_new_cmds='`$ECHO "$old_archive_from_new_cmds" | $SED "$delay_single_quote_subst"`'
old_archive_from_expsyms_cmds='`$ECHO "$old_archive_from_expsyms_cmds" | $SED "$delay_single_quote_subst"`'
archive_cmds='`$ECHO "$archive_cmds" | $SED "$delay_single_quote_subst"`'
archive_expsym_cmds='`$ECHO "$archive_expsym_cmds" | $SED "$delay_single_quote_subst"`'
module_cmds='`$ECHO "$module_cmds" | $SED "$delay_single_quote_subst"`'
module_expsym_cmds='`$ECHO "$module_expsym_cmds" | $SED "$delay_single_quote_subst"`'
with_gnu_ld='`$ECHO "$with_gnu_ld" | $SED "$delay_single_quote_subst"`'
allow_undefined_flag='`$ECHO "$allow_undefined_flag" | $SED "$delay_single_quote_subst"`'
no_undefined_flag='`$ECHO "$no_undefined_flag" | $SED "$delay_single_quote_subst"`'
hardcode_libdir_flag_spec='`$ECHO "$hardcode_libdir_flag_spec" | $SED "$delay_single_quote_subst"`'
hardcode_libdir_separator='`$ECHO "$hardcode_libdir_separator" | $SED "$delay_single_quote_subst"`'
hardcode_direct='`$ECHO "$hardcode_direct" | $SED "$delay_single_quote_subst"`'
hardcode_direct_absolute='`$ECHO "$hardcode_direct_absolute" | $SED "$delay_single_quote_subst"`'
hardcode_minus_L='`$ECHO "$hardcode_minus_L" | $SED "$delay_single_quote_subst"`'
hardcode_shlibpath_var='`$ECHO "$hardcode_shlibpath_var" | $SED "$delay_single_quote_subst"`'
hardcode_automatic='`$ECHO "$hardcode_automatic" | $SED "$delay_single_quote_subst"`'
inherit_rpath='`$ECHO "$inherit_rpath" | $SED "$delay_single_quote_subst"`'
link_all_deplibs='`$ECHO "$link_all_deplibs" | $SED "$delay_single_quote_subst"`'
always_export_symbols='`$ECHO "$always_export_symbols" | $SED "$delay_single_quote_subst"`'
export_symbols_cmds='`$ECHO "$export_symbols_cmds" | $SED "$delay_single_quote_subst"`'
exclude_expsyms='`$ECHO "$exclude_expsyms" | $SED "$delay_single_quote_subst"`'
include_expsyms='`$ECHO "$include_expsyms" | $SED "$delay_single_quote_subst"`'
prelink_cmds='`$ECHO "$prelink_cmds" | $SED "$delay_single_quote_subst"`'
postlink_cmds='`$ECHO "$postlink_cmds" | $SED "$delay_single_quote_subst"`'
file_list_spec='`$ECHO "$file_list_spec" | $SED "$delay_single_quote_subst"`'
variables_saved_for_relink='`$ECHO "$variables_saved_for_relink" | $SED "$delay_single_quote_subst"`'
need_lib_prefix='`$ECHO "$need_lib_prefix" | $SED "$delay_single_quote_subst"`'
need_version='`$ECHO "$need_version" | $SED "$delay_single_quote_subst"`'
version_type='`$ECHO "$version_type" | $SED "$delay_single_quote_subst"`'
runpath_var='`$ECHO "$runpath_var" | $SED "$delay_single_quote_subst"`'
shlibpath_var='`$ECHO "$shlibpath_var" | $SED "$delay_single_quote_subst"`'
shlibpath_overrides_runpath='`$ECHO "$shlibpath_overrides_runpath" | $SED "$delay_single_quote_subst"`'
libname_spec='`$ECHO "$libname_spec" | $SED "$delay_single_quote_subst"`'
library_names_spec='`$ECHO "$library_names_spec" | $SED "$delay_single_quote_subst"`'
soname_spec='`$ECHO "$soname_spec" | $SED "$delay_single_quote_subst"`'
install_override_mode='`$ECHO "$install_override_mode" | $SED "$delay_single_quote_subst"`'
postinstall_cmds='`$ECHO "$postinstall_cmds" | $SED "$delay_single_quote_subst"`'
postuninstall_cmds='`$ECHO "$postuninstall_cmds" | $SED "$delay_single_quote_subst"`'
finish_cmds='`$ECHO "$finish_cmds" | $SED "$delay_single_quote_subst"`'
finish_eval='`$ECHO "$finish_eval" | $SED "$delay_single_quote_subst"`'
hardcode_into_libs='`$ECHO "$hardcode_into_libs" | $SED "$delay_single_quote_subst"`'
sys_lib_search_path_spec='`$ECHO "$sys_lib_search_path_spec" | $SED "$delay_single_quote_subst"`'
configure_time_dlsearch_path='`$ECHO "$configure_time_dlsearch_path" | $SED "$delay_single_quote_subst"`'
configure_time_lt_sys_library_path='`$ECHO "$configure_time_lt_sys_library_path" | $SED "$delay_single_quote_subst"`'
hardcode_action='`$ECHO "$hardcode_action" | $SED "$delay_single_quote_subst"`'
enable_dlopen='`$ECHO "$enable_dlopen" | $SED "$delay_single_quote_subst"`'
enable_dlopen_self='`$ECHO "$enable_dlopen_self" | $SED "$delay_single_quote_subst"`'
enable_dlopen_self_static='`$ECHO "$enable_dlopen_self_static" | $SED "$delay_single_quote_subst"`'
old_striplib='`$ECHO "$old_striplib" | $SED "$delay_single_quote_subst"`'
striplib='`$ECHO "$striplib" | $SED "$delay_single_quote_subst"`'
LTCC='$LTCC'
LTCFLAGS='$LTCFLAGS'
compiler='$compiler_DEFAULT'
# A function that is used when there is no print builtin or printf.
func_fallback_echo ()
{
eval 'cat <<_LTECHO_EOF
\$1
_LTECHO_EOF'
}
# Quote evaled strings.
for var in SHELL \
ECHO \
PATH_SEPARATOR \
SED \
GREP \
EGREP \
FGREP \
LD \
NM \
LN_S \
lt_SP2NL \
lt_NL2SP \
reload_flag \
OBJDUMP \
deplibs_check_method \
file_magic_cmd \
file_magic_glob \
want_nocaseglob \
DLLTOOL \
sharedlib_from_linklib_cmd \
AR \
AR_FLAGS \
archiver_list_spec \
STRIP \
RANLIB \
CC \
CFLAGS \
compiler \
lt_cv_sys_global_symbol_pipe \
lt_cv_sys_global_symbol_to_cdecl \
lt_cv_sys_global_symbol_to_import \
lt_cv_sys_global_symbol_to_c_name_address \
lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \
lt_cv_nm_interface \
nm_file_list_spec \
lt_cv_truncate_bin \
lt_prog_compiler_no_builtin_flag \
lt_prog_compiler_pic \
lt_prog_compiler_wl \
lt_prog_compiler_static \
lt_cv_prog_compiler_c_o \
need_locks \
MANIFEST_TOOL \
DSYMUTIL \
NMEDIT \
LIPO \
OTOOL \
OTOOL64 \
shrext_cmds \
export_dynamic_flag_spec \
whole_archive_flag_spec \
compiler_needs_object \
with_gnu_ld \
allow_undefined_flag \
no_undefined_flag \
hardcode_libdir_flag_spec \
hardcode_libdir_separator \
exclude_expsyms \
include_expsyms \
file_list_spec \
variables_saved_for_relink \
libname_spec \
library_names_spec \
soname_spec \
install_override_mode \
finish_eval \
old_striplib \
striplib; do
case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
*[\\\\\\\`\\"\\\$]*)
eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes
;;
*)
eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
;;
esac
done
# Double-quote double-evaled strings.
for var in reload_cmds \
old_postinstall_cmds \
old_postuninstall_cmds \
old_archive_cmds \
extract_expsyms_cmds \
old_archive_from_new_cmds \
old_archive_from_expsyms_cmds \
archive_cmds \
archive_expsym_cmds \
module_cmds \
module_expsym_cmds \
export_symbols_cmds \
prelink_cmds \
postlink_cmds \
postinstall_cmds \
postuninstall_cmds \
finish_cmds \
sys_lib_search_path_spec \
configure_time_dlsearch_path \
configure_time_lt_sys_library_path; do
case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
*[\\\\\\\`\\"\\\$]*)
eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes
;;
*)
eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
;;
esac
done
ac_aux_dir='$ac_aux_dir'
# See if we are running on zsh, and set the options that allow our
# commands through without removal of \ escapes INIT.
if test -n "\${ZSH_VERSION+set}"; then
setopt NO_GLOB_SUBST
fi
PACKAGE='$PACKAGE'
VERSION='$VERSION'
RM='$RM'
ofile='$ofile'
_ACEOF
cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# Handling of arguments.
for ac_config_target in $ac_config_targets
do
case $ac_config_target in
"libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;;
"disable-rpath") CONFIG_COMMANDS="$CONFIG_COMMANDS disable-rpath" ;;
"Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
"doc/example.conf") CONFIG_FILES="$CONFIG_FILES doc/example.conf" ;;
"doc/libunbound.3") CONFIG_FILES="$CONFIG_FILES doc/libunbound.3" ;;
"doc/unbound.8") CONFIG_FILES="$CONFIG_FILES doc/unbound.8" ;;
"doc/unbound-anchor.8") CONFIG_FILES="$CONFIG_FILES doc/unbound-anchor.8" ;;
"doc/unbound-checkconf.8") CONFIG_FILES="$CONFIG_FILES doc/unbound-checkconf.8" ;;
"doc/unbound.conf.5") CONFIG_FILES="$CONFIG_FILES doc/unbound.conf.5" ;;
"doc/unbound-control.8") CONFIG_FILES="$CONFIG_FILES doc/unbound-control.8" ;;
"doc/unbound-host.1") CONFIG_FILES="$CONFIG_FILES doc/unbound-host.1" ;;
"smallapp/unbound-control-setup.sh") CONFIG_FILES="$CONFIG_FILES smallapp/unbound-control-setup.sh" ;;
"dnstap/dnstap_config.h") CONFIG_FILES="$CONFIG_FILES dnstap/dnstap_config.h" ;;
"dnscrypt/dnscrypt_config.h") CONFIG_FILES="$CONFIG_FILES dnscrypt/dnscrypt_config.h" ;;
"contrib/libunbound.pc") CONFIG_FILES="$CONFIG_FILES contrib/libunbound.pc" ;;
"contrib/unbound.socket") CONFIG_FILES="$CONFIG_FILES contrib/unbound.socket" ;;
"contrib/unbound.service") CONFIG_FILES="$CONFIG_FILES contrib/unbound.service" ;;
"contrib/unbound_portable.service") CONFIG_FILES="$CONFIG_FILES contrib/unbound_portable.service" ;;
"config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
*) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
esac
done
# If the user did not use the arguments to specify the items to instantiate,
# then the envvar interface is used. Set only those that are not.
# We use the long form for the default assignment because of an extremely
# bizarre bug on SunOS 4.1.3.
if $ac_need_defaults; then
test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands
fi
# Have a temporary directory for convenience. Make it in the build tree
# simply because there is no reason against having it here, and in addition,
# creating and moving files from /tmp can sometimes cause problems.
# Hook for its removal unless debugging.
# Note that there is a small window in which the directory will not be cleaned:
# after its creation but before its name has been assigned to `$tmp'.
$debug ||
{
tmp= ac_tmp=
trap 'exit_status=$?
: "${ac_tmp:=$tmp}"
{ test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
' 0
trap 'as_fn_exit 1' 1 2 13 15
}
# Create a (secure) tmp directory for tmp files.
{
tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
test -d "$tmp"
} ||
{
tmp=./conf$$-$RANDOM
(umask 077 && mkdir "$tmp")
} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
ac_tmp=$tmp
# Set up the scripts for CONFIG_FILES section.
# No need to generate them if there are no CONFIG_FILES.
# This happens for instance with `./config.status config.h'.
if test -n "$CONFIG_FILES"; then
ac_cr=`echo X | tr X '\015'`
# On cygwin, bash can eat \r inside `` if the user requested igncr.
# But we know of no other shell where ac_cr would be empty at this
# point, so we can use a bashism as a fallback.
if test "x$ac_cr" = x; then
eval ac_cr=\$\'\\r\'
fi
ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
ac_cs_awk_cr='\\r'
else
ac_cs_awk_cr=$ac_cr
fi
echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
_ACEOF
{
echo "cat >conf$$subs.awk <<_ACEOF" &&
echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
echo "_ACEOF"
} >conf$$subs.sh ||
as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
ac_delim='%!_!# '
for ac_last_try in false false false false false :; do
. ./conf$$subs.sh ||
as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
if test $ac_delim_n = $ac_delim_num; then
break
elif $ac_last_try; then
as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
else
ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
fi
done
rm -f conf$$subs.sh
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
_ACEOF
sed -n '
h
s/^/S["/; s/!.*/"]=/
p
g
s/^[^!]*!//
:repl
t repl
s/'"$ac_delim"'$//
t delim
:nl
h
s/\(.\{148\}\)..*/\1/
t more1
s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
p
n
b repl
:more1
s/["\\]/\\&/g; s/^/"/; s/$/"\\/
p
g
s/.\{148\}//
t nl
:delim
h
s/\(.\{148\}\)..*/\1/
t more2
s/["\\]/\\&/g; s/^/"/; s/$/"/
p
b
:more2
s/["\\]/\\&/g; s/^/"/; s/$/"\\/
p
g
s/.\{148\}//
t delim
' <conf$$subs.awk | sed '
/^[^""]/{
N
s/\n//
}
' >>$CONFIG_STATUS || ac_write_fail=1
rm -f conf$$subs.awk
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
_ACAWK
cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
for (key in S) S_is_set[key] = 1
FS = ""
}
{
line = $ 0
nfields = split(line, field, "@")
substed = 0
len = length(field[1])
for (i = 2; i < nfields; i++) {
key = field[i]
keylen = length(key)
if (S_is_set[key]) {
value = S[key]
line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
len += length(value) + length(field[++i])
substed = 1
} else
len += 1 + keylen
}
print line
}
_ACAWK
_ACEOF
cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
else
cat
fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
|| as_fn_error $? "could not setup config files machinery" "$LINENO" 5
_ACEOF
# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
# trailing colons and then remove the whole line if VPATH becomes empty
# (actually we leave an empty line to preserve line numbers).
if test "x$srcdir" = x.; then
ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{
h
s///
s/^/:/
s/[ ]*$/:/
s/:\$(srcdir):/:/g
s/:\${srcdir}:/:/g
s/:@srcdir@:/:/g
s/^:*//
s/:*$//
x
s/\(=[ ]*\).*/\1/
G
s/\n//
s/^[^=]*=[ ]*$//
}'
fi
cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
fi # test -n "$CONFIG_FILES"
# Set up the scripts for CONFIG_HEADERS section.
# No need to generate them if there are no CONFIG_HEADERS.
# This happens for instance with `./config.status Makefile'.
if test -n "$CONFIG_HEADERS"; then
cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
BEGIN {
_ACEOF
# Transform confdefs.h into an awk script `defines.awk', embedded as
# here-document in config.status, that substitutes the proper values into
# config.h.in to produce config.h.
# Create a delimiter string that does not exist in confdefs.h, to ease
# handling of long lines.
ac_delim='%!_!# '
for ac_last_try in false false :; do
ac_tt=`sed -n "/$ac_delim/p" confdefs.h`
if test -z "$ac_tt"; then
break
elif $ac_last_try; then
as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5
else
ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
fi
done
# For the awk script, D is an array of macro values keyed by name,
# likewise P contains macro parameters if any. Preserve backslash
# newline sequences.
ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
sed -n '
s/.\{148\}/&'"$ac_delim"'/g
t rset
:rset
s/^[ ]*#[ ]*define[ ][ ]*/ /
t def
d
:def
s/\\$//
t bsnl
s/["\\]/\\&/g
s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\
D["\1"]=" \3"/p
s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p
d
:bsnl
s/["\\]/\\&/g
s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\
D["\1"]=" \3\\\\\\n"\\/p
t cont
s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
t cont
d
:cont
n
s/.\{148\}/&'"$ac_delim"'/g
t clear
:clear
s/\\$//
t bsnlc
s/["\\]/\\&/g; s/^/"/; s/$/"/p
d
:bsnlc
s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
b cont
' <confdefs.h | sed '
s/'"$ac_delim"'/"\\\
"/g' >>$CONFIG_STATUS || ac_write_fail=1
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
for (key in D) D_is_set[key] = 1
FS = ""
}
/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
line = \$ 0
split(line, arg, " ")
if (arg[1] == "#") {
defundef = arg[2]
mac1 = arg[3]
} else {
defundef = substr(arg[1], 2)
mac1 = arg[2]
}
split(mac1, mac2, "(") #)
macro = mac2[1]
prefix = substr(line, 1, index(line, defundef) - 1)
if (D_is_set[macro]) {
# Preserve the white space surrounding the "#".
print prefix "define", macro P[macro] D[macro]
next
} else {
# Replace #undef with comments. This is necessary, for example,
# in the case of _POSIX_SOURCE, which is predefined and required
# on some systems where configure will not decide to define it.
if (defundef == "undef") {
print "/*", prefix defundef, macro, "*/"
next
}
}
}
{ print }
_ACAWK
_ACEOF
cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
fi # test -n "$CONFIG_HEADERS"
eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS"
shift
for ac_tag
do
case $ac_tag in
:[FHLC]) ac_mode=$ac_tag; continue;;
esac
case $ac_mode$ac_tag in
:[FHL]*:*);;
:L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
:[FH]-) ac_tag=-:-;;
:[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
esac
ac_save_IFS=$IFS
IFS=:
set x $ac_tag
IFS=$ac_save_IFS
shift
ac_file=$1
shift
case $ac_mode in
:L) ac_source=$1;;
:[FH])
ac_file_inputs=
for ac_f
do
case $ac_f in
-) ac_f="$ac_tmp/stdin";;
*) # Look for the file first in the build tree, then in the source tree
# (if the path is not absolute). The absolute path cannot be DOS-style,
# because $ac_f cannot contain `:'.
test -f "$ac_f" ||
case $ac_f in
[\\/$]*) false;;
*) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
esac ||
as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
esac
case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
as_fn_append ac_file_inputs " '$ac_f'"
done
# Let's still pretend it is `configure' which instantiates (i.e., don't
# use $as_me), people would be surprised to read:
# /* config.h. Generated by config.status. */
configure_input='Generated from '`
$as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
`' by configure.'
if test x"$ac_file" != x-; then
configure_input="$ac_file. $configure_input"
{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
$as_echo "$as_me: creating $ac_file" >&6;}
fi
# Neutralize special characters interpreted by sed in replacement strings.
case $configure_input in #(
*\&* | *\|* | *\\* )
ac_sed_conf_input=`$as_echo "$configure_input" |
sed 's/[\\\\&|]/\\\\&/g'`;; #(
*) ac_sed_conf_input=$configure_input;;
esac
case $ac_tag in
*:-:* | *:-) cat >"$ac_tmp/stdin" \
|| as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
esac
;;
esac
ac_dir=`$as_dirname -- "$ac_file" ||
$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
X"$ac_file" : 'X\(//\)[^/]' \| \
X"$ac_file" : 'X\(//\)$' \| \
X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
$as_echo X"$ac_file" |
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
s//\1/
q
}
/^X\(\/\/\)[^/].*/{
s//\1/
q
}
/^X\(\/\/\)$/{
s//\1/
q
}
/^X\(\/\).*/{
s//\1/
q
}
s/.*/./; q'`
as_dir="$ac_dir"; as_fn_mkdir_p
ac_builddir=.
case "$ac_dir" in
.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
*)
ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
# A ".." for each directory in $ac_dir_suffix.
ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
case $ac_top_builddir_sub in
"") ac_top_builddir_sub=. ac_top_build_prefix= ;;
*) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
esac ;;
esac
ac_abs_top_builddir=$ac_pwd
ac_abs_builddir=$ac_pwd$ac_dir_suffix
# for backward compatibility:
ac_top_builddir=$ac_top_build_prefix
case $srcdir in
.) # We are building in place.
ac_srcdir=.
ac_top_srcdir=$ac_top_builddir_sub
ac_abs_top_srcdir=$ac_pwd ;;
[\\/]* | ?:[\\/]* ) # Absolute name.
ac_srcdir=$srcdir$ac_dir_suffix;
ac_top_srcdir=$srcdir
ac_abs_top_srcdir=$srcdir ;;
*) # Relative name.
ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
ac_top_srcdir=$ac_top_build_prefix$srcdir
ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
esac
ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
case $ac_mode in
:F)
#
# CONFIG_FILE
#
_ACEOF
cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# If the template does not know about datarootdir, expand it.
# FIXME: This hack should be removed a few years after 2.60.
ac_datarootdir_hack=; ac_datarootdir_seen=
ac_sed_dataroot='
/datarootdir/ {
p
q
}
/@datadir@/p
/@docdir@/p
/@infodir@/p
/@localedir@/p
/@mandir@/p'
case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
*datarootdir*) ac_datarootdir_seen=yes;;
*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_datarootdir_hack='
s&@datadir@&$datadir&g
s&@docdir@&$docdir&g
s&@infodir@&$infodir&g
s&@localedir@&$localedir&g
s&@mandir@&$mandir&g
s&\\\${datarootdir}&$datarootdir&g' ;;
esac
_ACEOF
# Neutralize VPATH when `$srcdir' = `.'.
# Shell code in configure.ac might set extrasub.
# FIXME: do we really want to maintain this feature?
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_sed_extra="$ac_vpsub
$extrasub
_ACEOF
cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
:t
/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
s|@configure_input@|$ac_sed_conf_input|;t t
s&@top_builddir@&$ac_top_builddir_sub&;t t
s&@top_build_prefix@&$ac_top_build_prefix&;t t
s&@srcdir@&$ac_srcdir&;t t
s&@abs_srcdir@&$ac_abs_srcdir&;t t
s&@top_srcdir@&$ac_top_srcdir&;t t
s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
s&@builddir@&$ac_builddir&;t t
s&@abs_builddir@&$ac_abs_builddir&;t t
s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
$ac_datarootdir_hack
"
eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
>$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
{ ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
{ ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \
"$ac_tmp/out"`; test -z "$ac_out"; } &&
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
which seems to be undefined. Please make sure it is defined" >&5
$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
which seems to be undefined. Please make sure it is defined" >&2;}
rm -f "$ac_tmp/stdin"
case $ac_file in
-) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
*) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
esac \
|| as_fn_error $? "could not create $ac_file" "$LINENO" 5
;;
:H)
#
# CONFIG_HEADER
#
if test x"$ac_file" != x-; then
{
$as_echo "/* $configure_input */" \
&& eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
} >"$ac_tmp/config.h" \
|| as_fn_error $? "could not create $ac_file" "$LINENO" 5
if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
$as_echo "$as_me: $ac_file is unchanged" >&6;}
else
rm -f "$ac_file"
mv "$ac_tmp/config.h" "$ac_file" \
|| as_fn_error $? "could not create $ac_file" "$LINENO" 5
fi
else
$as_echo "/* $configure_input */" \
&& eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
|| as_fn_error $? "could not create -" "$LINENO" 5
fi
;;
:C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5
$as_echo "$as_me: executing $ac_file commands" >&6;}
;;
esac
case $ac_file$ac_mode in
"libtool":C)
# See if we are running on zsh, and set the options that allow our
# commands through without removal of \ escapes.
if test -n "${ZSH_VERSION+set}"; then
setopt NO_GLOB_SUBST
fi
cfgfile=${ofile}T
trap "$RM \"$cfgfile\"; exit 1" 1 2 15
$RM "$cfgfile"
cat <<_LT_EOF >> "$cfgfile"
#! $SHELL
# Generated automatically by $as_me ($PACKAGE) $VERSION
# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
# NOTE: Changes made to this file will be lost: look at ltmain.sh.
# Provide generalized library-building support services.
# Written by Gordon Matzigkeit, 1996
# Copyright (C) 2014 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.
# GNU Libtool is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of of the License, or
# (at your option) any later version.
#
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program or library that is built
# using GNU Libtool, you may include this file under the same
# distribution terms that you use for the rest of that program.
#
# GNU Libtool is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# The names of the tagged configurations supported by this script.
available_tags=''
# Configured defaults for sys_lib_dlsearch_path munging.
: \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"}
# ### BEGIN LIBTOOL CONFIG
# Which release of libtool.m4 was used?
macro_version=$macro_version
macro_revision=$macro_revision
# Whether or not to build shared libraries.
build_libtool_libs=$enable_shared
# Whether or not to build static libraries.
build_old_libs=$enable_static
# What type of objects to build.
pic_mode=$pic_mode
# Whether or not to optimize for fast installation.
fast_install=$enable_fast_install
# Shared archive member basename,for filename based shared library versioning on AIX.
shared_archive_member_spec=$shared_archive_member_spec
# Shell to use when invoking shell scripts.
SHELL=$lt_SHELL
# An echo program that protects backslashes.
ECHO=$lt_ECHO
# The PATH separator for the build system.
PATH_SEPARATOR=$lt_PATH_SEPARATOR
# The host system.
host_alias=$host_alias
host=$host
host_os=$host_os
# The build system.
build_alias=$build_alias
build=$build
build_os=$build_os
# A sed program that does not truncate output.
SED=$lt_SED
# Sed that helps us avoid accidentally triggering echo(1) options like -n.
Xsed="\$SED -e 1s/^X//"
# A grep program that handles long lines.
GREP=$lt_GREP
# An ERE matcher.
EGREP=$lt_EGREP
# A literal string matcher.
FGREP=$lt_FGREP
# A BSD- or MS-compatible name lister.
NM=$lt_NM
# Whether we need soft or hard links.
LN_S=$lt_LN_S
# What is the maximum length of a command?
max_cmd_len=$max_cmd_len
# Object file suffix (normally "o").
objext=$ac_objext
# Executable file suffix (normally "").
exeext=$exeext
# whether the shell understands "unset".
lt_unset=$lt_unset
# turn spaces into newlines.
SP2NL=$lt_lt_SP2NL
# turn newlines into spaces.
NL2SP=$lt_lt_NL2SP
# convert \$build file names to \$host format.
to_host_file_cmd=$lt_cv_to_host_file_cmd
# convert \$build files to toolchain format.
to_tool_file_cmd=$lt_cv_to_tool_file_cmd
# An object symbol dumper.
OBJDUMP=$lt_OBJDUMP
# Method to check whether dependent libraries are shared objects.
deplibs_check_method=$lt_deplibs_check_method
# Command to use when deplibs_check_method = "file_magic".
file_magic_cmd=$lt_file_magic_cmd
# How to find potential files when deplibs_check_method = "file_magic".
file_magic_glob=$lt_file_magic_glob
# Find potential files using nocaseglob when deplibs_check_method = "file_magic".
want_nocaseglob=$lt_want_nocaseglob
# DLL creation program.
DLLTOOL=$lt_DLLTOOL
# Command to associate shared and link libraries.
sharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd
# The archiver.
AR=$lt_AR
# Flags to create an archive.
AR_FLAGS=$lt_AR_FLAGS
# How to feed a file listing to the archiver.
archiver_list_spec=$lt_archiver_list_spec
# A symbol stripping program.
STRIP=$lt_STRIP
# Commands used to install an old-style archive.
RANLIB=$lt_RANLIB
old_postinstall_cmds=$lt_old_postinstall_cmds
old_postuninstall_cmds=$lt_old_postuninstall_cmds
# Whether to use a lock for old archive extraction.
lock_old_archive_extraction=$lock_old_archive_extraction
# A C compiler.
LTCC=$lt_CC
# LTCC compiler flags.
LTCFLAGS=$lt_CFLAGS
# Take the output of nm and produce a listing of raw symbols and C names.
global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe
# Transform the output of nm in a proper C declaration.
global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl
# Transform the output of nm into a list of symbols to manually relocate.
global_symbol_to_import=$lt_lt_cv_sys_global_symbol_to_import
# Transform the output of nm in a C name address pair.
global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address
# Transform the output of nm in a C name address pair when lib prefix is needed.
global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix
# The name lister interface.
nm_interface=$lt_lt_cv_nm_interface
# Specify filename containing input files for \$NM.
nm_file_list_spec=$lt_nm_file_list_spec
# The root where to search for dependent libraries,and where our libraries should be installed.
lt_sysroot=$lt_sysroot
# Command to truncate a binary pipe.
lt_truncate_bin=$lt_lt_cv_truncate_bin
# The name of the directory that contains temporary libtool files.
objdir=$objdir
# Used to examine libraries when file_magic_cmd begins with "file".
MAGIC_CMD=$MAGIC_CMD
# Must we lock files when doing compilation?
need_locks=$lt_need_locks
# Manifest tool.
MANIFEST_TOOL=$lt_MANIFEST_TOOL
# Tool to manipulate archived DWARF debug symbol files on Mac OS X.
DSYMUTIL=$lt_DSYMUTIL
# Tool to change global to local symbols on Mac OS X.
NMEDIT=$lt_NMEDIT
# Tool to manipulate fat objects and archives on Mac OS X.
LIPO=$lt_LIPO
# ldd/readelf like tool for Mach-O binaries on Mac OS X.
OTOOL=$lt_OTOOL
# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4.
OTOOL64=$lt_OTOOL64
# Old archive suffix (normally "a").
libext=$libext
# Shared library suffix (normally ".so").
shrext_cmds=$lt_shrext_cmds
# The commands to extract the exported symbol list from a shared archive.
extract_expsyms_cmds=$lt_extract_expsyms_cmds
# Variables whose values should be saved in libtool wrapper scripts and
# restored at link time.
variables_saved_for_relink=$lt_variables_saved_for_relink
# Do we need the "lib" prefix for modules?
need_lib_prefix=$need_lib_prefix
# Do we need a version for libraries?
need_version=$need_version
# Library versioning type.
version_type=$version_type
# Shared library runtime path variable.
runpath_var=$runpath_var
# Shared library path variable.
shlibpath_var=$shlibpath_var
# Is shlibpath searched before the hard-coded library search path?
shlibpath_overrides_runpath=$shlibpath_overrides_runpath
# Format of library name prefix.
libname_spec=$lt_libname_spec
# List of archive names. First name is the real one, the rest are links.
# The last name is the one that the linker finds with -lNAME
library_names_spec=$lt_library_names_spec
# The coded name of the library, if different from the real name.
soname_spec=$lt_soname_spec
# Permission mode override for installation of shared libraries.
install_override_mode=$lt_install_override_mode
# Command to use after installation of a shared archive.
postinstall_cmds=$lt_postinstall_cmds
# Command to use after uninstallation of a shared archive.
postuninstall_cmds=$lt_postuninstall_cmds
# Commands used to finish a libtool library installation in a directory.
finish_cmds=$lt_finish_cmds
# As "finish_cmds", except a single script fragment to be evaled but
# not shown.
finish_eval=$lt_finish_eval
# Whether we should hardcode library paths into libraries.
hardcode_into_libs=$hardcode_into_libs
# Compile-time system search path for libraries.
sys_lib_search_path_spec=$lt_sys_lib_search_path_spec
# Detected run-time system search path for libraries.
sys_lib_dlsearch_path_spec=$lt_configure_time_dlsearch_path
# Explicit LT_SYS_LIBRARY_PATH set during ./configure time.
configure_time_lt_sys_library_path=$lt_configure_time_lt_sys_library_path
# Whether dlopen is supported.
dlopen_support=$enable_dlopen
# Whether dlopen of programs is supported.
dlopen_self=$enable_dlopen_self
# Whether dlopen of statically linked programs is supported.
dlopen_self_static=$enable_dlopen_self_static
# Commands to strip libraries.
old_striplib=$lt_old_striplib
striplib=$lt_striplib
# The linker used to build libraries.
LD=$lt_LD
# How to create reloadable object files.
reload_flag=$lt_reload_flag
reload_cmds=$lt_reload_cmds
# Commands used to build an old-style archive.
old_archive_cmds=$lt_old_archive_cmds
# A language specific compiler.
CC=$lt_compiler
# Is the compiler the GNU compiler?
with_gcc=$GCC
# Compiler flag to turn off builtin functions.
no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag
# Additional compiler flags for building library objects.
pic_flag=$lt_lt_prog_compiler_pic
# How to pass a linker flag through the compiler.
wl=$lt_lt_prog_compiler_wl
# Compiler flag to prevent dynamic linking.
link_static_flag=$lt_lt_prog_compiler_static
# Does compiler simultaneously support -c and -o options?
compiler_c_o=$lt_lt_cv_prog_compiler_c_o
# Whether or not to add -lc for building shared libraries.
build_libtool_need_lc=$archive_cmds_need_lc
# Whether or not to disallow shared libs when runtime libs are static.
allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes
# Compiler flag to allow reflexive dlopens.
export_dynamic_flag_spec=$lt_export_dynamic_flag_spec
# Compiler flag to generate shared objects directly from archives.
whole_archive_flag_spec=$lt_whole_archive_flag_spec
# Whether the compiler copes with passing no objects directly.
compiler_needs_object=$lt_compiler_needs_object
# Create an old-style archive from a shared archive.
old_archive_from_new_cmds=$lt_old_archive_from_new_cmds
# Create a temporary old-style archive to link instead of a shared archive.
old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds
# Commands used to build a shared archive.
archive_cmds=$lt_archive_cmds
archive_expsym_cmds=$lt_archive_expsym_cmds
# Commands used to build a loadable module if different from building
# a shared archive.
module_cmds=$lt_module_cmds
module_expsym_cmds=$lt_module_expsym_cmds
# Whether we are building with GNU ld or not.
with_gnu_ld=$lt_with_gnu_ld
# Flag that allows shared libraries with undefined symbols to be built.
allow_undefined_flag=$lt_allow_undefined_flag
# Flag that enforces no undefined symbols.
no_undefined_flag=$lt_no_undefined_flag
# Flag to hardcode \$libdir into a binary during linking.
# This must work even if \$libdir does not exist
hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec
# Whether we need a single "-rpath" flag with a separated argument.
hardcode_libdir_separator=$lt_hardcode_libdir_separator
# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes
# DIR into the resulting binary.
hardcode_direct=$hardcode_direct
# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes
# DIR into the resulting binary and the resulting library dependency is
# "absolute",i.e impossible to change by setting \$shlibpath_var if the
# library is relocated.
hardcode_direct_absolute=$hardcode_direct_absolute
# Set to "yes" if using the -LDIR flag during linking hardcodes DIR
# into the resulting binary.
hardcode_minus_L=$hardcode_minus_L
# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
# into the resulting binary.
hardcode_shlibpath_var=$hardcode_shlibpath_var
# Set to "yes" if building a shared library automatically hardcodes DIR
# into the library and all subsequent libraries and executables linked
# against it.
hardcode_automatic=$hardcode_automatic
# Set to yes if linker adds runtime paths of dependent libraries
# to runtime path list.
inherit_rpath=$inherit_rpath
# Whether libtool must link a program against all its dependency libraries.
link_all_deplibs=$link_all_deplibs
# Set to "yes" if exported symbols are required.
always_export_symbols=$always_export_symbols
# The commands to list exported symbols.
export_symbols_cmds=$lt_export_symbols_cmds
# Symbols that should not be listed in the preloaded symbols.
exclude_expsyms=$lt_exclude_expsyms
# Symbols that must always be exported.
include_expsyms=$lt_include_expsyms
# Commands necessary for linking programs (against libraries) with templates.
prelink_cmds=$lt_prelink_cmds
# Commands necessary for finishing linking programs.
postlink_cmds=$lt_postlink_cmds
# Specify filename containing input files.
file_list_spec=$lt_file_list_spec
# How to hardcode a shared library path into an executable.
hardcode_action=$hardcode_action
# ### END LIBTOOL CONFIG
_LT_EOF
cat <<'_LT_EOF' >> "$cfgfile"
# ### BEGIN FUNCTIONS SHARED WITH CONFIGURE
# func_munge_path_list VARIABLE PATH
# -----------------------------------
# VARIABLE is name of variable containing _space_ separated list of
# directories to be munged by the contents of PATH, which is string
# having a format:
# "DIR[:DIR]:"
# string "DIR[ DIR]" will be prepended to VARIABLE
# ":DIR[:DIR]"
# string "DIR[ DIR]" will be appended to VARIABLE
# "DIRP[:DIRP]::[DIRA:]DIRA"
# string "DIRP[ DIRP]" will be prepended to VARIABLE and string
# "DIRA[ DIRA]" will be appended to VARIABLE
# "DIR[:DIR]"
# VARIABLE will be replaced by "DIR[ DIR]"
func_munge_path_list ()
{
case x$2 in
x)
;;
*:)
eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\"
;;
x:*)
eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\"
;;
*::*)
eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\"
eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\"
;;
*)
eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\"
;;
esac
}
# Calculate cc_basename. Skip known compiler wrappers and cross-prefix.
func_cc_basename ()
{
for cc_temp in $*""; do
case $cc_temp in
compile | *[\\/]compile | ccache | *[\\/]ccache ) ;;
distcc | *[\\/]distcc | purify | *[\\/]purify ) ;;
\-*) ;;
*) break;;
esac
done
func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"`
}
# ### END FUNCTIONS SHARED WITH CONFIGURE
_LT_EOF
case $host_os in
aix3*)
cat <<\_LT_EOF >> "$cfgfile"
# AIX sometimes has problems with the GCC collect2 program. For some
# reason, if we set the COLLECT_NAMES environment variable, the problems
# vanish in a puff of smoke.
if test set != "${COLLECT_NAMES+set}"; then
COLLECT_NAMES=
export COLLECT_NAMES
fi
_LT_EOF
;;
esac
ltmain=$ac_aux_dir/ltmain.sh
# We use sed instead of cat because bash on DJGPP gets confused if
# if finds mixed CR/LF and LF-only lines. Since sed operates in
# text mode, it properly converts lines to CR/LF. This bash problem
# is reportedly fixed, but why not run on old versions too?
sed '$q' "$ltmain" >> "$cfgfile" \
|| (rm -f "$cfgfile"; exit 1)
mv -f "$cfgfile" "$ofile" ||
(rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
chmod +x "$ofile"
;;
"disable-rpath":C)
sed < libtool > libtool-2 \
's/^hardcode_libdir_flag_spec.*$'/'hardcode_libdir_flag_spec=" -D__LIBTOOL_RPATH_SED__ "/'
mv libtool-2 libtool
chmod 755 libtool
libtool="./libtool"
;;
esac
done # for ac_tag
as_fn_exit 0
_ACEOF
ac_clean_files=$ac_clean_files_save
test $ac_write_fail = 0 ||
as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
# configure is writing to config.log, and then calls config.status.
# config.status does its own redirection, appending to config.log.
# Unfortunately, on DOS this fails, as config.log is still kept open
# by configure, so config.status won't be able to write to it; its
# output is simply discarded. So we exec the FD to /dev/null,
# effectively closing config.log, so it can be properly (re)opened and
# appended to by config.status. When coming back to configure, we
# need to make the FD available again.
if test "$no_create" != yes; then
ac_cs_success=:
ac_config_status_args=
test "$silent" = yes &&
ac_config_status_args="$ac_config_status_args --quiet"
exec 5>/dev/null
$SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
exec 5>>config.log
# Use ||, not &&, to avoid exiting from the if with $? = 1, which
# would make configure fail if this is the last instruction.
$ac_cs_success || as_fn_exit 1
fi
if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
fi
diff --git a/contrib/unbound/configure.ac b/contrib/unbound/configure.ac
index 098988f55f8a..1b999596d09a 100644
--- a/contrib/unbound/configure.ac
+++ b/contrib/unbound/configure.ac
@@ -1,2286 +1,2289 @@
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.56])
sinclude(acx_nlnetlabs.m4)
sinclude(ax_pthread.m4)
sinclude(acx_python.m4)
sinclude(ac_pkg_swig.m4)
sinclude(dnstap/dnstap.m4)
sinclude(dnscrypt/dnscrypt.m4)
# must be numbers. ac_defun because of later processing
m4_define([VERSION_MAJOR],[1])
-m4_define([VERSION_MINOR],[18])
+m4_define([VERSION_MINOR],[19])
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_MINOR, [VERSION_MINOR])
AC_SUBST(UNBOUND_VERSION_MICRO, [VERSION_MICRO])
LIBUNBOUND_CURRENT=9
-LIBUNBOUND_REVISION=22
+LIBUNBOUND_REVISION=23
LIBUNBOUND_AGE=1
# 1.0.0 had 0:12:0
# 1.0.1 had 0:13:0
# 1.0.2 had 0:14:0
# 1.1.0 had 0:15:0
# 1.1.1 had 0:16:0
# 1.2.0 had 0:17:0
# 1.2.1 had 0:18:0
# 1.3.0 had 1:0:0 # ub_cancel and -export-symbols.
# 1.3.1 had 1:1:0
# 1.3.2 had 1:2:0
# 1.3.3 had 1:3:0
# 1.3.4 had 1:4:0
# 1.4.0-snapshots had 1:5:0
# 1.4.0 had 1:5:0 (not 2:0:0) # ub_result.why_bogus
# 1.4.1 had 2:1:0
# 1.4.2 had 2:2:0
# 1.4.3 had 2:3:0
# 1.4.4 had 2:4:0
# 1.4.5 had 2:5:0
# 1.4.6 had 2:6:0
# 1.4.7 had 2:7:0
# 1.4.8 had 2:8:0
# 1.4.9 had 2:9:0
# 1.4.10 had 2:10:0
# 1.4.11 had 2:11:0
# 1.4.12 had 2:12:0
# 1.4.13 had 2:13:0
# and 1.4.13p1 and 1.4.13.p2
# 1.4.14 had 2:14:0
# 1.4.15 had 3:0:1 # adds ub_version()
# 1.4.16 had 3:1:1
# 1.4.17 had 3:2:1
# 1.4.18 had 3:3:1
# 1.4.19 had 3:4:1
# 1.4.20 had 4:0:2 # adds libunbound.ttl # but shipped 3:5:1
# 1.4.21 had 4:1:2
# 1.4.22 had 4:1:2
# 1.5.0 had 5:3:3 # adds ub_ctx_add_ta_autr
# 1.5.1 had 5:3:3
# 1.5.2 had 5:5:3
# 1.5.3 had 5:6:3
# 1.5.4 had 5:7:3
# 1.5.5 had 5:8:3
# 1.5.6 had 5:9:3
# 1.5.7 had 5:10:3
# 1.5.8 had 6:0:4 # adds ub_ctx_set_stub
# 1.5.9 had 6:1:4
# 1.5.10 had 6:2:4
# 1.6.0 had 6:3:4
# 1.6.1 had 7:0:5 # ub_callback_t typedef renamed to ub_callback_type
# 1.6.2 had 7:1:5
# 1.6.3 had 7:2:5
# 1.6.4 had 7:3:5
# 1.6.5 had 7:4:5
# 1.6.6 had 7:5:5
# 1.6.7 had 7:6:5
# 1.6.8 had 7:7:5
# 1.7.0 had 7:8:5
# 1.7.1 had 7:9:5
# 1.7.2 had 7:10:5
# 1.7.3 had 7:11:5
# 1.8.0 had 8:0:0 # changes the event callback function signature
# 1.8.1 had 8:1:0
# 1.8.2 had 8:2:0
# 1.8.3 had 8:3:0
# 1.9.0 had 9:0:1 # add ub_ctx_set_tls
# 1.9.1 had 9:1:1
# 1.9.2 had 9:2:1
# 1.9.3 had 9:3:1
# 1.9.4 had 9:4:1
# 1.9.5 had 9:5:1
# 1.9.6 had 9:6:1
# 1.10.0 had 9:7:1
# 1.10.1 had 9:8:1
# 1.11.0 had 9:9:1
# 1.12.0 had 9:10:1
# 1.13.0 had 9:11:1
# 1.13.1 had 9:12:1
# 1.13.2 had 9:13:1
# 1.14.0 had 9:14:1
# 1.15.0 had 9:15:1
# 1.16.0 had 9:16:1
# 1.16.1 had 9:17:1
# 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
+# 1.19.0 had 9:23:1
# Current -- the number of the binary API that we're implementing
# Revision -- which iteration of the implementation of the binary
# API are we supplying?
# Age -- How many previous binary API versions do we also
# support?
#
# If we release a new version that does not change the binary API,
# increment Revision.
#
# If we release a new version that changes the binary API, but does
# not break programs compiled against the old binary API, increment
# Current and Age. Set Revision to 0, since this is the first
# implementation of the new API.
#
# Otherwise, we're changing the binary API and breaking backward
# compatibility with old binaries. Increment Current. Set Age to 0,
# since we're backward compatible with no previous APIs. Set Revision
# to 0 too.
AC_SUBST(LIBUNBOUND_CURRENT)
AC_SUBST(LIBUNBOUND_REVISION)
AC_SUBST(LIBUNBOUND_AGE)
cmdln="`echo $@ | sed -e 's/\\\\/\\\\\\\\/g' | sed -e 's/"/\\\\"/'g`"
AC_DEFINE_UNQUOTED(CONFCMDLINE, ["$cmdln"], [Command line arguments used with configure])
CFLAGS="$CFLAGS"
AC_USE_SYSTEM_EXTENSIONS
if test "$ac_cv_header_minix_config_h" = "yes"; then
AC_DEFINE(_NETBSD_SOURCE,1, [Enable for compile on Minix])
fi
dnl
dnl By default set prefix to /usr/local
dnl
case "$prefix" in
NONE)
prefix="/usr/local"
;;
esac
case "$exec_prefix" in
NONE)
exec_prefix="$prefix"
;;
esac
# are we on MinGW?
if uname -s 2>&1 | grep MINGW >/dev/null; then on_mingw="yes"
else
if echo $host | grep mingw >/dev/null; then on_mingw="yes"
else on_mingw="no"; fi
fi
#
# Determine configuration file
# the eval is to evaluate shell expansion twice
UNBOUND_SBIN_DIR=`eval echo "${sbindir}"`
AC_SUBST(UNBOUND_SBIN_DIR)
UNBOUND_SYSCONF_DIR=`eval echo "${sysconfdir}"`
AC_SUBST(UNBOUND_SYSCONF_DIR)
UNBOUND_LOCALSTATE_DIR=`eval echo "${localstatedir}"`
AC_SUBST(UNBOUND_LOCALSTATE_DIR)
if test $on_mingw = "no"; then
ub_conf_file=`eval echo "${sysconfdir}/unbound/unbound.conf"`
else
ub_conf_file="C:\\Program Files\\Unbound\\service.conf"
fi
AC_ARG_WITH([conf_file],
AS_HELP_STRING([--with-conf-file=path],[Pathname to the Unbound configuration file]),
[ub_conf_file="$withval"])
AC_SUBST(ub_conf_file)
ACX_ESCAPE_BACKSLASH($ub_conf_file, hdr_config)
AC_DEFINE_UNQUOTED(CONFIGFILE, ["$hdr_config"], [Pathname to the Unbound configuration file])
ub_conf_dir=`AS_DIRNAME(["$ub_conf_file"])`
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",
if test $on_mingw = no; then
UNBOUND_RUN_DIR=`dirname "$ub_conf_file"`
else
UNBOUND_RUN_DIR=""
fi
)
AC_SUBST(UNBOUND_RUN_DIR)
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",
if test $on_mingw = no; then
UNBOUND_CHROOT_DIR="$UNBOUND_RUN_DIR"
else
UNBOUND_CHROOT_DIR=""
fi
)
AC_SUBST(UNBOUND_CHROOT_DIR)
ACX_ESCAPE_BACKSLASH($UNBOUND_CHROOT_DIR, hdr_chroot)
AC_DEFINE_UNQUOTED(CHROOT_DIR, ["$hdr_chroot"], [Directory to chroot to])
AC_ARG_WITH(share-dir,
AS_HELP_STRING([--with-share-dir=path],[set default directory with shared data (by default same as share/unbound)]),
UNBOUND_SHARE_DIR="$withval",
UNBOUND_SHARE_DIR="$UNBOUND_RUN_DIR")
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",
if test $on_mingw = no; then
UNBOUND_PIDFILE="$UNBOUND_RUN_DIR/unbound.pid"
else
UNBOUND_PIDFILE=""
fi
)
AC_SUBST(UNBOUND_PIDFILE)
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",
if test $on_mingw = no; then
UNBOUND_ROOTKEY_FILE="$UNBOUND_RUN_DIR/root.key"
else
UNBOUND_ROOTKEY_FILE="C:\\Program Files\\Unbound\\root.key"
fi
)
AC_SUBST(UNBOUND_ROOTKEY_FILE)
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",
if test $on_mingw = no; then
UNBOUND_ROOTCERT_FILE="$UNBOUND_RUN_DIR/icannbundle.pem"
else
UNBOUND_ROOTCERT_FILE="C:\\Program Files\\Unbound\\icannbundle.pem"
fi
)
AC_SUBST(UNBOUND_ROOTCERT_FILE)
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",
UNBOUND_USERNAME="unbound")
AC_SUBST(UNBOUND_USERNAME)
AC_DEFINE_UNQUOTED(UB_USERNAME, ["$UNBOUND_USERNAME"], [default username])
AC_DEFINE(WINVER, 0x0502, [the version of the windows API enabled])
ACX_RSRC_VERSION(wnvs)
AC_DEFINE_UNQUOTED(RSRC_PACKAGE_VERSION, [$wnvs], [version number for resource files])
# Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
AC_LANG([C])
# allow user to override the -g -O2 flags.
default_cflags=no
if test "x$CFLAGS" = "x" ; then
ACX_CHECK_COMPILER_FLAG(g, [CFLAGS="$CFLAGS -g"])
ACX_CHECK_COMPILER_FLAG(O2, [CFLAGS="$CFLAGS -O2"])
default_cflags=yes
fi
-AC_PROG_CC
+m4_version_prereq([2.70], [AC_PROG_CC], [AC_PROG_CC_STDC])
ACX_DEPFLAG
ACX_DETERMINE_EXT_FLAGS_UNBOUND
# 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";
else debug_enabled="$enable_checking"; fi
AC_SUBST(debug_enabled)
case "$debug_enabled" in
yes)
ACX_CHECK_COMPILER_FLAG(W, [CFLAGS="$CFLAGS -W"])
ACX_CHECK_COMPILER_FLAG(Wall, [CFLAGS="$CFLAGS -Wall"])
ACX_CHECK_COMPILER_FLAG(Wextra, [CFLAGS="$CFLAGS -Wextra"])
ACX_CHECK_COMPILER_FLAG(Wdeclaration-after-statement, [CFLAGS="$CFLAGS -Wdeclaration-after-statement"])
AC_DEFINE([UNBOUND_DEBUG], [], [define this to enable debug checks.])
;;
no|*)
# nothing to do.
;;
esac
if test "$default_cflags" = "yes"; then
# only when CFLAGS was "" at the start, if the users wants to
# override we shouldn't add default cflags, because they wouldn't
# be able to turn off these options and set the CFLAGS wanted.
ACX_CHECK_FLTO
ACX_CHECK_PIE
ACX_CHECK_RELRO_NOW
fi
AC_C_INLINE
ACX_CHECK_FORMAT_ATTRIBUTE
ACX_CHECK_UNUSED_ATTRIBUTE
AC_DEFUN([CHECK_WEAK_ATTRIBUTE],
[AC_REQUIRE([AC_PROG_CC])
AC_MSG_CHECKING(whether the C compiler (${CC-cc}) accepts the "weak" attribute)
AC_CACHE_VAL(ac_cv_c_weak_attribute,
[ac_cv_c_weak_attribute=no
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <stdio.h>
__attribute__((weak)) void f(int x) { printf("%d", x); }
]], [[
f(1);
]])],[ac_cv_c_weak_attribute="yes"],[ac_cv_c_weak_attribute="no"])
])
AC_MSG_RESULT($ac_cv_c_weak_attribute)
if test $ac_cv_c_weak_attribute = yes; then
AC_DEFINE(HAVE_ATTR_WEAK, 1, [Whether the C compiler accepts the "weak" attribute])
AC_DEFINE(ATTR_WEAK, [__attribute__((weak))], [apply the weak attribute to a symbol])
fi
])dnl End of CHECK_WEAK_ATTRIBUTE
CHECK_WEAK_ATTRIBUTE
AC_DEFUN([CHECK_NORETURN_ATTRIBUTE],
[AC_REQUIRE([AC_PROG_CC])
AC_MSG_CHECKING(whether the C compiler (${CC-cc}) accepts the "noreturn" attribute)
AC_CACHE_VAL(ac_cv_c_noreturn_attribute,
[ac_cv_c_noreturn_attribute=no
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <stdio.h>
__attribute__((noreturn)) void f(int x) { printf("%d", x); }
]], [[
f(1);
]])],[ac_cv_c_noreturn_attribute="yes"],[ac_cv_c_noreturn_attribute="no"])
])
AC_MSG_RESULT($ac_cv_c_noreturn_attribute)
if test $ac_cv_c_noreturn_attribute = yes; then
AC_DEFINE(HAVE_ATTR_NORETURN, 1, [Whether the C compiler accepts the "noreturn" attribute])
AC_DEFINE(ATTR_NORETURN, [__attribute__((__noreturn__))], [apply the noreturn attribute to a function that exits the program])
fi
])dnl End of CHECK_NORETURN_ATTRIBUTE
CHECK_NORETURN_ATTRIBUTE
if test "$srcdir" != "."; then
CPPFLAGS="$CPPFLAGS -I$srcdir"
fi
AC_DEFUN([ACX_YYLEX_DESTROY], [
AC_MSG_CHECKING([for yylex_destroy])
if echo %% | $LEX -t 2>&1 | grep yylex_destroy >/dev/null 2>&1; then
AC_DEFINE(LEX_HAS_YYLEX_DESTROY, 1, [if lex has yylex_destroy])
AC_MSG_RESULT(yes)
else AC_MSG_RESULT(no);
LEX=":"
fi
])
AC_DEFUN([ACX_YYLEX_OPTION], [
AC_MSG_CHECKING([for lex %option])
if cat <<EOF | $LEX -t 2>&1 | grep yy_delete_buffer >/dev/null 2>&1; then
%option nounput
%%
EOF
AC_MSG_RESULT(yes)
else AC_MSG_RESULT(no);
LEX=":"
fi
])
AC_PROG_LEX([noyywrap])
if test "$LEX" != "" -a "$LEX" != ":"; then
ACX_YYLEX_DESTROY
fi
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
# pkg-config is only needed for these options, do not require it otherwise
if test "$enable_systemd" = "yes" -o "$with_pyunbound" = "yes" -o "$with_pythonmod" = "yes"; then
PKG_PROG_PKG_CONFIG
fi
# Checks for header files.
AC_CHECK_HEADERS([stdarg.h stdbool.h netinet/in.h netinet/tcp.h sys/param.h sys/select.h sys/socket.h sys/un.h sys/uio.h sys/resource.h arpa/inet.h syslog.h netdb.h sys/wait.h pwd.h glob.h grp.h login_cap.h winsock2.h ws2tcpip.h endian.h sys/endian.h libkern/OSByteOrder.h sys/ipc.h sys/shm.h ifaddrs.h poll.h],,, [AC_INCLUDES_DEFAULT])
# net/if.h portability for Darwin see:
# https://www.gnu.org/software/autoconf/manual/autoconf-2.69/html_node/Header-Portability.html
AC_CHECK_HEADERS([net/if.h],,, [
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
])
# Check for Apple header. This uncovers TARGET_OS_IPHONE, TARGET_OS_TV or TARGET_OS_WATCH
AC_CHECK_HEADERS([TargetConditionals.h],,, [AC_INCLUDES_DEFAULT])
AC_CHECK_HEADERS([netioapi.h],,, [AC_INCLUDES_DEFAULT
#if HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_SYS_UIO_H
#include <sys/uio.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_WINSOCK2_H
#include <winsock2.h>
#endif
#ifdef HAVE_WS2TCPIP_H
#include <ws2tcpip.h>
#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)
AC_CHECK_TYPE(int16_t, short)
AC_CHECK_TYPE(int32_t, int)
AC_CHECK_TYPE(int64_t, long long)
AC_CHECK_TYPE(uint8_t, unsigned char)
AC_CHECK_TYPE(uint16_t, unsigned short)
AC_CHECK_TYPE(uint32_t, unsigned int)
AC_CHECK_TYPE(uint64_t, unsigned long long)
AC_TYPE_SIZE_T
AC_CHECK_TYPE(ssize_t, int)
AC_TYPE_UID_T
AC_TYPE_PID_T
AC_TYPE_OFF_T
ACX_TYPE_U_CHAR
ACX_TYPE_RLIM_T
ACX_TYPE_SOCKLEN_T
ACX_TYPE_IN_ADDR_T
ACX_TYPE_IN_PORT_T
ACX_CHECK_MEMCMP_SIGNED
AC_CHECK_SIZEOF(time_t,,[
AC_INCLUDES_DEFAULT
#ifdef TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
])
AC_CHECK_SIZEOF(size_t)
# add option to disable the evil rpath
ACX_ARG_RPATH
AC_SUBST(RUNTIME_PATH)
# check to see if libraries are needed for these functions.
AC_SEARCH_LIBS([inet_pton], [nsl])
AC_SEARCH_LIBS([socket], [socket])
# check whether strptime also works
AC_DEFUN([AC_CHECK_STRPTIME_WORKS],
[AC_REQUIRE([AC_PROG_CC])
AC_MSG_CHECKING(whether strptime works)
if test c${cross_compiling} = cno; then
AC_RUN_IFELSE([AC_LANG_SOURCE([[
#define _XOPEN_SOURCE 600
#include <time.h>
int main(void) { struct tm tm; char *res;
res = strptime("2010-07-15T00:00:00+00:00", "%t%Y%t-%t%m%t-%t%d%tT%t%H%t:%t%M%t:%t%S%t", &tm);
if (!res) return 2;
res = strptime("20070207111842", "%Y%m%d%H%M%S", &tm);
if (!res) return 1; return 0; }
-]])] , [eval "ac_cv_c_strptime_works=yes"], [eval "ac_cv_c_strptime_works=no"])
+]])] , [eval "ac_cv_c_strptime_works=yes"], [eval "ac_cv_c_strptime_works=no"],
+[eval "ac_cv_c_strptime_works=maybe"])
else
eval "ac_cv_c_strptime_works=maybe"
fi
AC_MSG_RESULT($ac_cv_c_strptime_works)
if test $ac_cv_c_strptime_works = no; then
AC_LIBOBJ(strptime)
else
AC_DEFINE_UNQUOTED([STRPTIME_WORKS], 1, [use default strptime.])
fi
])dnl
# check some functions of the OS before linking libs (while still runnable).
AC_FUNC_CHOWN
AC_FUNC_FORK
AC_DEFINE(RETSIGTYPE,void,[Return type of signal handlers, but autoconf 2.70 says 'your code may safely assume C89 semantics that RETSIGTYPE is void.'])
AC_FUNC_FSEEKO
ACX_SYS_LARGEFILE
ACX_CHECK_NONBLOCKING_BROKEN
ACX_MKDIR_ONE_ARG
AC_CHECK_FUNCS([strptime],[AC_CHECK_STRPTIME_WORKS],[AC_LIBOBJ([strptime])])
# check if we can use SO_REUSEPORT
reuseport_default=0
if echo "$host" | $GREP -i -e linux >/dev/null; then reuseport_default=1; fi
if echo "$host" | $GREP -i -e dragonfly >/dev/null; then reuseport_default=1; fi
if test "$reuseport_default" = 1; then
AC_DEFINE(REUSEPORT_DEFAULT, 1, [if REUSEPORT is enabled by default])
else
AC_DEFINE(REUSEPORT_DEFAULT, 0, [if REUSEPORT is enabled by default])
fi
# Include systemd.m4 - begin
sinclude(systemd.m4)
# 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-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 ]),
, )
if test x_$enable_alloc_nonregional = x_yes; then
AC_DEFINE(UNBOUND_ALLOC_NONREGIONAL, 1, [use malloc not regions, for debug use])
fi
if test x_$enable_alloc_checks = x_yes; then
AC_DEFINE(UNBOUND_ALLOC_STATS, 1, [use statistics for allocs and frees, for debug use])
SLDNS_ALLOCCHECK_EXTRA_OBJ="alloc.lo log.lo"
AC_SUBST(SLDNS_ALLOCCHECK_EXTRA_OBJ)
ASYNCLOOK_ALLOCCHECK_EXTRA_OBJ="alloc.lo"
AC_SUBST(ASYNCLOOK_ALLOCCHECK_EXTRA_OBJ)
else
if test x_$enable_alloc_lite = x_yes; then
AC_DEFINE(UNBOUND_ALLOC_LITE, 1, [use to enable lightweight alloc assertions, for debug use])
else
ACX_FUNC_MALLOC([unbound])
fi
fi
# check windows threads (we use them, not pthreads, on windows).
if test "$on_mingw" = "yes"; then
# check windows threads
AC_CHECK_HEADERS([windows.h],,, [AC_INCLUDES_DEFAULT])
AC_MSG_CHECKING([for CreateThread])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif
], [
HANDLE t = CreateThread(NULL, 0, NULL, NULL, 0, NULL);
])],
AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_WINDOWS_THREADS, 1, [Using Windows threads])
,
AC_MSG_RESULT(no)
)
else
# not on mingw, check thread libraries.
# check for thread library.
# 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.]),
[ ],[ withval="yes" ])
ub_have_pthreads=no
if test x_$withval != x_no; then
AX_PTHREAD([
AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.])
if test -n "$PTHREAD_LIBS"; then
LIBS="$PTHREAD_LIBS $LIBS"
fi
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
CC="$PTHREAD_CC"
ub_have_pthreads=yes
AC_CHECK_TYPES([pthread_spinlock_t, pthread_rwlock_t],,,[#include <pthread.h>])
AC_CHECK_SIZEOF([unsigned long])
AC_CHECK_SIZEOF(pthread_t)
if echo "$CFLAGS" | $GREP -e "-pthread" >/dev/null; then
AC_MSG_CHECKING([if -pthread unused during linking])
# catch clang warning 'argument unused during compilation'
AC_LANG_CONFTEST([AC_LANG_SOURCE(AC_INCLUDES_DEFAULT
[[
int main(void) {return 0;}
]])])
pthread_unused="yes"
# 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
# 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
if test $? -ne 0; then
AC_MSG_RESULT(yes)
CFLAGS=`echo "$CFLAGS" | sed -e 's/-pthread//'`
PTHREAD_CFLAGS_ONLY="-pthread"
AC_SUBST(PTHREAD_CFLAGS_ONLY)
else
AC_MSG_RESULT(no)
fi
else
AC_MSG_RESULT(no)
fi # endif cc successful
rm -f conftest conftest.c conftest.o
fi # endif -pthread in CFLAGS
])
fi
# 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
if test x_$ub_have_pthreads != x_no; then
AC_MSG_WARN([Have pthreads already, ignoring --with-solaris-threads])
else
AC_SEARCH_LIBS(thr_create, [thread],
[
AC_DEFINE(HAVE_SOLARIS_THREADS, 1, [Using Solaris threads])
ACX_CHECK_COMPILER_FLAG(mt, [CFLAGS="$CFLAGS -mt"],
[CFLAGS="$CFLAGS -D_REENTRANT"])
ub_have_sol_threads=yes
] , [
AC_MSG_ERROR([no solaris threads found.])
])
fi
fi
fi # end of non-mingw check of thread libraries
# Check for SYSLOG_FACILITY
AC_ARG_WITH(syslog-facility, AS_HELP_STRING([--with-syslog-facility=LOCAL0 - LOCAL7],[ set SYSLOG_FACILITY, default DAEMON ]),
[ UNBOUND_SYSLOG_FACILITY="$withval" ], [])
case "${UNBOUND_SYSLOG_FACILITY}" in
LOCAL[[0-7]]) UNBOUND_SYSLOG_FACILITY="LOG_${UNBOUND_SYSLOG_FACILITY}" ;;
*) UNBOUND_SYSLOG_FACILITY="LOG_DAEMON" ;;
esac
AC_DEFINE_UNQUOTED(UB_SYSLOG_FACILITY,${UNBOUND_SYSLOG_FACILITY},[the SYSLOG_FACILITY to use, default LOG_DAEMON])
# Check for dynamic library module
AC_ARG_WITH(dynlibmodule,
AS_HELP_STRING([--with-dynlibmodule],[build dynamic library module, or --without-dynlibmodule to disable it. (default=no)]),
[], [ withval="no" ])
if test x_$withval != x_no; then
AC_DEFINE(WITH_DYNLIBMODULE, 1, [Define if you want dynlib module.])
WITH_DYNLIBMODULE=yes
AC_SUBST(WITH_DYNLIBMODULE)
DYNLIBMOD_OBJ="dynlibmod.lo"
AC_SUBST(DYNLIBMOD_OBJ)
DYNLIBMOD_HEADER='$(srcdir)/dynlibmod/dynlibmod.h'
AC_SUBST(DYNLIBMOD_HEADER)
if test $on_mingw = "no"; then
# link with -ldl if not already there, for all executables because
# dlopen call is in the dynlib module. For unbound executable, also
# export symbols.
AC_SEARCH_LIBS([dlopen], [dl])
DYNLIBMOD_EXTRALIBS="-export-dynamic"
else
DYNLIBMOD_EXTRALIBS="-Wl,--export-all-symbols,--out-implib,libunbound.dll.a"
fi
AC_SUBST(DYNLIBMOD_EXTRALIBS)
fi
# Check for PyUnbound
AC_ARG_WITH(pyunbound,
AS_HELP_STRING([--with-pyunbound],[build PyUnbound, or --without-pyunbound to skip it. (default=no)]),
[], [ withval="no" ])
ub_test_python=no
ub_with_pyunbound=no
if test x_$withval != x_no; then
ub_with_pyunbound=yes
ub_test_python=yes
fi
# Check for Python module
AC_ARG_WITH(pythonmodule,
AS_HELP_STRING([--with-pythonmodule],[build Python module, or --without-pythonmodule to disable script engine. (default=no)]),
[], [ withval="no" ])
ub_with_pythonmod=no
if test x_$withval != x_no; then
ub_with_pythonmod=yes
ub_test_python=yes
fi
# Check for Python & SWIG only on PyUnbound or PyModule
if test x_$ub_test_python != x_no; then
# Check for Python
ub_have_python=no
ac_save_LIBS="$LIBS" dnl otherwise AC_PYTHON_DEVEL thrashes $LIBS
AC_PYTHON_DEVEL
if test ! -z "$PYTHON_VERSION"; 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
[PY_MAJOR_VERSION="`$PYTHON -c \"import sys; print(sys.version_info[0])\"`"]
AC_SUBST(PY_MAJOR_VERSION)
# Have Python
AC_DEFINE(HAVE_PYTHON,1,[Define if you have Python libraries and header files.])
if test x_$ub_with_pythonmod != x_no; then
if test -n "$LIBS"; then
LIBS="$PYTHON_LDFLAGS $LIBS"
else
LIBS="$PYTHON_LDFLAGS"
fi
fi
PYTHON_LIBS="$PYTHON_LDFLAGS"
AC_SUBST(PYTHON_LIBS)
if test -n "$CPPFLAGS"; then
CPPFLAGS="$CPPFLAGS $PYTHON_CPPFLAGS"
else
CPPFLAGS="$PYTHON_CPPFLAGS"
fi
if test "$PYTHON_LIBDIR" != "/usr/lib" -a "$PYTHON_LIBDIR" != "" -a "$PYTHON_LIBDIR" != "/usr/lib64"; then
ACX_RUNTIME_PATH_ADD([$PYTHON_LIBDIR])
fi
ub_have_python=yes
PKG_CHECK_EXISTS(["python${PY_MAJOR_VERSION}"],
[PC_PY_DEPENDENCY="python${PY_MAJOR_VERSION}"],
[PC_PY_DEPENDENCY="python"])
AC_SUBST(PC_PY_DEPENDENCY)
# Check for SWIG
ub_have_swig=no
AC_ARG_ENABLE(swig-version-check, AS_HELP_STRING([--disable-swig-version-check],[Disable swig version check to build python modules with older swig even though that is unreliable]))
if test "$enable_swig_version_check" = "yes"; then
AC_PROG_SWIG(2.0.1)
else
AC_PROG_SWIG
fi
AC_MSG_CHECKING(SWIG)
if test ! -x "$SWIG"; then
AC_MSG_ERROR([failed to find swig tool, install it, or do not build Python module and PyUnbound])
else
AC_DEFINE(HAVE_SWIG, 1, [Define if you have Swig libraries and header files.])
AC_SUBST(swig, "$SWIG")
AC_MSG_RESULT(present)
# If have Python & SWIG
# Declare PythonMod
if test x_$ub_with_pythonmod != x_no; then
AC_DEFINE(WITH_PYTHONMODULE, 1, [Define if you want Python module.])
WITH_PYTHONMODULE=yes
AC_SUBST(WITH_PYTHONMODULE)
PYTHONMOD_OBJ="pythonmod.lo pythonmod_utils.lo"
AC_SUBST(PYTHONMOD_OBJ)
PYTHONMOD_HEADER='$(srcdir)/pythonmod/pythonmod.h'
AC_SUBST(PYTHONMOD_HEADER)
PYTHONMOD_INSTALL=pythonmod-install
AC_SUBST(PYTHONMOD_INSTALL)
PYTHONMOD_UNINSTALL=pythonmod-uninstall
AC_SUBST(PYTHONMOD_UNINSTALL)
fi
# Declare PyUnbound
if test x_$ub_with_pyunbound != x_no; then
AC_DEFINE(WITH_PYUNBOUND, 1, [Define if you want PyUnbound.])
WITH_PYUNBOUND=yes
AC_SUBST(WITH_PYUNBOUND)
PYUNBOUND_OBJ="libunbound_wrap.lo"
AC_SUBST(PYUNBOUND_OBJ)
PYUNBOUND_TARGET="_unbound.la"
AC_SUBST(PYUNBOUND_TARGET)
PYUNBOUND_INSTALL=pyunbound-install
AC_SUBST(PYUNBOUND_INSTALL)
PYUNBOUND_UNINSTALL=pyunbound-uninstall
AC_SUBST(PYUNBOUND_UNINSTALL)
fi
fi
else
AC_MSG_RESULT([*** Python libraries not found, won't build PythonMod or PyUnbound ***])
ub_with_pyunbound=no
ub_with_pythonmod=no
fi
fi
if test "`uname`" = "NetBSD"; then
NETBSD_LINTFLAGS='"-D__RENAME(x)=" -D_NETINET_IN_H_'
AC_SUBST(NETBSD_LINTFLAGS)
fi
if test "`uname`" = "Linux"; then
# splint cannot parse modern c99 header files
GCC_DOCKER_LINTFLAGS='-syntax'
AC_SUBST(GCC_DOCKER_LINTFLAGS)
fi
CONFIG_DATE=`date +%Y%m%d`
AC_SUBST(CONFIG_DATE)
# Checks for libraries.
# libnss
USE_NSS="no"
AC_ARG_WITH([nss], AS_HELP_STRING([--with-nss=path],[use libnss instead of openssl, installed at path.]),
[
USE_NSS="yes"
AC_DEFINE(HAVE_NSS, 1, [Use libnss for crypto])
if test "$withval" != "" -a "$withval" != "yes"; then
CPPFLAGS="$CPPFLAGS -I$withval/include/nss3"
LDFLAGS="$LDFLAGS -L$withval/lib"
ACX_RUNTIME_PATH_ADD([$withval/lib])
CPPFLAGS="-I$withval/include/nspr4 $CPPFLAGS"
else
CPPFLAGS="$CPPFLAGS -I/usr/include/nss3"
CPPFLAGS="-I/usr/include/nspr4 $CPPFLAGS"
fi
LIBS="$LIBS -lnss3 -lnspr4"
SSLLIB=""
PC_CRYPTO_DEPENDENCY="nss nspr"
AC_SUBST(PC_CRYPTO_DEPENDENCY)
]
)
# libnettle
USE_NETTLE="no"
AC_ARG_WITH([nettle], AS_HELP_STRING([--with-nettle=path],[use libnettle as crypto library, installed at path.]),
[
USE_NETTLE="yes"
AC_DEFINE(HAVE_NETTLE, 1, [Use libnettle for crypto])
AC_CHECK_HEADERS([nettle/dsa-compat.h],,, [AC_INCLUDES_DEFAULT])
if test "$withval" != "" -a "$withval" != "yes"; then
CPPFLAGS="$CPPFLAGS -I$withval/include/nettle"
LDFLAGS="$LDFLAGS -L$withval/lib"
ACX_RUNTIME_PATH_ADD([$withval/lib])
else
CPPFLAGS="$CPPFLAGS -I/usr/include/nettle"
fi
LIBS="$LIBS -lhogweed -lnettle -lgmp"
SSLLIB=""
PC_CRYPTO_DEPENDENCY="hogweed nettle"
AC_SUBST(PC_CRYPTO_DEPENDENCY)
]
)
# openssl
if test $USE_NSS = "no" -a $USE_NETTLE = "no"; then
ACX_WITH_SSL
ACX_LIB_SSL
SSLLIB="-lssl"
PC_CRYPTO_DEPENDENCY=""
AC_SUBST(PC_CRYPTO_DEPENDENCY)
# check if -lcrypt32 is needed because CAPIENG needs that. (on windows)
BAKLIBS="$LIBS"
LIBS="-lssl $LIBS"
AC_MSG_CHECKING([if libssl needs -lcrypt32])
AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[
int EVP_sha256(void);
(void)EVP_sha256();
]])], [
AC_MSG_RESULT([no])
LIBS="$BAKLIBS"
], [
AC_MSG_RESULT([yes])
LIBS="$BAKLIBS"
LIBS="$LIBS -lcrypt32"
])
AC_MSG_CHECKING([for LibreSSL])
if grep VERSION_TEXT $ssldir_include/openssl/opensslv.h | grep "LibreSSL" >/dev/null; then
AC_MSG_RESULT([yes])
AC_DEFINE([HAVE_LIBRESSL], [1], [Define if we have LibreSSL])
# libressl provides these compat functions, but they may also be
# declared by the OS in libc. See if they have been declared.
AC_CHECK_DECLS([strlcpy,strlcat,arc4random,arc4random_uniform])
else
AC_MSG_RESULT([no])
fi
AC_CHECK_HEADERS([openssl/conf.h openssl/engine.h openssl/bn.h openssl/dh.h openssl/dsa.h openssl/rsa.h openssl/core_names.h openssl/param_build.h],,, [AC_INCLUDES_DEFAULT])
AC_CHECK_FUNCS([OPENSSL_config EVP_sha1 EVP_sha256 EVP_sha512 FIPS_mode EVP_default_properties_is_fips_enabled EVP_MD_CTX_new OpenSSL_add_all_digests OPENSSL_init_crypto EVP_cleanup ENGINE_cleanup ERR_load_crypto_strings CRYPTO_cleanup_all_ex_data ERR_free_strings RAND_cleanup DSA_SIG_set0 EVP_dss1 EVP_DigestVerify EVP_aes_256_cbc EVP_EncryptInit_ex HMAC_Init_ex CRYPTO_THREADID_set_callback EVP_MAC_CTX_set_params OSSL_PARAM_BLD_new BIO_set_callback_ex])
# these check_funcs need -lssl
BAKLIBS="$LIBS"
LIBS="-lssl $LIBS"
AC_CHECK_FUNCS([OPENSSL_init_ssl SSL_CTX_set_security_level SSL_set1_host SSL_get0_peername X509_VERIFY_PARAM_set1_host SSL_CTX_set_ciphersuites SSL_CTX_set_tlsext_ticket_key_evp_cb SSL_CTX_set_alpn_select_cb SSL_get0_alpn_selected SSL_CTX_set_alpn_protos SSL_get1_peer_certificate])
LIBS="$BAKLIBS"
AC_CHECK_DECLS([SSL_COMP_get_compression_methods,sk_SSL_COMP_pop_free,SSL_CTX_set_ecdh_auto], [], [], [
AC_INCLUDES_DEFAULT
#ifdef HAVE_OPENSSL_ERR_H
#include <openssl/err.h>
#endif
#ifdef HAVE_OPENSSL_RAND_H
#include <openssl/rand.h>
#endif
#ifdef HAVE_OPENSSL_CONF_H
#include <openssl/conf.h>
#endif
#ifdef HAVE_OPENSSL_ENGINE_H
#include <openssl/engine.h>
#endif
#include <openssl/ssl.h>
#include <openssl/evp.h>
])
if test "$ac_cv_func_HMAC_Init_ex" = "yes"; then
# check function return type.
AC_MSG_CHECKING(the return type of HMAC_Init_ex)
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
#ifdef HAVE_OPENSSL_ERR_H
#include <openssl/err.h>
#endif
#ifdef HAVE_OPENSSL_RAND_H
#include <openssl/rand.h>
#endif
#ifdef HAVE_OPENSSL_CONF_H
#include <openssl/conf.h>
#endif
#ifdef HAVE_OPENSSL_ENGINE_H
#include <openssl/engine.h>
#endif
#include <openssl/ssl.h>
#include <openssl/evp.h>
], [
HMAC_CTX* hmac_ctx = NULL;
void* hmac_key = NULL;
const EVP_MD* digest = NULL;
int x = HMAC_Init_ex(hmac_ctx, hmac_key, 32, digest, NULL);
(void)x;
])], [
AC_MSG_RESULT(int)
], [
AC_MSG_RESULT(void)
AC_DEFINE([HMAC_INIT_EX_RETURNS_VOID], 1, [If HMAC_Init_ex() returns void])
])
fi
fi
AC_SUBST(SSLLIB)
# libbsd
AC_ARG_WITH([libbsd], AS_HELP_STRING([--with-libbsd],[Use portable libbsd functions]), [
AC_CHECK_HEADERS([bsd/string.h bsd/stdlib.h],,, [AC_INCLUDES_DEFAULT])
if test "x$ac_cv_header_bsd_string_h" = xyes -a "x$ac_cv_header_bsd_stdlib_h" = xyes; then
for func in strlcpy strlcat arc4random arc4random_uniform reallocarray; do
AC_SEARCH_LIBS([$func], [bsd], [
AC_DEFINE(HAVE_LIBBSD, 1, [Use portable libbsd functions])
PC_LIBBSD_DEPENDENCY=libbsd
AC_SUBST(PC_LIBBSD_DEPENDENCY)
])
done
fi
])
AC_ARG_ENABLE(sha1, AS_HELP_STRING([--disable-sha1],[Disable SHA1 RRSIG support, does not disable nsec3 support]))
case "$enable_sha1" in
no)
;;
yes|*)
AC_DEFINE([USE_SHA1], [1], [Define this to enable SHA1 support.])
;;
esac
AC_ARG_ENABLE(sha2, AS_HELP_STRING([--disable-sha2],[Disable SHA256 and SHA512 RRSIG support]))
case "$enable_sha2" in
no)
;;
yes|*)
AC_DEFINE([USE_SHA2], [1], [Define this to enable SHA256 and SHA512 support.])
;;
esac
AC_ARG_ENABLE(subnet, AS_HELP_STRING([--enable-subnet],[Enable client subnet]))
case "$enable_subnet" in
yes)
AC_DEFINE([CLIENT_SUBNET], [1], [Define this to enable client subnet option.])
SUBNET_OBJ="edns-subnet.lo subnetmod.lo addrtree.lo subnet-whitelist.lo"
AC_SUBST(SUBNET_OBJ)
SUBNET_HEADER='$(srcdir)/edns-subnet/subnetmod.h $(srcdir)/edns-subnet/edns-subnet.h $(srcdir)/edns-subnet/subnet-whitelist.h $(srcdir)/edns-subnet/addrtree.h'
AC_SUBST(SUBNET_HEADER)
;;
no|*)
;;
esac
# check whether gost also works
AC_DEFUN([AC_CHECK_GOST_WORKS],
[AC_REQUIRE([AC_PROG_CC])
AC_MSG_CHECKING([if GOST works])
if test c${cross_compiling} = cno; then
BAKCFLAGS="$CFLAGS"
if test -n "$ssldir"; then
CFLAGS="$CFLAGS -Wl,-rpath,$ssldir_lib"
fi
AC_RUN_IFELSE([AC_LANG_SOURCE([[
#include <string.h>
#include <openssl/ssl.h>
#include <openssl/evp.h>
#include <openssl/engine.h>
#include <openssl/conf.h>
/* routine to load gost (from sldns) */
int load_gost_id(void)
{
static int gost_id = 0;
const EVP_PKEY_ASN1_METHOD* meth;
ENGINE* e;
if(gost_id) return gost_id;
/* see if configuration loaded gost implementation from other engine*/
meth = EVP_PKEY_asn1_find_str(NULL, "gost2001", -1);
if(meth) {
EVP_PKEY_asn1_get0_info(&gost_id, NULL, NULL, NULL, NULL, meth);
return gost_id;
}
/* see if engine can be loaded already */
e = ENGINE_by_id("gost");
if(!e) {
/* load it ourself, in case statically linked */
ENGINE_load_builtin_engines();
ENGINE_load_dynamic();
e = ENGINE_by_id("gost");
}
if(!e) {
/* no gost engine in openssl */
return 0;
}
if(!ENGINE_set_default(e, ENGINE_METHOD_ALL)) {
ENGINE_finish(e);
ENGINE_free(e);
return 0;
}
meth = EVP_PKEY_asn1_find_str(&e, "gost2001", -1);
if(!meth) {
/* algo not found */
ENGINE_finish(e);
ENGINE_free(e);
return 0;
}
EVP_PKEY_asn1_get0_info(&gost_id, NULL, NULL, NULL, NULL, meth);
return gost_id;
}
int main(void) {
EVP_MD_CTX* ctx;
const EVP_MD* md;
unsigned char digest[64]; /* its a 256-bit digest, so uses 32 bytes */
const char* str = "Hello world";
const unsigned char check[] = {
0x40 , 0xed , 0xf8 , 0x56 , 0x5a , 0xc5 , 0x36 , 0xe1 ,
0x33 , 0x7c , 0x7e , 0x87 , 0x62 , 0x1c , 0x42 , 0xe0 ,
0x17 , 0x1b , 0x5e , 0xce , 0xa8 , 0x46 , 0x65 , 0x4d ,
0x8d , 0x3e , 0x22 , 0x9b , 0xe1 , 0x30 , 0x19 , 0x9d
};
OPENSSL_config(NULL);
(void)load_gost_id();
md = EVP_get_digestbyname("md_gost94");
if(!md) return 1;
memset(digest, 0, sizeof(digest));
ctx = EVP_MD_CTX_create();
if(!ctx) return 2;
if(!EVP_DigestInit_ex(ctx, md, NULL)) return 3;
if(!EVP_DigestUpdate(ctx, str, 10)) return 4;
if(!EVP_DigestFinal_ex(ctx, digest, NULL)) return 5;
/* uncomment to see the hash calculated.
{int i;
for(i=0; i<32; i++)
printf(" %2.2x", (int)digest[i]);
printf("\n");}
*/
if(memcmp(digest, check, sizeof(check)) != 0)
return 6;
return 0;
}
-]])] , [eval "ac_cv_c_gost_works=yes"], [eval "ac_cv_c_gost_works=no"])
+]])] , [eval "ac_cv_c_gost_works=yes"], [eval "ac_cv_c_gost_works=no"],
+[eval "ac_cv_c_gost_works=maybe"])
CFLAGS="$BAKCFLAGS"
else
eval "ac_cv_c_gost_works=maybe"
fi
AC_MSG_RESULT($ac_cv_c_gost_works)
])dnl
AC_ARG_ENABLE(gost, AS_HELP_STRING([--disable-gost],[Disable GOST support]))
use_gost="no"
if test $USE_NSS = "no" -a $USE_NETTLE = "no"; then
case "$enable_gost" in
no)
;;
*)
AC_CHECK_FUNC(EVP_PKEY_set_type_str, [:],[AC_MSG_ERROR([OpenSSL 1.0.0 is needed for GOST support])])
AC_CHECK_FUNC(EC_KEY_new, [], [AC_MSG_ERROR([OpenSSL does not support ECC, needed for GOST support])])
AC_CHECK_GOST_WORKS
if test "$ac_cv_c_gost_works" != no; then
use_gost="yes"
AC_DEFINE([USE_GOST], [1], [Define this to enable GOST support.])
fi
;;
esac
fi dnl !USE_NSS && !USE_NETTLE
AC_ARG_ENABLE(ecdsa, AS_HELP_STRING([--disable-ecdsa],[Disable ECDSA support]))
use_ecdsa="no"
case "$enable_ecdsa" in
no)
;;
*)
if test $USE_NSS = "no" -a $USE_NETTLE = "no"; then
AC_CHECK_FUNC(ECDSA_sign, [], [AC_MSG_ERROR([OpenSSL does not support ECDSA: please upgrade or rerun with --disable-ecdsa])])
AC_CHECK_FUNC(SHA384_Init, [], [AC_MSG_ERROR([OpenSSL does not support SHA384: please upgrade or rerun with --disable-ecdsa])])
AC_CHECK_DECLS([NID_X9_62_prime256v1, NID_secp384r1], [], [AC_MSG_ERROR([OpenSSL does not support the ECDSA curves: please upgrade or rerun with --disable-ecdsa])], [AC_INCLUDES_DEFAULT
#include <openssl/evp.h>
])
# see if OPENSSL 1.0.0 or later (has EVP MD and Verify independency)
AC_MSG_CHECKING([if openssl supports SHA2 and ECDSA with EVP])
if grep OPENSSL_VERSION_TEXT $ssldir_include/openssl/opensslv.h | grep "OpenSSL" >/dev/null; then
if grep OPENSSL_VERSION_NUMBER $ssldir_include/openssl/opensslv.h | grep 0x0 >/dev/null; then
AC_MSG_RESULT([no])
AC_DEFINE_UNQUOTED([USE_ECDSA_EVP_WORKAROUND], [1], [Define this to enable an EVP workaround for older openssl])
else
AC_MSG_RESULT([yes])
fi
else
# not OpenSSL, thus likely LibreSSL, which supports it
AC_MSG_RESULT([yes])
fi
fi
# we now know we have ECDSA and the required curves.
AC_DEFINE_UNQUOTED([USE_ECDSA], [1], [Define this to enable ECDSA support.])
use_ecdsa="yes"
;;
esac
AC_ARG_ENABLE(dsa, AS_HELP_STRING([--disable-dsa],[Disable DSA support]))
use_dsa="no"
case "$enable_dsa" in
yes)
# detect if DSA is supported, and turn it off if not.
if test $USE_NSS = "no" -a $USE_NETTLE = "no"; then
AC_CHECK_FUNC(DSA_SIG_new, [
AC_CHECK_TYPE(DSA_SIG*, [
AC_DEFINE_UNQUOTED([USE_DSA], [1], [Define this to enable DSA support.])
], [if test "x$enable_dsa" = "xyes"; then AC_MSG_ERROR([OpenSSL does not support DSA and you used --enable-dsa.])
fi ], [
AC_INCLUDES_DEFAULT
#ifdef HAVE_OPENSSL_ERR_H
#include <openssl/err.h>
#endif
#ifdef HAVE_OPENSSL_RAND_H
#include <openssl/rand.h>
#endif
#ifdef HAVE_OPENSSL_CONF_H
#include <openssl/conf.h>
#endif
#ifdef HAVE_OPENSSL_ENGINE_H
#include <openssl/engine.h>
#endif
])
], [if test "x$enable_dsa" = "xyes"; then AC_MSG_ERROR([OpenSSL does not support DSA and you used --enable-dsa.])
fi ])
else
AC_DEFINE_UNQUOTED([USE_DSA], [1], [Define this to enable DSA support.])
fi
;;
*)
# disable dsa by default, RFC 8624 section 3.1, validators MUST NOT
# support DSA for DNSSEC Validation.
;;
esac
AC_ARG_WITH(deprecate-rsa-1024, AS_HELP_STRING([--with-deprecate-rsa-1024],[Deprecate RSA 1024 bit length, makes that an unsupported key, for use when OpenSSL FIPS refuses 1024 bit verification]))
if test "$with_deprecate_rsa_1024" = "yes"; then
AC_DEFINE([DEPRECATE_RSA_1024], [1], [Deprecate RSA 1024 bit length, makes that an unsupported key])
fi
AC_ARG_ENABLE(ed25519, AS_HELP_STRING([--disable-ed25519],[Disable ED25519 support]))
use_ed25519="no"
case "$enable_ed25519" in
no)
;;
*)
if test $USE_NSS = "no" -a $USE_NETTLE = "no"; then
AC_CHECK_DECLS([NID_ED25519], [
use_ed25519="yes"
], [ if test "x$enable_ed25519" = "xyes"; then AC_MSG_ERROR([OpenSSL does not support ED25519 and you used --enable-ed25519.])
fi ], [AC_INCLUDES_DEFAULT
#include <openssl/evp.h>
])
fi
if test $USE_NETTLE = "yes"; then
AC_CHECK_HEADERS([nettle/eddsa.h], use_ed25519="yes",, [AC_INCLUDES_DEFAULT])
fi
if test $use_ed25519 = "yes"; then
AC_DEFINE_UNQUOTED([USE_ED25519], [1], [Define this to enable ED25519 support.])
fi
;;
esac
AC_ARG_ENABLE(ed448, AS_HELP_STRING([--disable-ed448],[Disable ED448 support]))
use_ed448="no"
case "$enable_ed448" in
no)
;;
*)
if test $USE_NSS = "no" -a $USE_NETTLE = "no"; then
AC_CHECK_DECLS([NID_ED448], [
use_ed448="yes"
], [ if test "x$enable_ed448" = "xyes"; then AC_MSG_ERROR([OpenSSL does not support ED448 and you used --enable-ed448.])
fi ], [AC_INCLUDES_DEFAULT
#include <openssl/evp.h>
])
fi
if test $use_ed448 = "yes"; then
AC_DEFINE_UNQUOTED([USE_ED448], [1], [Define this to enable ED448 support.])
fi
;;
esac
AC_ARG_ENABLE(event-api, AS_HELP_STRING([--enable-event-api],[Enable (experimental) pluggable event base libunbound API installed to unbound-event.h]))
case "$enable_event_api" in
yes)
AC_SUBST(UNBOUND_EVENT_INSTALL, [unbound-event-install])
AC_SUBST(UNBOUND_EVENT_UNINSTALL, [unbound-event-uninstall])
;;
*)
;;
esac
AC_ARG_ENABLE(tfo-client, AS_HELP_STRING([--enable-tfo-client],[Enable TCP Fast Open for client mode]))
case "$enable_tfo_client" in
yes)
case "$host_os" in
linux*) AC_CHECK_DECL([MSG_FASTOPEN], [AC_MSG_WARN([Check the platform specific TFO kernel parameters are correctly configured to support client mode TFO])],
[AC_MSG_ERROR([TCP Fast Open is not available for client mode: please rerun without --enable-tfo-client])],
[AC_INCLUDES_DEFAULT
#include <netinet/tcp.h>
])
AC_DEFINE_UNQUOTED([USE_MSG_FASTOPEN], [1], [Define this to enable client TCP Fast Open.])
;;
darwin*) AC_CHECK_DECL([CONNECT_RESUME_ON_READ_WRITE], [AC_MSG_WARN([Check the platform specific TFO kernel parameters are correctly configured to support client mode TFO])],
[AC_MSG_ERROR([TCP Fast Open is not available for client mode: please rerun without --enable-tfo-client])],
[AC_INCLUDES_DEFAULT
#include <sys/socket.h>
])
AC_DEFINE_UNQUOTED([USE_OSX_MSG_FASTOPEN], [1], [Define this to enable client TCP Fast Open.])
;;
esac
;;
no|*)
;;
esac
AC_ARG_ENABLE(tfo-server, AS_HELP_STRING([--enable-tfo-server],[Enable TCP Fast Open for server mode]))
case "$enable_tfo_server" in
yes)
AC_CHECK_DECL([TCP_FASTOPEN], [AC_MSG_WARN([Check the platform specific TFO kernel parameters are correctly configured to support server mode TFO])], [AC_MSG_ERROR([TCP Fast Open is not available for server mode: please rerun without --enable-tfo-server])], [AC_INCLUDES_DEFAULT
#include <netinet/tcp.h>
])
AC_DEFINE_UNQUOTED([USE_TCP_FASTOPEN], [1], [Define this to enable server TCP Fast Open.])
;;
no|*)
;;
esac
# check for libevent
AC_ARG_WITH(libevent, AS_HELP_STRING([--with-libevent=pathname],[use libevent (will check /usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr or you can specify an explicit path). Slower, but allows use of large outgoing port ranges.]),
[ ],[ with_libevent="no" ])
if test "x_$with_libevent" != x_no; then
AC_DEFINE([USE_LIBEVENT], [1], [Define if you enable libevent])
AC_MSG_CHECKING(for libevent)
if test "x_$with_libevent" = x_ -o "x_$with_libevent" = x_yes; then
with_libevent="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr"
fi
for dir in $with_libevent; do
thedir="$dir"
if test -f "$dir/include/event.h" -o -f "$dir/include/event2/event.h"; then
found_libevent="yes"
dnl assume /usr is in default path.
if test "$thedir" != "/usr"; then
CPPFLAGS="$CPPFLAGS -I$thedir/include"
fi
break;
fi
done
if test x_$found_libevent != x_yes; then
if test -f "$dir/event.h" -a \( -f "$dir/libevent.la" -o -f "$dir/libev.la" \) ; then
# libevent source directory
AC_MSG_RESULT(found in $thedir)
CPPFLAGS="$CPPFLAGS -I$thedir -I$thedir/include"
BAK_LDFLAGS_SET="1"
BAK_LDFLAGS="$LDFLAGS"
# remove evdns from linking
mkdir build >/dev/null 2>&1
mkdir build/libevent >/dev/null 2>&1
mkdir build/libevent/.libs >/dev/null 2>&1
ev_files_o=`ls $thedir/*.o | grep -v evdns\.o | grep -v bufferevent_openssl\.o`
ev_files_lo=`ls $thedir/*.lo | grep -v evdns\.lo | grep -v bufferevent_openssl\.lo`
ev_files_libso=`ls $thedir/.libs/*.o | grep -v evdns\.o | grep -v bufferevent_openssl\.o`
cp $ev_files_o build/libevent
cp $ev_files_lo build/libevent
cp $ev_files_libso build/libevent/.libs
LATE_LDFLAGS="build/libevent/*.lo -lm"
LDFLAGS="build/libevent/*.o $LDFLAGS -lm"
else
AC_MSG_ERROR([Cannot find the libevent library in $with_libevent
You can restart ./configure --with-libevent=no to use a builtin alternative.
Please note that this alternative is not as capable as libevent when using
large outgoing port ranges. ])
fi
else
AC_MSG_RESULT(found in $thedir)
dnl if event2 exists and no event lib in dir itself, use subdir
if test ! -f $thedir/lib/libevent.a -a ! -f $thedir/lib/libevent.so -a -d "$thedir/lib/event2"; then
LDFLAGS="$LDFLAGS -L$thedir/lib/event2"
ACX_RUNTIME_PATH_ADD([$thedir/lib/event2])
else
dnl assume /usr is in default path, do not add "".
if test "$thedir" != "/usr" -a "$thedir" != ""; then
LDFLAGS="$LDFLAGS -L$thedir/lib"
ACX_RUNTIME_PATH_ADD([$thedir/lib])
fi
fi
fi
# check for library used by libevent after 1.3c
AC_SEARCH_LIBS([clock_gettime], [rt])
# is the event.h header libev or libevent?
AC_CHECK_HEADERS([event.h],,, [AC_INCLUDES_DEFAULT])
AC_CHECK_DECL(EV_VERSION_MAJOR, [
AC_SEARCH_LIBS(event_set, [ev])
],[
AC_SEARCH_LIBS(event_set, [event])
],[AC_INCLUDES_DEFAULT
#include <event.h>
])
AC_CHECK_FUNCS([event_base_free]) # only in libevent 1.2 and later
AC_CHECK_FUNCS([event_base_once]) # only in libevent 1.4.1 and later
AC_CHECK_FUNCS([event_base_new]) # only in libevent 1.4.1 and later
AC_CHECK_FUNCS([event_base_get_method]) # only in libevent 1.4.3 and later
AC_CHECK_FUNCS([ev_loop]) # only in libev. (tested on 3.51)
AC_CHECK_FUNCS([ev_default_loop]) # only in libev. (tested on 4.00)
AC_CHECK_FUNCS([event_assign]) # in libevent, for thread-safety
AC_CHECK_DECLS([evsignal_assign], [], [], [AC_INCLUDES_DEFAULT
#ifdef HAVE_EVENT_H
# include <event.h>
#else
# include "event2/event.h"
#endif
])
PC_LIBEVENT_DEPENDENCY="libevent"
AC_SUBST(PC_LIBEVENT_DEPENDENCY)
if test -n "$BAK_LDFLAGS_SET"; then
LDFLAGS="$BAK_LDFLAGS"
fi
else
AC_DEFINE(USE_MINI_EVENT, 1, [Define if you want to use internal select based events])
fi
# check for libexpat
AC_ARG_WITH(libexpat, AS_HELP_STRING([--with-libexpat=path],[specify explicit path for libexpat.]),
[ ],[ withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr" ])
AC_MSG_CHECKING(for libexpat)
found_libexpat="no"
for dir in $withval ; do
if test -f "$dir/include/expat.h"; then
found_libexpat="yes"
dnl assume /usr is in default path.
if test "$dir" != "/usr"; then
CPPFLAGS="$CPPFLAGS -I$dir/include"
LDFLAGS="$LDFLAGS -L$dir/lib"
fi
AC_MSG_RESULT(found in $dir)
break;
fi
done
if test x_$found_libexpat != x_yes; then
AC_MSG_ERROR([Could not find libexpat, expat.h])
fi
AC_CHECK_HEADERS([expat.h],,, [AC_INCLUDES_DEFAULT])
AC_CHECK_DECLS([XML_StopParser], [], [], [AC_INCLUDES_DEFAULT
#include <expat.h>
])
# hiredis (redis C client for cachedb)
AC_ARG_WITH(libhiredis, AS_HELP_STRING([--with-libhiredis=path],[specify explicit path for libhiredis.]),
[ ],[ withval="no" ])
found_libhiredis="no"
if test x_$withval = x_yes -o x_$withval != x_no; then
AC_MSG_CHECKING(for libhiredis)
if test x_$withval = x_ -o x_$withval = x_yes; then
withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr"
fi
for dir in $withval ; do
if test -f "$dir/include/hiredis/hiredis.h"; then
found_libhiredis="yes"
dnl assume /usr is in default path.
if test "$dir" != "/usr"; then
CPPFLAGS="$CPPFLAGS -I$dir/include"
LDFLAGS="$LDFLAGS -L$dir/lib"
fi
AC_MSG_RESULT(found in $dir)
AC_DEFINE([USE_REDIS], [1], [Define this to use hiredis client.])
LIBS="$LIBS -lhiredis"
break;
fi
done
if test x_$found_libhiredis != x_yes; then
AC_MSG_ERROR([Could not find libhiredis, hiredis.h])
fi
AC_CHECK_HEADERS([hiredis/hiredis.h],,, [AC_INCLUDES_DEFAULT])
AC_CHECK_DECLS([redisConnect], [], [], [AC_INCLUDES_DEFAULT
#include <hiredis/hiredis.h>
])
fi
# nghttp2
AC_ARG_WITH(libnghttp2, AS_HELP_STRING([--with-libnghttp2=path],[specify explicit path for libnghttp2.]),
[ ],[ withval="no" ])
found_libnghttp2="no"
if test x_$withval = x_yes -o x_$withval != x_no; then
AC_MSG_CHECKING(for libnghttp2)
if test x_$withval = x_ -o x_$withval = x_yes; then
withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr"
fi
for dir in $withval ; do
if test -f "$dir/include/nghttp2/nghttp2.h"; then
found_libnghttp2="yes"
dnl assume /usr is in default path.
if test "$dir" != "/usr"; then
CPPFLAGS="$CPPFLAGS -I$dir/include"
LDFLAGS="$LDFLAGS -L$dir/lib"
fi
AC_MSG_RESULT(found in $dir)
AC_DEFINE([HAVE_NGHTTP2], [1], [Define this to use nghttp2 client.])
LIBS="$LIBS -lnghttp2"
break;
fi
done
if test x_$found_libnghttp2 != x_yes; then
AC_MSG_ERROR([Could not find libnghttp2, nghttp2.h])
fi
AC_CHECK_HEADERS([nghttp2/nghttp2.h],,, [AC_INCLUDES_DEFAULT])
AC_CHECK_DECLS([nghttp2_session_server_new], [], [], [AC_INCLUDES_DEFAULT
#include <nghttp2/nghttp2.h>
])
fi
# set static linking for uninstalled libraries if requested
AC_SUBST(staticexe)
staticexe=""
AC_ARG_ENABLE(static-exe, AS_HELP_STRING([--enable-static-exe],[ enable to compile executables statically against (event) uninstalled libs, for debug purposes ]),
, )
if test x_$enable_static_exe = x_yes; then
staticexe="-static"
if test "$on_mingw" = yes; then
staticexe="-all-static"
# for static compile, include gdi32 and zlib here.
if echo $LIBS | grep 'lgdi32' >/dev/null; then
:
else
LIBS="$LIBS -lgdi32"
fi
AC_CHECK_LIB([z], [compress], [ LIBS="$LIBS -lz" ])
LIBS="$LIBS -l:libssp.a"
fi
fi
# set full static linking if requested
AC_ARG_ENABLE(fully-static, AS_HELP_STRING([--enable-fully-static],[ enable to compile fully static ]),
, )
if test x_$enable_fully_static = x_yes; then
staticexe="-all-static"
if test "$on_mingw" = yes; then
# for static compile, include gdi32 and zlib here.
if echo $LIBS | grep 'lgdi32' >/dev/null; then
:
else
LIBS="$LIBS -lgdi32"
fi
AC_CHECK_LIB([z], [compress], [ LIBS="$LIBS -lz" ])
LIBS="$LIBS -l:libssp.a"
fi
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 ]),
, )
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).])
CHECKLOCK_OBJ="checklocks.lo"
AC_SUBST(CHECKLOCK_OBJ)
fi
ACX_CHECK_GETADDRINFO_WITH_INCLUDES
if test "$USE_WINSOCK" = 1; then
AC_DEFINE(UB_ON_WINDOWS, 1, [Use win32 resources and API])
AC_CHECK_HEADERS([iphlpapi.h],,, [AC_INCLUDES_DEFAULT
#include <windows.h>
])
AC_CHECK_TOOL(WINDRES, windres)
LIBS="$LIBS -liphlpapi -lcrypt32"
WINAPPS="unbound-service-install.exe unbound-service-remove.exe anchor-update.exe"
AC_SUBST(WINAPPS)
WIN_DAEMON_SRC="winrc/win_svc.c winrc/w_inst.c"
AC_SUBST(WIN_DAEMON_SRC)
WIN_DAEMON_OBJ="win_svc.lo w_inst.lo"
AC_SUBST(WIN_DAEMON_OBJ)
WIN_DAEMON_OBJ_LINK="rsrc_unbound.o"
AC_SUBST(WIN_DAEMON_OBJ_LINK)
WIN_HOST_OBJ_LINK="rsrc_unbound_host.o"
AC_SUBST(WIN_HOST_OBJ_LINK)
WIN_UBANCHOR_OBJ_LINK="rsrc_unbound_anchor.o log.lo locks.lo"
AC_SUBST(WIN_UBANCHOR_OBJ_LINK)
WIN_CONTROL_OBJ_LINK="rsrc_unbound_control.o"
AC_SUBST(WIN_CONTROL_OBJ_LINK)
WIN_CHECKCONF_OBJ_LINK="rsrc_unbound_checkconf.o"
AC_SUBST(WIN_CHECKCONF_OBJ_LINK)
AC_DEFINE(__USE_MINGW_ANSI_STDIO, 1, [defined to use gcc ansi snprintf and sscanf that understands %lld when compiled for windows.])
fi
if test $ac_cv_func_getaddrinfo = no; then
AC_LIBOBJ([fake-rfc2553])
fi
# check after getaddrinfo for its libraries
ACX_FUNC_IOCTLSOCKET
# see if daemon(3) exists, and if it is deprecated.
AC_CHECK_FUNCS([daemon])
if test $ac_cv_func_daemon = yes; then
ACX_FUNC_DEPRECATED([daemon], [(void)daemon(0, 0);], [
#include <stdlib.h>
#include <unistd.h>
])
fi
AC_CHECK_MEMBERS([struct sockaddr_un.sun_len],,,[
AC_INCLUDES_DEFAULT
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif
])
AC_CHECK_MEMBERS([struct in_pktinfo.ipi_spec_dst],,,[
AC_INCLUDES_DEFAULT
#if HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_SYS_UIO_H
#include <sys/uio.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_WINSOCK2_H
#include <winsock2.h>
#endif
#ifdef HAVE_WS2TCPIP_H
#include <ws2tcpip.h>
#endif
])
AC_MSG_CHECKING([for htobe64])
AC_LINK_IFELSE([AC_LANG_PROGRAM([
#include <stdio.h>
#ifdef HAVE_ENDIAN_H
# include <endian.h>
#endif
#ifdef HAVE_SYS_ENDIAN_H
# include <sys/endian.h>
#endif
], [unsigned long long x = htobe64(0); printf("%u", (unsigned)x);])],
AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_HTOBE64, 1, [If we have htobe64]),
AC_MSG_RESULT(no))
AC_MSG_CHECKING([for be64toh])
AC_LINK_IFELSE([AC_LANG_PROGRAM([
#include <stdio.h>
#ifdef HAVE_ENDIAN_H
# include <endian.h>
#endif
#ifdef HAVE_SYS_ENDIAN_H
# include <sys/endian.h>
#endif
], [unsigned long long x = be64toh(0); printf("%u", (unsigned)x);])],
AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_BE64TOH, 1, [If we have be64toh]),
AC_MSG_RESULT(no))
AC_SEARCH_LIBS([setusercontext], [util])
AC_CHECK_FUNCS([tzset sigprocmask fcntl getpwnam endpwent getrlimit setrlimit setsid chroot kill chown sleep usleep random srandom recvmsg sendmsg writev socketpair glob initgroups strftime localtime_r setusercontext _beginthreadex endservent endprotoent fsync shmget accept4 getifaddrs if_nametoindex poll gettid])
AC_CHECK_FUNCS([setresuid],,[AC_CHECK_FUNCS([setreuid])])
AC_CHECK_FUNCS([setresgid],,[AC_CHECK_FUNCS([setregid])])
# check if setreuid en setregid fail, on MacOSX10.4(darwin8).
if echo $host_os | grep darwin8 > /dev/null; then
AC_DEFINE(DARWIN_BROKEN_SETREUID, 1, [Define this if on macOSX10.4-darwin8 and setreuid and setregid do not work])
fi
AC_CHECK_DECLS([inet_pton,inet_ntop], [], [], [
AC_INCLUDES_DEFAULT
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_WINSOCK2_H
#include <winsock2.h>
#endif
#ifdef HAVE_WS2TCPIP_H
#include <ws2tcpip.h>
#endif
])
AC_REPLACE_FUNCS(inet_aton)
AC_REPLACE_FUNCS(inet_pton)
AC_REPLACE_FUNCS(inet_ntop)
AC_REPLACE_FUNCS(snprintf)
# test if snprintf return the proper length
if test "x$ac_cv_func_snprintf" = xyes; then
if test c${cross_compiling} = cno; then
AC_MSG_CHECKING([for correct snprintf return value])
AC_RUN_IFELSE([AC_LANG_SOURCE(AC_INCLUDES_DEFAULT
[[
int main(void) { return !(snprintf(NULL, 0, "test") == 4); }
]])], [AC_MSG_RESULT(yes)], [
AC_MSG_RESULT(no)
AC_DEFINE([SNPRINTF_RET_BROKEN], [], [define if (v)snprintf does not return length needed, (but length used)])
AC_LIBOBJ(snprintf)
- ])
+ ], [AC_MSG_RESULT(maybe)])
fi
fi
AC_REPLACE_FUNCS(strlcat)
AC_REPLACE_FUNCS(strlcpy)
AC_REPLACE_FUNCS(memmove)
AC_REPLACE_FUNCS(gmtime_r)
AC_REPLACE_FUNCS(isblank)
AC_REPLACE_FUNCS(explicit_bzero)
dnl without CTIME, ARC4-functions and without reallocarray.
LIBOBJ_WITHOUT_CTIMEARC4="$LIBOBJS"
AC_SUBST(LIBOBJ_WITHOUT_CTIMEARC4)
AC_MSG_CHECKING([for reallocarray])
AC_LINK_IFELSE([AC_LANG_SOURCE(AC_INCLUDES_DEFAULT
[[
#ifndef _OPENBSD_SOURCE
#define _OPENBSD_SOURCE 1
#endif
#include <stdlib.h>
int main(void) {
void* p = reallocarray(NULL, 10, 100);
free(p);
return 0;
}
]])], [AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_REALLOCARRAY, 1, [If we have reallocarray(3)])
], [
AC_MSG_RESULT(no)
AC_LIBOBJ(reallocarray)
])
AC_CHECK_DECLS([reallocarray])
if test "$USE_NSS" = "no"; then
AC_REPLACE_FUNCS(arc4random)
AC_REPLACE_FUNCS(arc4random_uniform)
if test "$ac_cv_func_arc4random" = "no"; then
AC_LIBOBJ(arc4_lock)
AC_CHECK_FUNCS([getentropy],,[
if test "$USE_WINSOCK" = 1; then
AC_LIBOBJ(getentropy_win)
else
case "$host" in
Darwin|*darwin*)
AC_LIBOBJ(getentropy_osx)
;;
*solaris*|*sunos*|SunOS)
AC_LIBOBJ(getentropy_solaris)
AC_CHECK_HEADERS([sys/sha2.h],, [
AC_CHECK_FUNCS([SHA512_Update],,[
AC_LIBOBJ(sha512)
])
], [AC_INCLUDES_DEFAULT])
if test "$ac_cv_header_sys_sha2_h" = "yes"; then
# this lib needed for sha2 on solaris
LIBS="$LIBS -lmd"
fi
AC_SEARCH_LIBS([clock_gettime], [rt])
;;
*freebsd*|*FreeBSD)
AC_LIBOBJ(getentropy_freebsd)
;;
*linux*|Linux|*)
AC_LIBOBJ(getentropy_linux)
AC_CHECK_FUNCS([SHA512_Update],,[
AC_DEFINE([COMPAT_SHA512], [1], [Do sha512 definitions in config.h])
AC_LIBOBJ(sha512)
])
AC_CHECK_HEADERS([sys/sysctl.h],,, [AC_INCLUDES_DEFAULT])
AC_CHECK_FUNCS([getauxval])
AC_SEARCH_LIBS([clock_gettime], [rt])
;;
esac
fi
])
fi
fi
LIBOBJ_WITHOUT_CTIME="$LIBOBJS"
AC_SUBST(LIBOBJ_WITHOUT_CTIME)
AC_REPLACE_FUNCS(ctime_r)
AC_REPLACE_FUNCS(strsep)
AC_ARG_ENABLE(allsymbols, AS_HELP_STRING([--enable-allsymbols],[export all symbols from libunbound and link binaries to it, smaller install size but libunbound export table is polluted by internal symbols]))
case "$enable_allsymbols" in
yes)
COMMON_OBJ_ALL_SYMBOLS=""
UBSYMS=""
EXTRALINK="libunbound.la"
AC_DEFINE(EXPORT_ALL_SYMBOLS, 1, [Define this if you enabled-allsymbols from libunbound to link binaries to it for smaller install size, but the libunbound export table is polluted by internal symbols])
;;
no|*)
COMMON_OBJ_ALL_SYMBOLS='$(COMMON_OBJ)'
UBSYMS='-export-symbols $(srcdir)/libunbound/ubsyms.def'
EXTRALINK=""
;;
esac
AC_SUBST(COMMON_OBJ_ALL_SYMBOLS)
AC_SUBST(EXTRALINK)
AC_SUBST(UBSYMS)
if test x_$enable_lock_checks = x_yes; then
UBSYMS="-export-symbols clubsyms.def"
cp ${srcdir}/libunbound/ubsyms.def clubsyms.def
echo lock_protect >> clubsyms.def
echo lock_unprotect >> clubsyms.def
echo lock_get_mem >> clubsyms.def
echo checklock_start >> clubsyms.def
echo checklock_stop >> clubsyms.def
echo checklock_lock >> clubsyms.def
echo checklock_unlock >> clubsyms.def
echo checklock_init >> clubsyms.def
echo checklock_thrcreate >> clubsyms.def
echo checklock_thrjoin >> clubsyms.def
fi
# check for dnstap if requested
dt_DNSTAP([$UNBOUND_RUN_DIR/dnstap.sock],
[
AC_DEFINE([USE_DNSTAP], [1], [Define to 1 to enable dnstap support])
AC_SUBST([ENABLE_DNSTAP], [1])
AC_SUBST([opt_dnstap_socket_path])
ACX_ESCAPE_BACKSLASH($opt_dnstap_socket_path, hdr_dnstap_socket_path)
AC_DEFINE_UNQUOTED(DNSTAP_SOCKET_PATH,
["$hdr_dnstap_socket_path"], [default dnstap socket path])
AC_SUBST(DNSTAP_SOCKET_PATH,["$hdr_dnstap_socket_path"])
AC_SUBST(DNSTAP_SOCKET_TESTBIN,['unbound-dnstap-socket$(EXEEXT)'])
AC_SUBST([DNSTAP_SRC], ["dnstap/dnstap.c dnstap/dnstap.pb-c.c dnstap/dnstap_fstrm.c dnstap/dtstream.c"])
AC_SUBST([DNSTAP_OBJ], ["dnstap.lo dnstap.pb-c.lo dnstap_fstrm.lo dtstream.lo"])
],
[
AC_SUBST([ENABLE_DNSTAP], [0])
]
)
# check for dnscrypt if requested
dnsc_DNSCRYPT([
AC_DEFINE([USE_DNSCRYPT], [1], [Define to 1 to enable dnscrypt support])
AC_SUBST([ENABLE_DNSCRYPT], [1])
AC_SUBST([DNSCRYPT_SRC], ["dnscrypt/dnscrypt.c"])
AC_SUBST([DNSCRYPT_OBJ], ["dnscrypt.lo"])
],
[
AC_SUBST([ENABLE_DNSCRYPT], [0])
]
)
# check for cachedb if requested
AC_ARG_ENABLE(cachedb, AS_HELP_STRING([--enable-cachedb],[enable cachedb module that can use external cache storage]))
# turn on cachedb when hiredis support is enabled.
if test "$found_libhiredis" = "yes"; then enable_cachedb="yes"; fi
case "$enable_cachedb" in
yes)
AC_DEFINE([USE_CACHEDB], [1], [Define to 1 to use cachedb support])
AC_SUBST([CACHEDB_SRC], ["cachedb/cachedb.c cachedb/redis.c"])
AC_SUBST([CACHEDB_OBJ], ["cachedb.lo redis.lo"])
;;
no|*)
# nothing
;;
esac
# check for ipsecmod if requested
AC_ARG_ENABLE(ipsecmod, AS_HELP_STRING([--enable-ipsecmod],[Enable ipsecmod module that facilitates opportunistic IPsec]))
case "$enable_ipsecmod" in
yes)
AC_DEFINE([USE_IPSECMOD], [1], [Define to 1 to use ipsecmod support.])
IPSECMOD_OBJ="ipsecmod.lo ipsecmod-whitelist.lo"
AC_SUBST(IPSECMOD_OBJ)
IPSECMOD_HEADER='$(srcdir)/ipsecmod/ipsecmod.h $(srcdir)/ipsecmod/ipsecmod-whitelist.h'
AC_SUBST(IPSECMOD_HEADER)
;;
no|*)
# nothing
;;
esac
# check for ipset if requested
AC_ARG_ENABLE(ipset, AS_HELP_STRING([--enable-ipset],[enable ipset module]))
case "$enable_ipset" in
yes)
AC_DEFINE([USE_IPSET], [1], [Define to 1 to use ipset support])
IPSET_SRC="ipset/ipset.c"
AC_SUBST(IPSET_SRC)
IPSET_OBJ="ipset.lo"
AC_SUBST(IPSET_OBJ)
# mnl
AC_ARG_WITH(libmnl, AS_HELP_STRING([--with-libmnl=path],[specify explicit path for libmnl.]),
[ ],[ withval="yes" ])
found_libmnl="no"
AC_MSG_CHECKING(for libmnl)
if test x_$withval = x_ -o x_$withval = x_yes; then
withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr"
fi
for dir in $withval ; do
if test -f "$dir/include/libmnl/libmnl.h" -o -f "$dir/include/libmnl/libmnl/libmnl.h"; then
found_libmnl="yes"
dnl assume /usr is in default path.
extralibmnl=""
if test -f "$dir/include/libmnl/libmnl/libmnl.h"; then
extralibmnl="/libmnl"
fi
if test "$dir" != "/usr" -o -n "$extralibmnl"; then
CPPFLAGS="$CPPFLAGS -I$dir/include$extralibmnl"
fi
if test "$dir" != "/usr"; then
LDFLAGS="$LDFLAGS -L$dir/lib"
fi
AC_MSG_RESULT(found in $dir)
LIBS="$LIBS -lmnl"
break;
fi
done
if test x_$found_libmnl != x_yes; then
AC_MSG_ERROR([Could not find libmnl, libmnl.h])
fi
;;
no|*)
# nothing
;;
esac
AC_ARG_ENABLE(explicit-port-randomisation, AS_HELP_STRING([--disable-explicit-port-randomisation],[disable explicit source port randomisation and rely on the kernel to provide random source ports]))
case "$enable_explicit_port_randomisation" in
no)
AC_DEFINE([DISABLE_EXPLICIT_PORT_RANDOMISATION], [1], [Define this to enable kernel based UDP source port randomization.])
;;
yes|*)
;;
esac
if echo "$host" | $GREP -i -e linux >/dev/null; then
- AC_ARG_ENABLE(linux-ip-local-port-range, AC_HELP_STRING([--enable-linux-ip-local-port-range], [Define this to enable use of /proc/sys/net/ipv4/ip_local_port_range as a default outgoing port range. This is only for the libunbound on Linux and does not affect unbound resolving daemon itself. This may severely limit the number of available outgoing ports and thus decrease randomness. Define this only when the target system restricts (e.g. some of SELinux enabled distributions) the use of non-ephemeral ports.]))
+ AC_ARG_ENABLE(linux-ip-local-port-range, AS_HELP_STRING([--enable-linux-ip-local-port-range], [Define this to enable use of /proc/sys/net/ipv4/ip_local_port_range as a default outgoing port range. This is only for the libunbound on Linux and does not affect unbound resolving daemon itself. This may severely limit the number of available outgoing ports and thus decrease randomness. Define this only when the target system restricts (e.g. some of SELinux enabled distributions) the use of non-ephemeral ports.]))
case "$enable_linux_ip_local_port_range" in
yes)
AC_DEFINE([USE_LINUX_IP_LOCAL_PORT_RANGE], [1], [Define this to enable use of /proc/sys/net/ipv4/ip_local_port_range as a default outgoing port range. This is only for the libunbound on Linux and does not affect unbound resolving daemon itself. This may severely limit the number of available outgoing ports and thus decrease randomness. Define this only when the target system restricts (e.g. some of SELinux enabled distributions) the use of non-ephemeral ports.])
;;
no|*)
;;
esac
fi
AC_MSG_CHECKING([if ${MAKE:-make} supports $< with implicit rule in scope])
# on openBSD, the implicit rule make $< work.
# on Solaris, it does not work ($? is changed sources, $^ lists dependencies).
# gmake works.
cat >conftest.make <<EOF
all: conftest.lo
conftest.lo foo.lo bla.lo:
if test -f "\$<"; then touch \$@; fi
.SUFFIXES: .lo
.c.lo:
if test -f "\$<"; then touch \$@; fi
conftest.lo: conftest.dir/conftest.c
EOF
mkdir conftest.dir
touch conftest.dir/conftest.c
rm -f conftest.lo conftest.c
${MAKE:-make} -f conftest.make >/dev/null
rm -f conftest.make conftest.c conftest.dir/conftest.c
rm -rf conftest.dir
if test ! -f conftest.lo; then
AC_MSG_RESULT(no)
SOURCEDETERMINE='echo "$^" | awk "-F " "{print \$$1;}" > .source'
SOURCEFILE='`cat .source`'
else
AC_MSG_RESULT(yes)
SOURCEDETERMINE=':'
SOURCEFILE='$<'
fi
rm -f conftest.lo
AC_SUBST(SOURCEDETERMINE)
AC_SUBST(SOURCEFILE)
# see if we want to build the library or everything
ALLTARGET="alltargets"
INSTALLTARGET="install-all"
AC_ARG_WITH(libunbound-only, AS_HELP_STRING([--with-libunbound-only],[do not build daemon and tool programs]),
[
if test "$withval" = "yes"; then
ALLTARGET="lib"
INSTALLTARGET="install-lib"
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.])
fi
if test $USE_NETTLE = "yes"; then
AC_MSG_ERROR([--with-nettle can only be used in combination with --with-libunbound-only.])
fi
fi
AC_SUBST(ALLTARGET)
AC_SUBST(INSTALLTARGET)
ACX_STRIP_EXT_FLAGS
if test -n "$LATE_LDFLAGS"; then
LDFLAGS="$LATE_LDFLAGS $LDFLAGS"
fi
# remove start spaces
LDFLAGS=`echo "$LDFLAGS"|sed -e 's/^ *//'`
LIBS=`echo "$LIBS"|sed -e 's/^ *//'`
AC_DEFINE_UNQUOTED([MAXSYSLOGMSGLEN], [10240], [Define to the maximum message length to pass to syslog.])
AH_BOTTOM(
dnl this must be first AH_CONFIG, to define the flags before any includes.
AHX_CONFIG_EXT_FLAGS
dnl includes
[
#ifndef _OPENBSD_SOURCE
#define _OPENBSD_SOURCE 1
#endif
#ifndef UNBOUND_DEBUG
# ifndef NDEBUG
# define NDEBUG
# endif
#endif
/** Use small-ldns codebase */
#define USE_SLDNS 1
#ifdef HAVE_SSL
# define LDNS_BUILD_CONFIG_HAVE_SSL 1
#endif
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#if STDC_HEADERS
#include <stdlib.h>
#include <stddef.h>
#endif
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#include <errno.h>
#if HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_SYS_UIO_H
#include <sys/uio.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_WINSOCK2_H
#include <winsock2.h>
#endif
#ifdef HAVE_WS2TCPIP_H
#include <ws2tcpip.h>
#endif
#if !defined(USE_WINSOCK) || !defined(HAVE_SNPRINTF) || defined(SNPRINTF_RET_BROKEN) || defined(__USE_MINGW_ANSI_STDIO)
#define ARG_LL "%ll"
#else
#define ARG_LL "%I64"
#endif
#ifndef AF_LOCAL
#define AF_LOCAL AF_UNIX
#endif
]
AHX_CONFIG_FORMAT_ATTRIBUTE
AHX_CONFIG_UNUSED_ATTRIBUTE
AHX_CONFIG_FSEEKO
AHX_CONFIG_MAXHOSTNAMELEN
#if !defined(HAVE_SNPRINTF) || defined(SNPRINTF_RET_BROKEN)
#define snprintf snprintf_unbound
#define vsnprintf vsnprintf_unbound
#include <stdarg.h>
int snprintf (char *str, size_t count, const char *fmt, ...);
int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);
#endif /* HAVE_SNPRINTF or SNPRINTF_RET_BROKEN */
AHX_CONFIG_INET_PTON(unbound)
AHX_CONFIG_INET_NTOP(unbound)
AHX_CONFIG_INET_ATON(unbound)
AHX_CONFIG_MEMMOVE(unbound)
AHX_CONFIG_STRLCAT(unbound)
AHX_CONFIG_STRLCPY(unbound)
AHX_CONFIG_GMTIME_R(unbound)
AHX_CONFIG_REALLOCARRAY(unbound)
AHX_CONFIG_W32_SLEEP
AHX_CONFIG_W32_USLEEP
AHX_CONFIG_W32_RANDOM
AHX_CONFIG_W32_SRANDOM
AHX_CONFIG_W32_FD_SET_T
AHX_CONFIG_IPV6_MIN_MTU
AHX_MEMCMP_BROKEN(unbound)
[
#ifndef HAVE_CTIME_R
#define ctime_r unbound_ctime_r
char *ctime_r(const time_t *timep, char *buf);
#endif
#ifndef HAVE_STRSEP
#define strsep unbound_strsep
char *strsep(char **stringp, const char *delim);
#endif
#ifndef HAVE_ISBLANK
#define isblank unbound_isblank
int isblank(int c);
#endif
#ifndef HAVE_EXPLICIT_BZERO
#define explicit_bzero unbound_explicit_bzero
void explicit_bzero(void* buf, size_t len);
#endif
#if defined(HAVE_INET_NTOP) && !HAVE_DECL_INET_NTOP
const char *inet_ntop(int af, const void *src, char *dst, size_t size);
#endif
#if defined(HAVE_INET_PTON) && !HAVE_DECL_INET_PTON
int inet_pton(int af, const char* src, void* dst);
#endif
#if !defined(HAVE_STRPTIME) || !defined(STRPTIME_WORKS)
#define strptime unbound_strptime
struct tm;
char *strptime(const char *s, const char *format, struct tm *tm);
#endif
#if !HAVE_DECL_REALLOCARRAY
void *reallocarray(void *ptr, size_t nmemb, size_t size);
#endif
#ifdef HAVE_LIBBSD
#include <bsd/string.h>
#include <bsd/stdlib.h>
#endif
#ifdef HAVE_LIBRESSL
# if !HAVE_DECL_STRLCPY
size_t strlcpy(char *dst, const char *src, size_t siz);
# endif
# if !HAVE_DECL_STRLCAT
size_t strlcat(char *dst, const char *src, size_t siz);
# endif
# if !HAVE_DECL_ARC4RANDOM && defined(HAVE_ARC4RANDOM)
uint32_t arc4random(void);
# endif
# if !HAVE_DECL_ARC4RANDOM_UNIFORM && defined(HAVE_ARC4RANDOM_UNIFORM)
uint32_t arc4random_uniform(uint32_t upper_bound);
# endif
#endif /* HAVE_LIBRESSL */
#ifndef HAVE_ARC4RANDOM
int getentropy(void* buf, size_t len);
uint32_t arc4random(void);
void arc4random_buf(void* buf, size_t n);
void _ARC4_LOCK(void);
void _ARC4_UNLOCK(void);
void _ARC4_LOCK_DESTROY(void);
#endif
#ifndef HAVE_ARC4RANDOM_UNIFORM
uint32_t arc4random_uniform(uint32_t upper_bound);
#endif
#ifdef COMPAT_SHA512
#ifndef SHA512_DIGEST_LENGTH
#define SHA512_BLOCK_LENGTH 128
#define SHA512_DIGEST_LENGTH 64
#define SHA512_DIGEST_STRING_LENGTH (SHA512_DIGEST_LENGTH * 2 + 1)
typedef struct _SHA512_CTX {
uint64_t state[8];
uint64_t bitcount[2];
uint8_t buffer[SHA512_BLOCK_LENGTH];
} SHA512_CTX;
#endif /* SHA512_DIGEST_LENGTH */
void SHA512_Init(SHA512_CTX*);
void SHA512_Update(SHA512_CTX*, void*, size_t);
void SHA512_Final(uint8_t[SHA512_DIGEST_LENGTH], SHA512_CTX*);
unsigned char *SHA512(void* data, unsigned int data_len, unsigned char *digest);
#endif /* COMPAT_SHA512 */
#if defined(HAVE_EVENT_H) && !defined(HAVE_EVENT_BASE_ONCE) && !(defined(HAVE_EV_LOOP) || defined(HAVE_EV_DEFAULT_LOOP)) && (defined(HAVE_PTHREAD) || defined(HAVE_SOLARIS_THREADS))
/* using version of libevent that is not threadsafe. */
# define LIBEVENT_SIGNAL_PROBLEM 1
#endif
#ifndef CHECKED_INET6
# define CHECKED_INET6
# ifdef AF_INET6
# define INET6
# else
# define AF_INET6 28
# endif
#endif /* CHECKED_INET6 */
#ifndef HAVE_GETADDRINFO
struct sockaddr_storage;
#include "compat/fake-rfc2553.h"
#endif
#ifdef UNBOUND_ALLOC_STATS
# define malloc(s) unbound_stat_malloc_log(s, __FILE__, __LINE__, __func__)
# define calloc(n,s) unbound_stat_calloc_log(n, s, __FILE__, __LINE__, __func__)
# define free(p) unbound_stat_free_log(p, __FILE__, __LINE__, __func__)
# define realloc(p,s) unbound_stat_realloc_log(p, s, __FILE__, __LINE__, __func__)
void *unbound_stat_malloc(size_t size);
void *unbound_stat_calloc(size_t nmemb, size_t size);
void unbound_stat_free(void *ptr);
void *unbound_stat_realloc(void *ptr, size_t size);
void *unbound_stat_malloc_log(size_t size, const char* file, int line,
const char* func);
void *unbound_stat_calloc_log(size_t nmemb, size_t size, const char* file,
int line, const char* func);
void unbound_stat_free_log(void *ptr, const char* file, int line,
const char* func);
void *unbound_stat_realloc_log(void *ptr, size_t size, const char* file,
int line, const char* func);
#elif defined(UNBOUND_ALLOC_LITE)
# include "util/alloc.h"
#endif /* UNBOUND_ALLOC_LITE and UNBOUND_ALLOC_STATS */
/** default port for DNS traffic. */
#define UNBOUND_DNS_PORT 53
/** default port for DNS over TLS traffic. */
#define UNBOUND_DNS_OVER_TLS_PORT 853
/** default port for DNS over HTTPS traffic. */
#define UNBOUND_DNS_OVER_HTTPS_PORT 443
/** default port for unbound control traffic, registered port with IANA,
ub-dns-control 8953/tcp unbound dns nameserver control */
#define UNBOUND_CONTROL_PORT 8953
/** the version of unbound-control that this software implements */
#define UNBOUND_CONTROL_VERSION 1
])
dnl if we build from source tree, the man pages need @date@ and @version@
dnl if this is a distro tarball, that was already done by makedist.sh
AC_SUBST(version, [VERSION_MAJOR.VERSION_MINOR.VERSION_MICRO])
AC_SUBST(date, [`date +'%b %e, %Y'`])
AC_CONFIG_FILES([Makefile doc/example.conf doc/libunbound.3 doc/unbound.8 doc/unbound-anchor.8 doc/unbound-checkconf.8 doc/unbound.conf.5 doc/unbound-control.8 doc/unbound-host.1 smallapp/unbound-control-setup.sh dnstap/dnstap_config.h dnscrypt/dnscrypt_config.h contrib/libunbound.pc contrib/unbound.socket contrib/unbound.service contrib/unbound_portable.service])
AC_CONFIG_HEADERS([config.h])
AC_OUTPUT
diff --git a/contrib/unbound/daemon/remote.c b/contrib/unbound/daemon/remote.c
index 4990fc8e9195..3eb711ce6428 100644
--- a/contrib/unbound/daemon/remote.c
+++ b/contrib/unbound/daemon/remote.c
@@ -1,3356 +1,3359 @@
/*
* daemon/remote.c - remote control for the unbound daemon.
*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* 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.
* The channel is secured using TLSv1, and certificates.
* Both the server and the client(control tool) have their own keys.
*/
#include "config.h"
#ifdef HAVE_OPENSSL_ERR_H
#include <openssl/err.h>
#endif
#ifdef HAVE_OPENSSL_DH_H
#include <openssl/dh.h>
#endif
#ifdef HAVE_OPENSSL_BN_H
#include <openssl/bn.h>
#endif
#include <ctype.h>
#include "daemon/remote.h"
#include "daemon/worker.h"
#include "daemon/daemon.h"
#include "daemon/stats.h"
#include "daemon/cachedump.h"
#include "util/log.h"
#include "util/config_file.h"
#include "util/net_help.h"
#include "util/module.h"
#include "services/listen_dnsport.h"
#include "services/cache/rrset.h"
#include "services/cache/infra.h"
#include "services/mesh.h"
#include "services/localzone.h"
#include "services/authzone.h"
#include "services/rpz.h"
#include "util/storage/slabhash.h"
#include "util/fptr_wlist.h"
#include "util/data/dname.h"
#include "validator/validator.h"
#include "validator/val_kcache.h"
#include "validator/val_kentry.h"
#include "validator/val_anchor.h"
#include "iterator/iterator.h"
#include "iterator/iter_fwd.h"
#include "iterator/iter_hints.h"
#include "iterator/iter_delegpt.h"
#include "services/outbound_list.h"
#include "services/outside_network.h"
#include "sldns/str2wire.h"
#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>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
/* just for portability */
#ifdef SQ
#undef SQ
#endif
/** what to put on statistics lines between var and value, ": " or "=" */
#define SQ "="
static int
remote_setup_ctx(struct daemon_remote* rc, struct config_file* cfg)
{
char* s_cert;
char* s_key;
rc->ctx = SSL_CTX_new(SSLv23_server_method());
if(!rc->ctx) {
log_crypto_err("could not SSL_CTX_new");
return 0;
}
if(!listen_sslctx_setup(rc->ctx)) {
return 0;
}
s_cert = fname_after_chroot(cfg->server_cert_file, cfg, 1);
s_key = fname_after_chroot(cfg->server_key_file, cfg, 1);
if(!s_cert || !s_key) {
log_err("out of memory in remote control fname");
goto setup_error;
}
verbose(VERB_ALGO, "setup SSL certificates");
if (!SSL_CTX_use_certificate_chain_file(rc->ctx,s_cert)) {
log_err("Error for server-cert-file: %s", s_cert);
log_crypto_err("Error in SSL_CTX use_certificate_chain_file");
goto setup_error;
}
if(!SSL_CTX_use_PrivateKey_file(rc->ctx,s_key,SSL_FILETYPE_PEM)) {
log_err("Error for server-key-file: %s", s_key);
log_crypto_err("Error in SSL_CTX use_PrivateKey_file");
goto setup_error;
}
if(!SSL_CTX_check_private_key(rc->ctx)) {
log_err("Error for server-key-file: %s", s_key);
log_crypto_err("Error in SSL_CTX check_private_key");
goto setup_error;
}
listen_sslctx_setup_2(rc->ctx);
if(!SSL_CTX_load_verify_locations(rc->ctx, s_cert, NULL)) {
log_crypto_err("Error setting up SSL_CTX verify locations");
setup_error:
free(s_cert);
free(s_key);
return 0;
}
SSL_CTX_set_client_CA_list(rc->ctx, SSL_load_client_CA_file(s_cert));
SSL_CTX_set_verify(rc->ctx, SSL_VERIFY_PEER, NULL);
free(s_cert);
free(s_key);
return 1;
}
struct daemon_remote*
daemon_remote_create(struct config_file* cfg)
{
struct daemon_remote* rc = (struct daemon_remote*)calloc(1,
sizeof(*rc));
if(!rc) {
log_err("out of memory in daemon_remote_create");
return NULL;
}
rc->max_active = 10;
if(!cfg->remote_control_enable) {
rc->ctx = NULL;
return rc;
}
if(options_remote_is_address(cfg) && cfg->control_use_cert) {
if(!remote_setup_ctx(rc, cfg)) {
daemon_remote_delete(rc);
return NULL;
}
rc->use_cert = 1;
} else {
struct config_strlist* p;
rc->ctx = NULL;
rc->use_cert = 0;
if(!options_remote_is_address(cfg))
for(p = cfg->control_ifs.first; p; p = p->next) {
if(p->str && p->str[0] != '/')
log_warn("control-interface %s is not using TLS, but plain transfer, because first control-interface in config file is a local socket (starts with a /).", p->str);
}
}
return rc;
}
void daemon_remote_clear(struct daemon_remote* rc)
{
struct rc_state* p, *np;
if(!rc) return;
/* but do not close the ports */
listen_list_delete(rc->accept_list);
rc->accept_list = NULL;
/* do close these sockets */
p = rc->busy_list;
while(p) {
np = p->next;
if(p->ssl)
SSL_free(p->ssl);
comm_point_delete(p->c);
free(p);
p = np;
}
rc->busy_list = NULL;
rc->active = 0;
rc->worker = NULL;
}
void daemon_remote_delete(struct daemon_remote* rc)
{
if(!rc) return;
daemon_remote_clear(rc);
if(rc->ctx) {
SSL_CTX_free(rc->ctx);
}
free(rc);
}
/**
* Add and open a new control port
* @param ip: ip str
* @param nr: port nr
* @param list: list head
* @param noproto_is_err: if lack of protocol support is an error.
* @param cfg: config with username for chown of unix-sockets.
* @return false on failure.
*/
static int
add_open(const char* ip, int nr, struct listen_port** list, int noproto_is_err,
struct config_file* cfg)
{
struct addrinfo hints;
struct addrinfo* res;
struct listen_port* n;
int noproto = 0;
int fd, r;
char port[15];
snprintf(port, sizeof(port), "%d", nr);
port[sizeof(port)-1]=0;
memset(&hints, 0, sizeof(hints));
log_assert(ip);
if(ip[0] == '/') {
/* This looks like a local socket */
fd = create_local_accept_sock(ip, &noproto, cfg->use_systemd);
/*
* Change socket ownership and permissions so users other
* than root can access it provided they are in the same
* group as the user we run as.
*/
if(fd != -1) {
#ifdef HAVE_CHOWN
chmod(ip, (mode_t)(S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP));
if (cfg->username && cfg->username[0] &&
cfg_uid != (uid_t)-1) {
if(chown(ip, cfg_uid, cfg_gid) == -1)
verbose(VERB_QUERY, "cannot chown %u.%u %s: %s",
(unsigned)cfg_uid, (unsigned)cfg_gid,
ip, strerror(errno));
}
#else
(void)cfg;
#endif
}
} else {
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
if((r = getaddrinfo(ip, port, &hints, &res)) != 0 || !res) {
#ifdef USE_WINSOCK
if(!noproto_is_err && r == EAI_NONAME) {
/* tried to lookup the address as name */
return 1; /* return success, but do nothing */
}
#endif /* USE_WINSOCK */
log_err("control interface %s:%s getaddrinfo: %s %s",
ip?ip:"default", port, gai_strerror(r),
#ifdef EAI_SYSTEM
r==EAI_SYSTEM?(char*)strerror(errno):""
#else
""
#endif
);
return 0;
}
/* open fd */
fd = create_tcp_accept_sock(res, 1, &noproto, 0,
cfg->ip_transparent, 0, 0, cfg->ip_freebind,
cfg->use_systemd, cfg->ip_dscp);
freeaddrinfo(res);
}
if(fd == -1 && noproto) {
if(!noproto_is_err)
return 1; /* return success, but do nothing */
log_err("cannot open control interface %s %d : "
"protocol not supported", ip, nr);
return 0;
}
if(fd == -1) {
log_err("cannot open control interface %s %d", ip, nr);
return 0;
}
/* alloc */
n = (struct listen_port*)calloc(1, sizeof(*n));
if(!n) {
sock_close(fd);
log_err("out of memory");
return 0;
}
n->next = *list;
*list = n;
n->fd = fd;
return 1;
}
struct listen_port* daemon_remote_open_ports(struct config_file* cfg)
{
struct listen_port* l = NULL;
log_assert(cfg->remote_control_enable && cfg->control_port);
if(cfg->control_ifs.first) {
char** rcif = NULL;
int i, num_rcif = 0;
if(!resolve_interface_names(NULL, 0, cfg->control_ifs.first,
&rcif, &num_rcif)) {
return NULL;
}
for(i=0; i<num_rcif; i++) {
if(!add_open(rcif[i], cfg->control_port, &l, 1, cfg)) {
listening_ports_free(l);
config_del_strarray(rcif, num_rcif);
return NULL;
}
}
config_del_strarray(rcif, num_rcif);
} else {
/* defaults */
if(cfg->do_ip6 &&
!add_open("::1", cfg->control_port, &l, 0, cfg)) {
listening_ports_free(l);
return NULL;
}
if(cfg->do_ip4 &&
!add_open("127.0.0.1", cfg->control_port, &l, 1, cfg)) {
listening_ports_free(l);
return NULL;
}
}
return l;
}
/** open accept commpoint */
static int
accept_open(struct daemon_remote* rc, int fd)
{
struct listen_list* n = (struct listen_list*)malloc(sizeof(*n));
if(!n) {
log_err("out of memory");
return 0;
}
n->next = rc->accept_list;
rc->accept_list = n;
/* open commpt */
n->com = comm_point_create_raw(rc->worker->base, fd, 0,
&remote_accept_callback, rc);
if(!n->com)
return 0;
/* keep this port open, its fd is kept in the rc portlist */
n->com->do_not_close = 1;
return 1;
}
int daemon_remote_open_accept(struct daemon_remote* rc,
struct listen_port* ports, struct worker* worker)
{
struct listen_port* p;
rc->worker = worker;
for(p = ports; p; p = p->next) {
if(!accept_open(rc, p->fd)) {
log_err("could not create accept comm point");
return 0;
}
}
return 1;
}
void daemon_remote_stop_accept(struct daemon_remote* rc)
{
struct listen_list* p;
for(p=rc->accept_list; p; p=p->next) {
comm_point_stop_listening(p->com);
}
}
void daemon_remote_start_accept(struct daemon_remote* rc)
{
struct listen_list* p;
for(p=rc->accept_list; p; p=p->next) {
comm_point_start_listening(p->com, -1, -1);
}
}
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;
struct sockaddr_storage addr;
socklen_t addrlen;
int newfd;
struct rc_state* n;
if(err != NETEVENT_NOERROR) {
log_err("error %d on remote_accept_callback", err);
return 0;
}
/* perform the accept */
newfd = comm_point_perform_accept(c, &addr, &addrlen);
if(newfd == -1)
return 0;
/* create new commpoint unless we are servicing already */
if(rc->active >= rc->max_active) {
log_warn("drop incoming remote control: too many connections");
close_exit:
sock_close(newfd);
return 0;
}
/* setup commpoint to service the remote control command */
n = (struct rc_state*)calloc(1, sizeof(*n));
if(!n) {
log_err("out of memory");
goto close_exit;
}
n->fd = newfd;
/* start in reading state */
n->c = comm_point_create_raw(rc->worker->base, newfd, 0,
&remote_control_callback, n);
if(!n->c) {
log_err("out of memory");
free(n);
goto close_exit;
}
log_addr(VERB_QUERY, "new control connection from", &addr, addrlen);
n->c->do_not_close = 0;
comm_point_stop_listening(n->c);
comm_point_start_listening(n->c, -1, REMOTE_CONTROL_TCP_TIMEOUT);
memcpy(&n->c->repinfo.remote_addr, &addr, addrlen);
n->c->repinfo.remote_addrlen = addrlen;
if(rc->use_cert) {
n->shake_state = rc_hs_read;
n->ssl = SSL_new(rc->ctx);
if(!n->ssl) {
log_crypto_err("could not SSL_new");
comm_point_delete(n->c);
free(n);
goto close_exit;
}
SSL_set_accept_state(n->ssl);
(void)SSL_set_mode(n->ssl, (long)SSL_MODE_AUTO_RETRY);
if(!SSL_set_fd(n->ssl, newfd)) {
log_crypto_err("could not SSL_set_fd");
SSL_free(n->ssl);
comm_point_delete(n->c);
free(n);
goto close_exit;
}
} else {
n->ssl = NULL;
}
n->rc = rc;
n->next = rc->busy_list;
rc->busy_list = n;
rc->active ++;
/* 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;
}
/** delete from list */
static void
state_list_remove_elem(struct rc_state** list, struct comm_point* c)
{
while(*list) {
if( (*list)->c == c) {
*list = (*list)->next;
return;
}
list = &(*list)->next;
}
}
/** decrease active count and remove commpoint from busy list */
static void
clean_point(struct daemon_remote* rc, struct rc_state* s)
{
state_list_remove_elem(&rc->busy_list, s->c);
rc->active --;
if(s->ssl) {
SSL_shutdown(s->ssl);
SSL_free(s->ssl);
}
comm_point_delete(s->c);
free(s);
}
int
ssl_print_text(RES* res, const char* text)
{
int r;
if(!res)
return 0;
if(res->ssl) {
ERR_clear_error();
if((r=SSL_write(res->ssl, text, (int)strlen(text))) <= 0) {
- if(SSL_get_error(res->ssl, r) == SSL_ERROR_ZERO_RETURN) {
+ int r2;
+ if((r2=SSL_get_error(res->ssl, r)) == SSL_ERROR_ZERO_RETURN) {
verbose(VERB_QUERY, "warning, in SSL_write, peer "
"closed connection");
return 0;
}
- log_crypto_err("could not SSL_write");
+ log_crypto_err_io("could not SSL_write", r2);
return 0;
}
} else {
size_t at = 0;
while(at < strlen(text)) {
ssize_t r = send(res->fd, text+at, strlen(text)-at, 0);
if(r == -1) {
if(errno == EAGAIN || errno == EINTR)
continue;
log_err("could not send: %s",
sock_strerror(errno));
return 0;
}
at += r;
}
}
return 1;
}
/** print text over the ssl connection */
static int
ssl_print_vmsg(RES* ssl, const char* format, va_list args)
{
char msg[1024];
vsnprintf(msg, sizeof(msg), format, args);
return ssl_print_text(ssl, msg);
}
/** printf style printing to the ssl connection */
int ssl_printf(RES* ssl, const char* format, ...)
{
va_list args;
int ret;
va_start(args, format);
ret = ssl_print_vmsg(ssl, format, args);
va_end(args);
return ret;
}
int
ssl_read_line(RES* res, char* buf, size_t max)
{
int r;
size_t len = 0;
if(!res)
return 0;
while(len < max) {
if(res->ssl) {
ERR_clear_error();
if((r=SSL_read(res->ssl, buf+len, 1)) <= 0) {
- if(SSL_get_error(res->ssl, r) == SSL_ERROR_ZERO_RETURN) {
+ int r2;
+ if((r2=SSL_get_error(res->ssl, r)) == SSL_ERROR_ZERO_RETURN) {
buf[len] = 0;
return 1;
}
- log_crypto_err("could not SSL_read");
+ log_crypto_err_io("could not SSL_read", r2);
return 0;
}
} else {
while(1) {
ssize_t rr = recv(res->fd, buf+len, 1, 0);
if(rr <= 0) {
if(rr == 0) {
buf[len] = 0;
return 1;
}
if(errno == EINTR || errno == EAGAIN)
continue;
- log_err("could not recv: %s",
+ if(rr < 0) log_err("could not recv: %s",
sock_strerror(errno));
return 0;
}
break;
}
}
if(buf[len] == '\n') {
/* return string without \n */
buf[len] = 0;
return 1;
}
len++;
}
buf[max-1] = 0;
log_err("control line too long (%d): %s", (int)max, buf);
return 0;
}
/** skip whitespace, return new pointer into string */
static char*
skipwhite(char* str)
{
/* EOS \0 is not a space */
while( isspace((unsigned char)*str) )
str++;
return str;
}
/** send the OK to the control client */
static void send_ok(RES* ssl)
{
(void)ssl_printf(ssl, "ok\n");
}
/** do the stop command */
static void
do_stop(RES* ssl, struct worker* worker)
{
worker->need_to_exit = 1;
comm_base_exit(worker->base);
send_ok(ssl);
}
/** do the reload command */
static void
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);
}
/** do the verbosity command */
static void
do_verbosity(RES* ssl, char* str)
{
int val = atoi(str);
if(val == 0 && strcmp(str, "0") != 0) {
ssl_printf(ssl, "error in verbosity number syntax: %s\n", str);
return;
}
verbosity = val;
send_ok(ssl);
}
/** print stats from statinfo */
static int
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,
(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.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,
(unsigned long)s->svr.num_queries_missed_cache)) return 0;
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,
(unsigned long)s->mesh_replies_sent)) return 0;
#ifdef USE_DNSCRYPT
if(!ssl_printf(ssl, "%s.num.dnscrypt.crypted"SQ"%lu\n", nm,
(unsigned long)s->svr.num_query_dnscrypt_crypted)) return 0;
if(!ssl_printf(ssl, "%s.num.dnscrypt.cert"SQ"%lu\n", nm,
(unsigned long)s->svr.num_query_dnscrypt_cert)) return 0;
if(!ssl_printf(ssl, "%s.num.dnscrypt.cleartext"SQ"%lu\n", nm,
(unsigned long)s->svr.num_query_dnscrypt_cleartext)) return 0;
if(!ssl_printf(ssl, "%s.num.dnscrypt.malformed"SQ"%lu\n", nm,
(unsigned long)s->svr.num_query_dnscrypt_crypted_malformed)) return 0;
#endif
if(!ssl_printf(ssl, "%s.requestlist.avg"SQ"%g\n", nm,
(s->svr.num_queries_missed_cache+s->svr.num_queries_prefetch)?
(double)s->svr.sum_query_list_size/
(double)(s->svr.num_queries_missed_cache+
s->svr.num_queries_prefetch) : 0.0)) return 0;
if(!ssl_printf(ssl, "%s.requestlist.max"SQ"%lu\n", nm,
(unsigned long)s->svr.max_query_list_size)) return 0;
if(!ssl_printf(ssl, "%s.requestlist.overwritten"SQ"%lu\n", nm,
(unsigned long)s->mesh_jostled)) return 0;
if(!ssl_printf(ssl, "%s.requestlist.exceeded"SQ"%lu\n", nm,
(unsigned long)s->mesh_dropped)) return 0;
if(!ssl_printf(ssl, "%s.requestlist.current.all"SQ"%lu\n", nm,
(unsigned long)s->mesh_num_states)) return 0;
if(!ssl_printf(ssl, "%s.requestlist.current.user"SQ"%lu\n", nm,
(unsigned long)s->mesh_num_reply_states)) return 0;
#ifndef S_SPLINT_S
sumwait.tv_sec = s->mesh_replies_sum_wait_sec;
sumwait.tv_usec = s->mesh_replies_sum_wait_usec;
#endif
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,
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;
return 1;
}
/** print stats for one thread */
static int
print_thread_stats(RES* ssl, int i, struct ub_stats_info* s)
{
char nm[32];
snprintf(nm, sizeof(nm), "thread%d", i);
nm[sizeof(nm)-1]=0;
return print_stats(ssl, nm, s);
}
/** print long number */
static int
print_longnum(RES* ssl, const char* desc, size_t x)
{
if(x > 1024*1024*1024) {
/* 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,
(unsigned)front, (unsigned)back);
} else {
return ssl_printf(ssl, "%s%lu\n", desc, (unsigned long)x);
}
}
/** print mem stats */
static int
print_mem(RES* ssl, struct worker* worker, struct daemon* daemon,
struct ub_stats_info* s)
{
size_t msg, rrset, val, iter, respip;
#ifdef CLIENT_SUBNET
size_t subnet = 0;
#endif /* CLIENT_SUBNET */
#ifdef USE_IPSECMOD
size_t ipsecmod = 0;
#endif /* USE_IPSECMOD */
#ifdef USE_DNSCRYPT
size_t dnscrypt_shared_secret = 0;
size_t dnscrypt_nonce = 0;
#endif /* USE_DNSCRYPT */
#ifdef WITH_DYNLIBMODULE
size_t dynlib = 0;
#endif /* WITH_DYNLIBMODULE */
msg = slabhash_get_mem(daemon->env->msg_cache);
rrset = slabhash_get_mem(&daemon->env->rrset_cache->table);
val = mod_get_mem(&worker->env, "validator");
iter = mod_get_mem(&worker->env, "iterator");
respip = mod_get_mem(&worker->env, "respip");
#ifdef CLIENT_SUBNET
subnet = mod_get_mem(&worker->env, "subnetcache");
#endif /* CLIENT_SUBNET */
#ifdef USE_IPSECMOD
ipsecmod = mod_get_mem(&worker->env, "ipsecmod");
#endif /* USE_IPSECMOD */
#ifdef USE_DNSCRYPT
if(daemon->dnscenv) {
dnscrypt_shared_secret = slabhash_get_mem(
daemon->dnscenv->shared_secrets_cache);
dnscrypt_nonce = slabhash_get_mem(daemon->dnscenv->nonces_cache);
}
#endif /* USE_DNSCRYPT */
#ifdef WITH_DYNLIBMODULE
dynlib = mod_get_mem(&worker->env, "dynlib");
#endif /* WITH_DYNLIBMODULE */
if(!print_longnum(ssl, "mem.cache.rrset"SQ, rrset))
return 0;
if(!print_longnum(ssl, "mem.cache.message"SQ, msg))
return 0;
if(!print_longnum(ssl, "mem.mod.iterator"SQ, iter))
return 0;
if(!print_longnum(ssl, "mem.mod.validator"SQ, val))
return 0;
if(!print_longnum(ssl, "mem.mod.respip"SQ, respip))
return 0;
#ifdef CLIENT_SUBNET
if(!print_longnum(ssl, "mem.mod.subnet"SQ, subnet))
return 0;
#endif /* CLIENT_SUBNET */
#ifdef USE_IPSECMOD
if(!print_longnum(ssl, "mem.mod.ipsecmod"SQ, ipsecmod))
return 0;
#endif /* USE_IPSECMOD */
#ifdef USE_DNSCRYPT
if(!print_longnum(ssl, "mem.cache.dnscrypt_shared_secret"SQ,
dnscrypt_shared_secret))
return 0;
if(!print_longnum(ssl, "mem.cache.dnscrypt_nonce"SQ,
dnscrypt_nonce))
return 0;
#endif /* USE_DNSCRYPT */
#ifdef WITH_DYNLIBMODULE
if(!print_longnum(ssl, "mem.mod.dynlibmod"SQ, dynlib))
return 0;
#endif /* WITH_DYNLIBMODULE */
if(!print_longnum(ssl, "mem.streamwait"SQ,
(size_t)s->svr.mem_stream_wait))
return 0;
if(!print_longnum(ssl, "mem.http.query_buffer"SQ,
(size_t)s->svr.mem_http2_query_buffer))
return 0;
if(!print_longnum(ssl, "mem.http.response_buffer"SQ,
(size_t)s->svr.mem_http2_response_buffer))
return 0;
return 1;
}
/** print uptime stats */
static int
print_uptime(RES* ssl, struct worker* worker, int reset)
{
struct timeval now = *worker->env.now_tv;
struct timeval up, dt;
timeval_subtract(&up, &now, &worker->daemon->time_boot);
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",
(long long)now.tv_sec, (unsigned)now.tv_usec)) return 0;
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",
(long long)dt.tv_sec, (unsigned)dt.tv_usec)) return 0;
return 1;
}
/** print extended histogram */
static int
print_hist(RES* ssl, struct ub_stats_info* s)
{
struct timehist* hist;
size_t i;
hist = timehist_setup();
if(!hist) {
log_err("out of memory");
return 0;
}
timehist_import(hist, s->svr.hist, NUM_BUCKETS_HIST);
for(i=0; i<hist->num; i++) {
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,
(int)hist->buckets[i].upper.tv_sec,
(int)hist->buckets[i].upper.tv_usec,
(unsigned long)hist->buckets[i].count)) {
timehist_delete(hist);
return 0;
}
}
timehist_delete(hist);
return 1;
}
/** print extended stats */
static int
print_ext(RES* ssl, struct ub_stats_info* s, int inhibit_zero)
{
int i;
char nm[32];
const sldns_rr_descriptor* desc;
const sldns_lookup_table* lt;
/* TYPE */
for(i=0; i<UB_STATS_QTYPE_NUM; i++) {
if(inhibit_zero && s->svr.qtype[i] == 0)
continue;
desc = sldns_rr_descript((uint16_t)i);
if(desc && desc->_name) {
snprintf(nm, sizeof(nm), "%s", desc->_name);
} else if (i == LDNS_RR_TYPE_IXFR) {
snprintf(nm, sizeof(nm), "IXFR");
} else if (i == LDNS_RR_TYPE_AXFR) {
snprintf(nm, sizeof(nm), "AXFR");
} else if (i == LDNS_RR_TYPE_MAILA) {
snprintf(nm, sizeof(nm), "MAILA");
} else if (i == LDNS_RR_TYPE_MAILB) {
snprintf(nm, sizeof(nm), "MAILB");
} else if (i == LDNS_RR_TYPE_ANY) {
snprintf(nm, sizeof(nm), "ANY");
} else {
snprintf(nm, sizeof(nm), "TYPE%d", i);
}
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",
(unsigned long)s->svr.qtype_big)) return 0;
}
/* CLASS */
for(i=0; i<UB_STATS_QCLASS_NUM; i++) {
if(inhibit_zero && s->svr.qclass[i] == 0)
continue;
lt = sldns_lookup_by_id(sldns_rr_classes, i);
if(lt && lt->name) {
snprintf(nm, sizeof(nm), "%s", lt->name);
} else {
snprintf(nm, sizeof(nm), "CLASS%d", i);
}
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",
(unsigned long)s->svr.qclass_big)) return 0;
}
/* OPCODE */
for(i=0; i<UB_STATS_OPCODE_NUM; i++) {
if(inhibit_zero && s->svr.qopcode[i] == 0)
continue;
lt = sldns_lookup_by_id(sldns_opcodes, i);
if(lt && lt->name) {
snprintf(nm, sizeof(nm), "%s", lt->name);
} else {
snprintf(nm, sizeof(nm), "OPCODE%d", i);
}
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",
(unsigned long)s->svr.qtcp)) return 0;
if(!ssl_printf(ssl, "num.query.tcpout"SQ"%lu\n",
(unsigned long)s->svr.qtcp_outgoing)) return 0;
if(!ssl_printf(ssl, "num.query.udpout"SQ"%lu\n",
(unsigned long)s->svr.qudp_outgoing)) return 0;
if(!ssl_printf(ssl, "num.query.tls"SQ"%lu\n",
(unsigned long)s->svr.qtls)) return 0;
if(!ssl_printf(ssl, "num.query.tls.resume"SQ"%lu\n",
(unsigned long)s->svr.qtls_resume)) return 0;
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",
(unsigned long)s->svr.qbit_QR)) return 0;
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",
(unsigned long)s->svr.qbit_TC)) return 0;
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",
(unsigned long)s->svr.qbit_RA)) return 0;
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",
(unsigned long)s->svr.qbit_AD)) return 0;
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",
(unsigned long)s->svr.qEDNS)) return 0;
if(!ssl_printf(ssl, "num.query.edns.DO"SQ"%lu\n",
(unsigned long)s->svr.qEDNS_DO)) return 0;
/* RCODE */
for(i=0; i<UB_STATS_RCODE_NUM; i++) {
/* Always include RCODEs 0-5 */
if(inhibit_zero && i > LDNS_RCODE_REFUSED && s->svr.ans_rcode[i] == 0)
continue;
lt = sldns_lookup_by_id(sldns_rcodes, i);
if(lt && lt->name) {
snprintf(nm, sizeof(nm), "%s", lt->name);
} else {
snprintf(nm, sizeof(nm), "RCODE%d", i);
}
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",
(unsigned long)s->svr.ans_rcode_nodata)) return 0;
}
/* iteration */
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",
(unsigned long)s->svr.ans_secure)) return 0;
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",
(unsigned long)s->svr.rrset_bogus)) return 0;
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",
(unsigned long)s->svr.num_neg_cache_nxdomain)) return 0;
/* threat detection */
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",
(unsigned long)s->svr.unwanted_replies)) return 0;
/* cache counts */
if(!ssl_printf(ssl, "msg.cache.count"SQ"%u\n",
(unsigned)s->svr.msg_cache_count)) return 0;
if(!ssl_printf(ssl, "rrset.cache.count"SQ"%u\n",
(unsigned)s->svr.rrset_cache_count)) return 0;
if(!ssl_printf(ssl, "infra.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)
continue;
if(inhibit_zero && s->svr.rpz_action[i] == 0)
continue;
if(!ssl_printf(ssl, "num.rpz.action.%s"SQ"%lu\n",
rpz_action_to_string(i),
(unsigned long)s->svr.rpz_action[i])) return 0;
}
#ifdef USE_DNSCRYPT
if(!ssl_printf(ssl, "dnscrypt_shared_secret.cache.count"SQ"%u\n",
(unsigned)s->svr.shared_secret_cache_count)) return 0;
if(!ssl_printf(ssl, "dnscrypt_nonce.cache.count"SQ"%u\n",
(unsigned)s->svr.nonce_cache_count)) return 0;
if(!ssl_printf(ssl, "num.query.dnscrypt.shared_secret.cachemiss"SQ"%lu\n",
(unsigned long)s->svr.num_query_dnscrypt_secret_missed_cache)) return 0;
if(!ssl_printf(ssl, "num.query.dnscrypt.replay"SQ"%lu\n",
(unsigned long)s->svr.num_query_dnscrypt_replay)) return 0;
#endif /* USE_DNSCRYPT */
if(!ssl_printf(ssl, "num.query.authzone.up"SQ"%lu\n",
(unsigned long)s->svr.num_query_authzone_up)) return 0;
if(!ssl_printf(ssl, "num.query.authzone.down"SQ"%lu\n",
(unsigned long)s->svr.num_query_authzone_down)) return 0;
#ifdef CLIENT_SUBNET
if(!ssl_printf(ssl, "num.query.subnet"SQ"%lu\n",
(unsigned long)s->svr.num_query_subnet)) return 0;
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;
}
/** do the stats command */
static void
do_stats(RES* ssl, struct worker* worker, int reset)
{
struct daemon* daemon = worker->daemon;
struct ub_stats_info total;
struct ub_stats_info s;
int i;
memset(&total, 0, sizeof(total));
log_assert(daemon->num > 0);
/* gather all thread statistics in one place */
for(i=0; i<daemon->num; i++) {
server_stats_obtain(worker, daemon->workers[i], &s, reset);
if(!print_thread_stats(ssl, i, &s))
return;
if(i == 0)
total = s;
else server_stats_add(&total, &s);
}
/* print the thread statistics */
total.mesh_time_median /= (double)daemon->num;
if(!print_stats(ssl, "total", &total))
return;
if(!print_uptime(ssl, worker, reset))
return;
if(daemon->cfg->stat_extended) {
if(!print_mem(ssl, worker, daemon, &total))
return;
if(!print_hist(ssl, &total))
return;
if(!print_ext(ssl, &total, daemon->cfg->stat_inhibit_zero))
return;
}
}
/** parse commandline argument domain name */
static int
parse_arg_name(RES* ssl, char* str, uint8_t** res, size_t* len, int* labs)
{
uint8_t nm[LDNS_MAX_DOMAINLEN+1];
size_t nmlen = sizeof(nm);
int status;
*res = NULL;
*len = 0;
*labs = 0;
if(str[0] == '\0') {
ssl_printf(ssl, "error: this option requires a domain name\n");
return 0;
}
status = sldns_str2wire_dname_buf(str, nm, &nmlen);
if(status != 0) {
ssl_printf(ssl, "error cannot parse name %s at %d: %s\n", str,
LDNS_WIREPARSE_OFFSET(status),
sldns_get_errorstr_parse(status));
return 0;
}
*res = memdup(nm, nmlen);
if(!*res) {
ssl_printf(ssl, "error out of memory\n");
return 0;
}
*labs = dname_count_size_labels(*res, len);
return 1;
}
/** find second argument, modifies string */
static int
find_arg2(RES* ssl, char* arg, char** arg2)
{
char* as = strchr(arg, ' ');
char* at = strchr(arg, '\t');
if(as && at) {
if(at < as)
as = at;
as[0]=0;
*arg2 = skipwhite(as+1);
} else if(as) {
as[0]=0;
*arg2 = skipwhite(as+1);
} else if(at) {
at[0]=0;
*arg2 = skipwhite(at+1);
} else {
ssl_printf(ssl, "error could not find next argument "
"after %s\n", arg);
return 0;
}
return 1;
}
/** Add a new zone */
static int
perform_zone_add(RES* ssl, struct local_zones* zones, char* arg)
{
uint8_t* nm;
int nmlabs;
size_t nmlen;
char* arg2;
enum localzone_type t;
struct local_zone* z;
if(!find_arg2(ssl, arg, &arg2))
return 0;
if(!parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs))
return 0;
if(!local_zone_str2type(arg2, &t)) {
ssl_printf(ssl, "error not a zone type. %s\n", arg2);
free(nm);
return 0;
}
lock_rw_wrlock(&zones->lock);
if((z=local_zones_find(zones, nm, nmlen,
nmlabs, LDNS_RR_CLASS_IN))) {
/* already present in tree */
lock_rw_wrlock(&z->lock);
z->type = t; /* update type anyway */
lock_rw_unlock(&z->lock);
free(nm);
lock_rw_unlock(&zones->lock);
return 1;
}
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");
return 0;
}
lock_rw_unlock(&zones->lock);
return 1;
}
/** Do the local_zone command */
static void
do_zone_add(RES* ssl, struct local_zones* zones, char* arg)
{
if(!perform_zone_add(ssl, zones, arg))
return;
send_ok(ssl);
}
/** Do the local_zones command */
static void
do_zones_add(RES* ssl, struct local_zones* zones)
{
char buf[2048];
int num = 0;
while(ssl_read_line(ssl, buf, sizeof(buf))) {
- if(buf[0] == 0x04 && buf[1] == 0)
- break; /* end of transmission */
+ if(buf[0] == 0 || (buf[0] == 0x04 && buf[1] == 0))
+ break; /* zero byte line or end of transmission */
if(!perform_zone_add(ssl, zones, buf)) {
if(!ssl_printf(ssl, "error for input line: %s\n", buf))
return;
}
else
num++;
}
(void)ssl_printf(ssl, "added %d zones\n", num);
}
/** Remove a zone */
static int
perform_zone_remove(RES* ssl, struct local_zones* zones, char* arg)
{
uint8_t* nm;
int nmlabs;
size_t nmlen;
struct local_zone* z;
if(!parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs))
return 0;
lock_rw_wrlock(&zones->lock);
if((z=local_zones_find(zones, nm, nmlen,
nmlabs, LDNS_RR_CLASS_IN))) {
/* present in tree */
local_zones_del_zone(zones, z);
}
lock_rw_unlock(&zones->lock);
free(nm);
return 1;
}
/** Do the local_zone_remove command */
static void
do_zone_remove(RES* ssl, struct local_zones* zones, char* arg)
{
if(!perform_zone_remove(ssl, zones, arg))
return;
send_ok(ssl);
}
/** Do the local_zones_remove command */
static void
do_zones_remove(RES* ssl, struct local_zones* zones)
{
char buf[2048];
int num = 0;
while(ssl_read_line(ssl, buf, sizeof(buf))) {
- if(buf[0] == 0x04 && buf[1] == 0)
- break; /* end of transmission */
+ if(buf[0] == 0 || (buf[0] == 0x04 && buf[1] == 0))
+ break; /* zero byte line or end of transmission */
if(!perform_zone_remove(ssl, zones, buf)) {
if(!ssl_printf(ssl, "error for input line: %s\n", buf))
return;
}
else
num++;
}
(void)ssl_printf(ssl, "removed %d zones\n", num);
}
/** check syntax of newly added RR */
static int
check_RR_syntax(RES* ssl, char* str, int line)
{
uint8_t rr[LDNS_RR_BUF_SIZE];
size_t len = sizeof(rr), dname_len = 0;
int s = sldns_str2wire_rr_buf(str, rr, &len, &dname_len, 3600,
NULL, 0, NULL, 0);
if(s != 0) {
char linestr[32];
if(line == 0)
linestr[0]=0;
else snprintf(linestr, sizeof(linestr), "line %d ", line);
if(!ssl_printf(ssl, "error parsing local-data at %sposition %d '%s': %s\n",
linestr, LDNS_WIREPARSE_OFFSET(s), str,
sldns_get_errorstr_parse(s)))
return 0;
return 0;
}
return 1;
}
/** Add new RR data */
static int
perform_data_add(RES* ssl, struct local_zones* zones, char* arg, int line)
{
if(!check_RR_syntax(ssl, arg, line)) {
return 0;
}
if(!local_zones_add_RR(zones, arg)) {
ssl_printf(ssl,"error in syntax or out of memory, %s\n", arg);
return 0;
}
return 1;
}
/** Do the local_data command */
static void
do_data_add(RES* ssl, struct local_zones* zones, char* arg)
{
if(!perform_data_add(ssl, zones, arg, 0))
return;
send_ok(ssl);
}
/** Do the local_datas command */
static void
do_datas_add(RES* ssl, struct local_zones* zones)
{
char buf[2048];
int num = 0, line = 0;
while(ssl_read_line(ssl, buf, sizeof(buf))) {
- if(buf[0] == 0x04 && buf[1] == 0)
- break; /* end of transmission */
+ if(buf[0] == 0 || (buf[0] == 0x04 && buf[1] == 0))
+ break; /* zero byte line or end of transmission */
line++;
if(perform_data_add(ssl, zones, buf, line))
num++;
}
(void)ssl_printf(ssl, "added %d datas\n", num);
}
/** Remove RR data */
static int
perform_data_remove(RES* ssl, struct local_zones* zones, char* arg)
{
uint8_t* nm;
int nmlabs;
size_t nmlen;
if(!parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs))
return 0;
local_zones_del_data(zones, nm,
nmlen, nmlabs, LDNS_RR_CLASS_IN);
free(nm);
return 1;
}
/** Do the local_data_remove command */
static void
do_data_remove(RES* ssl, struct local_zones* zones, char* arg)
{
if(!perform_data_remove(ssl, zones, arg))
return;
send_ok(ssl);
}
/** Do the local_datas_remove command */
static void
do_datas_remove(RES* ssl, struct local_zones* zones)
{
char buf[2048];
int num = 0;
while(ssl_read_line(ssl, buf, sizeof(buf))) {
- if(buf[0] == 0x04 && buf[1] == 0)
- break; /* end of transmission */
+ if(buf[0] == 0 || (buf[0] == 0x04 && buf[1] == 0))
+ break; /* zero byte line or end of transmission */
if(!perform_data_remove(ssl, zones, buf)) {
if(!ssl_printf(ssl, "error for input line: %s\n", buf))
return;
}
else
num++;
}
(void)ssl_printf(ssl, "removed %d datas\n", num);
}
/** Add a new zone to view */
static void
do_view_zone_add(RES* ssl, struct worker* worker, char* arg)
{
char* arg2;
struct view* v;
if(!find_arg2(ssl, arg, &arg2))
return;
v = views_find_view(worker->daemon->views,
arg, 1 /* get write lock*/);
if(!v) {
ssl_printf(ssl,"no view with name: %s\n", arg);
return;
}
if(!v->local_zones) {
if(!(v->local_zones = local_zones_create())){
lock_rw_unlock(&v->lock);
ssl_printf(ssl,"error out of memory\n");
return;
}
if(!v->isfirst) {
/* Global local-zone is not used for this view,
* therefore add defaults to this view-specic
* local-zone. */
struct config_file lz_cfg;
memset(&lz_cfg, 0, sizeof(lz_cfg));
local_zone_enter_defaults(v->local_zones, &lz_cfg);
}
}
do_zone_add(ssl, v->local_zones, arg2);
lock_rw_unlock(&v->lock);
}
/** Remove a zone from view */
static void
do_view_zone_remove(RES* ssl, struct worker* worker, char* arg)
{
char* arg2;
struct view* v;
if(!find_arg2(ssl, arg, &arg2))
return;
v = views_find_view(worker->daemon->views,
arg, 1 /* get write lock*/);
if(!v) {
ssl_printf(ssl,"no view with name: %s\n", arg);
return;
}
if(!v->local_zones) {
lock_rw_unlock(&v->lock);
send_ok(ssl);
return;
}
do_zone_remove(ssl, v->local_zones, arg2);
lock_rw_unlock(&v->lock);
}
/** Add new RR data to view */
static void
do_view_data_add(RES* ssl, struct worker* worker, char* arg)
{
char* arg2;
struct view* v;
if(!find_arg2(ssl, arg, &arg2))
return;
v = views_find_view(worker->daemon->views,
arg, 1 /* get write lock*/);
if(!v) {
ssl_printf(ssl,"no view with name: %s\n", arg);
return;
}
if(!v->local_zones) {
if(!(v->local_zones = local_zones_create())){
lock_rw_unlock(&v->lock);
ssl_printf(ssl,"error out of memory\n");
return;
}
}
do_data_add(ssl, v->local_zones, arg2);
lock_rw_unlock(&v->lock);
}
/** Add new RR data from stdin to view */
static void
do_view_datas_add(RES* ssl, struct worker* worker, char* arg)
{
struct view* v;
v = views_find_view(worker->daemon->views,
arg, 1 /* get write lock*/);
if(!v) {
ssl_printf(ssl,"no view with name: %s\n", arg);
return;
}
if(!v->local_zones) {
if(!(v->local_zones = local_zones_create())){
lock_rw_unlock(&v->lock);
ssl_printf(ssl,"error out of memory\n");
return;
}
}
do_datas_add(ssl, v->local_zones);
lock_rw_unlock(&v->lock);
}
/** Remove RR data from view */
static void
do_view_data_remove(RES* ssl, struct worker* worker, char* arg)
{
char* arg2;
struct view* v;
if(!find_arg2(ssl, arg, &arg2))
return;
v = views_find_view(worker->daemon->views,
arg, 1 /* get write lock*/);
if(!v) {
ssl_printf(ssl,"no view with name: %s\n", arg);
return;
}
if(!v->local_zones) {
lock_rw_unlock(&v->lock);
send_ok(ssl);
return;
}
do_data_remove(ssl, v->local_zones, arg2);
lock_rw_unlock(&v->lock);
}
/** Remove RR data from stdin from view */
static void
do_view_datas_remove(RES* ssl, struct worker* worker, char* arg)
{
struct view* v;
v = views_find_view(worker->daemon->views,
arg, 1 /* get write lock*/);
if(!v) {
ssl_printf(ssl,"no view with name: %s\n", arg);
return;
}
if(!v->local_zones){
lock_rw_unlock(&v->lock);
ssl_printf(ssl, "removed 0 datas\n");
return;
}
do_datas_remove(ssl, v->local_zones);
lock_rw_unlock(&v->lock);
}
/** cache lookup of nameservers */
static void
do_lookup(RES* ssl, struct worker* worker, char* arg)
{
uint8_t* nm;
int nmlabs;
size_t nmlen;
if(!parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs))
return;
(void)print_deleg_lookup(ssl, worker, nm, nmlen, nmlabs);
free(nm);
}
/** flush something from rrset and msg caches */
static void
do_cache_remove(struct worker* worker, uint8_t* nm, size_t nmlen,
uint16_t t, uint16_t c)
{
hashvalue_type h;
struct query_info k;
rrset_cache_remove(worker->env.rrset_cache, nm, nmlen, t, c, 0);
if(t == LDNS_RR_TYPE_SOA)
rrset_cache_remove(worker->env.rrset_cache, nm, nmlen, t, c,
PACKED_RRSET_SOA_NEG);
k.qname = nm;
k.qname_len = nmlen;
k.qtype = t;
k.qclass = c;
k.local_alias = NULL;
h = query_info_hash(&k, 0);
slabhash_remove(worker->env.msg_cache, h, &k);
if(t == LDNS_RR_TYPE_AAAA) {
/* for AAAA also flush dns64 bit_cd packet */
h = query_info_hash(&k, BIT_CD);
slabhash_remove(worker->env.msg_cache, h, &k);
}
}
/** flush a type */
static void
do_flush_type(RES* ssl, struct worker* worker, char* arg)
{
uint8_t* nm;
int nmlabs;
size_t nmlen;
char* arg2;
uint16_t t;
if(!find_arg2(ssl, arg, &arg2))
return;
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);
}
/** flush statistics */
static void
do_flush_stats(RES* ssl, struct worker* worker)
{
worker_stats_clear(worker);
send_ok(ssl);
}
/**
* Local info for deletion functions
*/
struct del_info {
/** worker */
struct worker* worker;
/** name to delete */
uint8_t* name;
/** length */
size_t len;
/** labels */
int labs;
/** time to invalidate to */
time_t expired;
/** number of rrsets removed */
size_t num_rrsets;
/** number of msgs removed */
size_t num_msgs;
/** number of key entries removed */
size_t num_keys;
/** length of addr */
socklen_t addrlen;
/** socket address for host deletion */
struct sockaddr_storage addr;
};
/** callback to delete hosts in infra cache */
static void
infra_del_host(struct lruhash_entry* e, void* arg)
{
/* entry is locked */
struct del_info* inf = (struct del_info*)arg;
struct infra_key* k = (struct infra_key*)e->key;
if(sockaddr_cmp(&inf->addr, inf->addrlen, &k->addr, k->addrlen) == 0) {
struct infra_data* d = (struct infra_data*)e->data;
d->probedelay = 0;
d->timeout_A = 0;
d->timeout_AAAA = 0;
d->timeout_other = 0;
rtt_init(&d->rtt);
if(d->ttl > inf->expired) {
d->ttl = inf->expired;
inf->num_keys++;
}
}
}
/** flush infra cache */
static void
do_flush_infra(RES* ssl, struct worker* worker, char* arg)
{
struct sockaddr_storage addr;
socklen_t len;
struct del_info inf;
if(strcmp(arg, "all") == 0) {
slabhash_clear(worker->env.infra_cache->hosts);
send_ok(ssl);
return;
}
if(!ipstrtoaddr(arg, UNBOUND_DNS_PORT, &addr, &len)) {
(void)ssl_printf(ssl, "error parsing ip addr: '%s'\n", arg);
return;
}
/* delete all entries from cache */
/* what we do is to set them all expired */
inf.worker = worker;
inf.name = 0;
inf.len = 0;
inf.labs = 0;
inf.expired = *worker->env.now;
inf.expired -= 3; /* handle 3 seconds skew between threads */
inf.num_rrsets = 0;
inf.num_msgs = 0;
inf.num_keys = 0;
inf.addrlen = len;
memmove(&inf.addr, &addr, len);
slabhash_traverse(worker->env.infra_cache->hosts, 1, &infra_del_host,
&inf);
send_ok(ssl);
}
/** flush requestlist */
static void
do_flush_requestlist(RES* ssl, struct worker* worker)
{
mesh_delete_all(worker->env.mesh);
send_ok(ssl);
}
/** callback to delete rrsets in a zone */
static void
zone_del_rrset(struct lruhash_entry* e, void* arg)
{
/* entry is locked */
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*)e->data;
if(d->ttl > inf->expired) {
d->ttl = inf->expired;
inf->num_rrsets++;
}
}
}
/** callback to delete messages in a zone */
static void
zone_del_msg(struct lruhash_entry* e, void* arg)
{
/* entry is locked */
struct del_info* inf = (struct del_info*)arg;
struct msgreply_entry* k = (struct msgreply_entry*)e->key;
if(dname_subdomain_c(k->key.qname, inf->name)) {
struct reply_info* d = (struct reply_info*)e->data;
if(d->ttl > inf->expired) {
d->ttl = inf->expired;
d->prefetch_ttl = inf->expired;
d->serve_expired_ttl = inf->expired;
inf->num_msgs++;
}
}
}
/** callback to delete keys in zone */
static void
zone_del_kcache(struct lruhash_entry* e, void* arg)
{
/* entry is locked */
struct del_info* inf = (struct del_info*)arg;
struct key_entry_key* k = (struct key_entry_key*)e->key;
if(dname_subdomain_c(k->name, inf->name)) {
struct key_entry_data* d = (struct key_entry_data*)e->data;
if(d->ttl > inf->expired) {
d->ttl = inf->expired;
inf->num_keys++;
}
}
}
/** remove all rrsets and keys from zone from cache */
static void
do_flush_zone(RES* ssl, struct worker* worker, char* arg)
{
uint8_t* nm;
int nmlabs;
size_t nmlen;
struct del_info inf;
if(!parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs))
return;
/* delete all RRs and key entries from zone */
/* what we do is to set them all expired */
inf.worker = worker;
inf.name = nm;
inf.len = nmlen;
inf.labs = nmlabs;
inf.expired = *worker->env.now;
inf.expired -= 3; /* handle 3 seconds skew between threads */
inf.num_rrsets = 0;
inf.num_msgs = 0;
inf.num_keys = 0;
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,
&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,
(unsigned long)inf.num_msgs, (unsigned long)inf.num_keys);
}
/** callback to delete bogus rrsets */
static void
bogus_del_rrset(struct lruhash_entry* e, void* arg)
{
/* entry is locked */
struct del_info* inf = (struct del_info*)arg;
struct packed_rrset_data* d = (struct packed_rrset_data*)e->data;
if(d->security == sec_status_bogus) {
d->ttl = inf->expired;
inf->num_rrsets++;
}
}
/** callback to delete bogus messages */
static void
bogus_del_msg(struct lruhash_entry* e, void* arg)
{
/* entry is locked */
struct del_info* inf = (struct del_info*)arg;
struct reply_info* d = (struct reply_info*)e->data;
if(d->security == sec_status_bogus) {
d->ttl = inf->expired;
inf->num_msgs++;
}
}
/** callback to delete bogus keys */
static void
bogus_del_kcache(struct lruhash_entry* e, void* arg)
{
/* entry is locked */
struct del_info* inf = (struct del_info*)arg;
struct key_entry_data* d = (struct key_entry_data*)e->data;
if(d->isbad) {
d->ttl = inf->expired;
inf->num_keys++;
}
}
/** remove all bogus rrsets, msgs and keys from cache */
static void
do_flush_bogus(RES* ssl, struct worker* worker)
{
struct del_info inf;
/* what we do is to set them all expired */
inf.worker = worker;
inf.expired = *worker->env.now;
inf.expired -= 3; /* handle 3 seconds skew between threads */
inf.num_rrsets = 0;
inf.num_msgs = 0;
inf.num_keys = 0;
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,
&bogus_del_kcache, &inf);
}
(void)ssl_printf(ssl, "ok removed %lu rrsets, %lu messages "
"and %lu key entries\n", (unsigned long)inf.num_rrsets,
(unsigned long)inf.num_msgs, (unsigned long)inf.num_keys);
}
/** callback to delete negative and servfail rrsets */
static void
negative_del_rrset(struct lruhash_entry* e, void* arg)
{
/* entry is locked */
struct del_info* inf = (struct del_info*)arg;
struct ub_packed_rrset_key* k = (struct ub_packed_rrset_key*)e->key;
struct packed_rrset_data* d = (struct packed_rrset_data*)e->data;
/* delete the parentside negative cache rrsets,
* these are nameserver rrsets that failed lookup, rdata empty */
if((k->rk.flags & PACKED_RRSET_PARENT_SIDE) && d->count == 1 &&
d->rrsig_count == 0 && d->rr_len[0] == 0) {
d->ttl = inf->expired;
inf->num_rrsets++;
}
}
/** callback to delete negative and servfail messages */
static void
negative_del_msg(struct lruhash_entry* e, void* arg)
{
/* entry is locked */
struct del_info* inf = (struct del_info*)arg;
struct reply_info* d = (struct reply_info*)e->data;
/* rcode not NOERROR: NXDOMAIN, SERVFAIL, ..: an nxdomain or error
* or NOERROR rcode with ANCOUNT==0: a NODATA answer */
if(FLAGS_GET_RCODE(d->flags) != 0 || d->an_numrrsets == 0) {
d->ttl = inf->expired;
inf->num_msgs++;
}
}
/** callback to delete negative key entries */
static void
negative_del_kcache(struct lruhash_entry* e, void* arg)
{
/* entry is locked */
struct del_info* inf = (struct del_info*)arg;
struct key_entry_data* d = (struct key_entry_data*)e->data;
/* could be bad because of lookup failure on the DS, DNSKEY, which
* was nxdomain or servfail, and thus a result of negative lookups */
if(d->isbad) {
d->ttl = inf->expired;
inf->num_keys++;
}
}
/** remove all negative(NODATA,NXDOMAIN), and servfail messages from cache */
static void
do_flush_negative(RES* ssl, struct worker* worker)
{
struct del_info inf;
/* what we do is to set them all expired */
inf.worker = worker;
inf.expired = *worker->env.now;
inf.expired -= 3; /* handle 3 seconds skew between threads */
inf.num_rrsets = 0;
inf.num_msgs = 0;
inf.num_keys = 0;
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,
&negative_del_kcache, &inf);
}
(void)ssl_printf(ssl, "ok removed %lu rrsets, %lu messages "
"and %lu key entries\n", (unsigned long)inf.num_rrsets,
(unsigned long)inf.num_msgs, (unsigned long)inf.num_keys);
}
/** remove name rrset from cache */
static void
do_flush_name(RES* ssl, struct worker* w, char* arg)
{
uint8_t* nm;
int nmlabs;
size_t nmlen;
if(!parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs))
return;
do_cache_remove(w, nm, nmlen, LDNS_RR_TYPE_A, LDNS_RR_CLASS_IN);
do_cache_remove(w, nm, nmlen, LDNS_RR_TYPE_AAAA, LDNS_RR_CLASS_IN);
do_cache_remove(w, nm, nmlen, LDNS_RR_TYPE_NS, LDNS_RR_CLASS_IN);
do_cache_remove(w, nm, nmlen, LDNS_RR_TYPE_SOA, LDNS_RR_CLASS_IN);
do_cache_remove(w, nm, nmlen, LDNS_RR_TYPE_CNAME, LDNS_RR_CLASS_IN);
do_cache_remove(w, nm, nmlen, LDNS_RR_TYPE_DNAME, LDNS_RR_CLASS_IN);
do_cache_remove(w, nm, nmlen, LDNS_RR_TYPE_MX, LDNS_RR_CLASS_IN);
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);
}
/** printout a delegation point info */
static int
ssl_print_name_dp(RES* ssl, const char* str, uint8_t* nm, uint16_t dclass,
struct delegpt* dp)
{
char buf[257];
struct delegpt_ns* ns;
struct delegpt_addr* a;
int f = 0;
if(str) { /* print header for forward, stub */
char* c = sldns_wire2str_class(dclass);
dname_str(nm, buf);
if(!ssl_printf(ssl, "%s %s %s ", buf, (c?c:"CLASS??"), str)) {
free(c);
return 0;
}
free(c);
}
for(ns = dp->nslist; ns; ns = ns->next) {
dname_str(ns->name, buf);
if(!ssl_printf(ssl, "%s%s", (f?" ":""), buf))
return 0;
f = 1;
}
for(a = dp->target_list; a; a = a->next_target) {
addr_to_str(&a->addr, a->addrlen, buf, sizeof(buf));
if(!ssl_printf(ssl, "%s%s", (f?" ":""), buf))
return 0;
f = 1;
}
return ssl_printf(ssl, "\n");
}
/** print root forwards */
static int
print_root_fwds(RES* ssl, struct iter_forwards* fwds, uint8_t* root)
{
struct delegpt* dp;
dp = forwards_lookup(fwds, root, LDNS_RR_CLASS_IN);
if(!dp)
return ssl_printf(ssl, "off (using root hints)\n");
/* if dp is returned it must be the root */
log_assert(query_dname_compare(dp->name, root)==0);
return ssl_print_name_dp(ssl, NULL, root, LDNS_RR_CLASS_IN, dp);
}
/** parse args into delegpt */
static struct delegpt*
parse_delegpt(RES* ssl, char* args, uint8_t* nm)
{
/* parse args and add in */
char* p = args;
char* todo;
struct delegpt* dp = delegpt_create_mlc(nm);
struct sockaddr_storage addr;
socklen_t addrlen;
char* auth_name;
if(!dp) {
(void)ssl_printf(ssl, "error out of memory\n");
return NULL;
}
while(p) {
todo = p;
p = strchr(p, ' '); /* find next spot, if any */
if(p) {
*p++ = 0; /* end this spot */
p = skipwhite(p); /* position at next spot */
}
/* parse address */
if(!authextstrtoaddr(todo, &addr, &addrlen, &auth_name)) {
uint8_t* dname= NULL;
int port;
dname = authextstrtodname(todo, &port, &auth_name);
if(!dname) {
(void)ssl_printf(ssl, "error cannot parse"
" '%s'\n", todo);
delegpt_free_mlc(dp);
return NULL;
}
#if ! defined(HAVE_SSL_SET1_HOST) && ! defined(HAVE_X509_VERIFY_PARAM_SET1_HOST)
if(auth_name)
log_err("no name verification functionality in "
"ssl library, ignored name for %s", todo);
#endif
if(!delegpt_add_ns_mlc(dp, dname, 0, auth_name, port)) {
(void)ssl_printf(ssl, "error out of memory\n");
free(dname);
delegpt_free_mlc(dp);
return NULL;
}
} else {
#if ! defined(HAVE_SSL_SET1_HOST) && ! defined(HAVE_X509_VERIFY_PARAM_SET1_HOST)
if(auth_name)
log_err("no name verification functionality in "
"ssl library, ignored name for %s", todo);
#endif
/* add address */
if(!delegpt_add_addr_mlc(dp, &addr, addrlen, 0, 0,
auth_name, -1)) {
(void)ssl_printf(ssl, "error out of memory\n");
delegpt_free_mlc(dp);
return NULL;
}
}
}
dp->has_parent_side_NS = 1;
return dp;
}
/** do the status command */
static void
do_forward(RES* ssl, struct worker* worker, char* args)
{
struct iter_forwards* fwd = worker->env.fwds;
uint8_t* root = (uint8_t*)"\000";
if(!fwd) {
(void)ssl_printf(ssl, "error: structure not allocated\n");
return;
}
if(args == NULL || args[0] == 0) {
(void)print_root_fwds(ssl, fwd, root);
return;
}
/* set root forwards for this thread. since we are in remote control
* the actual mesh is not running, so we can freely edit it. */
/* delete all the existing queries first */
mesh_delete_all(worker->env.mesh);
if(strcmp(args, "off") == 0) {
forwards_delete_zone(fwd, LDNS_RR_CLASS_IN, root);
} else {
struct delegpt* dp;
if(!(dp = parse_delegpt(ssl, args, root)))
return;
if(!forwards_add_zone(fwd, LDNS_RR_CLASS_IN, dp)) {
(void)ssl_printf(ssl, "error out of memory\n");
return;
}
}
send_ok(ssl);
}
static int
parse_fs_args(RES* ssl, char* args, uint8_t** nm, struct delegpt** dp,
int* insecure, int* prime)
{
char* zonename;
char* rest;
size_t nmlen;
int nmlabs;
/* parse all -x args */
while(args[0] == '+') {
if(!find_arg2(ssl, args, &rest))
return 0;
while(*(++args) != 0) {
if(*args == 'i' && insecure)
*insecure = 1;
else if(*args == 'p' && prime)
*prime = 1;
else {
(void)ssl_printf(ssl, "error: unknown option %s\n", args);
return 0;
}
}
args = rest;
}
/* parse name */
if(dp) {
if(!find_arg2(ssl, args, &rest))
return 0;
zonename = args;
args = rest;
} else zonename = args;
if(!parse_arg_name(ssl, zonename, nm, &nmlen, &nmlabs))
return 0;
/* parse dp */
if(dp) {
if(!(*dp = parse_delegpt(ssl, args, *nm))) {
free(*nm);
return 0;
}
}
return 1;
}
/** do the forward_add command */
static void
do_forward_add(RES* ssl, struct worker* worker, char* args)
{
struct iter_forwards* fwd = worker->env.fwds;
int insecure = 0;
uint8_t* nm = NULL;
struct delegpt* dp = NULL;
if(!parse_fs_args(ssl, args, &nm, &dp, &insecure, NULL))
return;
if(insecure && worker->env.anchors) {
if(!anchors_add_insecure(worker->env.anchors, LDNS_RR_CLASS_IN,
nm)) {
(void)ssl_printf(ssl, "error out of memory\n");
delegpt_free_mlc(dp);
free(nm);
return;
}
}
if(!forwards_add_zone(fwd, LDNS_RR_CLASS_IN, dp)) {
(void)ssl_printf(ssl, "error out of memory\n");
free(nm);
return;
}
free(nm);
send_ok(ssl);
}
/** do the forward_remove command */
static void
do_forward_remove(RES* ssl, struct worker* worker, char* args)
{
struct iter_forwards* fwd = worker->env.fwds;
int insecure = 0;
uint8_t* nm = NULL;
if(!parse_fs_args(ssl, args, &nm, NULL, &insecure, NULL))
return;
if(insecure && worker->env.anchors)
anchors_delete_insecure(worker->env.anchors, LDNS_RR_CLASS_IN,
nm);
forwards_delete_zone(fwd, LDNS_RR_CLASS_IN, nm);
free(nm);
send_ok(ssl);
}
/** do the stub_add command */
static void
do_stub_add(RES* ssl, struct worker* worker, char* args)
{
struct iter_forwards* fwd = worker->env.fwds;
int insecure = 0, prime = 0;
uint8_t* nm = NULL;
struct delegpt* dp = NULL;
if(!parse_fs_args(ssl, args, &nm, &dp, &insecure, &prime))
return;
if(insecure && worker->env.anchors) {
if(!anchors_add_insecure(worker->env.anchors, LDNS_RR_CLASS_IN,
nm)) {
(void)ssl_printf(ssl, "error out of memory\n");
delegpt_free_mlc(dp);
free(nm);
return;
}
}
if(!forwards_add_stub_hole(fwd, LDNS_RR_CLASS_IN, nm)) {
if(insecure && worker->env.anchors)
anchors_delete_insecure(worker->env.anchors,
LDNS_RR_CLASS_IN, nm);
(void)ssl_printf(ssl, "error out of memory\n");
delegpt_free_mlc(dp);
free(nm);
return;
}
if(!hints_add_stub(worker->env.hints, LDNS_RR_CLASS_IN, dp, !prime)) {
(void)ssl_printf(ssl, "error out of memory\n");
forwards_delete_stub_hole(fwd, LDNS_RR_CLASS_IN, nm);
if(insecure && worker->env.anchors)
anchors_delete_insecure(worker->env.anchors,
LDNS_RR_CLASS_IN, nm);
free(nm);
return;
}
free(nm);
send_ok(ssl);
}
/** do the stub_remove command */
static void
do_stub_remove(RES* ssl, struct worker* worker, char* args)
{
struct iter_forwards* fwd = worker->env.fwds;
int insecure = 0;
uint8_t* nm = NULL;
if(!parse_fs_args(ssl, args, &nm, NULL, &insecure, NULL))
return;
if(insecure && worker->env.anchors)
anchors_delete_insecure(worker->env.anchors, LDNS_RR_CLASS_IN,
nm);
forwards_delete_stub_hole(fwd, LDNS_RR_CLASS_IN, nm);
hints_delete_stub(worker->env.hints, LDNS_RR_CLASS_IN, nm);
free(nm);
send_ok(ssl);
}
/** do the insecure_add command */
static void
do_insecure_add(RES* ssl, struct worker* worker, char* arg)
{
size_t nmlen;
int nmlabs;
uint8_t* nm = NULL;
if(!parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs))
return;
if(worker->env.anchors) {
if(!anchors_add_insecure(worker->env.anchors,
LDNS_RR_CLASS_IN, nm)) {
(void)ssl_printf(ssl, "error out of memory\n");
free(nm);
return;
}
}
free(nm);
send_ok(ssl);
}
/** do the insecure_remove command */
static void
do_insecure_remove(RES* ssl, struct worker* worker, char* arg)
{
size_t nmlen;
int nmlabs;
uint8_t* nm = NULL;
if(!parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs))
return;
if(worker->env.anchors)
anchors_delete_insecure(worker->env.anchors,
LDNS_RR_CLASS_IN, nm);
free(nm);
send_ok(ssl);
}
static void
do_insecure_list(RES* ssl, struct worker* worker)
{
char buf[257];
struct trust_anchor* a;
if(worker->env.anchors) {
RBTREE_FOR(a, struct trust_anchor*, worker->env.anchors->tree) {
if(a->numDS == 0 && a->numDNSKEY == 0) {
dname_str(a->name, buf);
ssl_printf(ssl, "%s\n", buf);
}
}
}
}
/** do the status command */
static void
do_status(RES* ssl, struct worker* worker)
{
int i;
time_t uptime;
if(!ssl_printf(ssl, "version: %s\n", PACKAGE_VERSION))
return;
if(!ssl_printf(ssl, "verbosity: %d\n", verbosity))
return;
if(!ssl_printf(ssl, "threads: %d\n", worker->daemon->num))
return;
if(!ssl_printf(ssl, "modules: %d [", worker->daemon->mods.num))
return;
for(i=0; i<worker->daemon->mods.num; i++) {
if(!ssl_printf(ssl, " %s", worker->daemon->mods.mod[i]->name))
return;
}
if(!ssl_printf(ssl, " ]\n"))
return;
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" ,
(worker->daemon->reuseport?" reuseport":""),
(worker->daemon->rc->accept_list?" control":""),
(worker->daemon->rc->accept_list && worker->daemon->rc->use_cert?"(ssl)":""),
(worker->daemon->rc->accept_list && worker->daemon->cfg->control_ifs.first && worker->daemon->cfg->control_ifs.first->str && worker->daemon->cfg->control_ifs.first->str[0] == '/'?"(namedpipe)":"")
))
return;
if(!ssl_printf(ssl, "unbound (pid %d) is running...\n",
(int)getpid()))
return;
}
/** get age for the mesh state */
static void
get_mesh_age(struct mesh_state* m, char* buf, size_t len,
struct module_env* env)
{
if(m->reply_list) {
struct timeval d;
struct mesh_reply* r = m->reply_list;
/* last reply is the oldest */
while(r && r->next)
r = r->next;
timeval_subtract(&d, env->now_tv, &r->start_time);
snprintf(buf, len, ARG_LL "d.%6.6d",
(long long)d.tv_sec, (int)d.tv_usec);
} else {
snprintf(buf, len, "-");
}
}
/** get status of a mesh state */
static void
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];
const char *modname = mesh->mods.mod[m->s.curmod]->name;
size_t l;
if(strcmp(modname, "iterator") == 0 && s == module_wait_reply &&
m->s.minfo[m->s.curmod]) {
/* break into iterator to find out who its waiting for */
struct iter_qstate* qstate = (struct iter_qstate*)
m->s.minfo[m->s.curmod];
struct outbound_list* ol = &qstate->outlist;
struct outbound_entry* e;
snprintf(buf, len, "%s wait for", modname);
l = strlen(buf);
buf += l; len -= l;
if(ol->first == NULL)
snprintf(buf, len, " (empty_list)");
for(e = ol->first; e; e = e->next) {
snprintf(buf, len, " ");
l = strlen(buf);
buf += l; len -= l;
addr_to_str(&e->qsent->addr, e->qsent->addrlen,
buf, len);
l = strlen(buf);
buf += l; len -= l;
}
} else if(s == module_wait_subquery) {
/* look in subs from mesh state to see what */
char nm[257];
struct mesh_state_ref* sub;
snprintf(buf, len, "%s wants", modname);
l = strlen(buf);
buf += l; len -= l;
if(m->sub_set.count == 0)
snprintf(buf, len, " (empty_list)");
RBTREE_FOR(sub, struct mesh_state_ref*, &m->sub_set) {
char* t = sldns_wire2str_type(sub->s->s.qinfo.qtype);
char* c = sldns_wire2str_class(sub->s->s.qinfo.qclass);
dname_str(sub->s->s.qinfo.qname, nm);
snprintf(buf, len, " %s %s %s", (t?t:"TYPE??"),
(c?c:"CLASS??"), nm);
l = strlen(buf);
buf += l; len -= l;
free(t);
free(c);
}
} else {
snprintf(buf, len, "%s is %s", modname, strextstate(s));
}
}
/** do the dump_requestlist command */
static void
do_dump_requestlist(RES* ssl, struct worker* worker)
{
struct mesh_area* mesh;
struct mesh_state* m;
int num = 0;
char buf[257];
char timebuf[32];
char statbuf[10240];
if(!ssl_printf(ssl, "thread #%d\n", worker->thread_num))
return;
if(!ssl_printf(ssl, "# type cl name seconds module status\n"))
return;
/* show worker mesh contents */
mesh = worker->env.mesh;
if(!mesh) return;
RBTREE_FOR(m, struct mesh_state*, &mesh->all) {
char* t = sldns_wire2str_type(m->s.qinfo.qtype);
char* c = sldns_wire2str_class(m->s.qinfo.qclass);
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",
num, (t?t:"TYPE??"), (c?c:"CLASS??"), buf, timebuf,
statbuf)) {
free(t);
free(c);
return;
}
num++;
free(t);
free(c);
}
}
/** structure for argument data for dump infra host */
struct infra_arg {
/** the infra cache */
struct infra_cache* infra;
/** the SSL connection */
RES* ssl;
/** the time now */
time_t now;
/** ssl failure? stop writing and skip the rest. If the tcp
* connection is broken, and writes fail, we then stop writing. */
int ssl_failed;
};
/** callback for every host element in the infra cache */
static void
dump_infra_host(struct lruhash_entry* e, void* arg)
{
struct infra_arg* a = (struct infra_arg*)arg;
struct infra_key* k = (struct infra_key*)e->key;
struct infra_data* d = (struct infra_data*)e->data;
char ip_str[1024];
char name[257];
int port;
if(a->ssl_failed)
return;
addr_to_str(&k->addr, k->addrlen, ip_str, sizeof(ip_str));
dname_str(k->zonename, name);
port = (int)ntohs(((struct sockaddr_in*)&k->addr)->sin_port);
if(port != UNBOUND_DNS_PORT) {
snprintf(ip_str+strlen(ip_str), sizeof(ip_str)-strlen(ip_str),
"@%d", port);
}
/* skip expired stuff (only backed off) */
if(d->ttl < a->now) {
if(d->rtt.rto >= USEFUL_SERVER_TOP_TIMEOUT) {
if(!ssl_printf(a->ssl, "%s %s expired rto %d\n", ip_str,
name, d->rtt.rto)) {
a->ssl_failed = 1;
return;
}
}
return;
}
if(!ssl_printf(a->ssl, "%s %s ttl %lu ping %d var %d rtt %d rto %d "
"tA %d tAAAA %d tother %d "
"ednsknown %d edns %d delay %d lame dnssec %d rec %d A %d "
"other %d\n", ip_str, name, (unsigned long)(d->ttl - a->now),
d->rtt.srtt, d->rtt.rttvar, rtt_notimeout(&d->rtt), d->rtt.rto,
d->timeout_A, d->timeout_AAAA, d->timeout_other,
(int)d->edns_lame_known, (int)d->edns_version,
(int)(a->now<d->probedelay?(d->probedelay - a->now):0),
(int)d->isdnsseclame, (int)d->rec_lame, (int)d->lame_type_A,
(int)d->lame_other)) {
a->ssl_failed = 1;
return;
}
}
/** do the dump_infra command */
static void
do_dump_infra(RES* ssl, struct worker* worker)
{
struct infra_arg arg;
arg.infra = worker->env.infra_cache;
arg.ssl = ssl;
arg.now = *worker->env.now;
arg.ssl_failed = 0;
slabhash_traverse(arg.infra->hosts, 0, &dump_infra_host, (void*)&arg);
}
/** do the log_reopen command */
static void
do_log_reopen(RES* ssl, struct worker* worker)
{
struct config_file* cfg = worker->env.cfg;
send_ok(ssl);
log_init(cfg->logfile, cfg->use_syslog, cfg->chrootdir);
}
/** do the auth_zone_reload command */
static void
do_auth_zone_reload(RES* ssl, struct worker* worker, char* arg)
{
size_t nmlen;
int nmlabs;
uint8_t* nm = NULL;
struct auth_zones* az = worker->env.auth_zones;
struct auth_zone* z = NULL;
struct auth_xfer* xfr = NULL;
char* reason = NULL;
if(!parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs))
return;
if(az) {
lock_rw_rdlock(&az->lock);
z = auth_zone_find(az, nm, nmlen, LDNS_RR_CLASS_IN);
if(z) {
lock_rw_wrlock(&z->lock);
}
xfr = auth_xfer_find(az, nm, nmlen, LDNS_RR_CLASS_IN);
if(xfr) {
lock_basic_lock(&xfr->lock);
}
lock_rw_unlock(&az->lock);
}
free(nm);
if(!z) {
if(xfr) {
lock_basic_unlock(&xfr->lock);
}
(void)ssl_printf(ssl, "error no auth-zone %s\n", arg);
return;
}
if(!auth_zone_read_zonefile(z, worker->env.cfg)) {
lock_rw_unlock(&z->lock);
if(xfr) {
lock_basic_unlock(&xfr->lock);
}
(void)ssl_printf(ssl, "error failed to read %s\n", arg);
return;
}
z->zone_expired = 0;
if(xfr) {
xfr->zone_expired = 0;
if(!xfr_find_soa(z, xfr)) {
if(z->data.count == 0) {
lock_rw_unlock(&z->lock);
lock_basic_unlock(&xfr->lock);
(void)ssl_printf(ssl, "zone %s has no contents\n", arg);
return;
}
lock_rw_unlock(&z->lock);
lock_basic_unlock(&xfr->lock);
(void)ssl_printf(ssl, "error: no SOA in zone after read %s\n", arg);
return;
}
if(xfr->have_zone)
xfr->lease_time = *worker->env.now;
lock_basic_unlock(&xfr->lock);
}
auth_zone_verify_zonemd(z, &worker->env, &worker->env.mesh->mods,
&reason, 0, 0);
if(reason && z->zone_expired) {
lock_rw_unlock(&z->lock);
(void)ssl_printf(ssl, "error zonemd for %s failed: %s\n",
arg, reason);
free(reason);
return;
} else if(reason && strcmp(reason, "ZONEMD verification successful")
==0) {
(void)ssl_printf(ssl, "%s: %s\n", arg, reason);
}
lock_rw_unlock(&z->lock);
free(reason);
send_ok(ssl);
}
/** do the auth_zone_transfer command */
static void
do_auth_zone_transfer(RES* ssl, struct worker* worker, char* arg)
{
size_t nmlen;
int nmlabs;
uint8_t* nm = NULL;
struct auth_zones* az = worker->env.auth_zones;
if(!parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs))
return;
if(!az || !auth_zones_startprobesequence(az, &worker->env, nm, nmlen,
LDNS_RR_CLASS_IN)) {
(void)ssl_printf(ssl, "error zone xfr task not found %s\n", arg);
free(nm);
return;
}
free(nm);
send_ok(ssl);
}
/** do the set_option command */
static void
do_set_option(RES* ssl, struct worker* worker, char* arg)
{
char* arg2;
if(!find_arg2(ssl, arg, &arg2))
return;
if(!config_set_option(worker->env.cfg, arg, arg2)) {
(void)ssl_printf(ssl, "error setting option\n");
return;
}
/* effectuate some arguments */
if(strcmp(arg, "val-override-date:") == 0) {
int m = modstack_find(&worker->env.mesh->mods, "validator");
struct val_env* val_env = NULL;
if(m != -1) val_env = (struct val_env*)worker->env.modinfo[m];
if(val_env)
val_env->date_override = worker->env.cfg->val_date_override;
}
send_ok(ssl);
}
/* routine to printout option values over SSL */
void remote_get_opt_ssl(char* line, void* arg)
{
RES* ssl = (RES*)arg;
(void)ssl_printf(ssl, "%s\n", line);
}
/** do the get_option command */
static void
do_get_option(RES* ssl, struct worker* worker, char* arg)
{
int r;
r = config_get_option(worker->env.cfg, arg, remote_get_opt_ssl, ssl);
if(!r) {
(void)ssl_printf(ssl, "error unknown option\n");
return;
}
}
/** do the list_forwards command */
static void
do_list_forwards(RES* ssl, struct worker* worker)
{
/* since its a per-worker structure no locks needed */
struct iter_forwards* fwds = worker->env.fwds;
struct iter_forward_zone* z;
struct trust_anchor* a;
int insecure;
RBTREE_FOR(z, struct iter_forward_zone*, fwds->tree) {
if(!z->dp) continue; /* skip empty marker for stub */
/* see if it is insecure */
insecure = 0;
if(worker->env.anchors &&
(a=anchor_find(worker->env.anchors, z->name,
z->namelabs, z->namelen, z->dclass))) {
if(!a->keylist && !a->numDS && !a->numDNSKEY)
insecure = 1;
lock_basic_unlock(&a->lock);
}
if(!ssl_print_name_dp(ssl, (insecure?"forward +i":"forward"),
z->name, z->dclass, z->dp))
return;
}
}
/** do the list_stubs command */
static void
do_list_stubs(RES* ssl, struct worker* worker)
{
struct iter_hints_stub* z;
struct trust_anchor* a;
int insecure;
char str[32];
RBTREE_FOR(z, struct iter_hints_stub*, &worker->env.hints->tree) {
/* see if it is insecure */
insecure = 0;
if(worker->env.anchors &&
(a=anchor_find(worker->env.anchors, z->node.name,
z->node.labs, z->node.len, z->node.dclass))) {
if(!a->keylist && !a->numDS && !a->numDNSKEY)
insecure = 1;
lock_basic_unlock(&a->lock);
}
snprintf(str, sizeof(str), "stub %sprime%s",
(z->noprime?"no":""), (insecure?" +i":""));
if(!ssl_print_name_dp(ssl, str, z->node.name,
z->node.dclass, z->dp))
return;
}
}
/** do the list_auth_zones command */
static void
do_list_auth_zones(RES* ssl, struct auth_zones* az)
{
struct auth_zone* z;
char buf[257], buf2[256];
lock_rw_rdlock(&az->lock);
RBTREE_FOR(z, struct auth_zone*, &az->ztree) {
lock_rw_rdlock(&z->lock);
dname_str(z->name, buf);
if(z->zone_expired)
snprintf(buf2, sizeof(buf2), "expired");
else {
uint32_t serial = 0;
if(auth_zone_get_serial(z, &serial))
snprintf(buf2, sizeof(buf2), "serial %u",
(unsigned)serial);
else snprintf(buf2, sizeof(buf2), "no serial");
}
if(!ssl_printf(ssl, "%s\t%s\n", buf, buf2)) {
/* failure to print */
lock_rw_unlock(&z->lock);
lock_rw_unlock(&az->lock);
return;
}
lock_rw_unlock(&z->lock);
}
lock_rw_unlock(&az->lock);
}
/** do the list_local_zones command */
static void
do_list_local_zones(RES* ssl, struct local_zones* zones)
{
struct local_zone* z;
char buf[257];
lock_rw_rdlock(&zones->lock);
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,
local_zone_type2str(z->type))) {
/* failure to print */
lock_rw_unlock(&z->lock);
lock_rw_unlock(&zones->lock);
return;
}
lock_rw_unlock(&z->lock);
}
lock_rw_unlock(&zones->lock);
}
/** do the list_local_data command */
static void
do_list_local_data(RES* ssl, struct worker* worker, struct local_zones* zones)
{
struct local_zone* z;
struct local_data* d;
struct local_rrset* p;
char* s = (char*)sldns_buffer_begin(worker->env.scratch_buffer);
size_t slen = sldns_buffer_capacity(worker->env.scratch_buffer);
lock_rw_rdlock(&zones->lock);
RBTREE_FOR(z, struct local_zone*, &zones->ztree) {
lock_rw_rdlock(&z->lock);
RBTREE_FOR(d, struct local_data*, &z->data) {
for(p = d->rrsets; p; p = p->next) {
struct packed_rrset_data* d =
(struct packed_rrset_data*)p->rrset->entry.data;
size_t i;
for(i=0; i<d->count + d->rrsig_count; i++) {
if(!packed_rr_to_string(p->rrset, i,
0, s, slen)) {
if(!ssl_printf(ssl, "BADRR\n")) {
lock_rw_unlock(&z->lock);
lock_rw_unlock(&zones->lock);
return;
}
}
if(!ssl_printf(ssl, "%s\n", s)) {
lock_rw_unlock(&z->lock);
lock_rw_unlock(&zones->lock);
return;
}
}
}
}
lock_rw_unlock(&z->lock);
}
lock_rw_unlock(&zones->lock);
}
/** do the view_list_local_zones command */
static void
do_view_list_local_zones(RES* ssl, struct worker* worker, char* arg)
{
struct view* v = views_find_view(worker->daemon->views,
arg, 0 /* get read lock*/);
if(!v) {
ssl_printf(ssl,"no view with name: %s\n", arg);
return;
}
if(v->local_zones) {
do_list_local_zones(ssl, v->local_zones);
}
lock_rw_unlock(&v->lock);
}
/** do the view_list_local_data command */
static void
do_view_list_local_data(RES* ssl, struct worker* worker, char* arg)
{
struct view* v = views_find_view(worker->daemon->views,
arg, 0 /* get read lock*/);
if(!v) {
ssl_printf(ssl,"no view with name: %s\n", arg);
return;
}
if(v->local_zones) {
do_list_local_data(ssl, worker, v->local_zones);
}
lock_rw_unlock(&v->lock);
}
/** struct for user arg ratelimit list */
struct ratelimit_list_arg {
/** the infra cache */
struct infra_cache* infra;
/** the SSL to print to */
RES* ssl;
/** all or only ratelimited */
int all;
/** current time */
time_t now;
/** if backoff is enabled */
int backoff;
};
#define ip_ratelimit_list_arg ratelimit_list_arg
/** list items in the ratelimit table */
static void
rate_list(struct lruhash_entry* e, void* arg)
{
struct ratelimit_list_arg* a = (struct ratelimit_list_arg*)arg;
struct rate_key* k = (struct rate_key*)e->key;
struct rate_data* d = (struct rate_data*)e->data;
char buf[257];
int lim = infra_find_ratelimit(a->infra, k->name, k->namelen);
int max = infra_rate_max(d, a->now, a->backoff);
if(a->all == 0) {
if(max < lim)
return;
}
dname_str(k->name, buf);
ssl_printf(a->ssl, "%s %d limit %d\n", buf, max, lim);
}
/** list items in the ip_ratelimit table */
static void
ip_rate_list(struct lruhash_entry* e, void* arg)
{
char ip[128];
struct ip_ratelimit_list_arg* a = (struct ip_ratelimit_list_arg*)arg;
struct ip_rate_key* k = (struct ip_rate_key*)e->key;
struct ip_rate_data* d = (struct ip_rate_data*)e->data;
int lim = infra_ip_ratelimit;
int max = infra_rate_max(d, a->now, a->backoff);
if(a->all == 0) {
if(max < lim)
return;
}
addr_to_str(&k->addr, k->addrlen, ip, sizeof(ip));
ssl_printf(a->ssl, "%s %d limit %d\n", ip, max, lim);
}
/** do the ratelimit_list command */
static void
do_ratelimit_list(RES* ssl, struct worker* worker, char* arg)
{
struct ratelimit_list_arg a;
a.all = 0;
a.infra = worker->env.infra_cache;
a.now = *worker->env.now;
a.ssl = ssl;
a.backoff = worker->env.cfg->ratelimit_backoff;
arg = skipwhite(arg);
if(strcmp(arg, "+a") == 0)
a.all = 1;
if(a.infra->domain_rates==NULL ||
(a.all == 0 && infra_dp_ratelimit == 0))
return;
slabhash_traverse(a.infra->domain_rates, 0, rate_list, &a);
}
/** do the ip_ratelimit_list command */
static void
do_ip_ratelimit_list(RES* ssl, struct worker* worker, char* arg)
{
struct ip_ratelimit_list_arg a;
a.all = 0;
a.infra = worker->env.infra_cache;
a.now = *worker->env.now;
a.ssl = ssl;
a.backoff = worker->env.cfg->ip_ratelimit_backoff;
arg = skipwhite(arg);
if(strcmp(arg, "+a") == 0)
a.all = 1;
if(a.infra->client_ip_rates==NULL ||
(a.all == 0 && infra_ip_ratelimit == 0))
return;
slabhash_traverse(a.infra->client_ip_rates, 0, ip_rate_list, &a);
}
/** do the rpz_enable/disable command */
static void
do_rpz_enable_disable(RES* ssl, struct worker* worker, char* arg, int enable) {
size_t nmlen;
int nmlabs;
uint8_t *nm = NULL;
struct auth_zones *az = worker->env.auth_zones;
struct auth_zone *z = NULL;
if (!parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs))
return;
if (az) {
lock_rw_rdlock(&az->lock);
z = auth_zone_find(az, nm, nmlen, LDNS_RR_CLASS_IN);
if (z) {
lock_rw_wrlock(&z->lock);
}
lock_rw_unlock(&az->lock);
}
free(nm);
if (!z) {
(void) ssl_printf(ssl, "error no auth-zone %s\n", arg);
return;
}
if (!z->rpz) {
(void) ssl_printf(ssl, "error auth-zone %s not RPZ\n", arg);
lock_rw_unlock(&z->lock);
return;
}
if (enable) {
rpz_enable(z->rpz);
} else {
rpz_disable(z->rpz);
}
lock_rw_unlock(&z->lock);
send_ok(ssl);
}
/** do the rpz_enable command */
static void
do_rpz_enable(RES* ssl, struct worker* worker, char* arg)
{
do_rpz_enable_disable(ssl, worker, arg, 1);
}
/** do the rpz_disable command */
static void
do_rpz_disable(RES* ssl, struct worker* worker, char* arg)
{
do_rpz_enable_disable(ssl, worker, arg, 0);
}
/** tell other processes to execute the command */
static void
distribute_cmd(struct daemon_remote* rc, RES* ssl, char* cmd)
{
int i;
if(!cmd || !ssl)
return;
/* skip i=0 which is me */
for(i=1; i<rc->worker->daemon->num; i++) {
worker_send_cmd(rc->worker->daemon->workers[i],
worker_cmd_remote);
if(!tube_write_msg(rc->worker->daemon->workers[i]->cmd,
(uint8_t*)cmd, strlen(cmd)+1, 0)) {
ssl_printf(ssl, "error could not distribute cmd\n");
return;
}
}
}
/** check for name with end-of-string, space or tab after it */
static int
cmdcmp(char* p, const char* cmd, size_t len)
{
return strncmp(p,cmd,len)==0 && (p[len]==0||p[len]==' '||p[len]=='\t');
}
/** execute a remote control command */
static void
execute_cmd(struct daemon_remote* rc, RES* ssl, char* cmd,
struct worker* worker)
{
char* p = skipwhite(cmd);
/* compare command */
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, 0);
return;
} else if(cmdcmp(p, "stats_noreset", 13)) {
do_stats(ssl, worker, 0);
return;
} else if(cmdcmp(p, "stats", 5)) {
do_stats(ssl, worker, 1);
return;
} else if(cmdcmp(p, "status", 6)) {
do_status(ssl, worker);
return;
} else if(cmdcmp(p, "dump_cache", 10)) {
(void)dump_cache(ssl, worker);
return;
} else if(cmdcmp(p, "load_cache", 10)) {
if(load_cache(ssl, worker)) send_ok(ssl);
return;
} else if(cmdcmp(p, "list_forwards", 13)) {
do_list_forwards(ssl, worker);
return;
} else if(cmdcmp(p, "list_stubs", 10)) {
do_list_stubs(ssl, worker);
return;
} else if(cmdcmp(p, "list_insecure", 13)) {
do_insecure_list(ssl, worker);
return;
} else if(cmdcmp(p, "list_local_zones", 16)) {
do_list_local_zones(ssl, worker->daemon->local_zones);
return;
} else if(cmdcmp(p, "list_local_data", 15)) {
do_list_local_data(ssl, worker, worker->daemon->local_zones);
return;
} else if(cmdcmp(p, "view_list_local_zones", 21)) {
do_view_list_local_zones(ssl, worker, skipwhite(p+21));
return;
} else if(cmdcmp(p, "view_list_local_data", 20)) {
do_view_list_local_data(ssl, worker, skipwhite(p+20));
return;
} else if(cmdcmp(p, "ratelimit_list", 14)) {
do_ratelimit_list(ssl, worker, p+14);
return;
} else if(cmdcmp(p, "ip_ratelimit_list", 17)) {
do_ip_ratelimit_list(ssl, worker, p+17);
return;
} else if(cmdcmp(p, "list_auth_zones", 15)) {
do_list_auth_zones(ssl, worker->env.auth_zones);
return;
} else if(cmdcmp(p, "auth_zone_reload", 16)) {
do_auth_zone_reload(ssl, worker, skipwhite(p+16));
return;
} else if(cmdcmp(p, "auth_zone_transfer", 18)) {
do_auth_zone_transfer(ssl, worker, skipwhite(p+18));
return;
} else if(cmdcmp(p, "stub_add", 8)) {
/* must always distribute this cmd */
if(rc) distribute_cmd(rc, ssl, cmd);
do_stub_add(ssl, worker, skipwhite(p+8));
return;
} else if(cmdcmp(p, "stub_remove", 11)) {
/* must always distribute this cmd */
if(rc) distribute_cmd(rc, ssl, cmd);
do_stub_remove(ssl, worker, skipwhite(p+11));
return;
} else if(cmdcmp(p, "forward_add", 11)) {
/* must always distribute this cmd */
if(rc) distribute_cmd(rc, ssl, cmd);
do_forward_add(ssl, worker, skipwhite(p+11));
return;
} else if(cmdcmp(p, "forward_remove", 14)) {
/* must always distribute this cmd */
if(rc) distribute_cmd(rc, ssl, cmd);
do_forward_remove(ssl, worker, skipwhite(p+14));
return;
} else if(cmdcmp(p, "insecure_add", 12)) {
/* must always distribute this cmd */
if(rc) distribute_cmd(rc, ssl, cmd);
do_insecure_add(ssl, worker, skipwhite(p+12));
return;
} else if(cmdcmp(p, "insecure_remove", 15)) {
/* must always distribute this cmd */
if(rc) distribute_cmd(rc, ssl, cmd);
do_insecure_remove(ssl, worker, skipwhite(p+15));
return;
} else if(cmdcmp(p, "forward", 7)) {
/* must always distribute this cmd */
if(rc) distribute_cmd(rc, ssl, cmd);
do_forward(ssl, worker, skipwhite(p+7));
return;
} else if(cmdcmp(p, "flush_stats", 11)) {
/* must always distribute this cmd */
if(rc) distribute_cmd(rc, ssl, cmd);
do_flush_stats(ssl, worker);
return;
} else if(cmdcmp(p, "flush_requestlist", 17)) {
/* must always distribute this cmd */
if(rc) distribute_cmd(rc, ssl, cmd);
do_flush_requestlist(ssl, worker);
return;
} else if(cmdcmp(p, "lookup", 6)) {
do_lookup(ssl, worker, skipwhite(p+6));
return;
}
#ifdef THREADS_DISABLED
/* other processes must execute the command as well */
/* commands that should not be distributed, returned above. */
if(rc) { /* only if this thread is the master (rc) thread */
/* done before the code below, which may split the string */
distribute_cmd(rc, ssl, cmd);
}
#endif
if(cmdcmp(p, "verbosity", 9)) {
do_verbosity(ssl, skipwhite(p+9));
} else if(cmdcmp(p, "local_zone_remove", 17)) {
do_zone_remove(ssl, worker->daemon->local_zones, skipwhite(p+17));
} else if(cmdcmp(p, "local_zones_remove", 18)) {
do_zones_remove(ssl, worker->daemon->local_zones);
} else if(cmdcmp(p, "local_zone", 10)) {
do_zone_add(ssl, worker->daemon->local_zones, skipwhite(p+10));
} else if(cmdcmp(p, "local_zones", 11)) {
do_zones_add(ssl, worker->daemon->local_zones);
} else if(cmdcmp(p, "local_data_remove", 17)) {
do_data_remove(ssl, worker->daemon->local_zones, skipwhite(p+17));
} else if(cmdcmp(p, "local_datas_remove", 18)) {
do_datas_remove(ssl, worker->daemon->local_zones);
} else if(cmdcmp(p, "local_data", 10)) {
do_data_add(ssl, worker->daemon->local_zones, skipwhite(p+10));
} else if(cmdcmp(p, "local_datas", 11)) {
do_datas_add(ssl, worker->daemon->local_zones);
} else if(cmdcmp(p, "view_local_zone_remove", 22)) {
do_view_zone_remove(ssl, worker, skipwhite(p+22));
} else if(cmdcmp(p, "view_local_zone", 15)) {
do_view_zone_add(ssl, worker, skipwhite(p+15));
} else if(cmdcmp(p, "view_local_data_remove", 22)) {
do_view_data_remove(ssl, worker, skipwhite(p+22));
} else if(cmdcmp(p, "view_local_datas_remove", 23)){
do_view_datas_remove(ssl, worker, skipwhite(p+23));
} else if(cmdcmp(p, "view_local_data", 15)) {
do_view_data_add(ssl, worker, skipwhite(p+15));
} else if(cmdcmp(p, "view_local_datas", 16)) {
do_view_datas_add(ssl, worker, skipwhite(p+16));
} else if(cmdcmp(p, "flush_zone", 10)) {
do_flush_zone(ssl, worker, skipwhite(p+10));
} else if(cmdcmp(p, "flush_type", 10)) {
do_flush_type(ssl, worker, skipwhite(p+10));
} else if(cmdcmp(p, "flush_infra", 11)) {
do_flush_infra(ssl, worker, skipwhite(p+11));
} else if(cmdcmp(p, "flush", 5)) {
do_flush_name(ssl, worker, skipwhite(p+5));
} else if(cmdcmp(p, "dump_requestlist", 16)) {
do_dump_requestlist(ssl, worker);
} else if(cmdcmp(p, "dump_infra", 10)) {
do_dump_infra(ssl, worker);
} else if(cmdcmp(p, "log_reopen", 10)) {
do_log_reopen(ssl, worker);
} else if(cmdcmp(p, "set_option", 10)) {
do_set_option(ssl, worker, skipwhite(p+10));
} else if(cmdcmp(p, "get_option", 10)) {
do_get_option(ssl, worker, skipwhite(p+10));
} else if(cmdcmp(p, "flush_bogus", 11)) {
do_flush_bogus(ssl, worker);
} else if(cmdcmp(p, "flush_negative", 14)) {
do_flush_negative(ssl, worker);
} else if(cmdcmp(p, "rpz_enable", 10)) {
do_rpz_enable(ssl, worker, skipwhite(p+10));
} else if(cmdcmp(p, "rpz_disable", 11)) {
do_rpz_disable(ssl, worker, skipwhite(p+11));
} else {
(void)ssl_printf(ssl, "error unknown command '%s'\n", p);
}
}
void
daemon_remote_exec(struct worker* worker)
{
/* read the cmd string */
uint8_t* msg = NULL;
uint32_t len = 0;
if(!tube_read_msg(worker->cmd, &msg, &len, 0)) {
log_err("daemon_remote_exec: tube_read_msg failed");
return;
}
verbose(VERB_ALGO, "remote exec distributed: %s", (char*)msg);
execute_cmd(NULL, NULL, (char*)msg, worker);
free(msg);
}
/** handle remote control request */
static void
handle_req(struct daemon_remote* rc, struct rc_state* s, RES* res)
{
int r;
char pre[10];
char magic[7];
char buf[1024];
#ifdef USE_WINSOCK
/* makes it possible to set the socket blocking again. */
/* basically removes it from winsock_event ... */
WSAEventSelect(s->c->fd, NULL, 0);
#endif
fd_set_block(s->c->fd);
/* try to read magic UBCT[version]_space_ string */
if(res->ssl) {
ERR_clear_error();
if((r=SSL_read(res->ssl, magic, (int)sizeof(magic)-1)) <= 0) {
- if(SSL_get_error(res->ssl, r) == SSL_ERROR_ZERO_RETURN)
+ int r2;
+ if((r2=SSL_get_error(res->ssl, r)) == SSL_ERROR_ZERO_RETURN)
return;
- log_crypto_err("could not SSL_read");
+ log_crypto_err_io("could not SSL_read", r2);
return;
}
} else {
while(1) {
ssize_t rr = recv(res->fd, magic, sizeof(magic)-1, 0);
if(rr <= 0) {
if(rr == 0) return;
if(errno == EINTR || errno == EAGAIN)
continue;
log_err("could not recv: %s", sock_strerror(errno));
return;
}
r = (int)rr;
break;
}
}
magic[6] = 0;
if( r != 6 || strncmp(magic, "UBCT", 4) != 0) {
verbose(VERB_QUERY, "control connection has bad magic string");
/* probably wrong tool connected, ignore it completely */
return;
}
/* read the command line */
if(!ssl_read_line(res, buf, sizeof(buf))) {
return;
}
snprintf(pre, sizeof(pre), "UBCT%d ", UNBOUND_CONTROL_VERSION);
if(strcmp(magic, pre) != 0) {
verbose(VERB_QUERY, "control connection had bad "
"version %s, cmd: %s", magic, buf);
ssl_printf(res, "error version mismatch\n");
return;
}
verbose(VERB_DETAIL, "control cmd: %s", buf);
/* figure out what to do */
execute_cmd(rc, res, buf, rc->worker);
}
/** handle SSL_do_handshake changes to the file descriptor to wait for later */
static int
remote_handshake_later(struct daemon_remote* rc, struct rc_state* s,
struct comm_point* c, int r, int r2)
{
if(r2 == SSL_ERROR_WANT_READ) {
if(s->shake_state == rc_hs_read) {
/* try again later */
return 0;
}
s->shake_state = rc_hs_read;
comm_point_listen_for_rw(c, 1, 0);
return 0;
} else if(r2 == SSL_ERROR_WANT_WRITE) {
if(s->shake_state == rc_hs_write) {
/* try again later */
return 0;
}
s->shake_state = rc_hs_write;
comm_point_listen_for_rw(c, 0, 1);
return 0;
} else {
if(r == 0)
log_err("remote control connection closed prematurely");
log_addr(VERB_OPS, "failed connection from",
&s->c->repinfo.remote_addr, s->c->repinfo.remote_addrlen);
- log_crypto_err("remote control failed ssl");
+ log_crypto_err_io("remote control failed ssl", r2);
clean_point(rc, s);
}
return 0;
}
int remote_control_callback(struct comm_point* c, void* arg, int err,
struct comm_reply* ATTR_UNUSED(rep))
{
RES res;
struct rc_state* s = (struct rc_state*)arg;
struct daemon_remote* rc = s->rc;
int r;
if(err != NETEVENT_NOERROR) {
if(err==NETEVENT_TIMEOUT)
log_err("remote control timed out");
clean_point(rc, s);
return 0;
}
if(s->ssl) {
/* (continue to) setup the SSL connection */
ERR_clear_error();
r = SSL_do_handshake(s->ssl);
if(r != 1) {
int r2 = SSL_get_error(s->ssl, r);
return remote_handshake_later(rc, s, c, r, r2);
}
s->shake_state = rc_none;
}
/* once handshake has completed, check authentication */
if (!rc->use_cert) {
verbose(VERB_ALGO, "unauthenticated remote control connection");
} else if(SSL_get_verify_result(s->ssl) == X509_V_OK) {
#ifdef HAVE_SSL_GET1_PEER_CERTIFICATE
X509* x = SSL_get1_peer_certificate(s->ssl);
#else
X509* x = SSL_get_peer_certificate(s->ssl);
#endif
if(!x) {
verbose(VERB_DETAIL, "remote control connection "
"provided no client certificate");
clean_point(rc, s);
return 0;
}
verbose(VERB_ALGO, "remote control connection authenticated");
X509_free(x);
} else {
verbose(VERB_DETAIL, "remote control connection failed to "
"authenticate with client certificate");
clean_point(rc, s);
return 0;
}
/* if OK start to actually handle the request */
res.ssl = s->ssl;
res.fd = c->fd;
handle_req(rc, s, &res);
verbose(VERB_ALGO, "remote control operation completed");
clean_point(rc, s);
return 0;
}
diff --git a/contrib/unbound/daemon/worker.c b/contrib/unbound/daemon/worker.c
index 8c6fa3b9af33..8ae05eb67e66 100644
--- a/contrib/unbound/daemon/worker.c
+++ b/contrib/unbound/daemon/worker.c
@@ -1,2514 +1,2522 @@
/*
* daemon/worker.c - worker that handles a pending list of requests.
*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file implements the worker that handles callbacks on events, for
* pending requests.
*/
#include "config.h"
#include "util/log.h"
#include "util/net_help.h"
#include "util/random.h"
#include "daemon/worker.h"
#include "daemon/daemon.h"
#include "daemon/remote.h"
#include "daemon/acl_list.h"
#include "util/netevent.h"
#include "util/config_file.h"
#include "util/module.h"
#include "util/regional.h"
#include "util/storage/slabhash.h"
#include "services/listen_dnsport.h"
#include "services/outside_network.h"
#include "services/outbound_list.h"
#include "services/cache/rrset.h"
#include "services/cache/infra.h"
#include "services/cache/dns.h"
#include "services/authzone.h"
#include "services/mesh.h"
#include "services/localzone.h"
#include "services/rpz.h"
#include "util/data/msgparse.h"
#include "util/data/msgencode.h"
#include "util/data/dname.h"
#include "util/fptr_wlist.h"
+#include "util/proxy_protocol.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"
#include "validator/autotrust.h"
#include "validator/val_anchor.h"
#include "respip/respip.h"
#include "libunbound/context.h"
#include "libunbound/libworker.h"
#include "sldns/sbuffer.h"
#include "sldns/wire2str.h"
#include "util/shm_side/shm_main.h"
#include "dnscrypt/dnscrypt.h"
#include "dnstap/dtstream.h"
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#include <signal.h>
#ifdef UB_ON_WINDOWS
#include "winrc/win_svc.h"
#endif
/** Size of an UDP datagram */
#define NORMAL_UDP_SIZE 512 /* bytes */
/** ratelimit for error responses */
#define ERROR_RATELIMIT 100 /* qps */
/**
* seconds to add to prefetch leeway. This is a TTL that expires old rrsets
* earlier than they should in order to put the new update into the cache.
* This additional value is to make sure that if not all TTLs are equal in
* the message to be updated(and replaced), that rrsets with up to this much
* extra TTL are also replaced. This means that the resulting new message
* will have (most likely) this TTL at least, avoiding very small 'split
* second' TTLs due to operators choosing relative primes for TTLs (or so).
* Also has to be at least one to break ties (and overwrite cached entry).
*/
#define PREFETCH_EXPIRY_ADD 60
/** Report on memory usage by this thread and global */
static void
worker_mem_report(struct worker* ATTR_UNUSED(worker),
struct serviced_query* ATTR_UNUSED(cur_serv))
{
#ifdef UNBOUND_ALLOC_STATS
/* measure memory leakage */
extern size_t unbound_mem_alloc, unbound_mem_freed;
/* debug func in validator module */
size_t total, front, back, mesh, msg, rrset, infra, ac, superac;
size_t me, iter, val, anch;
int i;
#ifdef CLIENT_SUBNET
size_t subnet = 0;
#endif /* CLIENT_SUBNET */
if(verbosity < VERB_ALGO)
return;
front = listen_get_mem(worker->front);
back = outnet_get_mem(worker->back);
msg = slabhash_get_mem(worker->env.msg_cache);
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);
superac = alloc_get_mem(&worker->daemon->superalloc);
anch = anchors_get_mem(worker->env.anchors);
iter = 0;
val = 0;
for(i=0; i<worker->env.mesh->mods.num; i++) {
fptr_ok(fptr_whitelist_mod_get_mem(worker->env.mesh->
mods.mod[i]->get_mem));
if(strcmp(worker->env.mesh->mods.mod[i]->name, "validator")==0)
val += (*worker->env.mesh->mods.mod[i]->get_mem)
(&worker->env, i);
#ifdef CLIENT_SUBNET
else if(strcmp(worker->env.mesh->mods.mod[i]->name,
"subnetcache")==0)
subnet += (*worker->env.mesh->mods.mod[i]->get_mem)
(&worker->env, i);
#endif /* CLIENT_SUBNET */
else iter += (*worker->env.mesh->mods.mod[i]->get_mem)
(&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)
+ sldns_buffer_capacity(worker->env.scratch_buffer)
+ forwards_get_mem(worker->env.fwds)
+ hints_get_mem(worker->env.hints);
if(worker->thread_num == 0)
me += acl_list_get_mem(worker->daemon->acl);
if(cur_serv) {
me += serviced_get_mem(cur_serv);
}
total = front+back+mesh+msg+rrset+infra+iter+val+ac+superac+me;
#ifdef CLIENT_SUBNET
total += subnet;
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)mesh, (unsigned)msg, (unsigned)rrset, (unsigned)infra,
(unsigned)iter, (unsigned)val,
(unsigned)subnet, (unsigned)anch, (unsigned)ac,
(unsigned)superac, (unsigned)me);
#else /* no CLIENT_SUBNET */
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)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,
(unsigned)unbound_mem_alloc, (unsigned)unbound_mem_freed);
#else /* no UNBOUND_ALLOC_STATS */
size_t val = 0;
#ifdef CLIENT_SUBNET
size_t subnet = 0;
#endif /* CLIENT_SUBNET */
int i;
if(verbosity < VERB_QUERY)
return;
for(i=0; i<worker->env.mesh->mods.num; i++) {
fptr_ok(fptr_whitelist_mod_get_mem(worker->env.mesh->
mods.mod[i]->get_mem));
if(strcmp(worker->env.mesh->mods.mod[i]->name, "validator")==0)
val += (*worker->env.mesh->mods.mod[i]->get_mem)
(&worker->env, i);
#ifdef CLIENT_SUBNET
else if(strcmp(worker->env.mesh->mods.mod[i]->name,
"subnetcache")==0)
subnet += (*worker->env.mesh->mods.mod[i]->get_mem)
(&worker->env, i);
#endif /* CLIENT_SUBNET */
}
#ifdef CLIENT_SUBNET
verbose(VERB_QUERY, "cache memory msg=%u rrset=%u infra=%u val=%u "
"subnet=%u",
(unsigned)slabhash_get_mem(worker->env.msg_cache),
(unsigned)slabhash_get_mem(&worker->env.rrset_cache->table),
(unsigned)infra_get_mem(worker->env.infra_cache),
(unsigned)val, (unsigned)subnet);
#else /* no CLIENT_SUBNET */
verbose(VERB_QUERY, "cache memory msg=%u rrset=%u infra=%u val=%u",
(unsigned)slabhash_get_mem(worker->env.msg_cache),
(unsigned)slabhash_get_mem(&worker->env.rrset_cache->table),
(unsigned)infra_get_mem(worker->env.infra_cache),
(unsigned)val);
#endif /* CLIENT_SUBNET */
#endif /* UNBOUND_ALLOC_STATS */
}
void
worker_send_cmd(struct worker* worker, enum worker_commands cmd)
{
uint32_t c = (uint32_t)htonl(cmd);
if(!tube_write_msg(worker->cmd, (uint8_t*)&c, sizeof(c), 0)) {
log_err("worker send cmd %d failed", (int)cmd);
}
}
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;
struct worker* worker = e->qstate->env->worker;
struct serviced_query *sq = e->qsent;
verbose(VERB_ALGO, "worker svcd callback for qstate %p", e->qstate);
if(error != 0) {
mesh_report_reply(worker->env.mesh, e, reply_info, error);
worker_mem_report(worker, sq);
return 0;
}
/* sanity check. */
if(!LDNS_QR_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,
NETEVENT_TIMEOUT);
worker_mem_report(worker, sq);
return 0;
}
mesh_report_reply(worker->env.mesh, e, reply_info, NETEVENT_NOERROR);
worker_mem_report(worker, sq);
return 0;
}
/** ratelimit error replies
* @param worker: the worker struct with ratelimit counter
* @param err: error code that would be wanted.
* @return value of err if okay, or -1 if it should be discarded instead.
*/
static int
worker_err_ratelimit(struct worker* worker, int err)
{
if(worker->err_limit_time == *worker->env.now) {
/* see if limit is exceeded for this second */
if(worker->err_limit_count++ > ERROR_RATELIMIT)
return -1;
} else {
/* new second, new limits */
worker->err_limit_time = *worker->env.now;
worker->err_limit_count = 1;
}
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.
* @param out: struct to update with the result.
*/
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");
out->value = -1;
return;
}
if(sldns_buffer_limit(pkt) > NORMAL_UDP_SIZE &&
worker->daemon->cfg->harden_large_queries) {
verbose(VERB_QUERY, "request too large, discarded");
out->value = -1;
return;
}
if(LDNS_QR_WIRE(sldns_buffer_begin(pkt))) {
verbose(VERB_QUERY, "request has QR bit on, discarded");
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");
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",
LDNS_OPCODE_WIRE(sldns_buffer_begin(pkt)));
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",
LDNS_QDCOUNT(sldns_buffer_begin(pkt)));
out->value = worker_err_ratelimit(worker, LDNS_RCODE_FORMERR);
return;
}
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",
LDNS_ANCOUNT(sldns_buffer_begin(pkt)));
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",
LDNS_NSCOUNT(sldns_buffer_begin(pkt)));
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",
LDNS_ARCOUNT(sldns_buffer_begin(pkt)));
out->value = worker_err_ratelimit(worker, LDNS_RCODE_FORMERR);
return;
}
out->value = 0;
return;
}
void
worker_handle_control_cmd(struct tube* ATTR_UNUSED(tube), uint8_t* msg,
size_t len, int error, void* arg)
{
struct worker* worker = (struct worker*)arg;
enum worker_commands cmd;
if(error != NETEVENT_NOERROR) {
free(msg);
if(error == NETEVENT_CLOSED)
comm_base_exit(worker->base);
else log_info("control event: %d", error);
return;
}
if(len != sizeof(uint32_t)) {
fatal_exit("bad control msg length %d", (int)len);
}
cmd = sldns_read_uint32(msg);
free(msg);
switch(cmd) {
case worker_cmd_quit:
verbose(VERB_ALGO, "got control cmd quit");
comm_base_exit(worker->base);
break;
case worker_cmd_stats:
verbose(VERB_ALGO, "got control cmd stats");
server_stats_reply(worker, 1);
break;
case worker_cmd_stats_noreset:
verbose(VERB_ALGO, "got control cmd stats_noreset");
server_stats_reply(worker, 0);
break;
case worker_cmd_remote:
verbose(VERB_ALGO, "got control cmd remote");
daemon_remote_exec(worker);
break;
default:
log_err("bad command %d", (int)cmd);
break;
}
}
/** check if a delegation is secure */
static enum sec_status
check_delegation_secure(struct reply_info *rep)
{
/* return smallest security status */
size_t i;
enum sec_status sec = sec_status_secure;
enum sec_status s;
size_t num = rep->an_numrrsets + rep->ns_numrrsets;
/* check if answer and authority are OK */
for(i=0; i<num; i++) {
s = ((struct packed_rrset_data*)rep->rrsets[i]->entry.data)
->security;
if(s < sec)
sec = s;
}
/* in additional, only unchecked triggers revalidation */
for(i=num; i<rep->rrset_count; i++) {
s = ((struct packed_rrset_data*)rep->rrsets[i]->entry.data)
->security;
if(s == sec_status_unchecked)
return s;
}
return sec;
}
/** remove nonsecure from a delegation referral additional section */
static void
deleg_remove_nonsecure_additional(struct reply_info* rep)
{
/* we can simply edit it, since we are working in the scratch region */
size_t i;
enum sec_status s;
for(i = rep->an_numrrsets+rep->ns_numrrsets; i<rep->rrset_count; 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*)*
(rep->rrset_count - i - 1));
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,
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)
*
* 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 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,
qinfo->qname_len, qinfo->qtype, qinfo->qclass,
worker->scratchpad, &msg, timenow, 0, NULL, 0);
if(!dp) { /* no delegation, need to reprime */
return 0;
}
/* In case we have a local alias, copy it into the delegation message.
* Shallow copy should be fine, as we'll be done with msg in this
* function. */
msg->qinfo.local_alias = qinfo->local_alias;
if(must_validate) {
switch(check_delegation_secure(msg->rep)) {
case sec_status_unchecked:
/* some rrsets have not been verified yet, go and
* let validator do that */
return 0;
case sec_status_bogus:
case sec_status_secure_sentinel_fail:
/* some rrsets are bogus, reply servfail */
edns->edns_version = EDNS_ADVERTISED_VERSION;
edns->udp_size = EDNS_ADVERTISED_SIZE;
edns->ext_rcode = 0;
edns->bits &= EDNS_DO;
if(!inplace_cb_reply_servfail_call(&worker->env, qinfo, NULL,
msg->rep, LDNS_RCODE_SERVFAIL, edns, repinfo, worker->scratchpad,
worker->env.now_tv))
return 0;
/* 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,
&msg->qinfo, id, flags, edns);
if(worker->stats.extended) {
worker->stats.ans_bogus++;
worker->stats.ans_rcode[LDNS_RCODE_SERVFAIL]++;
}
return 1;
case sec_status_secure:
/* all rrsets are secure */
/* remove non-secure rrsets from the add. section*/
if(worker->env.cfg->val_clean_additional)
deleg_remove_nonsecure_additional(msg->rep);
secure = 1;
break;
case sec_status_indeterminate:
case sec_status_insecure:
default:
/* not secure */
secure = 0;
break;
}
}
/* return this delegation from the cache */
edns->edns_version = EDNS_ADVERTISED_VERSION;
edns->udp_size = EDNS_ADVERTISED_SIZE;
edns->ext_rcode = 0;
edns->bits &= EDNS_DO;
+ if(worker->env.cfg->disable_edns_do && (edns->bits & EDNS_DO))
+ edns->edns_present = 0;
if(!inplace_cb_reply_cache_call(&worker->env, qinfo, NULL, msg->rep,
(int)(flags&LDNS_RCODE_MASK), edns, repinfo, worker->scratchpad,
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)) {
if(!inplace_cb_reply_servfail_call(&worker->env, qinfo, NULL, NULL,
LDNS_RCODE_SERVFAIL, edns, repinfo, worker->scratchpad,
worker->env.now_tv))
edns->opt_list_inplace_cb_out = NULL;
error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
&msg->qinfo, id, flags, edns);
}
if(worker->stats.extended) {
if(secure) worker->stats.ans_secure++;
server_stats_insrcode(&worker->stats, repinfo->c->buffer);
}
return 1;
}
/** Apply, if applicable, a response IP action to a cached answer.
* If the answer is rewritten as a result of an action, '*encode_repp' will
* point to the reply info containing the modified answer. '*encode_repp' will
* be intact otherwise.
* It returns 1 on success, 0 otherwise. */
static int
apply_respip_action(struct worker* worker, const struct query_info* qinfo,
struct respip_client_info* cinfo, struct reply_info* rep,
struct sockaddr_storage* addr, socklen_t addrlen,
struct ub_packed_rrset_key** alias_rrset,
struct reply_info** encode_repp, struct auth_zones* az)
{
struct respip_action_info actinfo = {0, 0, 0, 0, NULL, 0, NULL};
actinfo.action = respip_none;
if(qinfo->qtype != LDNS_RR_TYPE_A &&
qinfo->qtype != LDNS_RR_TYPE_AAAA &&
qinfo->qtype != LDNS_RR_TYPE_ANY)
return 1;
if(!respip_rewrite_reply(qinfo, cinfo, rep, encode_repp, &actinfo,
alias_rrset, 0, worker->scratchpad, az, NULL))
return 0;
/* xxx_deny actions mean dropping the reply, unless the original reply
* was redirected to response-ip data. */
if(actinfo.action == respip_always_deny ||
((actinfo.action == respip_deny ||
actinfo.action == respip_inform_deny) &&
*encode_repp == rep))
*encode_repp = NULL;
/* If address info is returned, it means the action should be an
* 'inform' variant and the information should be logged. */
if(actinfo.addrinfo) {
respip_inform_print(&actinfo, qinfo->qname,
qinfo->qtype, qinfo->qclass, qinfo->local_alias,
addr, addrlen);
if(worker->stats.extended && actinfo.rpz_used) {
if(actinfo.rpz_disabled)
worker->stats.rpz_action[RPZ_DISABLED_ACTION]++;
if(actinfo.rpz_cname_override)
worker->stats.rpz_action[RPZ_CNAME_OVERRIDE_ACTION]++;
else
worker->stats.rpz_action[
respip_action_to_rpz_action(actinfo.action)]++;
}
}
return 1;
}
/** answer query from the cache.
* Normally, the answer message will be built in repinfo->c->buffer; if the
* answer is supposed to be suppressed or the answer is supposed to be an
* incomplete CNAME chain, the buffer is explicitly cleared to signal the
* caller as such. In the latter case *partial_rep will point to the incomplete
* reply, and this function is (possibly) supposed to be called again with that
* *partial_rep value to complete the chain. In addition, if the query should
* be completely dropped, '*need_drop' will be set to 1. */
static int
answer_from_cache(struct worker* worker, struct query_info* qinfo,
struct respip_client_info* cinfo, int* need_drop, int* is_expired_answer,
int* is_secure_answer, struct ub_packed_rrset_key** alias_rrset,
struct reply_info** partial_repp,
struct reply_info* rep, uint16_t id, uint16_t flags,
struct comm_reply* repinfo, struct edns_data* edns)
{
time_t timenow = *worker->env.now;
uint16_t udpsize = edns->udp_size;
struct reply_info* encode_rep = rep;
struct reply_info* partial_rep = *partial_repp;
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 */
/* Check TTL */
if(rep->ttl < timenow) {
/* Check if we need to serve expired now */
if(worker->env.cfg->serve_expired &&
!worker->env.cfg->serve_expired_client_timeout) {
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;
} else {
/* the rrsets may have been updated in the meantime.
* we will refetch the message format from the
* authoritative server
*/
return 0;
}
} else {
if(!rrset_array_lock(rep->ref, rep->rrset_count, timenow))
return 0;
}
/* locked and ids and ttls are OK. */
/* check CNAME chain (if any) */
if(rep->an_numrrsets > 0 && (rep->rrsets[0]->rk.type ==
htons(LDNS_RR_TYPE_CNAME) || rep->rrsets[0]->rk.type ==
htons(LDNS_RR_TYPE_DNAME))) {
if(!reply_check_cname_chain(qinfo, rep)) {
/* cname chain invalid, redo iterator steps */
verbose(VERB_ALGO, "Cache reply: cname chain broken");
goto bail_out;
}
}
/* check security status of the cached answer */
if(must_validate && (rep->security == sec_status_bogus ||
rep->security == sec_status_secure_sentinel_fail)) {
/* BAD cached */
edns->edns_version = EDNS_ADVERTISED_VERSION;
edns->udp_size = EDNS_ADVERTISED_SIZE;
edns->ext_rcode = 0;
edns->bits &= EDNS_DO;
+ if(worker->env.cfg->disable_edns_do && (edns->bits & EDNS_DO))
+ edns->edns_present = 0;
if(!inplace_cb_reply_servfail_call(&worker->env, qinfo, NULL, rep,
LDNS_RCODE_SERVFAIL, edns, repinfo, worker->scratchpad,
worker->env.now_tv))
goto bail_out;
/* 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);
rrset_array_unlock_touch(worker->env.rrset_cache,
worker->scratchpad, rep->ref, rep->rrset_count);
if(worker->stats.extended) {
worker->stats.ans_bogus ++;
worker->stats.ans_rcode[LDNS_RCODE_SERVFAIL] ++;
}
return 1;
} else if(rep->security == sec_status_unchecked && must_validate) {
verbose(VERB_ALGO, "Cache reply: unchecked entry needs "
"validation");
goto bail_out; /* need to validate cache entry first */
} else if(rep->security == sec_status_secure) {
if(reply_all_rrsets_secure(rep)) {
*is_secure_answer = 1;
} else {
if(must_validate) {
verbose(VERB_ALGO, "Cache reply: secure entry"
" changed status");
goto bail_out; /* rrset changed, re-verify */
}
*is_secure_answer = 0;
}
} else *is_secure_answer = 0;
edns->edns_version = EDNS_ADVERTISED_VERSION;
edns->udp_size = EDNS_ADVERTISED_SIZE;
edns->ext_rcode = 0;
edns->bits &= EDNS_DO;
+ if(worker->env.cfg->disable_edns_do && (edns->bits & EDNS_DO))
+ edns->edns_present = 0;
*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,
&repinfo->client_addr, repinfo->client_addrlen, alias_rrset,
&encode_rep, worker->env.auth_zones)) {
goto bail_out;
} else if(partial_rep &&
!respip_merge_cname(partial_rep, qinfo, rep, cinfo,
must_validate, &encode_rep, worker->scratchpad,
worker->env.auth_zones)) {
goto bail_out;
}
if(encode_rep != rep) {
/* if rewritten, it can't be considered "secure" */
*is_secure_answer = 0;
}
if(!encode_rep || *alias_rrset) {
if(!encode_rep)
*need_drop = 1;
else {
/* If a partial CNAME chain is found, we first need to
* make a copy of the reply in the scratchpad so we
* can release the locks and lookup the cache again. */
*partial_repp = reply_info_copy(encode_rep, NULL,
worker->scratchpad);
if(!*partial_repp)
goto bail_out;
}
} else {
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),
*is_secure_answer)) {
if(!inplace_cb_reply_servfail_call(&worker->env, qinfo,
NULL, NULL, LDNS_RCODE_SERVFAIL, edns, repinfo,
worker->scratchpad, worker->env.now_tv))
edns->opt_list_inplace_cb_out = NULL;
error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL,
qinfo, id, flags, edns);
}
}
/* cannot send the reply right now, because blocking network syscall
* is bad while holding locks. */
rrset_array_unlock_touch(worker->env.rrset_cache, worker->scratchpad,
rep->ref, rep->rrset_count);
/* go and return this buffer to the client */
return 1;
bail_out:
rrset_array_unlock_touch(worker->env.rrset_cache,
worker->scratchpad, rep->ref, rep->rrset_count);
return 0;
}
/** Reply to client and perform prefetch to keep cache up to date. */
static void
reply_and_prefetch(struct worker* worker, struct query_info* qinfo,
uint16_t flags, struct comm_reply* repinfo, time_t leeway, int noreply,
int rpz_passthru, struct edns_option* opt_list)
{
(void)opt_list;
/* first send answer to client to keep its latency
* as small as a cachereply */
if(!noreply) {
if(repinfo->c->tcp_req_info) {
sldns_buffer_copy(
repinfo->c->tcp_req_info->spool_buffer,
repinfo->c->buffer);
}
comm_point_send_reply(repinfo);
}
server_stats_prefetch(&worker->stats, worker);
#ifdef CLIENT_SUBNET
/* Check if the subnet module is enabled. In that case pass over the
* comm_reply information for ECS generation later. The mesh states are
* unique when subnet is enabled. */
if(modstack_find(&worker->env.mesh->mods, "subnetcache") != -1
&& worker->env.unique_mesh) {
mesh_new_prefetch(worker->env.mesh, qinfo, flags, leeway +
PREFETCH_EXPIRY_ADD, rpz_passthru,
&repinfo->client_addr, opt_list);
return;
}
#endif
/* create the prefetch in the mesh as a normal lookup without
* client addrs waiting, which has the cache blacklisted (to bypass
* the cache and go to the network for the data). */
/* this (potentially) runs the mesh for the new query */
mesh_new_prefetch(worker->env.mesh, qinfo, flags, leeway +
PREFETCH_EXPIRY_ADD, rpz_passthru, NULL, NULL);
}
/**
* Fill CH class answer into buffer. Keeps query.
* @param pkt: buffer
* @param str: string to put into text record (<255).
* array of strings, every string becomes a text record.
* @param num: number of strings in array.
* @param edns: edns reply information.
* @param worker: worker with scratch region.
* @param repinfo: reply information for a communication point.
*/
static void
chaos_replystr(sldns_buffer* pkt, char** str, int num, struct edns_data* edns,
struct worker* worker, struct comm_reply* repinfo)
{
int i;
unsigned int rd = LDNS_RD_WIRE(sldns_buffer_begin(pkt));
unsigned int cd = LDNS_CD_WIRE(sldns_buffer_begin(pkt));
size_t udpsize = edns->udp_size;
edns->edns_version = EDNS_ADVERTISED_VERSION;
edns->udp_size = EDNS_ADVERTISED_SIZE;
edns->bits &= EDNS_DO;
if(!inplace_cb_reply_local_call(&worker->env, NULL, NULL, NULL,
LDNS_RCODE_NOERROR, edns, repinfo, worker->scratchpad,
worker->env.now_tv))
edns->opt_list_inplace_cb_out = NULL;
sldns_buffer_clear(pkt);
sldns_buffer_skip(pkt, (ssize_t)sizeof(uint16_t)); /* skip id */
sldns_buffer_write_u16(pkt, (uint16_t)(BIT_QR|BIT_RA));
if(rd) LDNS_RD_SET(sldns_buffer_begin(pkt));
if(cd) LDNS_CD_SET(sldns_buffer_begin(pkt));
sldns_buffer_write_u16(pkt, 1); /* qdcount */
sldns_buffer_write_u16(pkt, (uint16_t)num); /* ancount */
sldns_buffer_write_u16(pkt, 0); /* nscount */
sldns_buffer_write_u16(pkt, 0); /* arcount */
(void)query_dname_len(pkt); /* skip qname */
sldns_buffer_skip(pkt, (ssize_t)sizeof(uint16_t)); /* skip qtype */
sldns_buffer_skip(pkt, (ssize_t)sizeof(uint16_t)); /* skip qclass */
for(i=0; i<num; i++) {
size_t len = strlen(str[i]);
if(len>255) len=255; /* cap size of TXT record */
if(sldns_buffer_position(pkt)+2+2+2+4+2+1+len+
calc_edns_field_size(edns) > udpsize) {
sldns_buffer_write_u16_at(pkt, 6, i); /* ANCOUNT */
LDNS_TC_SET(sldns_buffer_begin(pkt));
break;
}
sldns_buffer_write_u16(pkt, 0xc00c); /* compr ptr to query */
sldns_buffer_write_u16(pkt, LDNS_RR_TYPE_TXT);
sldns_buffer_write_u16(pkt, LDNS_RR_CLASS_CH);
sldns_buffer_write_u32(pkt, 0); /* TTL */
sldns_buffer_write_u16(pkt, sizeof(uint8_t) + len);
sldns_buffer_write_u8(pkt, len);
sldns_buffer_write(pkt, str[i], len);
}
sldns_buffer_flip(pkt);
if(sldns_buffer_capacity(pkt) >=
sldns_buffer_limit(pkt)+calc_edns_field_size(edns))
attach_edns_record(pkt, edns);
}
/** Reply with one string */
static void
chaos_replyonestr(sldns_buffer* pkt, const char* str, struct edns_data* edns,
struct worker* worker, struct comm_reply* repinfo)
{
chaos_replystr(pkt, (char**)&str, 1, edns, worker, repinfo);
}
/**
* Create CH class trustanchor answer.
* @param pkt: buffer
* @param edns: edns reply information.
* @param w: worker with scratch region.
* @param repinfo: reply information for a communication point.
*/
static void
chaos_trustanchor(sldns_buffer* pkt, struct edns_data* edns, struct worker* w,
struct comm_reply* repinfo)
{
#define TA_RESPONSE_MAX_TXT 16 /* max number of TXT records */
#define TA_RESPONSE_MAX_TAGS 32 /* max number of tags printed per zone */
char* str_array[TA_RESPONSE_MAX_TXT];
uint16_t tags[TA_RESPONSE_MAX_TAGS];
int num = 0;
struct trust_anchor* ta;
if(!w->env.need_to_validate) {
/* no validator module, reply no trustanchors */
chaos_replystr(pkt, NULL, 0, edns, w, repinfo);
return;
}
/* fill the string with contents */
lock_basic_lock(&w->env.anchors->lock);
RBTREE_FOR(ta, struct trust_anchor*, w->env.anchors->tree) {
char* str;
size_t i, numtag, str_len = 255;
if(num == TA_RESPONSE_MAX_TXT) continue;
str = (char*)regional_alloc(w->scratchpad, str_len);
if(!str) continue;
lock_basic_lock(&ta->lock);
numtag = anchor_list_keytags(ta, tags, TA_RESPONSE_MAX_TAGS);
if(numtag == 0) {
/* empty, insecure point */
lock_basic_unlock(&ta->lock);
continue;
}
str_array[num] = str;
num++;
/* spool name of anchor */
(void)sldns_wire2str_dname_buf(ta->name, ta->namelen, str, str_len);
str_len -= strlen(str); str += strlen(str);
/* spool tags */
for(i=0; i<numtag; i++) {
snprintf(str, str_len, " %u", (unsigned)tags[i]);
str_len -= strlen(str); str += strlen(str);
}
lock_basic_unlock(&ta->lock);
}
lock_basic_unlock(&w->env.anchors->lock);
chaos_replystr(pkt, str_array, num, edns, w, repinfo);
regional_free_all(w->scratchpad);
}
/**
* Answer CH class queries.
* @param w: worker
* @param qinfo: query info. Pointer into packet buffer.
* @param edns: edns info from query.
* @param repinfo: reply information for a communication point.
* @param pkt: packet buffer.
* @return: true if a reply is to be sent.
*/
static int
answer_chaos(struct worker* w, struct query_info* qinfo,
struct edns_data* edns, struct comm_reply* repinfo, sldns_buffer* pkt)
{
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,
(uint8_t*)"\002id\006server") == 0 ||
query_dname_compare(qinfo->qname,
(uint8_t*)"\010hostname\004bind") == 0)
{
if(cfg->hide_identity)
return 0;
if(cfg->identity==NULL || cfg->identity[0]==0) {
char buf[MAXHOSTNAMELEN+1];
if (gethostname(buf, MAXHOSTNAMELEN) == 0) {
buf[MAXHOSTNAMELEN] = 0;
chaos_replyonestr(pkt, buf, edns, w, repinfo);
} else {
log_err("gethostname: %s", strerror(errno));
chaos_replyonestr(pkt, "no hostname", edns, w, repinfo);
}
}
else chaos_replyonestr(pkt, cfg->identity, edns, w, repinfo);
return 1;
}
if(query_dname_compare(qinfo->qname,
(uint8_t*)"\007version\006server") == 0 ||
query_dname_compare(qinfo->qname,
(uint8_t*)"\007version\004bind") == 0)
{
if(cfg->hide_version)
return 0;
if(cfg->version==NULL || cfg->version[0]==0)
chaos_replyonestr(pkt, PACKAGE_STRING, edns, w, repinfo);
else chaos_replyonestr(pkt, cfg->version, edns, w, repinfo);
return 1;
}
if(query_dname_compare(qinfo->qname,
(uint8_t*)"\013trustanchor\007unbound") == 0)
{
if(cfg->hide_trustanchor)
return 0;
chaos_trustanchor(pkt, edns, w, repinfo);
return 1;
}
return 0;
}
/**
* Answer notify queries. These are notifies for authoritative zones,
* the reply is an ack that the notify has been received. We need to check
* access permission here.
* @param w: worker
* @param qinfo: query info. Pointer into packet buffer.
* @param edns: edns info from query.
* @param addr: client address.
* @param addrlen: client address length.
* @param pkt: packet buffer.
*/
static void
answer_notify(struct worker* w, struct query_info* qinfo,
struct edns_data* edns, sldns_buffer* pkt,
struct sockaddr_storage* addr, socklen_t addrlen)
{
int refused = 0;
int rcode = LDNS_RCODE_NOERROR;
uint32_t serial = 0;
int has_serial;
if(!w->env.auth_zones) return;
has_serial = auth_zone_parse_notify_serial(pkt, &serial);
if(auth_zones_notify(w->env.auth_zones, &w->env, qinfo->qname,
qinfo->qname_len, qinfo->qclass, addr,
addrlen, has_serial, serial, &refused)) {
rcode = LDNS_RCODE_NOERROR;
} else {
if(refused)
rcode = LDNS_RCODE_REFUSED;
else rcode = LDNS_RCODE_SERVFAIL;
}
if(verbosity >= VERB_DETAIL) {
char buf[380];
char zname[255+1];
char sr[25];
dname_str(qinfo->qname, zname);
sr[0]=0;
if(has_serial)
snprintf(sr, sizeof(sr), "serial %u ",
(unsigned)serial);
if(rcode == LDNS_RCODE_REFUSED)
snprintf(buf, sizeof(buf),
"refused NOTIFY %sfor %s from", sr, zname);
else if(rcode == LDNS_RCODE_SERVFAIL)
snprintf(buf, sizeof(buf),
"servfail for NOTIFY %sfor %s from", sr, zname);
else snprintf(buf, sizeof(buf),
"received NOTIFY %sfor %s from", sr, zname);
log_addr(VERB_DETAIL, buf, addr, addrlen);
}
edns->edns_version = EDNS_ADVERTISED_VERSION;
edns->udp_size = EDNS_ADVERTISED_SIZE;
edns->ext_rcode = 0;
edns->bits &= EDNS_DO;
error_encode(pkt, rcode, qinfo,
*(uint16_t*)(void *)sldns_buffer_begin(pkt),
sldns_buffer_read_u16_at(pkt, 2), edns);
LDNS_OPCODE_SET(sldns_buffer_begin(pkt), LDNS_PACKET_NOTIFY);
}
static int
deny_refuse(struct comm_point* c, enum acl_access acl,
enum acl_access deny, enum acl_access refuse,
struct worker* worker, struct comm_reply* repinfo,
struct acl_addr* acladdr, int ede,
struct check_request_result* check_result)
{
if(acl == deny) {
if(verbosity >= VERB_ALGO) {
log_acl_action("dropped", &repinfo->client_addr,
repinfo->client_addrlen, acl, acladdr);
log_buf(VERB_ALGO, "dropped", c->buffer);
}
comm_point_drop_reply(repinfo);
if(worker->stats.extended)
worker->stats.unwanted_queries++;
return 0;
} else if(acl == refuse) {
size_t opt_rr_mark;
if(verbosity >= VERB_ALGO) {
log_acl_action("refused", &repinfo->client_addr,
repinfo->client_addrlen, acl, acladdr);
log_buf(VERB_ALGO, "refuse", c->buffer);
}
if(worker->stats.extended)
worker->stats.unwanted_queries++;
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;
}
/* worker_check_request() above guarantees that the buffer contains at
* least a header and that qdcount == 1
*/
log_assert(sldns_buffer_limit(c->buffer) >= LDNS_HEADER_SIZE
&& LDNS_QDCOUNT(sldns_buffer_begin(c->buffer)) == 1);
sldns_buffer_skip(c->buffer, LDNS_HEADER_SIZE); /* skip header */
/* check additional section is present and that we respond with EDEs */
if(LDNS_ARCOUNT(sldns_buffer_begin(c->buffer)) != 1
|| !ede) {
LDNS_QDCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
LDNS_ANCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
LDNS_NSCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
LDNS_QR_SET(sldns_buffer_begin(c->buffer));
LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
LDNS_RCODE_REFUSED);
sldns_buffer_flip(c->buffer);
return 1;
}
if (!query_dname_len(c->buffer)) {
LDNS_QDCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
LDNS_ANCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
LDNS_NSCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
LDNS_QR_SET(sldns_buffer_begin(c->buffer));
LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
LDNS_RCODE_FORMERR);
sldns_buffer_set_position(c->buffer, LDNS_HEADER_SIZE);
sldns_buffer_flip(c->buffer);
return 1;
}
/* space available for query type and class? */
if (sldns_buffer_remaining(c->buffer) < 2 * sizeof(uint16_t)) {
LDNS_QR_SET(sldns_buffer_begin(c->buffer));
LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
LDNS_RCODE_FORMERR);
LDNS_QDCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
LDNS_ANCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
LDNS_NSCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
sldns_buffer_set_position(c->buffer, LDNS_HEADER_SIZE);
sldns_buffer_flip(c->buffer);
return 1;
}
LDNS_QR_SET(sldns_buffer_begin(c->buffer));
LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
LDNS_RCODE_REFUSED);
sldns_buffer_skip(c->buffer, (ssize_t)sizeof(uint16_t)); /* skip qtype */
sldns_buffer_skip(c->buffer, (ssize_t)sizeof(uint16_t)); /* skip qclass */
/* The OPT RR to be returned should come directly after
* the query, so mark this spot.
*/
opt_rr_mark = sldns_buffer_position(c->buffer);
/* Skip through the RR records */
if(LDNS_ANCOUNT(sldns_buffer_begin(c->buffer)) != 0 ||
LDNS_NSCOUNT(sldns_buffer_begin(c->buffer)) != 0) {
if(!skip_pkt_rrs(c->buffer,
((int)LDNS_ANCOUNT(sldns_buffer_begin(c->buffer)))+
((int)LDNS_NSCOUNT(sldns_buffer_begin(c->buffer))))) {
LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
LDNS_RCODE_FORMERR);
LDNS_ANCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
LDNS_NSCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
sldns_buffer_set_position(c->buffer, opt_rr_mark);
sldns_buffer_flip(c->buffer);
return 1;
}
}
/* Do we have a valid OPT RR here? If not return REFUSED (could be a valid TSIG or something so no FORMERR) */
/* domain name must be the root of length 1. */
if(sldns_buffer_remaining(c->buffer) < 1 || *sldns_buffer_current(c->buffer) != 0) {
LDNS_ANCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
LDNS_NSCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
sldns_buffer_set_position(c->buffer, opt_rr_mark);
sldns_buffer_flip(c->buffer);
return 1;
} else {
sldns_buffer_skip(c->buffer, 1); /* skip root label */
}
if(sldns_buffer_remaining(c->buffer) < 2 ||
sldns_buffer_read_u16(c->buffer) != LDNS_RR_TYPE_OPT) {
LDNS_ANCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
LDNS_NSCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
sldns_buffer_set_position(c->buffer, opt_rr_mark);
sldns_buffer_flip(c->buffer);
return 1;
}
/* Write OPT RR directly after the query,
* so without the (possibly skipped) Answer and NS RRs
*/
LDNS_ANCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
LDNS_NSCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
sldns_buffer_clear(c->buffer); /* reset write limit */
sldns_buffer_set_position(c->buffer, opt_rr_mark);
/* Check if OPT record can be written
* 17 == root label (1) + RR type (2) + UDP Size (2)
* + Fields (4) + rdata len (2) + EDE Option code (2)
* + EDE Option length (2) + EDE info-code (2)
*/
if (sldns_buffer_available(c->buffer, 17) == 0) {
LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 0);
sldns_buffer_flip(c->buffer);
return 1;
}
LDNS_ARCOUNT_SET(sldns_buffer_begin(c->buffer), 1);
/* root label */
sldns_buffer_write_u8(c->buffer, 0);
sldns_buffer_write_u16(c->buffer, LDNS_RR_TYPE_OPT);
sldns_buffer_write_u16(c->buffer, EDNS_ADVERTISED_SIZE);
/* write OPT Record TTL Field */
sldns_buffer_write_u32(c->buffer, 0);
/* write rdata len: EDE option + length + info-code */
sldns_buffer_write_u16(c->buffer, 6);
/* write OPTIONS; add EDE option code */
sldns_buffer_write_u16(c->buffer, LDNS_EDNS_EDE);
/* write single EDE option length (for just 1 info-code) */
sldns_buffer_write_u16(c->buffer, 2);
/* write single EDE info-code */
sldns_buffer_write_u16(c->buffer, LDNS_EDE_PROHIBITED);
sldns_buffer_flip(c->buffer);
verbose(VERB_ALGO, "attached EDE code: %d", LDNS_EDE_PROHIBITED);
return 1;
}
return -1;
}
static int
deny_refuse_all(struct comm_point* c, enum acl_access* acl,
struct worker* worker, struct comm_reply* repinfo,
struct acl_addr** acladdr, int ede, int check_proxy,
struct check_request_result* check_result)
{
if(check_proxy) {
*acladdr = acl_addr_lookup(worker->daemon->acl,
&repinfo->remote_addr, repinfo->remote_addrlen);
} else {
*acladdr = acl_addr_lookup(worker->daemon->acl,
&repinfo->client_addr, repinfo->client_addrlen);
}
/* If there is no ACL based on client IP use the interface ACL. */
if(!(*acladdr) && c->socket) {
*acladdr = c->socket->acl;
}
*acl = acl_get_control(*acladdr);
return deny_refuse(c, *acl, acl_deny, acl_refuse, worker, repinfo,
*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 check_request_result* check_result)
{
return deny_refuse(c, acl, acl_deny_non_local, acl_refuse_non_local,
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
worker_handle_request(struct comm_point* c, void* arg, int error,
struct comm_reply* repinfo)
{
struct worker* worker = (struct worker*)arg;
int ret;
hashvalue_type h;
struct lruhash_entry* e;
struct query_info qinfo;
struct edns_data edns;
struct edns_option* original_edns_list = NULL;
enum acl_access acl;
struct acl_addr* acladdr;
int 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 ub_packed_rrset_key* alias_rrset = NULL;
struct reply_info* partial_rep = NULL;
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) {
/* some bad tcp query DNS formats give these error calls */
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)) {
worker->stats.num_query_dnscrypt_crypted_malformed++;
return 0;
}
if(c->dnscrypt && !repinfo->is_dnscrypted) {
char buf[LDNS_MAX_DOMAINLEN+1];
/* Check if this is unencrypted and asking for certs */
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,
repinfo->client_addrlen);
comm_point_drop_reply(repinfo);
return 0;
}
if(!query_info_parse(&qinfo, c->buffer)) {
verbose(VERB_ALGO,
"dnscrypt: worker parse request: formerror.");
log_addr(VERB_CLIENT, "from", &repinfo->client_addr,
repinfo->client_addrlen);
comm_point_drop_reply(repinfo);
return 0;
}
dname_str(qinfo.qname, buf);
if(!(qinfo.qtype == LDNS_RR_TYPE_TXT &&
strcasecmp(buf,
worker->daemon->dnscenv->provider_name) == 0)) {
verbose(VERB_ALGO,
"dnscrypt: not TXT \"%s\". Received: %s \"%s\"",
worker->daemon->dnscenv->provider_name,
sldns_rr_descript(qinfo.qtype)->_name,
buf);
comm_point_drop_reply(repinfo);
worker->stats.num_query_dnscrypt_cleartext++;
return 0;
}
worker->stats.num_query_dnscrypt_cert++;
sldns_buffer_rewind(c->buffer);
} else if(c->dnscrypt && repinfo->is_dnscrypted) {
worker->stats.num_query_dnscrypt_crypted++;
}
#endif
#ifdef USE_DNSTAP
/*
* sending src (client)/dst (local service) addresses over DNSTAP from incoming request handler
*/
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,
((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, &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, &check_result)) != -1) {
if(ret == 1)
goto send_reply;
return ret;
}
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(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;
}
worker->stats.num_queries++;
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;
}
}
if(!query_info_parse(&qinfo, c->buffer)) {
verbose(VERB_ALGO, "worker parse request: formerror.");
log_addr(VERB_CLIENT, "from", &repinfo->client_addr,
repinfo->client_addrlen);
memset(&qinfo, 0, sizeof(qinfo)); /* zero qinfo.qname */
if(worker_err_ratelimit(worker, LDNS_RCODE_FORMERR) == -1) {
comm_point_drop_reply(repinfo);
return 0;
}
sldns_buffer_rewind(c->buffer);
LDNS_QR_SET(sldns_buffer_begin(c->buffer));
LDNS_RCODE_SET(sldns_buffer_begin(c->buffer),
LDNS_RCODE_FORMERR);
goto send_reply;
}
if(worker->env.cfg->log_queries) {
char ip[128];
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 ||
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_REFUSED);
if(worker->stats.extended) {
worker->stats.qtype[qinfo.qtype]++;
}
goto send_reply;
}
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 ||
qinfo.qtype == LDNS_RR_TYPE_MAILB ||
(qinfo.qtype >= 128 && qinfo.qtype <= 248)) {
verbose(VERB_ALGO, "worker request: formerror for meta-type.");
log_addr(VERB_CLIENT, "from", &repinfo->client_addr,
repinfo->client_addrlen);
if(worker_err_ratelimit(worker, LDNS_RCODE_FORMERR) == -1) {
comm_point_drop_reply(repinfo);
return 0;
}
sldns_buffer_rewind(c->buffer);
LDNS_QR_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, 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;
error_encode(c->buffer, ret, &qinfo,
*(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
sldns_buffer_read_u16_at(c->buffer, 2), &reply_edns);
regional_free_all(worker->scratchpad);
goto send_reply;
}
if(edns.edns_present) {
if(edns.edns_version != 0) {
edns.opt_list_in = NULL;
edns.opt_list_out = NULL;
edns.opt_list_inplace_cb_out = NULL;
verbose(VERB_ALGO, "query with bad edns version.");
log_addr(VERB_CLIENT, "from", &repinfo->client_addr,
repinfo->client_addrlen);
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), 0, &edns);
regional_free_all(worker->scratchpad);
goto send_reply;
}
if(edns.udp_size < NORMAL_UDP_SIZE &&
worker->daemon->cfg->harden_short_bufsize) {
verbose(VERB_QUERY, "worker request: EDNS bufsize %d ignored",
(int)edns.udp_size);
log_addr(VERB_CLIENT, "from", &repinfo->client_addr,
repinfo->client_addrlen);
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,
"worker request: max UDP reply size modified"
" (%d to max-udp-size)", (int)edns.udp_size);
log_addr(VERB_CLIENT, "from", &repinfo->client_addr,
repinfo->client_addrlen);
edns.udp_size = worker->daemon->cfg->max_udp_size;
}
if(edns.udp_size < LDNS_HEADER_SIZE) {
verbose(VERB_ALGO, "worker request: edns is too small.");
log_addr(VERB_CLIENT, "from", &repinfo->client_addr,
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_SERVFAIL);
sldns_buffer_set_position(c->buffer, LDNS_HEADER_SIZE);
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);
goto send_reply;
}
if(worker->stats.extended)
server_stats_insquery(&worker->stats, c, qinfo.qtype,
qinfo.qclass, &edns, repinfo);
if(c->type != comm_udp)
edns.udp_size = 65535; /* max size for TCP replies */
if(qinfo.qclass == LDNS_RR_CLASS_CH && answer_chaos(worker, &qinfo,
&edns, repinfo, c->buffer)) {
regional_free_all(worker->scratchpad);
goto send_reply;
}
if(LDNS_OPCODE_WIRE(sldns_buffer_begin(c->buffer)) ==
LDNS_PACKET_NOTIFY) {
answer_notify(worker, &qinfo, &edns, c->buffer,
&repinfo->client_addr, repinfo->client_addrlen);
regional_free_all(worker->scratchpad);
goto send_reply;
}
if(local_zones_answer(worker->daemon->local_zones, &worker->env, &qinfo,
&edns, c->buffer, worker->scratchpad, repinfo, acladdr->taglist,
acladdr->taglen, acladdr->tag_actions,
acladdr->tag_actions_size, acladdr->tag_datas,
acladdr->tag_datas_size, worker->daemon->cfg->tagname,
worker->daemon->cfg->num_tags, acladdr->view)) {
regional_free_all(worker->scratchpad);
if(sldns_buffer_limit(c->buffer) == 0) {
comm_point_drop_reply(repinfo);
return 0;
}
goto send_reply;
}
if(worker->env.auth_zones &&
rpz_callback_from_worker_request(worker->env.auth_zones,
&worker->env, &qinfo, &edns, c->buffer, worker->scratchpad,
repinfo, acladdr->taglist, acladdr->taglen, &worker->stats,
&rpz_passthru)) {
regional_free_all(worker->scratchpad);
if(sldns_buffer_limit(c->buffer) == 0) {
comm_point_drop_reply(repinfo);
return 0;
}
goto send_reply;
}
if(worker->env.auth_zones &&
auth_zones_answer(worker->env.auth_zones, &worker->env,
&qinfo, &edns, repinfo, c->buffer, worker->scratchpad)) {
regional_free_all(worker->scratchpad);
if(sldns_buffer_limit(c->buffer) == 0) {
comm_point_drop_reply(repinfo);
return 0;
}
/* set RA for everyone that can have recursion (based on
* access control list) */
if(LDNS_RD_WIRE(sldns_buffer_begin(c->buffer)) &&
acl != acl_deny_non_local && acl != acl_refuse_non_local)
LDNS_RA_SET(sldns_buffer_begin(c->buffer));
goto send_reply;
}
/* 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, &check_result)) != -1)
{
regional_free_all(worker->scratchpad);
if(ret == 1)
goto send_reply;
return ret;
}
/* If this request does not have the recursion bit set, verify
* ACLs allow the recursion bit to be treated as set. */
if(!(LDNS_RD_WIRE(sldns_buffer_begin(c->buffer))) &&
acl == acl_allow_setrd ) {
LDNS_RD_SET(sldns_buffer_begin(c->buffer));
}
/* If this request does not have the recursion bit set, verify
* ACLs allow the snooping. */
if(!(LDNS_RD_WIRE(sldns_buffer_begin(c->buffer))) &&
acl != acl_allow_snoop ) {
if(worker->env.cfg->ede) {
EDNS_OPT_LIST_APPEND_EDE(&edns.opt_list_out,
worker->scratchpad, LDNS_EDE_NOT_AUTHORITATIVE, "");
}
error_encode(c->buffer, LDNS_RCODE_REFUSED, &qinfo,
*(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
sldns_buffer_read_u16_at(c->buffer, 2), &edns);
regional_free_all(worker->scratchpad);
log_addr(VERB_ALGO, "refused nonrec (cache snoop) query from",
&repinfo->client_addr, repinfo->client_addrlen);
goto send_reply;
}
/* If we've found a local alias, replace the qname with the alias
* target before resolving it. */
if(qinfo.local_alias) {
struct ub_packed_rrset_key* rrset = qinfo.local_alias->rrset;
struct packed_rrset_data* d = rrset->entry.data;
/* Sanity check: our current implementation only supports
* a single CNAME RRset as a local alias. */
if(qinfo.local_alias->next ||
rrset->rk.type != htons(LDNS_RR_TYPE_CNAME) ||
d->count != 1) {
log_err("assumption failure: unexpected local alias");
regional_free_all(worker->scratchpad);
return 0; /* drop it */
}
qinfo.qname = d->rr_data[0] + 2;
qinfo.qname_len = d->rr_len[0] - 2;
}
/* If we may apply IP-based actions to the answer, build the client
* information. As this can be expensive, skip it if there is
* absolutely no possibility of it. */
if((worker->daemon->use_response_ip || worker->daemon->use_rpz) &&
(qinfo.qtype == LDNS_RR_TYPE_A ||
qinfo.qtype == LDNS_RR_TYPE_AAAA ||
qinfo.qtype == LDNS_RR_TYPE_ANY)) {
cinfo_tmp.taglist = acladdr->taglist;
cinfo_tmp.taglen = acladdr->taglen;
cinfo_tmp.tag_actions = acladdr->tag_actions;
cinfo_tmp.tag_actions_size = acladdr->tag_actions_size;
cinfo_tmp.tag_datas = acladdr->tag_datas;
cinfo_tmp.tag_datas_size = acladdr->tag_datas_size;
cinfo_tmp.view = acladdr->view;
cinfo_tmp.respip_set = worker->daemon->respip_set;
cinfo = &cinfo_tmp;
}
/* Keep the original edns list around. The pointer could change if there is
* a cached answer (through the inplace callback function there).
* No need to actually copy the contents as they shouldn't change.
* Used while prefetching and subnet is enabled. */
original_edns_list = edns.opt_list_in;
lookup_cache:
/* Lookup the cache. In case we chase an intermediate CNAME chain
* this is a two-pass operation, and lookup_qinfo is different for
* each pass. We should still pass the original qinfo to
* answer_from_cache(), however, since it's used to build the reply. */
if(!edns_bypass_cache_stage(edns.opt_list_in, &worker->env)) {
is_expired_answer = 0;
is_secure_answer = 0;
h = query_info_hash(lookup_qinfo, sldns_buffer_read_u16_at(c->buffer, 2));
if((e=slabhash_lookup(worker->env.msg_cache, h, lookup_qinfo, 0))) {
struct reply_info* rep = (struct reply_info*)e->data;
/* answer from cache - we have acquired a readlock on it */
if(answer_from_cache(worker, &qinfo, cinfo, &need_drop,
&is_expired_answer, &is_secure_answer,
&alias_rrset, &partial_rep, rep,
*(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
sldns_buffer_read_u16_at(c->buffer, 2), repinfo,
&edns)) {
/* prefetch it if the prefetch TTL expired.
* Note that if there is more than one pass
* its qname must be that used for cache
* lookup. */
if((worker->env.cfg->prefetch &&
*worker->env.now >= rep->prefetch_ttl) ||
(worker->env.cfg->serve_expired &&
*worker->env.now > rep->ttl)) {
time_t leeway = rep->ttl - *worker->env.now;
if(rep->ttl < *worker->env.now)
leeway = 0;
lock_rw_unlock(&e->lock);
reply_and_prefetch(worker, lookup_qinfo,
sldns_buffer_read_u16_at(c->buffer, 2),
repinfo, leeway,
(partial_rep || need_drop),
rpz_passthru,
original_edns_list);
if(!partial_rep) {
rc = 0;
regional_free_all(worker->scratchpad);
goto send_reply_rc;
}
} else if(!partial_rep) {
lock_rw_unlock(&e->lock);
regional_free_all(worker->scratchpad);
goto send_reply;
} else {
/* Note that we've already released the
* lock if we're here after prefetch. */
lock_rw_unlock(&e->lock);
}
/* We've found a partial reply ending with an
* alias. Replace the lookup qinfo for the
* alias target and lookup the cache again to
* (possibly) complete the reply. As we're
* passing the "base" reply, there will be no
* more alias chasing. */
memset(&qinfo_tmp, 0, sizeof(qinfo_tmp));
get_cname_target(alias_rrset, &qinfo_tmp.qname,
&qinfo_tmp.qname_len);
if(!qinfo_tmp.qname) {
log_err("unexpected: invalid answer alias");
regional_free_all(worker->scratchpad);
return 0; /* drop query */
}
qinfo_tmp.qtype = qinfo.qtype;
qinfo_tmp.qclass = qinfo.qclass;
lookup_qinfo = &qinfo_tmp;
goto lookup_cache;
}
verbose(VERB_ALGO, "answer from the cache failed");
lock_rw_unlock(&e->lock);
}
if(!LDNS_RD_WIRE(sldns_buffer_begin(c->buffer))) {
if(answer_norec_from_cache(worker, &qinfo,
*(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
sldns_buffer_read_u16_at(c->buffer, 2), repinfo,
&edns)) {
regional_free_all(worker->scratchpad);
goto send_reply;
}
verbose(VERB_ALGO, "answer norec from cache -- "
"need to validate or not primed");
}
}
sldns_buffer_rewind(c->buffer);
server_stats_querymiss(&worker->stats, worker);
if(verbosity >= VERB_CLIENT) {
if(c->type == comm_udp)
log_addr(VERB_CLIENT, "udp request from",
&repinfo->client_addr, repinfo->client_addrlen);
else log_addr(VERB_CLIENT, "tcp request from",
&repinfo->client_addr, repinfo->client_addrlen);
}
/* grab a work request structure for this new request */
mesh_new_client(worker->env.mesh, &qinfo, cinfo,
sldns_buffer_read_u16_at(c->buffer, 2),
&edns, repinfo, *(uint16_t*)(void *)sldns_buffer_begin(c->buffer),
rpz_passthru);
regional_free_all(worker->scratchpad);
worker_mem_report(worker, NULL);
return 0;
send_reply:
rc = 1;
send_reply_rc:
if(need_drop) {
comm_point_drop_reply(repinfo);
return 0;
}
if(is_expired_answer) {
worker->stats.ans_expired++;
}
server_stats_insrcode(&worker->stats, c->buffer);
if(worker->stats.extended) {
if(is_secure_answer) worker->stats.ans_secure++;
}
#ifdef USE_DNSTAP
/*
* sending src (client)/dst (local service) addresses over DNSTAP from send_reply code label (when we serviced local zone for ex.)
*/
if(worker->dtenv.log_client_response_messages) {
log_addr(VERB_ALGO, "from local addr", (void*)repinfo->c->socket->addr->ai_addr, repinfo->c->socket->addr->ai_addrlen);
log_addr(VERB_ALGO, "response to client", &repinfo->client_addr, repinfo->client_addrlen);
dt_msg_send_client_response(&worker->dtenv, &repinfo->client_addr, (void*)repinfo->c->socket->addr->ai_addr, c->type, c->buffer);
}
#endif
if(worker->env.cfg->log_replies)
{
struct timeval tv;
memset(&tv, 0, sizeof(tv));
if(qinfo.local_alias && qinfo.local_alias->rrset &&
qinfo.local_alias->rrset->rk.dname) {
/* log original qname, before the local alias was
* used to resolve that CNAME to something else */
qinfo.qname = qinfo.local_alias->rrset->rk.dname;
log_reply_info(NO_VERBOSE, &qinfo,
&repinfo->client_addr, repinfo->client_addrlen,
tv, 1, c->buffer);
} else {
log_reply_info(NO_VERBOSE, &qinfo,
&repinfo->client_addr, repinfo->client_addrlen,
tv, 1, c->buffer);
}
}
#ifdef USE_DNSCRYPT
if(!dnsc_handle_uncurved_request(repinfo)) {
return 0;
}
#endif
return rc;
}
void
worker_sighandler(int sig, void* arg)
{
/* 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) {
#ifdef SIGHUP
case SIGHUP:
comm_base_exit(worker->base);
break;
#endif
#ifdef SIGBREAK
case SIGBREAK:
#endif
case SIGINT:
worker->need_to_exit = 1;
comm_base_exit(worker->base);
break;
#ifdef SIGQUIT
case SIGQUIT:
worker->need_to_exit = 1;
comm_base_exit(worker->base);
break;
#endif
case SIGTERM:
worker->need_to_exit = 1;
comm_base_exit(worker->base);
break;
default:
/* unknown signal, ignored */
break;
}
}
/** restart statistics timer for worker, if enabled */
static void
worker_restart_timer(struct worker* worker)
{
if(worker->env.cfg->stat_interval > 0) {
struct timeval tv;
#ifndef S_SPLINT_S
tv.tv_sec = worker->env.cfg->stat_interval;
tv.tv_usec = 0;
#endif
comm_timer_set(worker->stat_timer, &tv);
}
}
void worker_stat_timer_cb(void* arg)
{
struct worker* worker = (struct worker*)arg;
server_stats_log(&worker->stats, worker, worker->thread_num);
mesh_stats(worker->env.mesh, "mesh has");
worker_mem_report(worker, NULL);
/* SHM is enabled, process data to SHM */
if (worker->daemon->cfg->shm_enable) {
shm_main_run(worker);
}
if(!worker->daemon->cfg->stat_cumulative) {
worker_stats_clear(worker);
}
/* start next timer */
worker_restart_timer(worker);
}
void worker_probe_timer_cb(void* arg)
{
struct worker* worker = (struct worker*)arg;
struct timeval tv;
#ifndef S_SPLINT_S
tv.tv_sec = (time_t)autr_probe_timer(&worker->env);
tv.tv_usec = 0;
#endif
if(tv.tv_sec != 0)
comm_timer_set(worker->env.probe_timer, &tv);
}
struct worker*
worker_create(struct daemon* daemon, int id, int* ports, int n)
{
unsigned int seed;
struct worker* worker = (struct worker*)calloc(1,
sizeof(struct worker));
if(!worker)
return NULL;
worker->numports = n;
worker->ports = (int*)memdup(ports, sizeof(int)*n);
if(!worker->ports) {
free(worker);
return NULL;
}
worker->daemon = daemon;
worker->thread_num = id;
if(!(worker->cmd = tube_create())) {
free(worker->ports);
free(worker);
return NULL;
}
/* create random state here to avoid locking trouble in RAND_bytes */
if(!(worker->rndstate = ub_initstate(daemon->rand))) {
log_err("could not init random numbers.");
tube_delete(worker->cmd);
free(worker->ports);
free(worker);
return NULL;
}
explicit_bzero(&seed, sizeof(seed));
return worker;
}
int
worker_init(struct worker* worker, struct config_file *cfg,
struct listen_port* ports, int do_sigs)
{
#ifdef USE_DNSTAP
struct dt_env* dtenv = &worker->dtenv;
#else
void* dtenv = NULL;
#endif
#ifdef HAVE_GETTID
worker->thread_tid = gettid();
#endif
worker->need_to_exit = 0;
worker->base = comm_base_create(do_sigs);
if(!worker->base) {
log_err("could not create event handling base");
worker_delete(worker);
return 0;
}
comm_base_set_slow_accept_handlers(worker->base, &worker_stop_accept,
&worker_start_accept, worker);
if(do_sigs) {
#ifdef SIGHUP
ub_thread_sig_unblock(SIGHUP);
#endif
#ifdef SIGBREAK
ub_thread_sig_unblock(SIGBREAK);
#endif
ub_thread_sig_unblock(SIGINT);
#ifdef SIGQUIT
ub_thread_sig_unblock(SIGQUIT);
#endif
ub_thread_sig_unblock(SIGTERM);
#ifndef LIBEVENT_SIGNAL_PROBLEM
worker->comsig = comm_signal_create(worker->base,
worker_sighandler, worker);
if(!worker->comsig
#ifdef SIGHUP
|| !comm_signal_bind(worker->comsig, SIGHUP)
#endif
#ifdef SIGQUIT
|| !comm_signal_bind(worker->comsig, SIGQUIT)
#endif
|| !comm_signal_bind(worker->comsig, SIGTERM)
#ifdef SIGBREAK
|| !comm_signal_bind(worker->comsig, SIGBREAK)
#endif
|| !comm_signal_bind(worker->comsig, SIGINT)) {
log_err("could not create signal handlers");
worker_delete(worker);
return 0;
}
#endif /* LIBEVENT_SIGNAL_PROBLEM */
if(!daemon_remote_open_accept(worker->daemon->rc,
worker->daemon->rc_ports, worker)) {
worker_delete(worker);
return 0;
}
#ifdef UB_ON_WINDOWS
wsvc_setup_worker(worker);
#endif /* UB_ON_WINDOWS */
} else { /* !do_sigs */
worker->comsig = NULL;
}
#ifdef USE_DNSTAP
if(cfg->dnstap) {
log_assert(worker->daemon->dtenv != NULL);
memcpy(&worker->dtenv, worker->daemon->dtenv, sizeof(struct dt_env));
if(!dt_init(&worker->dtenv, worker->base))
fatal_exit("dt_init failed");
}
#endif
worker->front = listen_create(worker->base, ports,
cfg->msg_buffer_size, (int)cfg->incoming_num_tcp,
cfg->do_tcp_keepalive
? cfg->tcp_keepalive_timeout
: cfg->tcp_idle_timeout,
cfg->harden_large_queries, cfg->http_max_streams,
cfg->http_endpoint, cfg->http_notls_downstream,
worker->daemon->tcl, worker->daemon->listen_sslctx,
dtenv, worker_handle_request, worker);
if(!worker->front) {
log_err("could not create listening sockets");
worker_delete(worker);
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->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,
cfg->unwanted_threshold, cfg->outgoing_tcp_mss,
&worker_alloc_cleanup, worker,
cfg->do_udp || cfg->udp_upstream_without_downstream,
worker->daemon->connect_sslctx, cfg->delay_close,
cfg->tls_use_sni, dtenv, cfg->udp_connect,
cfg->max_reuse_tcp_queries, cfg->tcp_reuse_timeout,
cfg->tcp_auth_query_timeout);
if(!worker->back) {
log_err("could not create outgoing sockets");
worker_delete(worker);
return 0;
}
iterator_set_ip46_support(&worker->daemon->mods, worker->daemon->env,
worker->back);
/* start listening to commands */
if(!tube_setup_bg_listen(worker->cmd, worker->base,
&worker_handle_control_cmd, worker)) {
log_err("could not create control compt.");
worker_delete(worker);
return 0;
}
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
* user wants for memory usage sizes */
worker->scratchpad = regional_create_custom(cfg->msg_buffer_size);
if(!worker->scratchpad) {
log_err("malloc failure");
worker_delete(worker);
return 0;
}
server_stats_init(&worker->stats, cfg);
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.outnet = worker->back;
worker->env.rnd = worker->rndstate;
/* If case prefetch is triggered, the corresponding mesh will clear
* the scratchpad for the module env in the middle of request handling.
* It would be prone to a use-after-free kind of bug, so we avoid
* sharing it with worker's own scratchpad at the cost of having
* one more pad per worker. */
worker->env.scratch = regional_create_custom(cfg->msg_buffer_size);
if(!worker->env.scratch) {
log_err("malloc failure");
worker_delete(worker);
return 0;
}
worker->env.mesh = mesh_create(&worker->daemon->mods, &worker->env);
if(!worker->env.mesh) {
log_err("malloc failure");
worker_delete(worker);
return 0;
}
/* Pass on daemon variables that we would need in the mesh area */
worker->env.mesh->use_response_ip = worker->daemon->use_response_ip;
worker->env.mesh->use_rpz = worker->daemon->use_rpz;
worker->env.detach_subs = &mesh_detach_subs;
worker->env.attach_sub = &mesh_attach_sub;
worker->env.add_sub = &mesh_add_sub;
worker->env.kill_sub = &mesh_state_delete;
worker->env.detect_cycle = &mesh_detect_cycle;
worker->env.scratch_buffer = sldns_buffer_new(cfg->msg_buffer_size);
if(!worker->env.scratch_buffer) {
log_err("malloc failure");
worker_delete(worker);
return 0;
}
if(!(worker->env.fwds = forwards_create()) ||
!forwards_apply_cfg(worker->env.fwds, cfg)) {
log_err("Could not set forward zones");
worker_delete(worker);
return 0;
}
if(!(worker->env.hints = hints_create()) ||
!hints_apply_cfg(worker->env.hints, cfg)) {
log_err("Could not set root or stub hints");
worker_delete(worker);
return 0;
}
/* one probe timer per process -- if we have 5011 anchors */
if(autr_get_num_anchors(worker->env.anchors) > 0
#ifndef THREADS_DISABLED
&& worker->thread_num == 0
#endif
) {
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 0;
worker->env.probe_timer = comm_timer_create(worker->base,
worker_probe_timer_cb, worker);
if(!worker->env.probe_timer) {
log_err("could not create 5011-probe timer");
} else {
/* let timer fire, then it can reset itself */
comm_timer_set(worker->env.probe_timer, &tv);
}
}
/* zone transfer tasks, setup once per process, if any */
if(worker->env.auth_zones
#ifndef THREADS_DISABLED
&& worker->thread_num == 0
#endif
) {
auth_xfer_pickup_initial(worker->env.auth_zones, &worker->env);
auth_zones_pickup_zonemd_verify(worker->env.auth_zones,
&worker->env);
}
#ifdef USE_DNSTAP
if(worker->daemon->cfg->dnstap
#ifndef THREADS_DISABLED
&& worker->thread_num == 0
#endif
) {
if(!dt_io_thread_start(dtenv->dtio, comm_base_internal(
worker->base), worker->daemon->num)) {
log_err("could not start dnstap io thread");
worker_delete(worker);
return 0;
}
}
#endif /* USE_DNSTAP */
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",
worker->env.cfg->stat_interval);
worker_restart_timer(worker);
}
+ pp_init(&sldns_write_uint16, &sldns_write_uint32);
return 1;
}
void
worker_work(struct worker* worker)
{
comm_base_dispatch(worker->base);
}
void
worker_delete(struct worker* worker)
{
if(!worker)
return;
if(worker->env.mesh && verbosity >= VERB_OPS) {
server_stats_log(&worker->stats, worker, worker->thread_num);
mesh_stats(worker->env.mesh, "mesh has");
worker_mem_report(worker, NULL);
}
outside_network_quit_prepare(worker->back);
mesh_delete(worker->env.mesh);
sldns_buffer_free(worker->env.scratch_buffer);
forwards_delete(worker->env.fwds);
hints_delete(worker->env.hints);
listen_delete(worker->front);
outside_network_delete(worker->back);
comm_signal_delete(worker->comsig);
tube_delete(worker->cmd);
comm_timer_delete(worker->stat_timer);
comm_timer_delete(worker->env.probe_timer);
free(worker->ports);
if(worker->thread_num == 0) {
#ifdef UB_ON_WINDOWS
wsvc_desetup_worker(worker);
#endif /* UB_ON_WINDOWS */
}
#ifdef USE_DNSTAP
if(worker->daemon->cfg->dnstap
#ifndef THREADS_DISABLED
&& worker->thread_num == 0
#endif
) {
dt_io_thread_stop(worker->dtenv.dtio);
}
dt_deinit(&worker->dtenv);
#endif /* USE_DNSTAP */
comm_base_delete(worker->base);
ub_randfree(worker->rndstate);
/* don't touch worker->alloc, as it's maintained in daemon */
regional_destroy(worker->env.scratch);
regional_destroy(worker->scratchpad);
free(worker);
}
struct outbound_entry*
worker_send_query(struct query_info* qinfo, uint16_t flags, int dnssec,
int want_dnssec, int nocaps, int check_ratelimit,
struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
size_t zonelen, int tcp_upstream, int ssl_upstream, char* tls_auth_name,
struct module_qstate* q, int* was_ratelimited)
{
struct worker* worker = q->env->worker;
struct outbound_entry* e = (struct outbound_entry*)regional_alloc(
q->region, sizeof(*e));
if(!e)
return NULL;
e->qstate = q;
e->qsent = outnet_serviced_query(worker->back, qinfo, flags, dnssec,
want_dnssec, nocaps, check_ratelimit, tcp_upstream,
ssl_upstream, tls_auth_name, addr, addrlen, zone, zonelen, q,
worker_handle_service_reply, e, worker->back->udp_buff, q->env,
was_ratelimited);
if(!e->qsent) {
return NULL;
}
return e;
}
void
worker_alloc_cleanup(void* arg)
{
struct worker* worker = (struct worker*)arg;
slabhash_clear(&worker->env.rrset_cache->table);
slabhash_clear(worker->env.msg_cache);
}
void worker_stats_clear(struct worker* worker)
{
server_stats_init(&worker->stats, worker->env.cfg);
mesh_stats_clear(worker->env.mesh);
worker->back->unwanted_replies = 0;
worker->back->num_tcp_outgoing = 0;
worker->back->num_udp_outgoing = 0;
}
void worker_start_accept(void* arg)
{
struct worker* worker = (struct worker*)arg;
listen_start_accept(worker->front);
if(worker->thread_num == 0)
daemon_remote_start_accept(worker->daemon->rc);
}
void worker_stop_accept(void* arg)
{
struct worker* worker = (struct worker*)arg;
listen_stop_accept(worker->front);
if(worker->thread_num == 0)
daemon_remote_stop_accept(worker->daemon->rc);
}
/* --- fake callbacks for fptr_wlist to work --- */
struct outbound_entry* libworker_send_query(
struct query_info* ATTR_UNUSED(qinfo),
uint16_t ATTR_UNUSED(flags), int ATTR_UNUSED(dnssec),
int ATTR_UNUSED(want_dnssec), int ATTR_UNUSED(nocaps),
int ATTR_UNUSED(check_ratelimit),
struct sockaddr_storage* ATTR_UNUSED(addr), socklen_t ATTR_UNUSED(addrlen),
uint8_t* ATTR_UNUSED(zone), size_t ATTR_UNUSED(zonelen), int ATTR_UNUSED(tcp_upstream),
int ATTR_UNUSED(ssl_upstream), char* ATTR_UNUSED(tls_auth_name),
struct module_qstate* ATTR_UNUSED(q), int* ATTR_UNUSED(was_ratelimited))
{
log_assert(0);
return 0;
}
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))
{
log_assert(0);
return 0;
}
void libworker_handle_control_cmd(struct tube* ATTR_UNUSED(tube),
uint8_t* ATTR_UNUSED(buffer), size_t ATTR_UNUSED(len),
int ATTR_UNUSED(error), void* ATTR_UNUSED(arg))
{
log_assert(0);
}
void libworker_fg_done_cb(void* ATTR_UNUSED(arg), int ATTR_UNUSED(rcode),
sldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(s),
char* ATTR_UNUSED(why_bogus), int ATTR_UNUSED(was_ratelimited))
{
log_assert(0);
}
void libworker_bg_done_cb(void* ATTR_UNUSED(arg), int ATTR_UNUSED(rcode),
sldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(s),
char* ATTR_UNUSED(why_bogus), int ATTR_UNUSED(was_ratelimited))
{
log_assert(0);
}
void libworker_event_done_cb(void* ATTR_UNUSED(arg), int ATTR_UNUSED(rcode),
sldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(s),
char* ATTR_UNUSED(why_bogus), int ATTR_UNUSED(was_ratelimited))
{
log_assert(0);
}
int context_query_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
{
log_assert(0);
return 0;
}
int order_lock_cmp(const void* ATTR_UNUSED(e1), const void* ATTR_UNUSED(e2))
{
log_assert(0);
return 0;
}
int codeline_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
{
log_assert(0);
return 0;
}
#ifdef USE_DNSTAP
void dtio_tap_callback(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev),
void* ATTR_UNUSED(arg))
{
log_assert(0);
}
#endif
#ifdef USE_DNSTAP
void dtio_mainfdcallback(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev),
void* ATTR_UNUSED(arg))
{
log_assert(0);
}
#endif
diff --git a/contrib/unbound/dns64/dns64.c b/contrib/unbound/dns64/dns64.c
index 1e31f51e831f..83fb027790e0 100644
--- a/contrib/unbound/dns64/dns64.c
+++ b/contrib/unbound/dns64/dns64.c
@@ -1,1043 +1,1058 @@
/*
* dns64/dns64.c - DNS64 module
*
* Copyright (c) 2009, Viagénie. 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 Viagénie 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 A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file contains a module that performs DNS64 query processing.
*/
#include "config.h"
#include "dns64/dns64.h"
#include "services/cache/dns.h"
#include "services/cache/rrset.h"
#include "util/config_file.h"
#include "util/data/msgreply.h"
#include "util/fptr_wlist.h"
#include "util/net_help.h"
#include "util/regional.h"
#include "util/storage/dnstree.h"
#include "util/data/dname.h"
#include "sldns/str2wire.h"
/******************************************************************************
* *
* STATIC CONSTANTS *
* *
******************************************************************************/
/**
* 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";
/**
* Maximum length of a domain name in a PTR query in the .in-addr.arpa tree.
*/
#define MAX_PTR_QNAME_IPV4 30
/**
* State of DNS64 processing for a query.
*/
enum dns64_state {
DNS64_INTERNAL_QUERY, /**< Internally-generated query, no DNS64
processing. */
DNS64_NEW_QUERY, /**< Query for which we're the first module in
line. */
DNS64_SUBQUERY_FINISHED /**< Query for which we generated a sub-query, and
for which this sub-query is finished. */
};
/**
* Per-query module-specific state. For the DNS64 module.
*/
struct dns64_qstate {
/** State of the DNS64 module. */
enum dns64_state state;
/** If the dns64 module started with no_cache bool set in the qstate,
* a message to tell it to not modify the cache contents, then this
* is true. The dns64 module is then free to modify that flag for
* its own purposes.
* Otherwise, it is false, the dns64 module was not told to no_cache */
int started_no_cache_store;
};
/******************************************************************************
* *
* STRUCTURES *
* *
******************************************************************************/
/**
* This structure contains module configuration information. One instance of
* this structure exists per instance of the module. Normally there is only one
* instance of the module.
*/
struct dns64_env {
/**
* DNS64 prefix address. We're using a full sockaddr instead of just an
* in6_addr because we can reuse Unbound's generic string parsing functions.
* It will always contain a sockaddr_in6, and only the sin6_addr member will
* ever be used.
*/
struct sockaddr_storage prefix_addr;
/**
* This is always sizeof(sockaddr_in6).
*/
socklen_t prefix_addrlen;
/**
* This is the CIDR length of the prefix. It needs to be between 0 and 96.
*/
int prefix_net;
/**
* Tree of names for which AAAA is ignored. always synthesize from A.
*/
rbtree_type ignore_aaaa;
};
/******************************************************************************
* *
* UTILITY FUNCTIONS *
* *
******************************************************************************/
/**
* Generic macro for swapping two variables.
*
* \param t Type of the variables. (e.g. int)
* \param a First variable.
* \param b Second variable.
*
* \warning Do not attempt something foolish such as swap(int,a++,b++)!
*/
#define swap(t,a,b) do {t x = a; a = b; b = x;} while(0)
/**
* Reverses a string.
*
* \param begin Points to the first character of the string.
* \param end Points one past the last character of the string.
*/
static void
reverse(char* begin, char* end)
{
while ( begin < --end ) {
swap(char, *begin, *end);
++begin;
}
}
/**
* Convert an unsigned integer to a string. The point of this function is that
* of being faster than sprintf().
*
* \param n The number to be converted.
* \param s The result will be written here. Must be large enough, be careful!
*
* \return The number of characters written.
*/
static int
uitoa(unsigned n, char* s)
{
char* ss = s;
do {
*ss++ = '0' + n % 10;
} while (n /= 10);
reverse(s, ss);
return ss - s;
}
/**
* Extract an IPv4 address embedded in the IPv6 address \a ipv6 at offset \a
* offset (in bits). Note that bits are not necessarily aligned on bytes so we
* need to be careful.
*
* \param ipv6 IPv6 address represented as a 128-bit array in big-endian
* order.
* \param ipv6_len length of the ipv6 byte array.
* \param offset Index of the MSB of the IPv4 address embedded in the IPv6
* address.
*/
static uint32_t
extract_ipv4(const uint8_t ipv6[], size_t ipv6_len, const int offset)
{
uint32_t ipv4 = 0;
int i, pos;
log_assert(ipv6_len == 16); (void)ipv6_len;
log_assert(offset == 32 || offset == 40 || offset == 48 || offset == 56 ||
offset == 64 || offset == 96);
for(i = 0, pos = offset / 8; i < 4; i++, pos++) {
if (pos == 8)
pos++;
ipv4 = ipv4 << 8;
ipv4 |= ipv6[pos];
}
return ipv4;
}
/**
* Builds the PTR query name corresponding to an IPv4 address. For example,
* given the number 3,464,175,361, this will build the string
* "\03206\03123\0231\011\07in-addr\04arpa".
*
* \param ipv4 IPv4 address represented as an unsigned 32-bit number.
* \param ptr The result will be written here. Must be large enough, be
* careful!
* \param nm_len length of the ptr buffer.
*
* \return The number of characters written.
*/
static size_t
ipv4_to_ptr(uint32_t ipv4, char ptr[], size_t nm_len)
{
static const char IPV4_PTR_SUFFIX[] = "\07in-addr\04arpa";
int i;
char* c = ptr;
log_assert(nm_len == MAX_PTR_QNAME_IPV4); (void)nm_len;
for (i = 0; i < 4; ++i) {
*c = uitoa((unsigned int)(ipv4 % 256), c + 1);
c += *c + 1;
log_assert(c < ptr+nm_len);
ipv4 /= 256;
}
log_assert(c + sizeof(IPV4_PTR_SUFFIX) <= ptr+nm_len);
memmove(c, IPV4_PTR_SUFFIX, sizeof(IPV4_PTR_SUFFIX));
return c + sizeof(IPV4_PTR_SUFFIX) - ptr;
}
/**
* Converts an IPv6-related domain name string from a PTR query into an IPv6
* address represented as a 128-bit array.
*
* \param ptr The domain name. (e.g. "\011[...]\010\012\016\012\03ip6\04arpa")
* \param ipv6 The result will be written here, in network byte order.
* \param ipv6_len length of the ipv6 byte array.
*
* \return 1 on success, 0 on failure.
*/
static int
ptr_to_ipv6(const char* ptr, uint8_t ipv6[], size_t ipv6_len)
{
int i;
log_assert(ipv6_len == 16); (void)ipv6_len;
for (i = 0; i < 64; i++) {
int x;
if (ptr[i++] != 1)
return 0;
if (ptr[i] >= '0' && ptr[i] <= '9') {
x = ptr[i] - '0';
} else if (ptr[i] >= 'a' && ptr[i] <= 'f') {
x = ptr[i] - 'a' + 10;
} else if (ptr[i] >= 'A' && ptr[i] <= 'F') {
x = ptr[i] - 'A' + 10;
} else {
return 0;
}
ipv6[15-i/4] |= x << (2 * ((i-1) % 4));
}
return 1;
}
/**
* Synthesize an IPv6 address based on an IPv4 address and the DNS64 prefix.
*
* \param prefix_addr DNS64 prefix address.
* \param prefix_addr_len length of the prefix_addr buffer.
* \param prefix_net CIDR length of the DNS64 prefix. Must be between 0 and 96.
* \param a IPv4 address.
* \param a_len length of the a buffer.
* \param aaaa IPv6 address. The result will be written here.
* \param aaaa_len length of the aaaa buffer.
*/
static void
synthesize_aaaa(const uint8_t prefix_addr[], size_t prefix_addr_len,
int prefix_net, const uint8_t a[], size_t a_len, uint8_t aaaa[],
size_t aaaa_len)
{
size_t i;
int pos;
log_assert(prefix_addr_len == 16 && a_len == 4 && aaaa_len == 16);
log_assert(prefix_net == 32 || prefix_net == 40 || prefix_net == 48 ||
prefix_net == 56 || prefix_net == 64 || prefix_net == 96);
(void)prefix_addr_len; (void)a_len; (void)aaaa_len;
memcpy(aaaa, prefix_addr, 16);
for(i = 0, pos = prefix_net / 8; i < a_len; i++, pos++) {
if(pos == 8)
aaaa[pos++] = 0;
aaaa[pos] = a[i];
}
}
/******************************************************************************
* *
* DNS64 MODULE FUNCTIONS *
* *
******************************************************************************/
/**
* insert ignore_aaaa element into the tree
* @param dns64_env: module env.
* @param str: string with domain name.
* @return false on failure.
*/
static int
dns64_insert_ignore_aaaa(struct dns64_env* dns64_env, char* str)
{
/* parse and insert element */
struct name_tree_node* node;
node = (struct name_tree_node*)calloc(1, sizeof(*node));
if(!node) {
log_err("out of memory");
return 0;
}
node->name = sldns_str2wire_dname(str, &node->len);
if(!node->name) {
free(node);
log_err("cannot parse dns64-ignore-aaaa: %s", str);
return 0;
}
node->labs = dname_count_labels(node->name);
node->dclass = LDNS_RR_CLASS_IN;
if(!name_tree_insert(&dns64_env->ignore_aaaa, node,
node->name, node->len, node->labs, node->dclass)) {
/* ignore duplicate element */
free(node->name);
free(node);
return 1;
}
return 1;
}
/**
* This function applies the configuration found in the parsed configuration
* file \a cfg to this instance of the dns64 module. Currently only the DNS64
* prefix (a.k.a. Pref64) is configurable.
*
* \param dns64_env Module-specific global parameters.
* \param cfg Parsed configuration file.
*/
static int
dns64_apply_cfg(struct dns64_env* dns64_env, struct config_file* cfg)
{
struct config_strlist* s;
verbose(VERB_ALGO, "dns64-prefix: %s", cfg->dns64_prefix);
if (!netblockstrtoaddr(cfg->dns64_prefix ? cfg->dns64_prefix :
DEFAULT_DNS64_PREFIX, 0, &dns64_env->prefix_addr,
&dns64_env->prefix_addrlen, &dns64_env->prefix_net)) {
log_err("cannot parse dns64-prefix netblock: %s", cfg->dns64_prefix);
return 0;
}
if (!addr_is_ip6(&dns64_env->prefix_addr, dns64_env->prefix_addrlen)) {
log_err("dns64_prefix is not IPv6: %s", cfg->dns64_prefix);
return 0;
}
if (dns64_env->prefix_net != 32 && dns64_env->prefix_net != 40 &&
dns64_env->prefix_net != 48 && dns64_env->prefix_net != 56 &&
dns64_env->prefix_net != 64 && dns64_env->prefix_net != 96 ) {
log_err("dns64-prefix length it not 32, 40, 48, 56, 64 or 96: %s",
cfg->dns64_prefix);
return 0;
}
for(s = cfg->dns64_ignore_aaaa; s; s = s->next) {
if(!dns64_insert_ignore_aaaa(dns64_env, s->str))
return 0;
}
name_tree_init_parents(&dns64_env->ignore_aaaa);
return 1;
}
/**
* Initializes this instance of the dns64 module.
*
* \param env Global state of all module instances.
* \param id This instance's ID number.
*/
int
dns64_init(struct module_env* env, int id)
{
struct dns64_env* dns64_env =
(struct dns64_env*)calloc(1, sizeof(struct dns64_env));
if (!dns64_env) {
log_err("malloc failure");
return 0;
}
env->modinfo[id] = (void*)dns64_env;
name_tree_init(&dns64_env->ignore_aaaa);
if (!dns64_apply_cfg(dns64_env, env->cfg)) {
log_err("dns64: could not apply configuration settings.");
return 0;
}
return 1;
}
/** free ignore AAAA elements */
static void
free_ignore_aaaa_node(rbnode_type* node, void* ATTR_UNUSED(arg))
{
struct name_tree_node* n = (struct name_tree_node*)node;
if(!n) return;
free(n->name);
free(n);
}
/**
* Deinitializes this instance of the dns64 module.
*
* \param env Global state of all module instances.
* \param id This instance's ID number.
*/
void
dns64_deinit(struct module_env* env, int id)
{
struct dns64_env* dns64_env;
if (!env)
return;
dns64_env = (struct dns64_env*)env->modinfo[id];
if(dns64_env) {
traverse_postorder(&dns64_env->ignore_aaaa, free_ignore_aaaa_node,
NULL);
}
free(env->modinfo[id]);
env->modinfo[id] = NULL;
}
/**
* Handle PTR queries for IPv6 addresses. If the address belongs to the DNS64
* prefix, we must do a PTR query for the corresponding IPv4 address instead.
*
* \param qstate Query state structure.
* \param id This module instance's ID number.
*
* \return The new state of the query.
*/
static enum module_ext_state
handle_ipv6_ptr(struct module_qstate* qstate, int id)
{
struct dns64_env* dns64_env = (struct dns64_env*)qstate->env->modinfo[id];
struct module_qstate* subq = NULL;
struct query_info qinfo;
struct sockaddr_in6 sin6;
/* Convert the PTR query string to an IPv6 address. */
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
if (!ptr_to_ipv6((char*)qstate->qinfo.qname, sin6.sin6_addr.s6_addr,
sizeof(sin6.sin6_addr.s6_addr)))
return module_wait_module; /* Let other module handle this. */
/*
* If this IPv6 address is not part of our DNS64 prefix, then we don't need
* to do anything. Let another module handle the query.
*/
if (addr_in_common((struct sockaddr_storage*)&sin6, 128,
&dns64_env->prefix_addr, dns64_env->prefix_net,
(socklen_t)sizeof(sin6)) != dns64_env->prefix_net)
return module_wait_module;
verbose(VERB_ALGO, "dns64: rewrite PTR record");
/*
* Create a new PTR query info for the domain name corresponding to the IPv4
* address corresponding to the IPv6 address corresponding to the original
* PTR query domain name.
*/
qinfo = qstate->qinfo;
if (!(qinfo.qname = regional_alloc(qstate->region, MAX_PTR_QNAME_IPV4)))
return module_error;
qinfo.qname_len = ipv4_to_ptr(extract_ipv4(sin6.sin6_addr.s6_addr,
sizeof(sin6.sin6_addr.s6_addr), dns64_env->prefix_net),
(char*)qinfo.qname, MAX_PTR_QNAME_IPV4);
/* Create the new sub-query. */
fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub));
if(!(*qstate->env->attach_sub)(qstate, &qinfo, qstate->query_flags, 0, 0,
&subq))
return module_error;
if (subq) {
subq->curmod = id;
subq->ext_state[id] = module_state_initial;
subq->minfo[id] = NULL;
}
return module_wait_subquery;
}
static enum module_ext_state
generate_type_A_query(struct module_qstate* qstate, int id)
{
struct module_qstate* subq = NULL;
struct query_info qinfo;
verbose(VERB_ALGO, "dns64: query A record");
/* Create a new query info. */
qinfo = qstate->qinfo;
qinfo.qtype = LDNS_RR_TYPE_A;
/* Start the sub-query. */
fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub));
if(!(*qstate->env->attach_sub)(qstate, &qinfo, qstate->query_flags, 0,
0, &subq))
{
verbose(VERB_ALGO, "dns64: sub-query creation failed");
return module_error;
}
if (subq) {
subq->curmod = id;
subq->ext_state[id] = module_state_initial;
subq->minfo[id] = NULL;
}
return module_wait_subquery;
}
/**
* See if query name is in the always synth config.
* The ignore-aaaa list has names for which the AAAA for the domain is
* ignored and the A is always used to create the answer.
* @param qstate: query state.
* @param id: module id.
* @return true if the name is covered by ignore-aaaa.
*/
static int
dns64_always_synth_for_qname(struct module_qstate* qstate, int id)
{
struct dns64_env* dns64_env = (struct dns64_env*)qstate->env->modinfo[id];
int labs = dname_count_labels(qstate->qinfo.qname);
struct name_tree_node* node = name_tree_lookup(&dns64_env->ignore_aaaa,
qstate->qinfo.qname, qstate->qinfo.qname_len, labs,
qstate->qinfo.qclass);
return (node != NULL);
}
/**
* Handles the "pass" event for a query. This event is received when a new query
* is received by this module. The query may have been generated internally by
* another module, in which case we don't want to do any special processing
* (this is an interesting discussion topic), or it may be brand new, e.g.
* received over a socket, in which case we do want to apply DNS64 processing.
*
* \param qstate A structure representing the state of the query that has just
* received the "pass" event.
* \param id This module's instance ID.
*
* \return The new state of the query.
*/
static enum module_ext_state
handle_event_pass(struct module_qstate* qstate, int id)
{
struct dns64_qstate* iq = (struct dns64_qstate*)qstate->minfo[id];
- if (iq && iq->state == DNS64_NEW_QUERY
- && qstate->qinfo.qtype == LDNS_RR_TYPE_PTR
- && qstate->qinfo.qname_len == 74
- && !strcmp((char*)&qstate->qinfo.qname[64], "\03ip6\04arpa"))
- /* Handle PTR queries for IPv6 addresses. */
- return handle_ipv6_ptr(qstate, id);
-
- if (qstate->env->cfg->dns64_synthall &&
- iq && iq->state == DNS64_NEW_QUERY
- && qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA)
- return generate_type_A_query(qstate, id);
+ int synth_all_cfg = qstate->env->cfg->dns64_synthall;
+ int synth_qname = 0;
+
+ if(iq && iq->state == DNS64_NEW_QUERY
+ && qstate->qinfo.qtype == LDNS_RR_TYPE_PTR
+ && qstate->qinfo.qname_len == 74
+ && !strcmp((char*)&qstate->qinfo.qname[64], "\03ip6\04arpa")) {
+ /* Handle PTR queries for IPv6 addresses. */
+ return handle_ipv6_ptr(qstate, id);
+ }
- if(dns64_always_synth_for_qname(qstate, id) &&
- iq && iq->state == DNS64_NEW_QUERY
- && !(qstate->query_flags & BIT_CD)
- && qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA) {
- verbose(VERB_ALGO, "dns64: ignore-aaaa and synthesize anyway");
+ if(iq && iq->state == DNS64_NEW_QUERY &&
+ qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA &&
+ (synth_all_cfg ||
+ (synth_qname=(dns64_always_synth_for_qname(qstate, id)
+ && !(qstate->query_flags & BIT_CD))))) {
+ if(synth_qname)
+ verbose(VERB_ALGO, "dns64: ignore-aaaa and synthesize anyway");
return generate_type_A_query(qstate, id);
}
/* We are finished when our sub-query is finished. */
- if (iq && iq->state == DNS64_SUBQUERY_FINISHED)
+ if(iq && iq->state == DNS64_SUBQUERY_FINISHED)
return module_finished;
/* Otherwise, pass request to next module. */
verbose(VERB_ALGO, "dns64: pass to next module");
return module_wait_module;
}
/**
* Handles the "done" event for a query. We need to analyze the response and
* maybe issue a new sub-query for the A record.
*
* \param qstate A structure representing the state of the query that has just
* received the "pass" event.
* \param id This module's instance ID.
*
* \return The new state of the query.
*/
static enum module_ext_state
handle_event_moddone(struct module_qstate* qstate, int id)
{
struct dns64_qstate* iq = (struct dns64_qstate*)qstate->minfo[id];
/*
* In many cases we have nothing special to do. From most to least common:
*
* - An internal query.
* - A query for a record type other than AAAA.
* - CD FLAG was set on querier
* - An AAAA query for which an error was returned.(qstate.return_rcode)
* -> treated as servfail thus synthesize (sec 5.1.3 6147), thus
* synthesize in (sec 5.1.2 of RFC6147).
* - A successful AAAA query with an answer.
*/
- if((!iq || iq->state != DNS64_INTERNAL_QUERY)
- && qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA
- && !(qstate->query_flags & BIT_CD)
- && !(qstate->return_msg &&
- qstate->return_msg->rep &&
- reply_find_answer_rrset(&qstate->qinfo,
- qstate->return_msg->rep)))
- /* not internal, type AAAA, not CD, and no answer RRset,
- * So, this is a AAAA noerror/nodata answer */
- return generate_type_A_query(qstate, id);
- if((!iq || iq->state != DNS64_INTERNAL_QUERY)
- && qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA
- && !(qstate->query_flags & BIT_CD)
- && dns64_always_synth_for_qname(qstate, id)) {
- /* if it is not internal, AAAA, not CD and listed domain,
- * generate from A record and ignore AAAA */
- verbose(VERB_ALGO, "dns64: ignore-aaaa and synthesize anyway");
+ /* When an AAAA query completes check if we want to perform DNS64
+ * synthesis. We skip queries with DNSSEC enabled (!CD) and
+ * ones generated by us to retrive the A/PTR record to use for
+ * synth. */
+ int could_synth =
+ qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA &&
+ (!iq || iq->state != DNS64_INTERNAL_QUERY) &&
+ !(qstate->query_flags & BIT_CD);
+ int has_data = /* whether query returned non-empty rrset */
+ qstate->return_msg &&
+ qstate->return_msg->rep &&
+ reply_find_answer_rrset(&qstate->qinfo, qstate->return_msg->rep);
+ int synth_qname = 0;
+
+ if(could_synth &&
+ (!has_data ||
+ (synth_qname=dns64_always_synth_for_qname(qstate, id)))) {
+ if(synth_qname)
+ verbose(VERB_ALGO, "dns64: ignore-aaaa and synthesize anyway");
return generate_type_A_query(qstate, id);
}
/* Store the response in cache. */
- if ( (!iq || !iq->started_no_cache_store) &&
- qstate->return_msg && qstate->return_msg->rep &&
- !dns_cache_store(qstate->env, &qstate->qinfo, qstate->return_msg->rep,
- 0, 0, 0, NULL, qstate->query_flags, qstate->qstarttime))
+ if( (!iq || !iq->started_no_cache_store) &&
+ qstate->return_msg &&
+ qstate->return_msg->rep &&
+ !dns_cache_store(
+ qstate->env, &qstate->qinfo, qstate->return_msg->rep,
+ 0, 0, 0, NULL,
+ qstate->query_flags, qstate->qstarttime))
log_err("out of memory");
/* do nothing */
return module_finished;
}
/**
* This is the module's main() function. It gets called each time a query
* receives an event which we may need to handle. We respond by updating the
* state of the query.
*
* \param qstate Structure containing the state of the query.
* \param event Event that has just been received.
* \param id This module's instance ID.
* \param outbound State of a DNS query on an authoritative server. We never do
* our own queries ourselves (other modules do it for us), so
* this is unused.
*/
void
dns64_operate(struct module_qstate* qstate, enum module_ev event, int id,
struct outbound_entry* outbound)
{
struct dns64_qstate* iq;
(void)outbound;
verbose(VERB_QUERY, "dns64[module %d] operate: extstate:%s event:%s",
id, strextstate(qstate->ext_state[id]),
strmodulevent(event));
log_query_info(VERB_QUERY, "dns64 operate: query", &qstate->qinfo);
switch(event) {
case module_event_new:
/* Tag this query as being new and fall through. */
if (!(iq = (struct dns64_qstate*)regional_alloc(
qstate->region, sizeof(*iq)))) {
log_err("out of memory");
qstate->ext_state[id] = module_error;
return;
}
qstate->minfo[id] = iq;
iq->state = DNS64_NEW_QUERY;
iq->started_no_cache_store = qstate->no_cache_store;
qstate->no_cache_store = 1;
/* fallthrough */
case module_event_pass:
qstate->ext_state[id] = handle_event_pass(qstate, id);
break;
case module_event_moddone:
qstate->ext_state[id] = handle_event_moddone(qstate, id);
break;
default:
qstate->ext_state[id] = module_finished;
break;
}
if(qstate->ext_state[id] == module_finished) {
iq = (struct dns64_qstate*)qstate->minfo[id];
if(iq && iq->state != DNS64_INTERNAL_QUERY)
qstate->no_cache_store = iq->started_no_cache_store;
}
}
static void
dns64_synth_aaaa_data(const struct ub_packed_rrset_key* fk,
const struct packed_rrset_data* fd,
struct ub_packed_rrset_key *dk,
struct packed_rrset_data **dd_out, struct regional *region,
struct dns64_env* dns64_env )
{
struct packed_rrset_data *dd;
size_t i;
/*
* Create synthesized AAAA RR set data. We need to allocated extra memory
* for the RRs themselves. Each RR has a length, TTL, pointer to wireformat
* data, 2 bytes of data length, and 16 bytes of IPv6 address.
*/
if(fd->count > RR_COUNT_MAX) {
*dd_out = NULL;
return; /* integer overflow protection in alloc */
}
if (!(dd = *dd_out = regional_alloc_zero(region,
sizeof(struct packed_rrset_data)
+ fd->count * (sizeof(size_t) + sizeof(time_t) +
sizeof(uint8_t*) + 2 + 16)))) {
log_err("out of memory");
return;
}
/* Copy attributes from A RR set. */
dd->ttl = fd->ttl;
dd->count = fd->count;
dd->rrsig_count = 0;
dd->trust = fd->trust;
dd->security = fd->security;
/*
* Synthesize AAAA records. Adjust pointers in structure.
*/
dd->rr_len =
(size_t*)((uint8_t*)dd + sizeof(struct packed_rrset_data));
dd->rr_data = (uint8_t**)&dd->rr_len[dd->count];
dd->rr_ttl = (time_t*)&dd->rr_data[dd->count];
for(i = 0; i < fd->count; ++i) {
if (fd->rr_len[i] != 6 || fd->rr_data[i][0] != 0
|| fd->rr_data[i][1] != 4) {
*dd_out = NULL;
return;
}
dd->rr_len[i] = 18;
dd->rr_data[i] =
(uint8_t*)&dd->rr_ttl[dd->count] + 18*i;
dd->rr_data[i][0] = 0;
dd->rr_data[i][1] = 16;
synthesize_aaaa(
((struct sockaddr_in6*)&dns64_env->prefix_addr)->sin6_addr.s6_addr,
sizeof(((struct sockaddr_in6*)&dns64_env->prefix_addr)->sin6_addr.s6_addr),
dns64_env->prefix_net, &fd->rr_data[i][2],
fd->rr_len[i]-2, &dd->rr_data[i][2],
dd->rr_len[i]-2);
dd->rr_ttl[i] = fd->rr_ttl[i];
}
/*
* Create synthesized AAAA RR set key. This is mostly just bookkeeping,
* nothing interesting here.
*/
if(!dk) {
log_err("no key");
*dd_out = NULL;
return;
}
dk->rk.dname = (uint8_t*)regional_alloc_init(region,
fk->rk.dname, fk->rk.dname_len);
if(!dk->rk.dname) {
log_err("out of memory");
*dd_out = NULL;
return;
}
dk->rk.type = htons(LDNS_RR_TYPE_AAAA);
memset(&dk->entry, 0, sizeof(dk->entry));
dk->entry.key = dk;
dk->entry.hash = rrset_key_hash(&dk->rk);
dk->entry.data = dd;
}
/**
* Synthesize an AAAA RR set from an A sub-query's answer and add it to the
* original empty response.
*
* \param id This module's instance ID.
* \param super Original AAAA query.
* \param qstate A query.
*/
static void
dns64_adjust_a(int id, struct module_qstate* super, struct module_qstate* qstate)
{
struct dns64_env* dns64_env = (struct dns64_env*)super->env->modinfo[id];
struct reply_info *rep, *cp;
size_t i, s;
struct packed_rrset_data* fd, *dd;
struct ub_packed_rrset_key* fk, *dk;
verbose(VERB_ALGO, "converting A answers to AAAA answers");
log_assert(super->region);
log_assert(qstate->return_msg);
log_assert(qstate->return_msg->rep);
/* If dns64-synthall is enabled, return_msg is not initialized */
if(!super->return_msg) {
super->return_msg = (struct dns_msg*)regional_alloc(
super->region, sizeof(struct dns_msg));
if(!super->return_msg)
return;
memset(super->return_msg, 0, sizeof(*super->return_msg));
super->return_msg->qinfo = super->qinfo;
}
rep = qstate->return_msg->rep;
/*
* Build the actual reply.
*/
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, LDNS_EDE_NONE);
if(!cp)
return;
/* allocate ub_key structures special or not */
if(!reply_info_alloc_rrset_keys(cp, NULL, super->region)) {
return;
}
/* copy everything and replace A by AAAA */
for(i=0; i<cp->rrset_count; i++) {
fk = rep->rrsets[i];
dk = cp->rrsets[i];
fd = (struct packed_rrset_data*)fk->entry.data;
dk->rk = fk->rk;
dk->id = fk->id;
if(i<rep->an_numrrsets && fk->rk.type == htons(LDNS_RR_TYPE_A)) {
/* also sets dk->entry.hash */
dns64_synth_aaaa_data(fk, fd, dk, &dd, super->region, dns64_env);
if(!dd)
return;
/* Delete negative AAAA record from cache stored by
* the iterator module */
rrset_cache_remove(super->env->rrset_cache, dk->rk.dname,
dk->rk.dname_len, LDNS_RR_TYPE_AAAA,
LDNS_RR_CLASS_IN, 0);
/* Delete negative AAAA in msg cache for CNAMEs,
* stored by the iterator module */
if(i != 0) /* if not the first RR */
msg_cache_remove(super->env, dk->rk.dname,
dk->rk.dname_len, LDNS_RR_TYPE_AAAA,
LDNS_RR_CLASS_IN, 0);
} else {
dk->entry.hash = fk->entry.hash;
dk->rk.dname = (uint8_t*)regional_alloc_init(super->region,
fk->rk.dname, fk->rk.dname_len);
if(!dk->rk.dname)
return;
s = packed_rrset_sizeof(fd);
dd = (struct packed_rrset_data*)regional_alloc_init(
super->region, fd, s);
if(!dd)
return;
}
packed_rrset_ptr_fixup(dd);
dk->entry.data = (void*)dd;
}
/* Commit changes. */
super->return_msg->rep = cp;
}
/**
* Generate a response for the original IPv6 PTR query based on an IPv4 PTR
* sub-query's response.
*
* \param qstate IPv4 PTR sub-query.
* \param super Original IPv6 PTR query.
*/
static void
dns64_adjust_ptr(struct module_qstate* qstate, struct module_qstate* super)
{
struct ub_packed_rrset_key* answer;
verbose(VERB_ALGO, "adjusting PTR reply");
/* Copy the sub-query's reply to the parent. */
if (!(super->return_msg = (struct dns_msg*)regional_alloc(super->region,
sizeof(struct dns_msg))))
return;
super->return_msg->qinfo = super->qinfo;
if (!(super->return_msg->rep = reply_info_copy(qstate->return_msg->rep,
NULL, super->region)))
return;
/*
* Adjust the domain name of the answer RR set so that it matches the
* initial query's domain name.
*/
answer = reply_find_answer_rrset(&qstate->qinfo, super->return_msg->rep);
if(answer) {
answer->rk.dname = super->qinfo.qname;
answer->rk.dname_len = super->qinfo.qname_len;
}
}
/**
* This function is called when a sub-query finishes to inform the parent query.
*
* We issue two kinds of sub-queries: PTR and A.
*
* \param qstate State of the sub-query.
* \param id This module's instance ID.
* \param super State of the super-query.
*/
void
dns64_inform_super(struct module_qstate* qstate, int id,
struct module_qstate* super)
{
struct dns64_qstate* super_dq = (struct dns64_qstate*)super->minfo[id];
log_query_info(VERB_ALGO, "dns64: inform_super, sub is",
&qstate->qinfo);
log_query_info(VERB_ALGO, "super is", &super->qinfo);
/*
* Signal that the sub-query is finished, no matter whether we are
* successful or not. This lets the state machine terminate.
*/
if(!super_dq) {
super_dq = (struct dns64_qstate*)regional_alloc(super->region,
sizeof(*super_dq));
if(!super_dq) {
log_err("out of memory");
super->return_rcode = LDNS_RCODE_SERVFAIL;
super->return_msg = NULL;
return;
}
super->minfo[id] = super_dq;
memset(super_dq, 0, sizeof(*super_dq));
super_dq->started_no_cache_store = super->no_cache_store;
}
super_dq->state = DNS64_SUBQUERY_FINISHED;
- /* If there is no successful answer, we're done. */
- if (qstate->return_rcode != LDNS_RCODE_NOERROR
- || !qstate->return_msg
- || !qstate->return_msg->rep) {
+ /* If there is no successful answer, we're done.
+ * Guarantee that we have at least a NOERROR reply further on. */
+ if(qstate->return_rcode != LDNS_RCODE_NOERROR
+ || !qstate->return_msg
+ || !qstate->return_msg->rep) {
+ return;
+ }
+
+ /* When no A record is found for synthesis fall back to AAAA again. */
+ if(qstate->qinfo.qtype == LDNS_RR_TYPE_A &&
+ !reply_find_answer_rrset(&qstate->qinfo,
+ qstate->return_msg->rep)) {
+ super_dq->state = DNS64_INTERNAL_QUERY;
return;
}
/* Use return code from A query in response to client. */
if (super->return_rcode != LDNS_RCODE_NOERROR)
super->return_rcode = qstate->return_rcode;
/* Generate a response suitable for the original query. */
if (qstate->qinfo.qtype == LDNS_RR_TYPE_A) {
dns64_adjust_a(id, super, qstate);
} else {
log_assert(qstate->qinfo.qtype == LDNS_RR_TYPE_PTR);
dns64_adjust_ptr(qstate, super);
}
/* Store the generated response in cache. */
if ( (!super_dq || !super_dq->started_no_cache_store) &&
!dns_cache_store(super->env, &super->qinfo, super->return_msg->rep,
0, 0, 0, NULL, super->query_flags, qstate->qstarttime))
log_err("out of memory");
}
/**
* Clear module-specific data from query state. Since we do not allocate memory,
* it's just a matter of setting a pointer to NULL.
*
* \param qstate Query state.
* \param id This module's instance ID.
*/
void
dns64_clear(struct module_qstate* qstate, int id)
{
qstate->minfo[id] = NULL;
}
/**
* Returns the amount of global memory that this module uses, not including
* per-query data.
*
* \param env Module environment.
* \param id This module's instance ID.
*/
size_t
dns64_get_mem(struct module_env* env, int id)
{
struct dns64_env* dns64_env = (struct dns64_env*)env->modinfo[id];
if (!dns64_env)
return 0;
return sizeof(*dns64_env);
}
/**
* The dns64 function block.
*/
static struct module_func_block dns64_block = {
"dns64",
&dns64_init, &dns64_deinit, &dns64_operate, &dns64_inform_super,
&dns64_clear, &dns64_get_mem
};
/**
* Function for returning the above function block.
*/
struct module_func_block *
dns64_get_funcblock(void)
{
return &dns64_block;
}
diff --git a/contrib/unbound/dnstap/dnstap.m4 b/contrib/unbound/dnstap/dnstap.m4
index 1ff6c3fea2ef..be8b40c4505d 100644
--- a/contrib/unbound/dnstap/dnstap.m4
+++ b/contrib/unbound/dnstap/dnstap.m4
@@ -1,49 +1,49 @@
# dnstap.m4
# dt_DNSTAP(default_dnstap_socket_path, [action-if-true], [action-if-false])
# --------------------------------------------------------------------------
# Check for required dnstap libraries and add dnstap configure args.
AC_DEFUN([dt_DNSTAP],
[
AC_ARG_ENABLE([dnstap],
AS_HELP_STRING([--enable-dnstap],
[Enable dnstap support (requires protobuf-c)]),
[opt_dnstap=$enableval], [opt_dnstap=no])
AC_ARG_WITH([dnstap-socket-path],
AS_HELP_STRING([--with-dnstap-socket-path=pathname],
[set default dnstap socket path]),
[opt_dnstap_socket_path=$withval], [opt_dnstap_socket_path="$1"])
if test "x$opt_dnstap" != "xno"; then
AC_PATH_PROG([PROTOC_C], [protoc-c])
if test -z "$PROTOC_C"; then
AC_MSG_ERROR([The protoc-c program was not found. Please install protobuf-c!])
fi
AC_ARG_WITH([protobuf-c], AS_HELP_STRING([--with-protobuf-c=path],
[Path where protobuf-c is installed, for dnstap]), [
# workaround for protobuf-c includes at old dir before protobuf-c-1.0.0
if test -f $withval/include/google/protobuf-c/protobuf-c.h; then
CFLAGS="$CFLAGS -I$withval/include/google"
else
CFLAGS="$CFLAGS -I$withval/include"
fi
LDFLAGS="$LDFLAGS -L$withval/lib"
], [
# workaround for protobuf-c includes at old dir before protobuf-c-1.0.0
if test -f /usr/include/google/protobuf-c/protobuf-c.h; then
CFLAGS="$CFLAGS -I/usr/include/google"
else
if test -f /usr/local/include/google/protobuf-c/protobuf-c.h; then
CFLAGS="$CFLAGS -I/usr/local/include/google"
LDFLAGS="$LDFLAGS -L/usr/local/lib"
fi
fi
])
AC_SEARCH_LIBS([protobuf_c_message_pack], [protobuf-c], [],
- AC_MSG_ERROR([The protobuf-c library was not found. Please install protobuf-c!]))
+ AC_MSG_ERROR([The protobuf-c library was not found. Please install the development libraries for protobuf-c!]))
$2
else
$3
fi
])
diff --git a/contrib/unbound/dnstap/dtstream.c b/contrib/unbound/dnstap/dtstream.c
index 9153f040472d..69c951276ff0 100644
--- a/contrib/unbound/dnstap/dtstream.c
+++ b/contrib/unbound/dnstap/dtstream.c
@@ -1,2189 +1,2189 @@
/*
* dnstap/dtstream.c - Frame Streams thread for unbound DNSTAP
*
* Copyright (c) 2020, 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/**
* \file
*
* An implementation of the Frame Streams data transport protocol for
* the Unbound DNSTAP message logging facility.
*/
#include "config.h"
#include "dnstap/dtstream.h"
#include "dnstap/dnstap_fstrm.h"
#include "util/config_file.h"
#include "util/ub_event.h"
#include "util/net_help.h"
#include "services/outside_network.h"
#include "sldns/sbuffer.h"
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif
#include <fcntl.h>
#ifdef HAVE_OPENSSL_SSL_H
#include <openssl/ssl.h>
#endif
#ifdef HAVE_OPENSSL_ERR_H
#include <openssl/err.h>
#endif
/** number of messages to process in one output callback */
#define DTIO_MESSAGES_PER_CALLBACK 100
/** the msec to wait for reconnect (if not immediate, the first attempt) */
#define DTIO_RECONNECT_TIMEOUT_MIN 10
/** the msec to wait for reconnect max after backoff */
#define DTIO_RECONNECT_TIMEOUT_MAX 1000
/** the msec to wait for reconnect slow, to stop busy spinning on reconnect */
#define DTIO_RECONNECT_TIMEOUT_SLOW 1000
/** number of messages before wakeup of thread */
#define DTIO_MSG_FOR_WAKEUP 32
/** maximum length of received frame */
#define DTIO_RECV_FRAME_MAX_LEN 1000
struct stop_flush_info;
/** DTIO command channel commands */
enum {
/** DTIO command channel stop */
DTIO_COMMAND_STOP = 0,
/** DTIO command channel wakeup */
DTIO_COMMAND_WAKEUP = 1
} dtio_channel_command;
/** open the output channel */
static void dtio_open_output(struct dt_io_thread* dtio);
/** add output event for read and write */
static int dtio_add_output_event_write(struct dt_io_thread* dtio);
/** start reconnection attempts */
static void dtio_reconnect_enable(struct dt_io_thread* dtio);
/** stop from stop_flush event loop */
static void dtio_stop_flush_exit(struct stop_flush_info* info);
/** setup a start control message */
static int dtio_control_start_send(struct dt_io_thread* dtio);
#ifdef HAVE_SSL
/** enable briefly waiting for a read event, for SSL negotiation */
static int dtio_enable_brief_read(struct dt_io_thread* dtio);
/** enable briefly waiting for a write event, for SSL negotiation */
static int dtio_enable_brief_write(struct dt_io_thread* dtio);
#endif
struct dt_msg_queue*
dt_msg_queue_create(struct comm_base* base)
{
struct dt_msg_queue* mq = calloc(1, sizeof(*mq));
if(!mq) return NULL;
mq->maxsize = 1*1024*1024; /* set max size of buffer, per worker,
about 1 M should contain 64K messages with some overhead,
or a whole bunch smaller ones */
mq->wakeup_timer = comm_timer_create(base, mq_wakeup_cb, mq);
if(!mq->wakeup_timer) {
free(mq);
return NULL;
}
lock_basic_init(&mq->lock);
lock_protect(&mq->lock, mq, sizeof(*mq));
return mq;
}
/** clear the message list, caller must hold the lock */
static void
dt_msg_queue_clear(struct dt_msg_queue* mq)
{
struct dt_msg_entry* e = mq->first, *next=NULL;
while(e) {
next = e->next;
free(e->buf);
free(e);
e = next;
}
mq->first = NULL;
mq->last = NULL;
mq->cursize = 0;
mq->msgcount = 0;
}
void
dt_msg_queue_delete(struct dt_msg_queue* mq)
{
if(!mq) return;
lock_basic_destroy(&mq->lock);
dt_msg_queue_clear(mq);
comm_timer_delete(mq->wakeup_timer);
free(mq);
}
/** make the dtio wake up by sending a wakeup command */
static void dtio_wakeup(struct dt_io_thread* dtio)
{
uint8_t cmd = DTIO_COMMAND_WAKEUP;
if(!dtio) return;
if(!dtio->started) return;
while(1) {
ssize_t r = write(dtio->commandpipe[1], &cmd, sizeof(cmd));
if(r == -1) {
#ifndef USE_WINSOCK
if(errno == EINTR || errno == EAGAIN)
continue;
#else
if(WSAGetLastError() == WSAEINPROGRESS)
continue;
if(WSAGetLastError() == WSAEWOULDBLOCK)
continue;
#endif
log_err("dnstap io wakeup: write: %s",
sock_strerror(errno));
break;
}
break;
}
}
void
mq_wakeup_cb(void* arg)
{
struct dt_msg_queue* mq = (struct dt_msg_queue*)arg;
/* even if the dtio is already active, because perhaps much
* traffic suddenly, we leave the timer running to save on
* managing it, the once a second timer is less work then
* starting and stopping the timer frequently */
lock_basic_lock(&mq->dtio->wakeup_timer_lock);
mq->dtio->wakeup_timer_enabled = 0;
lock_basic_unlock(&mq->dtio->wakeup_timer_lock);
dtio_wakeup(mq->dtio);
}
/** start timer to wakeup dtio because there is content in the queue */
static void
dt_msg_queue_start_timer(struct dt_msg_queue* mq, int wakeupnow)
{
struct timeval tv = {0};
/* Start a timer to process messages to be logged.
* If we woke up the dtio thread for every message, the wakeup
* messages take up too much processing power. If the queue
* fills up the wakeup happens immediately. The timer wakes it up
* if there are infrequent messages to log. */
/* we cannot start a timer in dtio thread, because it is a different
* thread and its event base is in use by the other thread, it would
* give race conditions if we tried to modify its event base,
* and locks would wait until it woke up, and this is what we do. */
/* do not start the timer if a timer already exists, perhaps
* in another worker. So this variable is protected by a lock in
* dtio. */
/* If we need to wakeupnow, 0 the timer to force the callback. */
lock_basic_lock(&mq->dtio->wakeup_timer_lock);
if(mq->dtio->wakeup_timer_enabled) {
if(wakeupnow) {
comm_timer_set(mq->wakeup_timer, &tv);
}
lock_basic_unlock(&mq->dtio->wakeup_timer_lock);
return;
}
mq->dtio->wakeup_timer_enabled = 1; /* we are going to start one */
/* start the timer, in mq, in the event base of our worker */
if(!wakeupnow) {
tv.tv_sec = 1;
tv.tv_usec = 0;
}
comm_timer_set(mq->wakeup_timer, &tv);
lock_basic_unlock(&mq->dtio->wakeup_timer_lock);
}
void
dt_msg_queue_submit(struct dt_msg_queue* mq, void* buf, size_t len)
{
int wakeupnow = 0, wakeupstarttimer = 0;
struct dt_msg_entry* entry;
/* check conditions */
if(!buf) return;
if(len == 0) {
/* it is not possible to log entries with zero length,
* because the framestream protocol does not carry it.
* However the protobuf serialization does not create zero
* length datagrams for dnstap, so this should not happen. */
free(buf);
return;
}
if(!mq) {
free(buf);
return;
}
/* allocate memory for queue entry */
entry = malloc(sizeof(*entry));
if(!entry) {
log_err("out of memory logging dnstap");
free(buf);
return;
}
entry->next = NULL;
entry->buf = buf;
entry->len = len;
/* acquire lock */
lock_basic_lock(&mq->lock);
/* if list was empty, start timer for (eventual) wakeup */
if(mq->first == NULL)
wakeupstarttimer = 1;
/* if list contains more than wakeupnum elements, wakeup now,
* or if list is (going to be) almost full */
if(mq->msgcount == DTIO_MSG_FOR_WAKEUP ||
(mq->cursize < mq->maxsize * 9 / 10 &&
mq->cursize+len >= mq->maxsize * 9 / 10))
wakeupnow = 1;
/* see if it is going to fit */
if(mq->cursize + len > mq->maxsize) {
/* buffer full, or congested. */
/* drop */
lock_basic_unlock(&mq->lock);
free(buf);
free(entry);
return;
}
mq->cursize += len;
mq->msgcount ++;
/* append to list */
if(mq->last) {
mq->last->next = entry;
} else {
mq->first = entry;
}
mq->last = entry;
/* release lock */
lock_basic_unlock(&mq->lock);
if(wakeupnow || wakeupstarttimer) {
dt_msg_queue_start_timer(mq, wakeupnow);
}
}
struct dt_io_thread* dt_io_thread_create(void)
{
struct dt_io_thread* dtio = calloc(1, sizeof(*dtio));
lock_basic_init(&dtio->wakeup_timer_lock);
lock_protect(&dtio->wakeup_timer_lock, &dtio->wakeup_timer_enabled,
sizeof(dtio->wakeup_timer_enabled));
return dtio;
}
void dt_io_thread_delete(struct dt_io_thread* dtio)
{
struct dt_io_list_item* item, *nextitem;
if(!dtio) return;
lock_basic_destroy(&dtio->wakeup_timer_lock);
item=dtio->io_list;
while(item) {
nextitem = item->next;
free(item);
item = nextitem;
}
free(dtio->socket_path);
free(dtio->ip_str);
free(dtio->tls_server_name);
free(dtio->client_key_file);
free(dtio->client_cert_file);
if(dtio->ssl_ctx) {
#ifdef HAVE_SSL
SSL_CTX_free(dtio->ssl_ctx);
#endif
}
free(dtio);
}
int dt_io_thread_apply_cfg(struct dt_io_thread* dtio, struct config_file *cfg)
{
if(!cfg->dnstap) {
log_warn("cannot setup dnstap because dnstap-enable is no");
return 0;
}
/* what type of connectivity do we have */
if(cfg->dnstap_ip && cfg->dnstap_ip[0]) {
if(cfg->dnstap_tls)
dtio->upstream_is_tls = 1;
else dtio->upstream_is_tcp = 1;
} else {
dtio->upstream_is_unix = 1;
}
dtio->is_bidirectional = cfg->dnstap_bidirectional;
if(dtio->upstream_is_unix) {
char* nm;
if(!cfg->dnstap_socket_path ||
cfg->dnstap_socket_path[0]==0) {
log_err("dnstap setup: no dnstap-socket-path for "
"socket connect");
return 0;
}
nm = cfg->dnstap_socket_path;
if(cfg->chrootdir && cfg->chrootdir[0] && strncmp(nm,
cfg->chrootdir, strlen(cfg->chrootdir)) == 0)
nm += strlen(cfg->chrootdir);
free(dtio->socket_path);
dtio->socket_path = strdup(nm);
if(!dtio->socket_path) {
log_err("dnstap setup: malloc failure");
return 0;
}
}
if(dtio->upstream_is_tcp || dtio->upstream_is_tls) {
if(!cfg->dnstap_ip || cfg->dnstap_ip[0] == 0) {
log_err("dnstap setup: no dnstap-ip for TCP connect");
return 0;
}
free(dtio->ip_str);
dtio->ip_str = strdup(cfg->dnstap_ip);
if(!dtio->ip_str) {
log_err("dnstap setup: malloc failure");
return 0;
}
}
if(dtio->upstream_is_tls) {
#ifdef HAVE_SSL
if(cfg->dnstap_tls_server_name &&
cfg->dnstap_tls_server_name[0]) {
free(dtio->tls_server_name);
dtio->tls_server_name = strdup(
cfg->dnstap_tls_server_name);
if(!dtio->tls_server_name) {
log_err("dnstap setup: malloc failure");
return 0;
}
if(!check_auth_name_for_ssl(dtio->tls_server_name))
return 0;
}
if(cfg->dnstap_tls_client_key_file &&
cfg->dnstap_tls_client_key_file[0]) {
dtio->use_client_certs = 1;
free(dtio->client_key_file);
dtio->client_key_file = strdup(
cfg->dnstap_tls_client_key_file);
if(!dtio->client_key_file) {
log_err("dnstap setup: malloc failure");
return 0;
}
if(!cfg->dnstap_tls_client_cert_file ||
cfg->dnstap_tls_client_cert_file[0]==0) {
log_err("dnstap setup: client key "
"authentication enabled with "
"dnstap-tls-client-key-file, but "
"no dnstap-tls-client-cert-file "
"is given");
return 0;
}
free(dtio->client_cert_file);
dtio->client_cert_file = strdup(
cfg->dnstap_tls_client_cert_file);
if(!dtio->client_cert_file) {
log_err("dnstap setup: malloc failure");
return 0;
}
} else {
dtio->use_client_certs = 0;
dtio->client_key_file = NULL;
dtio->client_cert_file = NULL;
}
if(cfg->dnstap_tls_cert_bundle) {
dtio->ssl_ctx = connect_sslctx_create(
dtio->client_key_file,
dtio->client_cert_file,
cfg->dnstap_tls_cert_bundle, 0);
} else {
dtio->ssl_ctx = connect_sslctx_create(
dtio->client_key_file,
dtio->client_cert_file,
cfg->tls_cert_bundle, cfg->tls_win_cert);
}
if(!dtio->ssl_ctx) {
log_err("could not setup SSL CTX");
return 0;
}
dtio->tls_use_sni = cfg->tls_use_sni;
#endif /* HAVE_SSL */
}
return 1;
}
int dt_io_thread_register_queue(struct dt_io_thread* dtio,
struct dt_msg_queue* mq)
{
struct dt_io_list_item* item = malloc(sizeof(*item));
if(!item) return 0;
lock_basic_lock(&mq->lock);
mq->dtio = dtio;
lock_basic_unlock(&mq->lock);
item->queue = mq;
item->next = dtio->io_list;
dtio->io_list = item;
dtio->io_list_iter = NULL;
return 1;
}
void dt_io_thread_unregister_queue(struct dt_io_thread* dtio,
struct dt_msg_queue* mq)
{
struct dt_io_list_item* item, *prev=NULL;
if(!dtio) return;
item = dtio->io_list;
while(item) {
if(item->queue == mq) {
/* found it */
if(prev) prev->next = item->next;
else dtio->io_list = item->next;
/* the queue itself only registered, not deleted */
lock_basic_lock(&item->queue->lock);
item->queue->dtio = NULL;
lock_basic_unlock(&item->queue->lock);
free(item);
dtio->io_list_iter = NULL;
return;
}
prev = item;
item = item->next;
}
}
/** pick a message from the queue, the routine locks and unlocks,
* returns true if there is a message */
static int dt_msg_queue_pop(struct dt_msg_queue* mq, void** buf,
size_t* len)
{
lock_basic_lock(&mq->lock);
if(mq->first) {
struct dt_msg_entry* entry = mq->first;
mq->first = entry->next;
if(!entry->next) mq->last = NULL;
mq->cursize -= entry->len;
mq->msgcount --;
lock_basic_unlock(&mq->lock);
*buf = entry->buf;
*len = entry->len;
free(entry);
return 1;
}
lock_basic_unlock(&mq->lock);
return 0;
}
/** find message in queue, false if no message, true if message to send */
static int dtio_find_in_queue(struct dt_io_thread* dtio,
struct dt_msg_queue* mq)
{
void* buf=NULL;
size_t len=0;
if(dt_msg_queue_pop(mq, &buf, &len)) {
dtio->cur_msg = buf;
dtio->cur_msg_len = len;
dtio->cur_msg_done = 0;
dtio->cur_msg_len_done = 0;
return 1;
}
return 0;
}
/** find a new message to write, search message queues, false if none */
static int dtio_find_msg(struct dt_io_thread* dtio)
{
struct dt_io_list_item *spot, *item;
spot = dtio->io_list_iter;
/* use the next queue for the next message lookup,
* if we hit the end(NULL) the NULL restarts the iter at start. */
if(spot)
dtio->io_list_iter = spot->next;
else if(dtio->io_list)
dtio->io_list_iter = dtio->io_list->next;
/* scan from spot to end-of-io_list */
item = spot;
while(item) {
if(dtio_find_in_queue(dtio, item->queue))
return 1;
item = item->next;
}
/* scan starting at the start-of-list (to wrap around the end) */
item = dtio->io_list;
while(item) {
if(dtio_find_in_queue(dtio, item->queue))
return 1;
item = item->next;
}
return 0;
}
/** callback for the dnstap reconnect, to start reconnecting to output */
void dtio_reconnect_timeout_cb(int ATTR_UNUSED(fd),
short ATTR_UNUSED(bits), void* arg)
{
struct dt_io_thread* dtio = (struct dt_io_thread*)arg;
dtio->reconnect_is_added = 0;
verbose(VERB_ALGO, "dnstap io: reconnect timer");
dtio_open_output(dtio);
if(dtio->event) {
if(!dtio_add_output_event_write(dtio))
return;
/* nothing wrong so far, wait on the output event */
return;
}
/* exponential backoff and retry on timer */
dtio_reconnect_enable(dtio);
}
/** attempt to reconnect to the output, after a timeout */
static void dtio_reconnect_enable(struct dt_io_thread* dtio)
{
struct timeval tv;
int msec;
if(dtio->want_to_exit) return;
if(dtio->reconnect_is_added)
return; /* already done */
/* exponential backoff, store the value for next timeout */
msec = dtio->reconnect_timeout;
if(msec == 0) {
dtio->reconnect_timeout = DTIO_RECONNECT_TIMEOUT_MIN;
} else {
dtio->reconnect_timeout = msec*2;
if(dtio->reconnect_timeout > DTIO_RECONNECT_TIMEOUT_MAX)
dtio->reconnect_timeout = DTIO_RECONNECT_TIMEOUT_MAX;
}
verbose(VERB_ALGO, "dnstap io: set reconnect attempt after %d msec",
msec);
/* setup wait timer */
memset(&tv, 0, sizeof(tv));
tv.tv_sec = msec/1000;
tv.tv_usec = (msec%1000)*1000;
if(ub_timer_add(dtio->reconnect_timer, dtio->event_base,
&dtio_reconnect_timeout_cb, dtio, &tv) != 0) {
log_err("dnstap io: could not reconnect ev timer add");
return;
}
dtio->reconnect_is_added = 1;
}
/** remove dtio reconnect timer */
static void dtio_reconnect_del(struct dt_io_thread* dtio)
{
if(!dtio->reconnect_is_added)
return;
ub_timer_del(dtio->reconnect_timer);
dtio->reconnect_is_added = 0;
}
/** clear the reconnect exponential backoff timer.
* We have successfully connected so we can try again with short timeouts. */
static void dtio_reconnect_clear(struct dt_io_thread* dtio)
{
dtio->reconnect_timeout = 0;
dtio_reconnect_del(dtio);
}
/** reconnect slowly, because we already know we have to wait for a bit */
static void dtio_reconnect_slow(struct dt_io_thread* dtio, int msec)
{
dtio_reconnect_del(dtio);
dtio->reconnect_timeout = msec;
dtio_reconnect_enable(dtio);
}
/** delete the current message in the dtio, and reset counters */
static void dtio_cur_msg_free(struct dt_io_thread* dtio)
{
free(dtio->cur_msg);
dtio->cur_msg = NULL;
dtio->cur_msg_len = 0;
dtio->cur_msg_done = 0;
dtio->cur_msg_len_done = 0;
}
/** delete the buffer and counters used to read frame */
static void dtio_read_frame_free(struct dt_frame_read_buf* rb)
{
if(rb->buf) {
free(rb->buf);
rb->buf = NULL;
}
rb->buf_count = 0;
rb->buf_cap = 0;
rb->frame_len = 0;
rb->frame_len_done = 0;
rb->control_frame = 0;
}
/** del the output file descriptor event for listening */
static void dtio_del_output_event(struct dt_io_thread* dtio)
{
if(!dtio->event_added)
return;
ub_event_del(dtio->event);
dtio->event_added = 0;
dtio->event_added_is_write = 0;
}
/** close dtio socket and set it to -1 */
static void dtio_close_fd(struct dt_io_thread* dtio)
{
sock_close(dtio->fd);
dtio->fd = -1;
}
/** close and stop the output file descriptor event */
static void dtio_close_output(struct dt_io_thread* dtio)
{
if(!dtio->event)
return;
ub_event_free(dtio->event);
dtio->event = NULL;
if(dtio->ssl) {
#ifdef HAVE_SSL
SSL_shutdown(dtio->ssl);
SSL_free(dtio->ssl);
dtio->ssl = NULL;
#endif
}
dtio_close_fd(dtio);
/* if there is a (partial) message, discard it
* we cannot send (the remainder of) it, and a new
* connection needs to start with a control frame. */
if(dtio->cur_msg) {
dtio_cur_msg_free(dtio);
}
dtio->ready_frame_sent = 0;
dtio->accept_frame_received = 0;
dtio_read_frame_free(&dtio->read_frame);
dtio_reconnect_enable(dtio);
}
/** check for pending nonblocking connect errors,
* returns 1 if it is okay. -1 on error (close it), 0 to try later */
static int dtio_check_nb_connect(struct dt_io_thread* dtio)
{
int error = 0;
socklen_t len = (socklen_t)sizeof(error);
if(!dtio->check_nb_connect)
return 1; /* everything okay */
if(getsockopt(dtio->fd, SOL_SOCKET, SO_ERROR, (void*)&error,
&len) < 0) {
#ifndef USE_WINSOCK
error = errno; /* on solaris errno is error */
#else
error = WSAGetLastError();
#endif
}
#ifndef USE_WINSOCK
#if defined(EINPROGRESS) && defined(EWOULDBLOCK)
if(error == EINPROGRESS || error == EWOULDBLOCK)
return 0; /* try again later */
#endif
#else
if(error == WSAEINPROGRESS) {
return 0; /* try again later */
} else if(error == WSAEWOULDBLOCK) {
ub_winsock_tcp_wouldblock((dtio->stop_flush_event?
dtio->stop_flush_event:dtio->event), UB_EV_WRITE);
return 0; /* try again later */
}
#endif
if(error != 0) {
char* to = dtio->socket_path;
if(!to) to = dtio->ip_str;
if(!to) to = "";
log_err("dnstap io: failed to connect to \"%s\": %s",
to, sock_strerror(error));
return -1; /* error, close it */
}
if(dtio->ip_str)
verbose(VERB_DETAIL, "dnstap io: connected to %s",
dtio->ip_str);
else if(dtio->socket_path)
verbose(VERB_DETAIL, "dnstap io: connected to \"%s\"",
dtio->socket_path);
dtio_reconnect_clear(dtio);
dtio->check_nb_connect = 0;
return 1; /* everything okay */
}
#ifdef HAVE_SSL
/** write to ssl output
* returns number of bytes written, 0 if nothing happened,
* try again later, or -1 if the channel is to be closed. */
static int dtio_write_ssl(struct dt_io_thread* dtio, uint8_t* buf,
size_t len)
{
int r;
ERR_clear_error();
r = SSL_write(dtio->ssl, buf, len);
if(r <= 0) {
int want = SSL_get_error(dtio->ssl, r);
if(want == SSL_ERROR_ZERO_RETURN) {
/* closed */
return -1;
} else if(want == SSL_ERROR_WANT_READ) {
/* we want a brief read event */
dtio_enable_brief_read(dtio);
return 0;
} else if(want == SSL_ERROR_WANT_WRITE) {
/* write again later */
return 0;
} else if(want == SSL_ERROR_SYSCALL) {
#ifdef EPIPE
if(errno == EPIPE && verbosity < 2)
return -1; /* silence 'broken pipe' */
#endif
#ifdef ECONNRESET
if(errno == ECONNRESET && verbosity < 2)
return -1; /* silence reset by peer */
#endif
if(errno != 0) {
log_err("dnstap io, SSL_write syscall: %s",
strerror(errno));
}
return -1;
}
- log_crypto_err("dnstap io, could not SSL_write");
+ log_crypto_err_io("dnstap io, could not SSL_write", want);
return -1;
}
return r;
}
#endif /* HAVE_SSL */
/** write buffer to output.
* returns number of bytes written, 0 if nothing happened,
* try again later, or -1 if the channel is to be closed. */
static int dtio_write_buf(struct dt_io_thread* dtio, uint8_t* buf,
size_t len)
{
ssize_t ret;
if(dtio->fd == -1)
return -1;
#ifdef HAVE_SSL
if(dtio->ssl)
return dtio_write_ssl(dtio, buf, len);
#endif
ret = send(dtio->fd, (void*)buf, len, 0);
if(ret == -1) {
#ifndef USE_WINSOCK
if(errno == EINTR || errno == EAGAIN)
return 0;
#else
if(WSAGetLastError() == WSAEINPROGRESS)
return 0;
if(WSAGetLastError() == WSAEWOULDBLOCK) {
ub_winsock_tcp_wouldblock((dtio->stop_flush_event?
dtio->stop_flush_event:dtio->event),
UB_EV_WRITE);
return 0;
}
#endif
log_err("dnstap io: failed send: %s", sock_strerror(errno));
return -1;
}
return ret;
}
#ifdef HAVE_WRITEV
/** write with writev, len and message, in one write, if possible.
* return true if message is done, false if incomplete */
static int dtio_write_with_writev(struct dt_io_thread* dtio)
{
uint32_t sendlen = htonl(dtio->cur_msg_len);
struct iovec iov[2];
ssize_t r;
iov[0].iov_base = ((uint8_t*)&sendlen)+dtio->cur_msg_len_done;
iov[0].iov_len = sizeof(sendlen)-dtio->cur_msg_len_done;
iov[1].iov_base = dtio->cur_msg;
iov[1].iov_len = dtio->cur_msg_len;
log_assert(iov[0].iov_len > 0);
r = writev(dtio->fd, iov, 2);
if(r == -1) {
#ifndef USE_WINSOCK
if(errno == EINTR || errno == EAGAIN)
return 0;
#else
if(WSAGetLastError() == WSAEINPROGRESS)
return 0;
if(WSAGetLastError() == WSAEWOULDBLOCK) {
ub_winsock_tcp_wouldblock((dtio->stop_flush_event?
dtio->stop_flush_event:dtio->event),
UB_EV_WRITE);
return 0;
}
#endif
log_err("dnstap io: failed writev: %s", sock_strerror(errno));
/* close the channel */
dtio_del_output_event(dtio);
dtio_close_output(dtio);
return 0;
}
/* written r bytes */
dtio->cur_msg_len_done += r;
if(dtio->cur_msg_len_done < 4)
return 0;
if(dtio->cur_msg_len_done > 4) {
dtio->cur_msg_done = dtio->cur_msg_len_done-4;
dtio->cur_msg_len_done = 4;
}
if(dtio->cur_msg_done < dtio->cur_msg_len)
return 0;
return 1;
}
#endif /* HAVE_WRITEV */
/** write more of the length, preceding the data frame.
* return true if message is done, false if incomplete. */
static int dtio_write_more_of_len(struct dt_io_thread* dtio)
{
uint32_t sendlen;
int r;
if(dtio->cur_msg_len_done >= 4)
return 1;
#ifdef HAVE_WRITEV
if(!dtio->ssl) {
/* we try writev for everything.*/
return dtio_write_with_writev(dtio);
}
#endif /* HAVE_WRITEV */
sendlen = htonl(dtio->cur_msg_len);
r = dtio_write_buf(dtio,
((uint8_t*)&sendlen)+dtio->cur_msg_len_done,
sizeof(sendlen)-dtio->cur_msg_len_done);
if(r == -1) {
/* close the channel */
dtio_del_output_event(dtio);
dtio_close_output(dtio);
return 0;
} else if(r == 0) {
/* try again later */
return 0;
}
dtio->cur_msg_len_done += r;
if(dtio->cur_msg_len_done < 4)
return 0;
return 1;
}
/** write more of the data frame.
* return true if message is done, false if incomplete. */
static int dtio_write_more_of_data(struct dt_io_thread* dtio)
{
int r;
if(dtio->cur_msg_done >= dtio->cur_msg_len)
return 1;
r = dtio_write_buf(dtio,
((uint8_t*)dtio->cur_msg)+dtio->cur_msg_done,
dtio->cur_msg_len - dtio->cur_msg_done);
if(r == -1) {
/* close the channel */
dtio_del_output_event(dtio);
dtio_close_output(dtio);
return 0;
} else if(r == 0) {
/* try again later */
return 0;
}
dtio->cur_msg_done += r;
if(dtio->cur_msg_done < dtio->cur_msg_len)
return 0;
return 1;
}
/** write more of the current message. false if incomplete, true if
* the message is done */
static int dtio_write_more(struct dt_io_thread* dtio)
{
if(dtio->cur_msg_len_done < 4) {
if(!dtio_write_more_of_len(dtio))
return 0;
}
if(dtio->cur_msg_done < dtio->cur_msg_len) {
if(!dtio_write_more_of_data(dtio))
return 0;
}
return 1;
}
/** Receive bytes from dtio->fd, store in buffer. Returns 0: closed,
* -1: continue, >0: number of bytes read into buffer */
static ssize_t receive_bytes(struct dt_io_thread* dtio, void* buf, size_t len) {
ssize_t r;
r = recv(dtio->fd, (void*)buf, len, MSG_DONTWAIT);
if(r == -1) {
char* to = dtio->socket_path;
if(!to) to = dtio->ip_str;
if(!to) to = "";
#ifndef USE_WINSOCK
if(errno == EINTR || errno == EAGAIN)
return -1; /* try later */
#else
if(WSAGetLastError() == WSAEINPROGRESS) {
return -1; /* try later */
} else if(WSAGetLastError() == WSAEWOULDBLOCK) {
ub_winsock_tcp_wouldblock(
(dtio->stop_flush_event?
dtio->stop_flush_event:dtio->event),
UB_EV_READ);
return -1; /* try later */
}
#endif
if(dtio->reconnect_timeout > DTIO_RECONNECT_TIMEOUT_MIN &&
verbosity < 4)
return 0; /* no log retries on low verbosity */
log_err("dnstap io: output closed, recv %s: %s", to,
strerror(errno));
/* and close below */
return 0;
}
if(r == 0) {
if(dtio->reconnect_timeout > DTIO_RECONNECT_TIMEOUT_MIN &&
verbosity < 4)
return 0; /* no log retries on low verbosity */
verbose(VERB_DETAIL, "dnstap io: output closed by the other side");
/* and close below */
return 0;
}
/* something was received */
return r;
}
#ifdef HAVE_SSL
/** Receive bytes over TLS from dtio->fd, store in buffer. Returns 0: closed,
* -1: continue, >0: number of bytes read into buffer */
static int ssl_read_bytes(struct dt_io_thread* dtio, void* buf, size_t len)
{
int r;
ERR_clear_error();
r = SSL_read(dtio->ssl, buf, len);
if(r <= 0) {
int want = SSL_get_error(dtio->ssl, r);
if(want == SSL_ERROR_ZERO_RETURN) {
if(dtio->reconnect_timeout > DTIO_RECONNECT_TIMEOUT_MIN &&
verbosity < 4)
return 0; /* no log retries on low verbosity */
verbose(VERB_DETAIL, "dnstap io: output closed by the "
"other side");
return 0;
} else if(want == SSL_ERROR_WANT_READ) {
/* continue later */
return -1;
} else if(want == SSL_ERROR_WANT_WRITE) {
(void)dtio_enable_brief_write(dtio);
return -1;
} else if(want == SSL_ERROR_SYSCALL) {
#ifdef ECONNRESET
if(dtio->reconnect_timeout > DTIO_RECONNECT_TIMEOUT_MIN &&
errno == ECONNRESET && verbosity < 4)
return 0; /* silence reset by peer */
#endif
if(errno != 0)
log_err("SSL_read syscall: %s",
strerror(errno));
verbose(VERB_DETAIL, "dnstap io: output closed by the "
"other side");
return 0;
}
- log_crypto_err("could not SSL_read");
+ log_crypto_err_io("could not SSL_read", want);
verbose(VERB_DETAIL, "dnstap io: output closed by the "
"other side");
return 0;
}
return r;
}
#endif /* HAVE_SSL */
/** check if the output fd has been closed,
* it returns false if the stream is closed. */
static int dtio_check_close(struct dt_io_thread* dtio)
{
/* we don't want to read any packets, but if there are we can
* discard the input (ignore it). Ignore of unknown (control)
* packets is okay for the framestream protocol. And also, the
* read call can return that the stream has been closed by the
* other side. */
uint8_t buf[1024];
int r = -1;
if(dtio->fd == -1) return 0;
while(r != 0) {
/* not interested in buffer content, overwrite */
r = receive_bytes(dtio, (void*)buf, sizeof(buf));
if(r == -1)
return 1;
}
/* the other end has been closed */
/* close the channel */
dtio_del_output_event(dtio);
dtio_close_output(dtio);
return 0;
}
/** Read accept frame. Returns -1: continue reading, 0: closed,
* 1: valid accept received. */
static int dtio_read_accept_frame(struct dt_io_thread* dtio)
{
int r;
size_t read_frame_done;
while(dtio->read_frame.frame_len_done < 4) {
#ifdef HAVE_SSL
if(dtio->ssl) {
r = ssl_read_bytes(dtio,
(uint8_t*)&dtio->read_frame.frame_len+
dtio->read_frame.frame_len_done,
4-dtio->read_frame.frame_len_done);
} else {
#endif
r = receive_bytes(dtio,
(uint8_t*)&dtio->read_frame.frame_len+
dtio->read_frame.frame_len_done,
4-dtio->read_frame.frame_len_done);
#ifdef HAVE_SSL
}
#endif
if(r == -1)
return -1; /* continue reading */
if(r == 0) {
/* connection closed */
goto close_connection;
}
dtio->read_frame.frame_len_done += r;
if(dtio->read_frame.frame_len_done < 4)
return -1; /* continue reading */
if(dtio->read_frame.frame_len == 0) {
dtio->read_frame.frame_len_done = 0;
dtio->read_frame.control_frame = 1;
continue;
}
dtio->read_frame.frame_len = ntohl(dtio->read_frame.frame_len);
if(dtio->read_frame.frame_len > DTIO_RECV_FRAME_MAX_LEN) {
verbose(VERB_OPS, "dnstap: received frame exceeds max "
"length of %d bytes, closing connection",
DTIO_RECV_FRAME_MAX_LEN);
goto close_connection;
}
dtio->read_frame.buf = calloc(1, dtio->read_frame.frame_len);
dtio->read_frame.buf_cap = dtio->read_frame.frame_len;
if(!dtio->read_frame.buf) {
log_err("dnstap io: out of memory (creating read "
"buffer)");
goto close_connection;
}
}
if(dtio->read_frame.buf_count < dtio->read_frame.frame_len) {
#ifdef HAVE_SSL
if(dtio->ssl) {
r = ssl_read_bytes(dtio, dtio->read_frame.buf+
dtio->read_frame.buf_count,
dtio->read_frame.buf_cap-
dtio->read_frame.buf_count);
} else {
#endif
r = receive_bytes(dtio, dtio->read_frame.buf+
dtio->read_frame.buf_count,
dtio->read_frame.buf_cap-
dtio->read_frame.buf_count);
#ifdef HAVE_SSL
}
#endif
if(r == -1)
return -1; /* continue reading */
if(r == 0) {
/* connection closed */
goto close_connection;
}
dtio->read_frame.buf_count += r;
if(dtio->read_frame.buf_count < dtio->read_frame.frame_len)
return -1; /* continue reading */
}
/* Complete frame received, check if this is a valid ACCEPT control
* frame. */
if(dtio->read_frame.frame_len < 4) {
verbose(VERB_OPS, "dnstap: invalid data received");
goto close_connection;
}
if(sldns_read_uint32(dtio->read_frame.buf) !=
FSTRM_CONTROL_FRAME_ACCEPT) {
verbose(VERB_ALGO, "dnstap: invalid control type received, "
"ignored");
dtio->ready_frame_sent = 0;
dtio->accept_frame_received = 0;
dtio_read_frame_free(&dtio->read_frame);
return -1;
}
read_frame_done = 4; /* control frame type */
/* Iterate over control fields, ignore unknown types.
* Need to be able to read at least 8 bytes (control field type +
* length). */
while(read_frame_done+8 < dtio->read_frame.frame_len) {
uint32_t type = sldns_read_uint32(dtio->read_frame.buf +
read_frame_done);
uint32_t len = sldns_read_uint32(dtio->read_frame.buf +
read_frame_done + 4);
if(type == FSTRM_CONTROL_FIELD_TYPE_CONTENT_TYPE) {
if(len == strlen(DNSTAP_CONTENT_TYPE) &&
read_frame_done+8+len <=
dtio->read_frame.frame_len &&
memcmp(dtio->read_frame.buf + read_frame_done +
+ 8, DNSTAP_CONTENT_TYPE, len) == 0) {
if(!dtio_control_start_send(dtio)) {
verbose(VERB_OPS, "dnstap io: out of "
"memory while sending START frame");
goto close_connection;
}
dtio->accept_frame_received = 1;
if(!dtio_add_output_event_write(dtio))
goto close_connection;
return 1;
} else {
/* unknown content type */
verbose(VERB_ALGO, "dnstap: ACCEPT frame "
"contains unknown content type, "
"closing connection");
goto close_connection;
}
}
/* unknown option, try next */
read_frame_done += 8+len;
}
close_connection:
dtio_del_output_event(dtio);
dtio_reconnect_slow(dtio, DTIO_RECONNECT_TIMEOUT_SLOW);
dtio_close_output(dtio);
return 0;
}
/** add the output file descriptor event for listening, read only */
static int dtio_add_output_event_read(struct dt_io_thread* dtio)
{
if(!dtio->event)
return 0;
if(dtio->event_added && !dtio->event_added_is_write)
return 1;
/* we have to (re-)register the event */
if(dtio->event_added)
ub_event_del(dtio->event);
ub_event_del_bits(dtio->event, UB_EV_WRITE);
if(ub_event_add(dtio->event, NULL) != 0) {
log_err("dnstap io: out of memory (adding event)");
dtio->event_added = 0;
dtio->event_added_is_write = 0;
/* close output and start reattempts to open it */
dtio_close_output(dtio);
return 0;
}
dtio->event_added = 1;
dtio->event_added_is_write = 0;
return 1;
}
/** add the output file descriptor event for listening, read and write */
static int dtio_add_output_event_write(struct dt_io_thread* dtio)
{
if(!dtio->event)
return 0;
if(dtio->event_added && dtio->event_added_is_write)
return 1;
/* we have to (re-)register the event */
if(dtio->event_added)
ub_event_del(dtio->event);
ub_event_add_bits(dtio->event, UB_EV_WRITE);
if(ub_event_add(dtio->event, NULL) != 0) {
log_err("dnstap io: out of memory (adding event)");
dtio->event_added = 0;
dtio->event_added_is_write = 0;
/* close output and start reattempts to open it */
dtio_close_output(dtio);
return 0;
}
dtio->event_added = 1;
dtio->event_added_is_write = 1;
return 1;
}
/** put the dtio thread to sleep */
static void dtio_sleep(struct dt_io_thread* dtio)
{
/* unregister the event polling for write, because there is
* nothing to be written */
(void)dtio_add_output_event_read(dtio);
}
#ifdef HAVE_SSL
/** enable the brief read condition */
static int dtio_enable_brief_read(struct dt_io_thread* dtio)
{
dtio->ssl_brief_read = 1;
if(dtio->stop_flush_event) {
ub_event_del(dtio->stop_flush_event);
ub_event_del_bits(dtio->stop_flush_event, UB_EV_WRITE);
if(ub_event_add(dtio->stop_flush_event, NULL) != 0) {
log_err("dnstap io, stop flush, could not ub_event_add");
return 0;
}
return 1;
}
return dtio_add_output_event_read(dtio);
}
#endif /* HAVE_SSL */
#ifdef HAVE_SSL
/** disable the brief read condition */
static int dtio_disable_brief_read(struct dt_io_thread* dtio)
{
dtio->ssl_brief_read = 0;
if(dtio->stop_flush_event) {
ub_event_del(dtio->stop_flush_event);
ub_event_add_bits(dtio->stop_flush_event, UB_EV_WRITE);
if(ub_event_add(dtio->stop_flush_event, NULL) != 0) {
log_err("dnstap io, stop flush, could not ub_event_add");
return 0;
}
return 1;
}
return dtio_add_output_event_write(dtio);
}
#endif /* HAVE_SSL */
#ifdef HAVE_SSL
/** enable the brief write condition */
static int dtio_enable_brief_write(struct dt_io_thread* dtio)
{
dtio->ssl_brief_write = 1;
return dtio_add_output_event_write(dtio);
}
#endif /* HAVE_SSL */
#ifdef HAVE_SSL
/** disable the brief write condition */
static int dtio_disable_brief_write(struct dt_io_thread* dtio)
{
dtio->ssl_brief_write = 0;
return dtio_add_output_event_read(dtio);
}
#endif /* HAVE_SSL */
#ifdef HAVE_SSL
/** check peer verification after ssl handshake connection, false if closed*/
static int dtio_ssl_check_peer(struct dt_io_thread* dtio)
{
if((SSL_get_verify_mode(dtio->ssl)&SSL_VERIFY_PEER)) {
/* verification */
if(SSL_get_verify_result(dtio->ssl) == X509_V_OK) {
X509* x = SSL_get_peer_certificate(dtio->ssl);
if(!x) {
verbose(VERB_ALGO, "dnstap io, %s, SSL "
"connection failed no certificate",
dtio->ip_str);
return 0;
}
log_cert(VERB_ALGO, "dnstap io, peer certificate",
x);
#ifdef HAVE_SSL_GET0_PEERNAME
if(SSL_get0_peername(dtio->ssl)) {
verbose(VERB_ALGO, "dnstap io, %s, SSL "
"connection to %s authenticated",
dtio->ip_str,
SSL_get0_peername(dtio->ssl));
} else {
#endif
verbose(VERB_ALGO, "dnstap io, %s, SSL "
"connection authenticated",
dtio->ip_str);
#ifdef HAVE_SSL_GET0_PEERNAME
}
#endif
X509_free(x);
} else {
X509* x = SSL_get_peer_certificate(dtio->ssl);
if(x) {
log_cert(VERB_ALGO, "dnstap io, peer "
"certificate", x);
X509_free(x);
}
verbose(VERB_ALGO, "dnstap io, %s, SSL connection "
"failed: failed to authenticate",
dtio->ip_str);
return 0;
}
} else {
/* unauthenticated, the verify peer flag was not set
* in ssl when the ssl object was created from ssl_ctx */
verbose(VERB_ALGO, "dnstap io, %s, SSL connection",
dtio->ip_str);
}
return 1;
}
#endif /* HAVE_SSL */
#ifdef HAVE_SSL
/** perform ssl handshake, returns 1 if okay, 0 to stop */
static int dtio_ssl_handshake(struct dt_io_thread* dtio,
struct stop_flush_info* info)
{
int r;
if(dtio->ssl_brief_read) {
/* assume the brief read condition is satisfied,
* if we need more or again, we can set it again */
if(!dtio_disable_brief_read(dtio)) {
if(info) dtio_stop_flush_exit(info);
return 0;
}
}
if(dtio->ssl_handshake_done)
return 1;
ERR_clear_error();
r = SSL_do_handshake(dtio->ssl);
if(r != 1) {
int want = SSL_get_error(dtio->ssl, r);
if(want == SSL_ERROR_WANT_READ) {
/* we want to read on the connection */
if(!dtio_enable_brief_read(dtio)) {
if(info) dtio_stop_flush_exit(info);
return 0;
}
return 0;
} else if(want == SSL_ERROR_WANT_WRITE) {
/* we want to write on the connection */
return 0;
} else if(r == 0) {
/* closed */
if(info) dtio_stop_flush_exit(info);
dtio_del_output_event(dtio);
dtio_reconnect_slow(dtio, DTIO_RECONNECT_TIMEOUT_SLOW);
dtio_close_output(dtio);
return 0;
} else if(want == SSL_ERROR_SYSCALL) {
/* SYSCALL and errno==0 means closed uncleanly */
int silent = 0;
#ifdef EPIPE
if(errno == EPIPE && verbosity < 2)
silent = 1; /* silence 'broken pipe' */
#endif
#ifdef ECONNRESET
if(errno == ECONNRESET && verbosity < 2)
silent = 1; /* silence reset by peer */
#endif
if(errno == 0)
silent = 1;
if(!silent)
log_err("dnstap io, SSL_handshake syscall: %s",
strerror(errno));
/* closed */
if(info) dtio_stop_flush_exit(info);
dtio_del_output_event(dtio);
dtio_reconnect_slow(dtio, DTIO_RECONNECT_TIMEOUT_SLOW);
dtio_close_output(dtio);
return 0;
} else {
unsigned long err = ERR_get_error();
if(!squelch_err_ssl_handshake(err)) {
- log_crypto_err_code("dnstap io, ssl handshake failed",
- err);
+ log_crypto_err_io_code("dnstap io, ssl handshake failed",
+ want, err);
verbose(VERB_OPS, "dnstap io, ssl handshake failed "
"from %s", dtio->ip_str);
}
/* closed */
if(info) dtio_stop_flush_exit(info);
dtio_del_output_event(dtio);
dtio_reconnect_slow(dtio, DTIO_RECONNECT_TIMEOUT_SLOW);
dtio_close_output(dtio);
return 0;
}
}
/* check peer verification */
dtio->ssl_handshake_done = 1;
if(!dtio_ssl_check_peer(dtio)) {
/* closed */
if(info) dtio_stop_flush_exit(info);
dtio_del_output_event(dtio);
dtio_reconnect_slow(dtio, DTIO_RECONNECT_TIMEOUT_SLOW);
dtio_close_output(dtio);
return 0;
}
return 1;
}
#endif /* HAVE_SSL */
/** callback for the dnstap events, to write to the output */
void dtio_output_cb(int ATTR_UNUSED(fd), short bits, void* arg)
{
struct dt_io_thread* dtio = (struct dt_io_thread*)arg;
int i;
if(dtio->check_nb_connect) {
int connect_err = dtio_check_nb_connect(dtio);
if(connect_err == -1) {
/* close the channel */
dtio_del_output_event(dtio);
dtio_close_output(dtio);
return;
} else if(connect_err == 0) {
/* try again later */
return;
}
/* nonblocking connect check passed, continue */
}
#ifdef HAVE_SSL
if(dtio->ssl &&
(!dtio->ssl_handshake_done || dtio->ssl_brief_read)) {
if(!dtio_ssl_handshake(dtio, NULL))
return;
}
#endif
if((bits&UB_EV_READ || dtio->ssl_brief_write)) {
if(dtio->ssl_brief_write)
(void)dtio_disable_brief_write(dtio);
if(dtio->ready_frame_sent && !dtio->accept_frame_received) {
if(dtio_read_accept_frame(dtio) <= 0)
return;
} else if(!dtio_check_close(dtio))
return;
}
/* loop to process a number of messages. This improves throughput,
* because selecting on write-event if not needed for busy messages
* (dnstap log) generation and if they need to all be written back.
* The write event is usually not blocked up. But not forever,
* because the event loop needs to stay responsive for other events.
* If there are no (more) messages, or if the output buffers get
* full, it returns out of the loop. */
for(i=0; i<DTIO_MESSAGES_PER_CALLBACK; i++) {
/* see if there are messages that need writing */
if(!dtio->cur_msg) {
if(!dtio_find_msg(dtio)) {
if(i == 0) {
/* no messages on the first iteration,
* the queues are all empty */
dtio_sleep(dtio);
}
return; /* nothing to do */
}
}
/* write it */
if(dtio->cur_msg_done < dtio->cur_msg_len) {
if(!dtio_write_more(dtio))
return;
}
/* done with the current message */
dtio_cur_msg_free(dtio);
/* If this is a bidirectional stream the first message will be
* the READY control frame. We can only continue writing after
* receiving an ACCEPT control frame. */
if(dtio->is_bidirectional && !dtio->ready_frame_sent) {
dtio->ready_frame_sent = 1;
(void)dtio_add_output_event_read(dtio);
break;
}
}
}
/** callback for the dnstap commandpipe, to stop the dnstap IO */
void dtio_cmd_cb(int fd, short ATTR_UNUSED(bits), void* arg)
{
struct dt_io_thread* dtio = (struct dt_io_thread*)arg;
uint8_t cmd;
ssize_t r;
if(dtio->want_to_exit)
return;
r = read(fd, &cmd, sizeof(cmd));
if(r == -1) {
#ifndef USE_WINSOCK
if(errno == EINTR || errno == EAGAIN)
return; /* ignore this */
#else
if(WSAGetLastError() == WSAEINPROGRESS)
return;
if(WSAGetLastError() == WSAEWOULDBLOCK)
return;
#endif
log_err("dnstap io: failed to read: %s", sock_strerror(errno));
/* and then fall through to quit the thread */
} else if(r == 0) {
verbose(VERB_ALGO, "dnstap io: cmd channel closed");
} else if(r == 1 && cmd == DTIO_COMMAND_STOP) {
verbose(VERB_ALGO, "dnstap io: cmd channel cmd quit");
} else if(r == 1 && cmd == DTIO_COMMAND_WAKEUP) {
verbose(VERB_ALGO, "dnstap io: cmd channel cmd wakeup");
if(dtio->is_bidirectional && !dtio->accept_frame_received) {
verbose(VERB_ALGO, "dnstap io: cmd wakeup ignored, "
"waiting for ACCEPT control frame");
return;
}
/* reregister event */
if(!dtio_add_output_event_write(dtio))
return;
return;
} else if(r == 1) {
verbose(VERB_ALGO, "dnstap io: cmd channel unknown command");
}
dtio->want_to_exit = 1;
if(ub_event_base_loopexit((struct ub_event_base*)dtio->event_base)
!= 0) {
log_err("dnstap io: could not loopexit");
}
}
#ifndef THREADS_DISABLED
/** setup the event base for the dnstap io thread */
static void dtio_setup_base(struct dt_io_thread* dtio, time_t* secs,
struct timeval* now)
{
memset(now, 0, sizeof(*now));
dtio->event_base = ub_default_event_base(0, secs, now);
if(!dtio->event_base) {
fatal_exit("dnstap io: could not create event_base");
}
}
#endif /* THREADS_DISABLED */
/** setup the cmd event for dnstap io */
static void dtio_setup_cmd(struct dt_io_thread* dtio)
{
struct ub_event* cmdev;
fd_set_nonblock(dtio->commandpipe[0]);
cmdev = ub_event_new(dtio->event_base, dtio->commandpipe[0],
UB_EV_READ | UB_EV_PERSIST, &dtio_cmd_cb, dtio);
if(!cmdev) {
fatal_exit("dnstap io: out of memory");
}
dtio->command_event = cmdev;
if(ub_event_add(cmdev, NULL) != 0) {
fatal_exit("dnstap io: out of memory (adding event)");
}
}
/** setup the reconnect event for dnstap io */
static void dtio_setup_reconnect(struct dt_io_thread* dtio)
{
dtio_reconnect_clear(dtio);
dtio->reconnect_timer = ub_event_new(dtio->event_base, -1,
UB_EV_TIMEOUT, &dtio_reconnect_timeout_cb, dtio);
if(!dtio->reconnect_timer) {
fatal_exit("dnstap io: out of memory");
}
}
/**
* structure to keep track of information during stop flush
*/
struct stop_flush_info {
/** the event base during stop flush */
struct ub_event_base* base;
/** did we already want to exit this stop-flush event base */
int want_to_exit_flush;
/** has the timer fired */
int timer_done;
/** the dtio */
struct dt_io_thread* dtio;
/** the stop control frame */
void* stop_frame;
/** length of the stop frame */
size_t stop_frame_len;
/** how much we have done of the stop frame */
size_t stop_frame_done;
};
/** exit the stop flush base */
static void dtio_stop_flush_exit(struct stop_flush_info* info)
{
if(info->want_to_exit_flush)
return;
info->want_to_exit_flush = 1;
if(ub_event_base_loopexit(info->base) != 0) {
log_err("dnstap io: could not loopexit");
}
}
/** send the stop control,
* return true if completed the frame. */
static int dtio_control_stop_send(struct stop_flush_info* info)
{
struct dt_io_thread* dtio = info->dtio;
int r;
if(info->stop_frame_done >= info->stop_frame_len)
return 1;
r = dtio_write_buf(dtio, ((uint8_t*)info->stop_frame) +
info->stop_frame_done, info->stop_frame_len -
info->stop_frame_done);
if(r == -1) {
verbose(VERB_ALGO, "dnstap io: stop flush: output closed");
dtio_stop_flush_exit(info);
return 0;
}
if(r == 0) {
/* try again later, or timeout */
return 0;
}
info->stop_frame_done += r;
if(info->stop_frame_done < info->stop_frame_len)
return 0; /* not done yet */
return 1;
}
void dtio_stop_timer_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(bits),
void* arg)
{
struct stop_flush_info* info = (struct stop_flush_info*)arg;
if(info->want_to_exit_flush)
return;
verbose(VERB_ALGO, "dnstap io: stop flush timer expired, stop flush");
info->timer_done = 1;
dtio_stop_flush_exit(info);
}
void dtio_stop_ev_cb(int ATTR_UNUSED(fd), short bits, void* arg)
{
struct stop_flush_info* info = (struct stop_flush_info*)arg;
struct dt_io_thread* dtio = info->dtio;
if(info->want_to_exit_flush)
return;
if(dtio->check_nb_connect) {
/* we don't start the stop_flush if connect still
* in progress, but the check code is here, just in case */
int connect_err = dtio_check_nb_connect(dtio);
if(connect_err == -1) {
/* close the channel, exit the stop flush */
dtio_stop_flush_exit(info);
dtio_del_output_event(dtio);
dtio_close_output(dtio);
return;
} else if(connect_err == 0) {
/* try again later */
return;
}
/* nonblocking connect check passed, continue */
}
#ifdef HAVE_SSL
if(dtio->ssl &&
(!dtio->ssl_handshake_done || dtio->ssl_brief_read)) {
if(!dtio_ssl_handshake(dtio, info))
return;
}
#endif
if((bits&UB_EV_READ)) {
if(!dtio_check_close(dtio)) {
if(dtio->fd == -1) {
verbose(VERB_ALGO, "dnstap io: "
"stop flush: output closed");
dtio_stop_flush_exit(info);
}
return;
}
}
/* write remainder of last frame */
if(dtio->cur_msg) {
if(dtio->cur_msg_done < dtio->cur_msg_len) {
if(!dtio_write_more(dtio)) {
if(dtio->fd == -1) {
verbose(VERB_ALGO, "dnstap io: "
"stop flush: output closed");
dtio_stop_flush_exit(info);
}
return;
}
}
verbose(VERB_ALGO, "dnstap io: stop flush completed "
"last frame");
dtio_cur_msg_free(dtio);
}
/* write stop frame */
if(info->stop_frame_done < info->stop_frame_len) {
if(!dtio_control_stop_send(info))
return;
verbose(VERB_ALGO, "dnstap io: stop flush completed "
"stop control frame");
}
/* when last frame and stop frame are sent, exit */
dtio_stop_flush_exit(info);
}
/** flush at end, last packet and stop control */
static void dtio_control_stop_flush(struct dt_io_thread* dtio)
{
/* briefly attempt to flush the previous packet to the output,
* this could be a partial packet, or even the start control frame */
time_t secs = 0;
struct timeval now;
struct stop_flush_info info;
struct timeval tv;
struct ub_event* timer, *stopev;
if(dtio->fd == -1 || dtio->check_nb_connect) {
/* no connection or we have just connected, so nothing is
* sent yet, so nothing to stop or flush */
return;
}
if(dtio->ssl && !dtio->ssl_handshake_done) {
/* no SSL connection has been established yet */
return;
}
memset(&info, 0, sizeof(info));
memset(&now, 0, sizeof(now));
info.dtio = dtio;
info.base = ub_default_event_base(0, &secs, &now);
if(!info.base) {
log_err("dnstap io: malloc failure");
return;
}
timer = ub_event_new(info.base, -1, UB_EV_TIMEOUT,
&dtio_stop_timer_cb, &info);
if(!timer) {
log_err("dnstap io: malloc failure");
ub_event_base_free(info.base);
return;
}
memset(&tv, 0, sizeof(tv));
tv.tv_sec = 2;
if(ub_timer_add(timer, info.base, &dtio_stop_timer_cb, &info,
&tv) != 0) {
log_err("dnstap io: cannot event_timer_add");
ub_event_free(timer);
ub_event_base_free(info.base);
return;
}
stopev = ub_event_new(info.base, dtio->fd, UB_EV_READ |
UB_EV_WRITE | UB_EV_PERSIST, &dtio_stop_ev_cb, &info);
if(!stopev) {
log_err("dnstap io: malloc failure");
ub_timer_del(timer);
ub_event_free(timer);
ub_event_base_free(info.base);
return;
}
if(ub_event_add(stopev, NULL) != 0) {
log_err("dnstap io: cannot event_add");
ub_event_free(stopev);
ub_timer_del(timer);
ub_event_free(timer);
ub_event_base_free(info.base);
return;
}
info.stop_frame = fstrm_create_control_frame_stop(
&info.stop_frame_len);
if(!info.stop_frame) {
log_err("dnstap io: malloc failure");
ub_event_del(stopev);
ub_event_free(stopev);
ub_timer_del(timer);
ub_event_free(timer);
ub_event_base_free(info.base);
return;
}
dtio->stop_flush_event = stopev;
/* wait briefly, or until finished */
verbose(VERB_ALGO, "dnstap io: stop flush started");
if(ub_event_base_dispatch(info.base) < 0) {
log_err("dnstap io: dispatch flush failed, errno is %s",
strerror(errno));
}
verbose(VERB_ALGO, "dnstap io: stop flush ended");
free(info.stop_frame);
dtio->stop_flush_event = NULL;
ub_event_del(stopev);
ub_event_free(stopev);
ub_timer_del(timer);
ub_event_free(timer);
ub_event_base_free(info.base);
}
/** perform desetup and free stuff when the dnstap io thread exits */
static void dtio_desetup(struct dt_io_thread* dtio)
{
dtio_control_stop_flush(dtio);
dtio_del_output_event(dtio);
dtio_close_output(dtio);
ub_event_del(dtio->command_event);
ub_event_free(dtio->command_event);
#ifndef USE_WINSOCK
close(dtio->commandpipe[0]);
#else
_close(dtio->commandpipe[0]);
#endif
dtio->commandpipe[0] = -1;
dtio_reconnect_del(dtio);
ub_event_free(dtio->reconnect_timer);
dtio_cur_msg_free(dtio);
#ifndef THREADS_DISABLED
ub_event_base_free(dtio->event_base);
#endif
}
/** setup a start control message */
static int dtio_control_start_send(struct dt_io_thread* dtio)
{
log_assert(dtio->cur_msg == NULL && dtio->cur_msg_len == 0);
dtio->cur_msg = fstrm_create_control_frame_start(DNSTAP_CONTENT_TYPE,
&dtio->cur_msg_len);
if(!dtio->cur_msg) {
return 0;
}
/* setup to send the control message */
/* set that the buffer needs to be sent, but the length
* of that buffer is already written, that way the buffer can
* start with 0 length and then the length of the control frame
* in it */
dtio->cur_msg_done = 0;
dtio->cur_msg_len_done = 4;
return 1;
}
/** setup a ready control message */
static int dtio_control_ready_send(struct dt_io_thread* dtio)
{
log_assert(dtio->cur_msg == NULL && dtio->cur_msg_len == 0);
dtio->cur_msg = fstrm_create_control_frame_ready(DNSTAP_CONTENT_TYPE,
&dtio->cur_msg_len);
if(!dtio->cur_msg) {
return 0;
}
/* setup to send the control message */
/* set that the buffer needs to be sent, but the length
* of that buffer is already written, that way the buffer can
* start with 0 length and then the length of the control frame
* in it */
dtio->cur_msg_done = 0;
dtio->cur_msg_len_done = 4;
return 1;
}
/** open the output file descriptor for af_local */
static int dtio_open_output_local(struct dt_io_thread* dtio)
{
#ifdef HAVE_SYS_UN_H
struct sockaddr_un s;
dtio->fd = socket(AF_LOCAL, SOCK_STREAM, 0);
if(dtio->fd == -1) {
log_err("dnstap io: failed to create socket: %s",
sock_strerror(errno));
return 0;
}
memset(&s, 0, sizeof(s));
#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
/* this member exists on BSDs, not Linux */
s.sun_len = (unsigned)sizeof(s);
#endif
s.sun_family = AF_LOCAL;
/* length is 92-108, 104 on FreeBSD */
(void)strlcpy(s.sun_path, dtio->socket_path, sizeof(s.sun_path));
fd_set_nonblock(dtio->fd);
if(connect(dtio->fd, (struct sockaddr*)&s, (socklen_t)sizeof(s))
== -1) {
char* to = dtio->socket_path;
if(dtio->reconnect_timeout > DTIO_RECONNECT_TIMEOUT_MIN &&
verbosity < 4) {
dtio_close_fd(dtio);
return 0; /* no log retries on low verbosity */
}
log_err("dnstap io: failed to connect to \"%s\": %s",
to, sock_strerror(errno));
dtio_close_fd(dtio);
return 0;
}
return 1;
#else
log_err("cannot create af_local socket");
return 0;
#endif /* HAVE_SYS_UN_H */
}
/** open the output file descriptor for af_inet and af_inet6 */
static int dtio_open_output_tcp(struct dt_io_thread* dtio)
{
struct sockaddr_storage addr;
socklen_t addrlen;
memset(&addr, 0, sizeof(addr));
addrlen = (socklen_t)sizeof(addr);
if(!extstrtoaddr(dtio->ip_str, &addr, &addrlen, UNBOUND_DNS_PORT)) {
log_err("could not parse IP '%s'", dtio->ip_str);
return 0;
}
dtio->fd = socket(addr.ss_family, SOCK_STREAM, 0);
if(dtio->fd == -1) {
log_err("can't create socket: %s", sock_strerror(errno));
return 0;
}
fd_set_nonblock(dtio->fd);
if(connect(dtio->fd, (struct sockaddr*)&addr, addrlen) == -1) {
if(errno == EINPROGRESS)
return 1; /* wait until connect done*/
if(dtio->reconnect_timeout > DTIO_RECONNECT_TIMEOUT_MIN &&
verbosity < 4) {
dtio_close_fd(dtio);
return 0; /* no log retries on low verbosity */
}
#ifndef USE_WINSOCK
if(tcp_connect_errno_needs_log(
(struct sockaddr *)&addr, addrlen)) {
log_err("dnstap io: failed to connect to %s: %s",
dtio->ip_str, strerror(errno));
}
#else
if(WSAGetLastError() == WSAEINPROGRESS ||
WSAGetLastError() == WSAEWOULDBLOCK)
return 1; /* wait until connect done*/
if(tcp_connect_errno_needs_log(
(struct sockaddr *)&addr, addrlen)) {
log_err("dnstap io: failed to connect to %s: %s",
dtio->ip_str, wsa_strerror(WSAGetLastError()));
}
#endif
dtio_close_fd(dtio);
return 0;
}
return 1;
}
/** setup the SSL structure for new connection */
static int dtio_setup_ssl(struct dt_io_thread* dtio)
{
dtio->ssl = outgoing_ssl_fd(dtio->ssl_ctx, dtio->fd);
if(!dtio->ssl) return 0;
dtio->ssl_handshake_done = 0;
dtio->ssl_brief_read = 0;
if(!set_auth_name_on_ssl(dtio->ssl, dtio->tls_server_name,
dtio->tls_use_sni)) {
return 0;
}
return 1;
}
/** open the output file descriptor */
static void dtio_open_output(struct dt_io_thread* dtio)
{
struct ub_event* ev;
if(dtio->upstream_is_unix) {
if(!dtio_open_output_local(dtio)) {
dtio_reconnect_enable(dtio);
return;
}
} else if(dtio->upstream_is_tcp || dtio->upstream_is_tls) {
if(!dtio_open_output_tcp(dtio)) {
dtio_reconnect_enable(dtio);
return;
}
if(dtio->upstream_is_tls) {
if(!dtio_setup_ssl(dtio)) {
dtio_close_fd(dtio);
dtio_reconnect_enable(dtio);
return;
}
}
}
dtio->check_nb_connect = 1;
/* the EV_READ is to read ACCEPT control messages, and catch channel
* close. EV_WRITE is to write packets */
ev = ub_event_new(dtio->event_base, dtio->fd,
UB_EV_READ | UB_EV_WRITE | UB_EV_PERSIST, &dtio_output_cb,
dtio);
if(!ev) {
log_err("dnstap io: out of memory");
if(dtio->ssl) {
#ifdef HAVE_SSL
SSL_free(dtio->ssl);
dtio->ssl = NULL;
#endif
}
dtio_close_fd(dtio);
dtio_reconnect_enable(dtio);
return;
}
dtio->event = ev;
/* setup protocol control message to start */
if((!dtio->is_bidirectional && !dtio_control_start_send(dtio)) ||
(dtio->is_bidirectional && !dtio_control_ready_send(dtio)) ) {
log_err("dnstap io: out of memory");
ub_event_free(dtio->event);
dtio->event = NULL;
if(dtio->ssl) {
#ifdef HAVE_SSL
SSL_free(dtio->ssl);
dtio->ssl = NULL;
#endif
}
dtio_close_fd(dtio);
dtio_reconnect_enable(dtio);
return;
}
}
/** perform the setup of the writer thread on the established event_base */
static void dtio_setup_on_base(struct dt_io_thread* dtio)
{
dtio_setup_cmd(dtio);
dtio_setup_reconnect(dtio);
dtio_open_output(dtio);
if(!dtio_add_output_event_write(dtio))
return;
}
#ifndef THREADS_DISABLED
/** the IO thread function for the DNSTAP IO */
static void* dnstap_io(void* arg)
{
struct dt_io_thread* dtio = (struct dt_io_thread*)arg;
time_t secs = 0;
struct timeval now;
log_thread_set(&dtio->threadnum);
/* setup */
verbose(VERB_ALGO, "start dnstap io thread");
dtio_setup_base(dtio, &secs, &now);
dtio_setup_on_base(dtio);
/* run */
if(ub_event_base_dispatch(dtio->event_base) < 0) {
log_err("dnstap io: dispatch failed, errno is %s",
strerror(errno));
}
/* cleanup */
verbose(VERB_ALGO, "stop dnstap io thread");
dtio_desetup(dtio);
return NULL;
}
#endif /* THREADS_DISABLED */
int dt_io_thread_start(struct dt_io_thread* dtio, void* event_base_nothr,
int numworkers)
{
/* set up the thread, can fail */
#ifndef USE_WINSOCK
if(pipe(dtio->commandpipe) == -1) {
log_err("failed to create pipe: %s", strerror(errno));
return 0;
}
#else
if(_pipe(dtio->commandpipe, 4096, _O_BINARY) == -1) {
log_err("failed to create _pipe: %s",
wsa_strerror(WSAGetLastError()));
return 0;
}
#endif
/* start the thread */
dtio->threadnum = numworkers+1;
dtio->started = 1;
#ifndef THREADS_DISABLED
ub_thread_create(&dtio->tid, dnstap_io, dtio);
(void)event_base_nothr;
#else
dtio->event_base = event_base_nothr;
dtio_setup_on_base(dtio);
#endif
return 1;
}
void dt_io_thread_stop(struct dt_io_thread* dtio)
{
#ifndef THREADS_DISABLED
uint8_t cmd = DTIO_COMMAND_STOP;
#endif
if(!dtio) return;
if(!dtio->started) return;
verbose(VERB_ALGO, "dnstap io: send stop cmd");
#ifndef THREADS_DISABLED
while(1) {
ssize_t r = write(dtio->commandpipe[1], &cmd, sizeof(cmd));
if(r == -1) {
#ifndef USE_WINSOCK
if(errno == EINTR || errno == EAGAIN)
continue;
#else
if(WSAGetLastError() == WSAEINPROGRESS)
continue;
if(WSAGetLastError() == WSAEWOULDBLOCK)
continue;
#endif
log_err("dnstap io stop: write: %s",
sock_strerror(errno));
break;
}
break;
}
dtio->started = 0;
#endif /* THREADS_DISABLED */
#ifndef USE_WINSOCK
close(dtio->commandpipe[1]);
#else
_close(dtio->commandpipe[1]);
#endif
dtio->commandpipe[1] = -1;
#ifndef THREADS_DISABLED
ub_thread_join(dtio->tid);
#else
dtio->want_to_exit = 1;
dtio_desetup(dtio);
#endif
}
diff --git a/contrib/unbound/dnstap/unbound-dnstap-socket.c b/contrib/unbound/dnstap/unbound-dnstap-socket.c
index d172a6744a07..04fda74b80e1 100644
--- a/contrib/unbound/dnstap/unbound-dnstap-socket.c
+++ b/contrib/unbound/dnstap/unbound-dnstap-socket.c
@@ -1,1572 +1,1574 @@
/*
* dnstap/unbound-dnstap-socket.c - debug program that listens for DNSTAP logs.
*
* Copyright (c) 2020, 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This program listens on a DNSTAP socket for logged messages.
*/
#include "config.h"
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <ctype.h>
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif
#include <openssl/ssl.h>
#include <openssl/rand.h>
#include <openssl/err.h>
#include "dnstap/dtstream.h"
#include "dnstap/dnstap_fstrm.h"
#include "util/log.h"
#include "util/ub_event.h"
#include "util/net_help.h"
#include "services/listen_dnsport.h"
#include "sldns/sbuffer.h"
#include "sldns/wire2str.h"
#include "sldns/pkthdr.h"
#ifdef USE_DNSTAP
#include <protobuf-c/protobuf-c.h>
#include "dnstap/dnstap.pb-c.h"
#endif /* USE_DNSTAP */
#include "util/config_file.h"
/** listen backlog on TCP connections for dnstap logs */
#define LISTEN_BACKLOG 16
/** usage information for streamtcp */
static void usage(char* argv[])
{
printf("usage: %s [options]\n", argv[0]);
printf(" Listen to dnstap messages\n");
printf("stdout has dnstap log, stderr has verbose server log\n");
printf("-u <socketpath> listen to unix socket with this file name\n");
printf("-s <serverip[@port]> listen for TCP on the IP and port\n");
printf("-t <serverip[@port]> listen for TLS on IP and port\n");
printf("-x <server.key> server key file for TLS service\n");
printf("-y <server.pem> server cert file for TLS service\n");
printf("-z <verify.pem> cert file to verify client connections\n");
printf("-l long format for DNS printout\n");
printf("-v more verbose log output\n");
printf("-h this help text\n");
exit(1);
}
/** long format option, for multiline printout per message */
static int longformat = 0;
struct tap_socket_list;
struct tap_socket;
/** main tap callback data */
struct main_tap_data {
/** the event base (to loopexit) */
struct ub_event_base* base;
/** the list of accept sockets */
struct tap_socket_list* acceptlist;
};
/** tap callback variables */
struct tap_data {
/** the fd */
int fd;
/** the ub event */
struct ub_event* ev;
/** the SSL for TLS streams */
SSL* ssl;
/** is the ssl handshake done */
int ssl_handshake_done;
/** we are briefly waiting to write (in the struct event) */
int ssl_brief_write;
/** string that identifies the socket (or NULL), like IP address */
char* id;
/** have we read the length, and how many bytes of it */
int len_done;
/** have we read the data, and how many bytes of it */
size_t data_done;
/** are we reading a control frame */
int control_frame;
/** are we bi-directional (if false, uni-directional) */
int is_bidirectional;
/** data of the frame */
uint8_t* frame;
/** length of this frame */
size_t len;
};
/** list of sockets */
struct tap_socket_list {
/** next in list */
struct tap_socket_list* next;
/** the socket */
struct tap_socket* s;
};
/** tap socket */
struct tap_socket {
/** fd of socket */
int fd;
/** the event for it */
struct ub_event *ev;
/** has the event been added */
int ev_added;
/** the callback, for the event, ev_cb(fd, bits, arg) */
void (*ev_cb)(int, short, void*);
/** data element, (arg for the tap_socket struct) */
void* data;
/** socketpath, if this is an AF_LOCAL socket */
char* socketpath;
/** IP, if this is a TCP socket */
char* ip;
/** for a TLS socket, the tls context */
SSL_CTX* sslctx;
};
/** del the tap event */
static void tap_socket_delev(struct tap_socket* s)
{
if(!s) return;
if(!s->ev) return;
if(!s->ev_added) return;
ub_event_del(s->ev);
s->ev_added = 0;
}
/** close the tap socket */
static void tap_socket_close(struct tap_socket* s)
{
if(!s) return;
if(s->fd == -1) return;
close(s->fd);
s->fd = -1;
}
/** delete tap socket */
static void tap_socket_delete(struct tap_socket* s)
{
if(!s) return;
#ifdef HAVE_SSL
SSL_CTX_free(s->sslctx);
#endif
ub_event_free(s->ev);
free(s->socketpath);
free(s->ip);
free(s);
}
/** create new socket (unconnected, not base-added), or NULL malloc fail */
static struct tap_socket* tap_socket_new_local(char* socketpath,
void (*ev_cb)(int, short, void*), void* data)
{
struct tap_socket* s = calloc(1, sizeof(*s));
if(!s) {
log_err("malloc failure");
return NULL;
}
s->socketpath = strdup(socketpath);
if(!s->socketpath) {
free(s);
log_err("malloc failure");
return NULL;
}
s->fd = -1;
s->ev_cb = ev_cb;
s->data = data;
return s;
}
/** create new socket (unconnected, not base-added), or NULL malloc fail */
static struct tap_socket* tap_socket_new_tcpaccept(char* ip,
void (*ev_cb)(int, short, void*), void* data)
{
struct tap_socket* s = calloc(1, sizeof(*s));
if(!s) {
log_err("malloc failure");
return NULL;
}
s->ip = strdup(ip);
if(!s->ip) {
free(s);
log_err("malloc failure");
return NULL;
}
s->fd = -1;
s->ev_cb = ev_cb;
s->data = data;
return s;
}
/** create new socket (unconnected, not base-added), or NULL malloc fail */
static struct tap_socket* tap_socket_new_tlsaccept(char* ip,
void (*ev_cb)(int, short, void*), void* data, char* server_key,
char* server_cert, char* verifypem)
{
struct tap_socket* s = calloc(1, sizeof(*s));
if(!s) {
log_err("malloc failure");
return NULL;
}
s->ip = strdup(ip);
if(!s->ip) {
free(s);
log_err("malloc failure");
return NULL;
}
s->fd = -1;
s->ev_cb = ev_cb;
s->data = data;
s->sslctx = listen_sslctx_create(server_key, server_cert, verifypem);
if(!s->sslctx) {
log_err("could not create ssl context");
free(s->ip);
free(s);
return NULL;
}
return s;
}
/** setup tcp accept socket on IP string */
static int make_tcp_accept(char* ip)
{
#ifdef SO_REUSEADDR
int on = 1;
#endif
struct sockaddr_storage addr;
socklen_t len;
int s;
memset(&addr, 0, sizeof(addr));
len = (socklen_t)sizeof(addr);
if(!extstrtoaddr(ip, &addr, &len, UNBOUND_DNS_PORT)) {
log_err("could not parse IP '%s'", ip);
return -1;
}
if((s = socket(addr.ss_family, SOCK_STREAM, 0)) == -1) {
log_err("can't create socket: %s", sock_strerror(errno));
return -1;
}
#ifdef SO_REUSEADDR
if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
log_err("setsockopt(.. SO_REUSEADDR ..) failed: %s",
sock_strerror(errno));
sock_close(s);
return -1;
}
#endif /* SO_REUSEADDR */
if(bind(s, (struct sockaddr*)&addr, len) != 0) {
log_err_addr("can't bind socket", sock_strerror(errno),
&addr, len);
sock_close(s);
return -1;
}
if(!fd_set_nonblock(s)) {
sock_close(s);
return -1;
}
if(listen(s, LISTEN_BACKLOG) == -1) {
log_err("can't listen: %s", sock_strerror(errno));
sock_close(s);
return -1;
}
return s;
}
/** setup socket on event base */
static int tap_socket_setup(struct tap_socket* s, struct ub_event_base* base)
{
if(s->socketpath) {
/* AF_LOCAL accept socket */
s->fd = create_local_accept_sock(s->socketpath, NULL, 0);
if(s->fd == -1) {
log_err("could not create local socket");
return 0;
}
} else if(s->ip || s->sslctx) {
/* TCP accept socket */
s->fd = make_tcp_accept(s->ip);
if(s->fd == -1) {
log_err("could not create tcp socket");
return 0;
}
}
s->ev = ub_event_new(base, s->fd, UB_EV_READ | UB_EV_PERSIST,
s->ev_cb, s);
if(!s->ev) {
log_err("could not ub_event_new");
return 0;
}
if(ub_event_add(s->ev, NULL) != 0) {
log_err("could not ub_event_add");
return 0;
}
s->ev_added = 1;
return 1;
}
/** add tap socket to list */
static int tap_socket_list_insert(struct tap_socket_list** liststart,
struct tap_socket* s)
{
struct tap_socket_list* entry = (struct tap_socket_list*)
malloc(sizeof(*entry));
if(!entry)
return 0;
entry->next = *liststart;
entry->s = s;
*liststart = entry;
return 1;
}
/** delete the list */
static void tap_socket_list_delete(struct tap_socket_list* list)
{
struct tap_socket_list* e = list, *next;
while(e) {
next = e->next;
tap_socket_delev(e->s);
tap_socket_close(e->s);
tap_socket_delete(e->s);
free(e);
e = next;
}
}
/** setup accept events */
static int tap_socket_list_addevs(struct tap_socket_list* list,
struct ub_event_base* base)
{
struct tap_socket_list* entry;
for(entry = list; entry; entry = entry->next) {
if(!tap_socket_setup(entry->s, base)) {
log_err("could not setup socket");
return 0;
}
}
return 1;
}
#ifdef USE_DNSTAP
/** log control frame contents */
static void log_control_frame(uint8_t* pkt, size_t len)
{
char* desc;
if(verbosity == 0) return;
desc = fstrm_describe_control(pkt, len);
if(!desc) {
log_err("out of memory");
return;
}
log_info("control frame %s", desc);
free(desc);
}
/** convert mtype to string */
static const char* mtype_to_str(enum _Dnstap__Message__Type mtype)
{
switch(mtype) {
case DNSTAP__MESSAGE__TYPE__AUTH_QUERY:
return "AUTH_QUERY";
case DNSTAP__MESSAGE__TYPE__AUTH_RESPONSE:
return "AUTH_RESPONSE";
case DNSTAP__MESSAGE__TYPE__RESOLVER_QUERY:
return "RESOLVER_QUERY";
case DNSTAP__MESSAGE__TYPE__RESOLVER_RESPONSE:
return "RESOLVER_RESPONSE";
case DNSTAP__MESSAGE__TYPE__CLIENT_QUERY:
return "CLIENT_QUERY";
case DNSTAP__MESSAGE__TYPE__CLIENT_RESPONSE:
return "CLIENT_RESPONSE";
case DNSTAP__MESSAGE__TYPE__FORWARDER_QUERY:
return "FORWARDER_QUERY";
case DNSTAP__MESSAGE__TYPE__FORWARDER_RESPONSE:
return "FORWARDER_RESPONSE";
case DNSTAP__MESSAGE__TYPE__STUB_QUERY:
return "STUB_QUERY";
case DNSTAP__MESSAGE__TYPE__STUB_RESPONSE:
return "STUB_RESPONSE";
default: break;
}
return "unknown_message_type";
}
/** convert type address to a string ip4 or ip6, malloced or NULL on fail */
static char* str_of_addr(ProtobufCBinaryData address)
{
char buf[64];
socklen_t len = sizeof(buf);
if(address.len == 4) {
if(inet_ntop(AF_INET, address.data, buf, len)!=0)
return strdup(buf);
} else if(address.len == 16) {
if(inet_ntop(AF_INET6, address.data, buf, len)!=0)
return strdup(buf);
}
return NULL;
}
/** convert message buffer (of dns bytes) to the first qname, type, class,
* malloced or NULL on fail */
static char* q_of_msg(ProtobufCBinaryData message)
{
char buf[300];
/* header, name, type, class minimum to get the query tuple */
if(message.len < 12 + 1 + 4 + 4) return NULL;
if(LDNS_QDCOUNT(message.data) < 1) return NULL;
if(sldns_wire2str_rrquestion_buf(message.data+12, message.len-12,
buf, sizeof(buf)) != 0) {
/* remove trailing newline, tabs to spaces */
/* remove the newline: */
if(buf[0] != 0) buf[strlen(buf)-1]=0;
/* remove first tab (before type) */
if(strrchr(buf, '\t')) *strrchr(buf, '\t')=' ';
/* remove second tab (before class) */
if(strrchr(buf, '\t')) *strrchr(buf, '\t')=' ';
return strdup(buf);
}
return NULL;
}
/** convert possible string or hex data to string. malloced or NULL */
static char* possible_str(ProtobufCBinaryData str)
{
int is_str = 1;
size_t i;
for(i=0; i<str.len; i++) {
if(!isprint((unsigned char)str.data[i]))
is_str = 0;
}
if(is_str) {
char* res = malloc(str.len+1);
if(res) {
memmove(res, str.data, str.len);
res[str.len] = 0;
return res;
}
} else {
const char* hex = "0123456789ABCDEF";
char* res = malloc(str.len*2+1);
if(res) {
for(i=0; i<str.len; i++) {
res[i*2] = hex[(str.data[i]&0xf0)>>4];
res[i*2+1] = hex[str.data[i]&0x0f];
}
res[str.len*2] = 0;
return res;
}
}
return NULL;
}
/** convert timeval to string, malloced or NULL */
static char* tv_to_str(protobuf_c_boolean has_time_sec, uint64_t time_sec,
protobuf_c_boolean has_time_nsec, uint32_t time_nsec)
{
char buf[64], buf2[256];
struct timeval tv;
time_t time_t_sec;
memset(&tv, 0, sizeof(tv));
if(has_time_sec) tv.tv_sec = time_sec;
if(has_time_nsec) tv.tv_usec = time_nsec/1000;
buf[0]=0;
time_t_sec = tv.tv_sec;
(void)ctime_r(&time_t_sec, buf);
snprintf(buf2, sizeof(buf2), "%u.%9.9u %s",
(unsigned)time_sec, (unsigned)time_nsec, buf);
return strdup(buf2);
}
/** log data frame contents */
static void log_data_frame(uint8_t* pkt, size_t len)
{
Dnstap__Dnstap* d = dnstap__dnstap__unpack(NULL, len, pkt);
const char* mtype = NULL;
char* maddr=NULL, *qinf=NULL;
if(!d) {
log_err("could not unpack");
return;
}
if(d->base.descriptor != &dnstap__dnstap__descriptor) {
log_err("wrong base descriptor");
dnstap__dnstap__free_unpacked(d, NULL);
return;
}
if(d->type != DNSTAP__DNSTAP__TYPE__MESSAGE) {
log_err("dnstap type not type_message");
dnstap__dnstap__free_unpacked(d, NULL);
return;
}
if(d->message) {
mtype = mtype_to_str(d->message->type);
if(d->message->has_query_address)
maddr = str_of_addr(d->message->query_address);
else if(d->message->has_response_address)
maddr = str_of_addr(d->message->response_address);
if(d->message->has_query_message)
qinf = q_of_msg(d->message->query_message);
else if(d->message->has_response_message)
qinf = q_of_msg(d->message->response_message);
} else {
mtype = "nomessage";
}
printf("%s%s%s%s%s\n", mtype, (maddr?" ":""), (maddr?maddr:""),
(qinf?" ":""), (qinf?qinf:""));
free(maddr);
free(qinf);
if(longformat) {
char* id=NULL, *vs=NULL;
if(d->has_identity) {
id=possible_str(d->identity);
}
if(d->has_version) {
vs=possible_str(d->version);
}
if(id || vs)
printf("identity: %s%s%s\n", (id?id:""),
(id&&vs?" ":""), (vs?vs:""));
free(id);
free(vs);
if(d->message && d->message->has_query_message &&
d->message->query_message.data) {
char* qmsg = sldns_wire2str_pkt(
d->message->query_message.data,
d->message->query_message.len);
if(qmsg) {
printf("query_message:\n%s", qmsg);
free(qmsg);
}
}
if(d->message && d->message->has_query_time_sec) {
char* qtv = tv_to_str(d->message->has_query_time_sec,
d->message->query_time_sec,
d->message->has_query_time_nsec,
d->message->query_time_nsec);
if(qtv) {
printf("query_time: %s\n", qtv);
free(qtv);
}
}
if(d->message && d->message->has_response_message &&
d->message->response_message.data) {
char* rmsg = sldns_wire2str_pkt(
d->message->response_message.data,
d->message->response_message.len);
if(rmsg) {
printf("response_message:\n%s", rmsg);
free(rmsg);
}
}
if(d->message && d->message->has_response_time_sec) {
char* rtv = tv_to_str(d->message->has_response_time_sec,
d->message->response_time_sec,
d->message->has_response_time_nsec,
d->message->response_time_nsec);
if(rtv) {
printf("response_time: %s\n", rtv);
free(rtv);
}
}
}
fflush(stdout);
dnstap__dnstap__free_unpacked(d, NULL);
}
#endif /* USE_DNSTAP */
/** receive bytes from fd, prints errors if bad,
* returns 0: closed/error, -1: continue, >0 number of bytes */
static ssize_t receive_bytes(struct tap_data* data, int fd, void* buf,
size_t len)
{
ssize_t ret = recv(fd, buf, len, MSG_DONTWAIT);
if(ret == 0) {
/* closed */
if(verbosity) log_info("dnstap client stream closed from %s",
(data->id?data->id:""));
return 0;
} else if(ret == -1) {
/* error */
#ifndef USE_WINSOCK
if(errno == EINTR || errno == EAGAIN)
return -1;
#else /* USE_WINSOCK */
if(WSAGetLastError() == WSAEINPROGRESS)
return -1;
if(WSAGetLastError() == WSAEWOULDBLOCK) {
ub_winsock_tcp_wouldblock(data->ev, UB_EV_READ);
return -1;
}
#endif
log_err("could not recv: %s", sock_strerror(errno));
if(verbosity) log_info("dnstap client stream closed from %s",
(data->id?data->id:""));
return 0;
}
return ret;
}
/* define routine for have_ssl only to avoid unused function warning */
#ifdef HAVE_SSL
/** set to wait briefly for a write event, for one event call */
static void tap_enable_brief_write(struct tap_data* data)
{
ub_event_del(data->ev);
ub_event_del_bits(data->ev, UB_EV_READ);
ub_event_add_bits(data->ev, UB_EV_WRITE);
if(ub_event_add(data->ev, NULL) != 0)
log_err("could not ub_event_add in tap_enable_brief_write");
data->ssl_brief_write = 1;
}
#endif /* HAVE_SSL */
/* define routine for have_ssl only to avoid unused function warning */
#ifdef HAVE_SSL
/** stop the brief wait for a write event. back to reading. */
static void tap_disable_brief_write(struct tap_data* data)
{
ub_event_del(data->ev);
ub_event_del_bits(data->ev, UB_EV_WRITE);
ub_event_add_bits(data->ev, UB_EV_READ);
if(ub_event_add(data->ev, NULL) != 0)
log_err("could not ub_event_add in tap_disable_brief_write");
data->ssl_brief_write = 0;
}
#endif /* HAVE_SSL */
#ifdef HAVE_SSL
/** receive bytes over ssl stream, prints errors if bad,
* returns 0: closed/error, -1: continue, >0 number of bytes */
static ssize_t ssl_read_bytes(struct tap_data* data, void* buf, size_t len)
{
int r;
ERR_clear_error();
r = SSL_read(data->ssl, buf, len);
if(r <= 0) {
int want = SSL_get_error(data->ssl, r);
if(want == SSL_ERROR_ZERO_RETURN) {
/* closed */
if(verbosity) log_info("dnstap client stream closed from %s",
(data->id?data->id:""));
return 0;
} else if(want == SSL_ERROR_WANT_READ) {
/* continue later */
return -1;
} else if(want == SSL_ERROR_WANT_WRITE) {
/* set to briefly write */
tap_enable_brief_write(data);
return -1;
} else if(want == SSL_ERROR_SYSCALL) {
#ifdef ECONNRESET
if(errno == ECONNRESET && verbosity < 2)
return 0; /* silence reset by peer */
#endif
if(errno != 0)
log_err("SSL_read syscall: %s",
strerror(errno));
if(verbosity) log_info("dnstap client stream closed from %s",
(data->id?data->id:""));
return 0;
}
- log_crypto_err("could not SSL_read");
+ log_crypto_err_io("could not SSL_read", want);
if(verbosity) log_info("dnstap client stream closed from %s",
(data->id?data->id:""));
return 0;
}
return r;
}
#endif /* HAVE_SSL */
/** receive bytes on the tap connection, prints errors if bad,
* returns 0: closed/error, -1: continue, >0 number of bytes */
static ssize_t tap_receive(struct tap_data* data, void* buf, size_t len)
{
#ifdef HAVE_SSL
if(data->ssl)
return ssl_read_bytes(data, buf, len);
#endif
return receive_bytes(data, data->fd, buf, len);
}
/** delete the tap structure */
static void tap_data_free(struct tap_data* data)
{
ub_event_del(data->ev);
ub_event_free(data->ev);
#ifdef HAVE_SSL
SSL_free(data->ssl);
#endif
close(data->fd);
free(data->id);
free(data->frame);
free(data);
}
/** reply with ACCEPT control frame to bidirectional client,
* returns 0 on error */
static int reply_with_accept(struct tap_data* data)
{
#ifdef USE_DNSTAP
/* len includes the escape and framelength */
int r;
size_t len = 0;
void* acceptframe = fstrm_create_control_frame_accept(
DNSTAP_CONTENT_TYPE, &len);
if(!acceptframe) {
log_err("out of memory");
return 0;
}
fd_set_block(data->fd);
if(data->ssl) {
if((r=SSL_write(data->ssl, acceptframe, len)) <= 0) {
- if(SSL_get_error(data->ssl, r) == SSL_ERROR_ZERO_RETURN)
+ int r2;
+ if((r2=SSL_get_error(data->ssl, r)) == SSL_ERROR_ZERO_RETURN)
log_err("SSL_write, peer closed connection");
else
- log_err("could not SSL_write");
+ log_crypto_err_io("could not SSL_write", r2);
fd_set_nonblock(data->fd);
free(acceptframe);
return 0;
}
} else {
if(send(data->fd, acceptframe, len, 0) == -1) {
log_err("send failed: %s", sock_strerror(errno));
fd_set_nonblock(data->fd);
free(acceptframe);
return 0;
}
}
if(verbosity) log_info("sent control frame(accept) content-type:(%s)",
DNSTAP_CONTENT_TYPE);
fd_set_nonblock(data->fd);
free(acceptframe);
return 1;
#else
log_err("no dnstap compiled, no reply");
(void)data;
return 0;
#endif
}
/** reply with FINISH control frame to bidirectional client,
* returns 0 on error */
static int reply_with_finish(struct tap_data* data)
{
#ifdef USE_DNSTAP
size_t len = 0;
void* finishframe = fstrm_create_control_frame_finish(&len);
if(!finishframe) {
log_err("out of memory");
return 0;
}
fd_set_block(data->fd);
if(data->ssl) {
int r;
if((r=SSL_write(data->ssl, finishframe, len)) <= 0) {
- if(SSL_get_error(data->ssl, r) == SSL_ERROR_ZERO_RETURN)
+ int r2;
+ if((r2=SSL_get_error(data->ssl, r)) == SSL_ERROR_ZERO_RETURN)
log_err("SSL_write, peer closed connection");
else
- log_err("could not SSL_write");
+ log_crypto_err_io("could not SSL_write", r2);
fd_set_nonblock(data->fd);
free(finishframe);
return 0;
}
} else {
if(send(data->fd, finishframe, len, 0) == -1) {
log_err("send failed: %s", sock_strerror(errno));
fd_set_nonblock(data->fd);
free(finishframe);
return 0;
}
}
if(verbosity) log_info("sent control frame(finish)");
fd_set_nonblock(data->fd);
free(finishframe);
return 1;
#else
log_err("no dnstap compiled, no reply");
(void)data;
return 0;
#endif
}
#ifdef HAVE_SSL
/** check SSL peer certificate, return 0 on fail */
static int tap_check_peer(struct tap_data* data)
{
if((SSL_get_verify_mode(data->ssl)&SSL_VERIFY_PEER)) {
/* verification */
if(SSL_get_verify_result(data->ssl) == X509_V_OK) {
X509* x = SSL_get_peer_certificate(data->ssl);
if(!x) {
if(verbosity) log_info("SSL connection %s"
" failed no certificate", data->id);
return 0;
}
if(verbosity)
log_cert(VERB_ALGO, "peer certificate", x);
#ifdef HAVE_SSL_GET0_PEERNAME
if(SSL_get0_peername(data->ssl)) {
if(verbosity) log_info("SSL connection %s "
"to %s authenticated", data->id,
SSL_get0_peername(data->ssl));
} else {
#endif
if(verbosity) log_info("SSL connection %s "
"authenticated", data->id);
#ifdef HAVE_SSL_GET0_PEERNAME
}
#endif
X509_free(x);
} else {
X509* x = SSL_get_peer_certificate(data->ssl);
if(x) {
if(verbosity)
log_cert(VERB_ALGO, "peer certificate", x);
X509_free(x);
}
if(verbosity) log_info("SSL connection %s failed: "
"failed to authenticate", data->id);
return 0;
}
} else {
/* unauthenticated, the verify peer flag was not set
* in ssl when the ssl object was created from ssl_ctx */
if(verbosity) log_info("SSL connection %s", data->id);
}
return 1;
}
#endif /* HAVE_SSL */
#ifdef HAVE_SSL
/** perform SSL handshake, return 0 to wait for events, 1 if done */
static int tap_handshake(struct tap_data* data)
{
int r;
if(data->ssl_brief_write) {
/* write condition has been satisfied, back to reading */
tap_disable_brief_write(data);
}
if(data->ssl_handshake_done)
return 1;
ERR_clear_error();
r = SSL_do_handshake(data->ssl);
if(r != 1) {
int want = SSL_get_error(data->ssl, r);
if(want == SSL_ERROR_WANT_READ) {
return 0;
} else if(want == SSL_ERROR_WANT_WRITE) {
tap_enable_brief_write(data);
return 0;
} else if(r == 0) {
/* closed */
tap_data_free(data);
return 0;
} else if(want == SSL_ERROR_SYSCALL) {
/* SYSCALL and errno==0 means closed uncleanly */
int silent = 0;
#ifdef EPIPE
if(errno == EPIPE && verbosity < 2)
silent = 1; /* silence 'broken pipe' */
#endif
#ifdef ECONNRESET
if(errno == ECONNRESET && verbosity < 2)
silent = 1; /* silence reset by peer */
#endif
if(errno == 0)
silent = 1;
if(!silent)
log_err("SSL_handshake syscall: %s",
strerror(errno));
tap_data_free(data);
return 0;
} else {
unsigned long err = ERR_get_error();
if(!squelch_err_ssl_handshake(err)) {
log_crypto_err_code("ssl handshake failed",
err);
verbose(VERB_OPS, "ssl handshake failed "
"from %s", data->id);
}
tap_data_free(data);
return 0;
}
}
/* check peer verification */
data->ssl_handshake_done = 1;
if(!tap_check_peer(data)) {
/* closed */
tap_data_free(data);
return 0;
}
return 1;
}
#endif /* HAVE_SSL */
/** callback for dnstap listener */
void dtio_tap_callback(int ATTR_UNUSED(fd), short ATTR_UNUSED(bits), void* arg)
{
struct tap_data* data = (struct tap_data*)arg;
if(verbosity>=3) log_info("tap callback");
#ifdef HAVE_SSL
if(data->ssl && (!data->ssl_handshake_done ||
data->ssl_brief_write)) {
if(!tap_handshake(data))
return;
}
#endif
while(data->len_done < 4) {
uint32_t l = (uint32_t)data->len;
ssize_t ret = tap_receive(data,
((uint8_t*)&l)+data->len_done, 4-data->len_done);
if(verbosity>=4) log_info("s recv %d", (int)ret);
if(ret == 0) {
/* closed or error */
tap_data_free(data);
return;
} else if(ret == -1) {
/* continue later */
return;
}
data->len_done += ret;
data->len = (size_t)l;
if(data->len_done < 4)
return; /* continue later */
data->len = (size_t)(ntohl(l));
if(verbosity>=3) log_info("length is %d", (int)data->len);
if(data->len == 0) {
/* it is a control frame */
data->control_frame = 1;
/* read controlframelen */
data->len_done = 0;
} else {
/* allocate frame size */
data->frame = calloc(1, data->len);
if(!data->frame) {
log_err("out of memory");
tap_data_free(data);
return;
}
}
}
/* we want to read the full length now */
if(data->data_done < data->len) {
ssize_t r = tap_receive(data, data->frame + data->data_done,
data->len - data->data_done);
if(verbosity>=4) log_info("f recv %d", (int)r);
if(r == 0) {
/* closed or error */
tap_data_free(data);
return;
} else if(r == -1) {
/* continue later */
return;
}
data->data_done += r;
if(data->data_done < data->len)
return; /* continue later */
}
/* we are done with a frame */
if(verbosity>=3) log_info("received %sframe len %d",
(data->control_frame?"control ":""), (int)data->len);
#ifdef USE_DNSTAP
if(data->control_frame)
log_control_frame(data->frame, data->len);
else log_data_frame(data->frame, data->len);
#endif
if(data->len >= 4 && sldns_read_uint32(data->frame) ==
FSTRM_CONTROL_FRAME_READY) {
data->is_bidirectional = 1;
if(verbosity) log_info("bidirectional stream");
if(!reply_with_accept(data)) {
tap_data_free(data);
return;
}
} else if(data->len >= 4 && sldns_read_uint32(data->frame) ==
FSTRM_CONTROL_FRAME_STOP && data->is_bidirectional) {
if(!reply_with_finish(data)) {
tap_data_free(data);
return;
}
}
/* prepare for next frame */
free(data->frame);
data->frame = NULL;
data->control_frame = 0;
data->len = 0;
data->len_done = 0;
data->data_done = 0;
}
/** callback for main listening file descriptor */
void dtio_mainfdcallback(int fd, short ATTR_UNUSED(bits), void* arg)
{
struct tap_socket* tap_sock = (struct tap_socket*)arg;
struct main_tap_data* maindata = (struct main_tap_data*)
tap_sock->data;
struct tap_data* data;
char* id = NULL;
struct sockaddr_storage addr;
socklen_t addrlen = (socklen_t)sizeof(addr);
int s = accept(fd, (struct sockaddr*)&addr, &addrlen);
if(s == -1) {
#ifndef USE_WINSOCK
/* EINTR is signal interrupt. others are closed connection. */
if( errno == EINTR || errno == EAGAIN
#ifdef EWOULDBLOCK
|| errno == EWOULDBLOCK
#endif
#ifdef ECONNABORTED
|| errno == ECONNABORTED
#endif
#ifdef EPROTO
|| errno == EPROTO
#endif /* EPROTO */
)
return;
#else /* USE_WINSOCK */
if(WSAGetLastError() == WSAEINPROGRESS ||
WSAGetLastError() == WSAECONNRESET)
return;
if(WSAGetLastError() == WSAEWOULDBLOCK) {
ub_winsock_tcp_wouldblock(maindata->ev, UB_EV_READ);
return;
}
#endif
log_err_addr("accept failed", sock_strerror(errno), &addr,
addrlen);
return;
}
fd_set_nonblock(s);
if(verbosity) {
if(addr.ss_family == AF_LOCAL) {
#ifdef HAVE_SYS_UN_H
struct sockaddr_un* usock = calloc(1, sizeof(struct sockaddr_un) + 1);
if(usock) {
socklen_t ulen = sizeof(struct sockaddr_un);
if(getsockname(fd, (struct sockaddr*)usock, &ulen) != -1) {
log_info("accepted new dnstap client from %s", usock->sun_path);
id = strdup(usock->sun_path);
} else {
log_info("accepted new dnstap client");
}
free(usock);
} else {
log_info("accepted new dnstap client");
}
#endif /* HAVE_SYS_UN_H */
} else if(addr.ss_family == AF_INET ||
addr.ss_family == AF_INET6) {
char ip[256];
addr_to_str(&addr, addrlen, ip, sizeof(ip));
log_info("accepted new dnstap client from %s", ip);
id = strdup(ip);
} else {
log_info("accepted new dnstap client");
}
}
data = calloc(1, sizeof(*data));
if(!data) fatal_exit("out of memory");
data->fd = s;
data->id = id;
if(tap_sock->sslctx) {
data->ssl = incoming_ssl_fd(tap_sock->sslctx, data->fd);
if(!data->ssl) fatal_exit("could not SSL_new");
}
data->ev = ub_event_new(maindata->base, s, UB_EV_READ | UB_EV_PERSIST,
&dtio_tap_callback, data);
if(!data->ev) fatal_exit("could not ub_event_new");
if(ub_event_add(data->ev, NULL) != 0) fatal_exit("could not ub_event_add");
}
/** setup local accept sockets */
static void setup_local_list(struct main_tap_data* maindata,
struct config_strlist_head* local_list)
{
struct config_strlist* item;
for(item = local_list->first; item; item = item->next) {
struct tap_socket* s;
s = tap_socket_new_local(item->str, &dtio_mainfdcallback,
maindata);
if(!s) fatal_exit("out of memory");
if(!tap_socket_list_insert(&maindata->acceptlist, s))
fatal_exit("out of memory");
}
}
/** setup tcp accept sockets */
static void setup_tcp_list(struct main_tap_data* maindata,
struct config_strlist_head* tcp_list)
{
struct config_strlist* item;
for(item = tcp_list->first; item; item = item->next) {
struct tap_socket* s;
s = tap_socket_new_tcpaccept(item->str, &dtio_mainfdcallback,
maindata);
if(!s) fatal_exit("out of memory");
if(!tap_socket_list_insert(&maindata->acceptlist, s))
fatal_exit("out of memory");
}
}
/** setup tls accept sockets */
static void setup_tls_list(struct main_tap_data* maindata,
struct config_strlist_head* tls_list, char* server_key,
char* server_cert, char* verifypem)
{
struct config_strlist* item;
for(item = tls_list->first; item; item = item->next) {
struct tap_socket* s;
s = tap_socket_new_tlsaccept(item->str, &dtio_mainfdcallback,
maindata, server_key, server_cert, verifypem);
if(!s) fatal_exit("out of memory");
if(!tap_socket_list_insert(&maindata->acceptlist, s))
fatal_exit("out of memory");
}
}
/** signal variable */
static struct ub_event_base* sig_base = NULL;
/** do we have to quit */
int sig_quit = 0;
/** signal handler for user quit */
static RETSIGTYPE main_sigh(int sig)
{
if(!sig_quit) {
char str[] = "exit on signal \n";
str[15] = '0' + (sig/10)%10;
str[16] = '0' + sig%10;
/* simple cast to void will not silence Wunused-result */
(void)!write(STDERR_FILENO, str, strlen(str));
}
if(sig_base) {
ub_event_base_loopexit(sig_base);
sig_base = NULL;
}
sig_quit = 1;
}
/** setup and run the server to listen to DNSTAP messages */
static void
setup_and_run(struct config_strlist_head* local_list,
struct config_strlist_head* tcp_list,
struct config_strlist_head* tls_list, char* server_key,
char* server_cert, char* verifypem)
{
time_t secs = 0;
struct timeval now;
struct main_tap_data* maindata;
struct ub_event_base* base;
const char *evnm="event", *evsys="", *evmethod="";
maindata = calloc(1, sizeof(*maindata));
if(!maindata) fatal_exit("out of memory");
memset(&now, 0, sizeof(now));
base = ub_default_event_base(1, &secs, &now);
if(!base) fatal_exit("could not create ub_event base");
maindata->base = base;
sig_base = base;
if(sig_quit) {
ub_event_base_free(base);
free(maindata);
return;
}
ub_get_event_sys(base, &evnm, &evsys, &evmethod);
if(verbosity) log_info("%s %s uses %s method", evnm, evsys, evmethod);
setup_local_list(maindata, local_list);
setup_tcp_list(maindata, tcp_list);
setup_tls_list(maindata, tls_list, server_key, server_cert,
verifypem);
if(!tap_socket_list_addevs(maindata->acceptlist, base))
fatal_exit("could not setup accept events");
if(verbosity) log_info("start of service");
ub_event_base_dispatch(base);
sig_base = NULL;
if(verbosity) log_info("end of service");
tap_socket_list_delete(maindata->acceptlist);
ub_event_base_free(base);
free(maindata);
}
/** getopt global, in case header files fail to declare it. */
extern int optind;
/** getopt global, in case header files fail to declare it. */
extern char* optarg;
/** main program for streamtcp */
int main(int argc, char** argv)
{
int c;
int usessl = 0;
struct config_strlist_head local_list;
struct config_strlist_head tcp_list;
struct config_strlist_head tls_list;
char* server_key = NULL, *server_cert = NULL, *verifypem = NULL;
#ifdef USE_WINSOCK
WSADATA wsa_data;
if(WSAStartup(MAKEWORD(2,2), &wsa_data) != 0) {
printf("WSAStartup failed\n");
return 1;
}
#endif
if(signal(SIGINT, main_sigh) == SIG_ERR ||
#ifdef SIGQUIT
signal(SIGQUIT, main_sigh) == SIG_ERR ||
#endif
#ifdef SIGHUP
signal(SIGHUP, main_sigh) == SIG_ERR ||
#endif
#ifdef SIGBREAK
signal(SIGBREAK, main_sigh) == SIG_ERR ||
#endif
signal(SIGTERM, main_sigh) == SIG_ERR)
fatal_exit("could not bind to signal");
memset(&local_list, 0, sizeof(local_list));
memset(&tcp_list, 0, sizeof(tcp_list));
memset(&tls_list, 0, sizeof(tls_list));
/* lock debug start (if any) */
checklock_start();
log_ident_set("unbound-dnstap-socket");
log_init(0, 0, 0);
#ifdef SIGPIPE
if(signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
perror("could not install signal handler for SIGPIPE");
return 1;
}
#endif
/* command line options */
while( (c=getopt(argc, argv, "hls:t:u:vx:y:z:")) != -1) {
switch(c) {
case 'u':
if(!cfg_strlist_append(&local_list,
strdup(optarg)))
fatal_exit("out of memory");
break;
case 's':
if(!cfg_strlist_append(&tcp_list,
strdup(optarg)))
fatal_exit("out of memory");
break;
case 't':
if(!cfg_strlist_append(&tls_list,
strdup(optarg)))
fatal_exit("out of memory");
usessl = 1;
break;
case 'x':
server_key = optarg;
usessl = 1;
break;
case 'y':
server_cert = optarg;
usessl = 1;
break;
case 'z':
verifypem = optarg;
usessl = 1;
break;
case 'l':
longformat = 1;
break;
case 'v':
verbosity++;
break;
case 'h':
case '?':
default:
usage(argv);
}
}
argc -= optind;
argv += optind;
if(usessl) {
#ifdef HAVE_SSL
#if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL)
ERR_load_SSL_strings();
#endif
#if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_CRYPTO)
# ifndef S_SPLINT_S
OpenSSL_add_all_algorithms();
# endif
#else
OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS
| OPENSSL_INIT_ADD_ALL_DIGESTS
| OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
#endif
#if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL)
(void)SSL_library_init();
#else
(void)OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL);
#endif
#endif /* HAVE_SSL */
}
setup_and_run(&local_list, &tcp_list, &tls_list, server_key,
server_cert, verifypem);
config_delstrlist(local_list.first);
config_delstrlist(tcp_list.first);
config_delstrlist(tls_list.first);
checklock_stop();
#ifdef USE_WINSOCK
WSACleanup();
#endif
return 0;
}
/***--- definitions to make fptr_wlist work. ---***/
/* These are callbacks, similar to smallapp callbacks, except the debug
* tool callbacks are not in it */
struct tube;
struct query_info;
#include "util/data/packed_rrset.h"
#include "daemon/worker.h"
#include "daemon/remote.h"
#include "util/fptr_wlist.h"
#include "libunbound/context.h"
void worker_handle_control_cmd(struct tube* ATTR_UNUSED(tube),
uint8_t* ATTR_UNUSED(buffer), size_t ATTR_UNUSED(len),
int ATTR_UNUSED(error), void* ATTR_UNUSED(arg))
{
log_assert(0);
}
int worker_handle_request(struct comm_point* ATTR_UNUSED(c),
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
struct comm_reply* ATTR_UNUSED(repinfo))
{
log_assert(0);
return 0;
}
int worker_handle_service_reply(struct comm_point* ATTR_UNUSED(c),
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
struct comm_reply* ATTR_UNUSED(reply_info))
{
log_assert(0);
return 0;
}
int remote_accept_callback(struct comm_point* ATTR_UNUSED(c),
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
struct comm_reply* ATTR_UNUSED(repinfo))
{
log_assert(0);
return 0;
}
int remote_control_callback(struct comm_point* ATTR_UNUSED(c),
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
struct comm_reply* ATTR_UNUSED(repinfo))
{
log_assert(0);
return 0;
}
void worker_sighandler(int ATTR_UNUSED(sig), void* ATTR_UNUSED(arg))
{
log_assert(0);
}
struct outbound_entry* worker_send_query(
struct query_info* ATTR_UNUSED(qinfo), uint16_t ATTR_UNUSED(flags),
int ATTR_UNUSED(dnssec), int ATTR_UNUSED(want_dnssec),
int ATTR_UNUSED(nocaps), int ATTR_UNUSED(check_ratelimit),
struct sockaddr_storage* ATTR_UNUSED(addr),
socklen_t ATTR_UNUSED(addrlen), uint8_t* ATTR_UNUSED(zone),
size_t ATTR_UNUSED(zonelen), int ATTR_UNUSED(tcp_upstream),
int ATTR_UNUSED(ssl_upstream), char* ATTR_UNUSED(tls_auth_name),
struct module_qstate* ATTR_UNUSED(q), int* ATTR_UNUSED(was_ratelimited))
{
log_assert(0);
return 0;
}
#ifdef UB_ON_WINDOWS
void
worker_win_stop_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev), void*
ATTR_UNUSED(arg)) {
log_assert(0);
}
void
wsvc_cron_cb(void* ATTR_UNUSED(arg))
{
log_assert(0);
}
#endif /* UB_ON_WINDOWS */
void
worker_alloc_cleanup(void* ATTR_UNUSED(arg))
{
log_assert(0);
}
struct outbound_entry* libworker_send_query(
struct query_info* ATTR_UNUSED(qinfo), uint16_t ATTR_UNUSED(flags),
int ATTR_UNUSED(dnssec), int ATTR_UNUSED(want_dnssec),
int ATTR_UNUSED(nocaps), int ATTR_UNUSED(check_ratelimit),
struct sockaddr_storage* ATTR_UNUSED(addr),
socklen_t ATTR_UNUSED(addrlen), uint8_t* ATTR_UNUSED(zone),
size_t ATTR_UNUSED(zonelen), int ATTR_UNUSED(tcp_upstream),
int ATTR_UNUSED(ssl_upstream), char* ATTR_UNUSED(tls_auth_name),
struct module_qstate* ATTR_UNUSED(q), int* ATTR_UNUSED(was_ratelimited))
{
log_assert(0);
return 0;
}
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))
{
log_assert(0);
return 0;
}
void libworker_handle_control_cmd(struct tube* ATTR_UNUSED(tube),
uint8_t* ATTR_UNUSED(buffer), size_t ATTR_UNUSED(len),
int ATTR_UNUSED(error), void* ATTR_UNUSED(arg))
{
log_assert(0);
}
void libworker_fg_done_cb(void* ATTR_UNUSED(arg), int ATTR_UNUSED(rcode),
struct sldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(s),
char* ATTR_UNUSED(why_bogus), int ATTR_UNUSED(was_ratelimited))
{
log_assert(0);
}
void libworker_bg_done_cb(void* ATTR_UNUSED(arg), int ATTR_UNUSED(rcode),
struct sldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(s),
char* ATTR_UNUSED(why_bogus), int ATTR_UNUSED(was_ratelimited))
{
log_assert(0);
}
void libworker_event_done_cb(void* ATTR_UNUSED(arg), int ATTR_UNUSED(rcode),
struct sldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(s),
char* ATTR_UNUSED(why_bogus), int ATTR_UNUSED(was_ratelimited))
{
log_assert(0);
}
int context_query_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
{
log_assert(0);
return 0;
}
void worker_stat_timer_cb(void* ATTR_UNUSED(arg))
{
log_assert(0);
}
void worker_probe_timer_cb(void* ATTR_UNUSED(arg))
{
log_assert(0);
}
void worker_start_accept(void* ATTR_UNUSED(arg))
{
log_assert(0);
}
void worker_stop_accept(void* ATTR_UNUSED(arg))
{
log_assert(0);
}
/** keep track of lock id in lock-verify application */
struct order_id {
/** the thread id that created it */
int thr;
/** the instance number of creation */
int instance;
};
int order_lock_cmp(const void* e1, const void* e2)
{
const struct order_id* o1 = e1;
const struct order_id* o2 = e2;
if(o1->thr < o2->thr) return -1;
if(o1->thr > o2->thr) return 1;
if(o1->instance < o2->instance) return -1;
if(o1->instance > o2->instance) return 1;
return 0;
}
int
codeline_cmp(const void* a, const void* b)
{
return strcmp(a, b);
}
int replay_var_compare(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
{
log_assert(0);
return 0;
}
void remote_get_opt_ssl(char* ATTR_UNUSED(str), void* ATTR_UNUSED(arg))
{
log_assert(0);
}
diff --git a/contrib/unbound/doc/Changelog b/contrib/unbound/doc/Changelog
index a7c9c40026e5..05112e898a39 100644
--- a/contrib/unbound/doc/Changelog
+++ b/contrib/unbound/doc/Changelog
@@ -1,11529 +1,11679 @@
+2 November 2023: Wouter
+ - Set version number to 1.19.0.
+ - Tag for 1.19.0rc1 release.
+
+1 November 2023: George
+ - Mention flex and bison in README.md when building from repository
+ source.
+
+1 November 2023: Wouter
+ - Fix SSL compile failure for definition in log_crypto_err_io_code_arg.
+ - Fix SSL compile failure for other missing definitions in
+ log_crypto_err_io_code_arg.
+ - Fix compilation without openssl, remove unused function warning.
+
+31 October 2023: George
+ - Fix #941: dnscrypt doesn't work after upgrade to 1.18 with
+ suggestion by dukeartem to also fix the udp_ancil with dnscrypt.
+
+30 October 2023: George
+ - Merge #930 from Stuart Henderson: add void to
+ log_ident_revert_to_default declaration.
+
+30 October 2023: Wouter
+ - autoconf.
+
+24 October 2023: George
+ - Clearer configure text for missing protobuf-c development libraries.
+
+20 October 2023: Wouter
+ - Merge #951: Cachedb no store. The cachedb-no-store: yes option is
+ used to stop cachedb from writing messages to the backend storage.
+ It reads messages when data is available from the backend. The
+ default is no.
+
+19 October 2023: Wouter
+ - Fix to print detailed errors when an SSL IO routine fails via
+ SSL_get_error.
+
+18 October 2023: George
+ - Mailing list patches from Daniel Gröber for DNS64 fallback to plain
+ AAAA when no A record exists for synthesis, and minor DNS64 code
+ refactoring for better readability.
+ - Fixes for the DNS64 patches.
+ - Update the dns64_lookup.rpl test for the DNS64 fallback patch.
+ - Merge #955 from buevsan: fix ipset wrong behavior.
+ - Update testdata/ipset.tdir test for ipset fix.
+
+17 October 2023: Wouter
+ - Fix #954: Inconsistent RPZ handling for A record returned along with
+ CNAME.
+
+16 October 2023: George
+ - Expose the script filename in the Python module environment 'mod_env'
+ instead of the config_file structure which includes the linked list
+ of scripts in a multi Python module setup; fixes #79.
+ - Expose the configured listening and outgoing interfaces, if any, as
+ a list of strings in the Python 'config_file' class instead of the
+ current Swig object proxy; fixes #79.
+ - For multi Python module setups, clean previously parsed module
+ functions in __main__'s dictionary, if any, so that only current
+ module functions are registered.
+
+13 October 2023: George
+ - Better fix for infinite loop when reading multiple lines of input on
+ a broken remote control socket, by treating a zero byte line the
+ same as transmission end. Addesses #947 and #948.
+
+12 October 2023: Wouter
+ - Merge #944: Disable EDNS DO.
+ Disable the EDNS DO flag in upstream requests. This can be helpful
+ for devices that cannot handle DNSSEC information. But it should not
+ be enabled otherwise, because that would stop DNSSEC validation. The
+ DNSSEC validation would not work for Unbound itself, and also not
+ for downstream users. Default is no. The option
+ is disable-edns-do: no
+
+11 October 2023: George
+ - Fix #850: [FR] Ability to use specific database in Redis, with new
+ redis-logical-db configuration option.
+
+11 October 2023: Wouter
+ - Fix #949: "could not create control compt".
+ - Fix that cachedb does not warn when serve-expired is disabled about
+ use of serve-expired-reply-ttl and serve-expired-client-timeout.
+ - Fix for #949: Fix pythonmod/ubmodule-tst.py for Python 3.x.
+
+10 October 2023: George
+ - Fix infinite loop when reading multiple lines of input on a broken
+ remote control socket. Addesses #947 and #948.
+
+9 October 2023: Wouter
+ - Fix edns subnet so that queries with a source prefix of zero cause
+ the recursor send no edns subnet option to the upstream.
+ - Fix that printout of EDNS options shows the EDNS cookie option by
+ name.
+
+4 October 2023: Wouter
+ - Fix #946: Forwarder returns servfail on upstream response noerror no
+ data.
+
+3 October 2023: George
+ - Merge #881: Generalise the proxy protocol code.
+
+2 October 2023: George
+ - Fix misplaced comment.
+
+22 September 2023: Wouter
+ - Fix #942: 1.18.0 libunbound DNS regression when built without
+ OpenSSL.
+
+18 September 2023: Wouter
+ - Fix rpz tcp-only action with rpz triggers nsdname and nsip.
+
+15 September 2023: Wouter
+ - Merge #936: Check for c99 with autoconf versions prior to 2.70.
+ - Fix to remove two c99 notations.
+
+14 September 2023: Wouter
+ - Fix authority zone answers for obscured DNAMEs and delegations.
+
+8 September 2023: Wouter
+ - Fix send of udp retries when ENOBUFS is returned. It stops looping
+ and also waits for the condition to go away. Reported by Florian
+ Obser.
+
+7 September 2023: Wouter
+ - Fix to scrub resource records of type A and AAAA that have an
+ inappropriate size. They are removed from responses.
+ - Fix to move msgparse_rrset_remove_rr code to util/msgparse.c.
+ - Fix to add EDE text when RRs have been removed due to length.
+ - Fix to set ede match in unit test for rr length removal.
+ - Fix to print EDE text in readable form in output logs.
+
+6 September 2023: Wouter
+ - Merge #931: Prevent warnings from -Wmissing-prototypes.
+
+31 August 2023: Wouter
+ - Fix autoconf 2.69 warnings in configure.
+ - Fix #927: unbound 1.18.0 make test error. Fix make test without SHA1.
+
+30 August 2023: Wouter
+ - Fix for WKS call to getservbyname that creates allocation on exit
+ in unit test by testing numbers first and testing from the services
+ list later.
+
+28 August 2023: Wouter
+ - Fix for version generation race condition that ignored changes.
+
25 August 2023: Wouter
- Fix compile error on NetBSD in util/netevent.h.
23 August 2023: Wouter
- - Tag for 1.18.0rc1 release.
+ - Tag for 1.18.0rc1 release. This became the 1.18.0 release on
+ 30 aug 2023, with the fix from 25 aug, fix compile on NetBSD
+ included. The repository continues with version 1.18.1.
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
and Karthik Umashankar.
- Fix unit test to properly test the reuse_write_wait_pop function.
6 October 2022: Wouter
- Fix to stop responses with TC flag from resulting in partial
responses. It retries to fetch the data elsewhere, or fails the
query and in depth fix removes the TC flag from the cached item.
- Fix proxy length debug output printout typecasts.
5 October 2022: Wouter
- Fix dnscrypt compile for proxy protocol code changes.
5 October 2022: George
- Use DEBUG_TDIR from environment in mini_tdir.sh for debugging.
- Fix string comparison in mini_tdir.sh.
- Make ede.tdir test more predictable by using static data.
- Fix checkconf test for dnscrypt and proxy port.
4 October 2022: George
- Merge #764: Leniency for target discovery when under load (for
NRDelegation changes).
4 October 2022: Wouter
- Fix static analysis report to remove dead code from the
rpz_callback_from_iterator_module function.
- Fix to clean up after the acl_interface unit test.
3 October 2022: George
- Merge #760: PROXYv2 downstream support. (New proxy-protocol-port
configuration option).
3 October 2022: Wouter
- Fix to remove erroneous TC flag from TCP upstream.
- Fix test tdir skip report printout.
- Fix windows compile, the identifier interface is defined in headers.
- Fix to close errno block in comm_point_tcp_handle_read outside of
ifdef.
26 September 2022: George
- Better output for skipped tdir tests.
21 September 2022: Wouter
- Patch for CVE-2022-3204 Non-Responsive Delegation Attack.
- This patch was released in 1.16.3, the code repository continues
with the previous features and fixes for 1.17.0.
- Fix doxygen warning in respip.h.
20 September 2022: George
- Convert tdir tests to use the new skip_test functionality.
- Remove unused testcode/mini_tpkg.sh file.
16 September 2022: George
- Merge #753: ACL per interface. (New interface-* configuration
options).
2 September 2022: Wouter
- Remove include that was there for debug purposes.
- Fix to check pthread_t size after pthread has been detected.
1 September 2022: Wouter
- Fix to update config tests to fix checking if nonblocking sockets
work on OpenBSD.
- Slow down log frequency of write wait failures.
- Fix to set out of file descriptor warning to operational verbosity.
- Fix to log a verbose message at operational notice level if a
thread is not responding, to stats requests. It is logged with
thread identifiers.
31 August 2022: Wouter
- Fix to avoid process wide fcntl calls mixed with nonblocking
operations after a blocked write.
- Patch from Vadim Fedorenko that adds MSG_DONTWAIT to receive
operations, so that instruction reordering does not cause mistakenly
blocking socket operations.
- Fix to wait for blocked write on UDP sockets, with a timeout if it
takes too long the packet is dropped.
- Fix for wait for udp send to stop when packet is successfully sent.
22 August 2022: Wouter
- Fix #741: systemd socket activation fails on IPv6.
12 August 2022: Wouter
- Fix to log accept error ENFILE and EMFILE errno, but slowly, once
per 10 seconds. Also log accept failures when no slow down is used.
5 August 2022: Wouter
- Fix #734 [FR] enable unbound-checkconf to detect more (basic)
errors.
4 August 2022: Wouter
- Fix ratelimit inconsistency, for ip-ratelimits the value is the
amount allowed, like for ratelimits.
2 August 2022: Wouter
- Fix edns subnet so that scope 0 answers only match sourcemask 0
queries for answers from cache if from a query with sourcemask 0.
- Fix unittest for edns subnet change.
- Merge #730 from luisdallos: Fix startup failure on Windows 8.1 due
to unsupported IPV6_USER_MTU socket option being set.
1 August 2022: Wouter
- Fix the novel ghost domain issues CVE-2022-30698 and CVE-2022-30699.
- Tests for ghost domain fixes.
- Tag for 1.16.2 release. The code repo continues with 1.16.3.
- Fix #728: alloc_reg_obtain() core dump. Stop double
alloc_reg_release when serviced_create fails.
19 July 2022: George
- Update documentation for 'outbound-msg-retry:'.
19 July 2022: Wouter
- Merge #718: Introduce infra-cache-max-rtt option to config max
retransmit timeout.
15 July 2022: Wouter
- Merge PR 714: Avoid treat normal hosts as unresponsive servers.
And fixup the lock code.
- iana portlist update.
12 July 2022: George
- For windows crosscompile, fix setting the IPV6_MTU socket option
equivalent (IPV6_USER_MTU); allows cross compiling with latest
cross-compiler versions.
12 July 2022: Wouter
- Fix dname count in sldns parse type descriptor for SVCB and HTTPS.
11 July 2022: Wouter
- Fix verbose EDE error printout.
4 July 2022: George
- Fix bug introduced in 'improve val_sigcrypt.c::algo_needs_missing for
one loop pass'.
- Merge PR #668 from Cristian Rodríguez: Set IP_BIND_ADDRESS_NO_PORT on
outbound tcp sockets.
4 July 2022: Wouter
- Tag for 1.16.1rc1 release. This became 1.16.1 on 11 July 2022.
The code repo continues with version 1.16.2 under development.
3 July 2022: George
- Merge PR #671 from Petr Menšík: Disable ED25519 and ED448 in FIPS
mode on openssl3.
- Merge PR #660 from Petr Menšík: Sha1 runtime insecure.
- For #660: formatting, less verbose logging, add EDE information.
- Fix for correct openssl error when adding windows CA certificates to
the openssl trust store.
- Improve val_sigcrypt.c::algo_needs_missing for one loop pass.
- Reintroduce documentation and more EDE support for
val_sigcrypt.c::dnskeyset_verify_rrset_sig.
1 July 2022: George
- Merge PR #706: NXNS fallback.
- From #706: Cached NXDOMAIN does not increase the target nx
responses.
- From #706: Don't generate parent side queries if we already
have the lame records in cache.
- From #706: When a lame address is the best choice, don't try to
generate target queries when the missing targets are all lame.
29 June 2022: Wouter
- iana portlist update.
- Fix detection of libz on windows compile with static option.
- Fix compile warning for windows compile.
29 June 2022: George
- Add debug option to the mini_tdir.sh test code.
- Fix #704: [FR] Statistics counter for number of outgoing UDP queries
sent; introduces 'num.query.udpout' to the 'unbound-control stats'
command.
- Fix to not count cached NXDOMAIN for MAX_TARGET_NX.
- Allow fallback to the parent side when MAX_TARGET_NX is reached.
This will also allow MAX_TARGET_NX more NXDOMAINs.
28 June 2022: George
- Show the output of the exact .rpl run that failed with 'make test'.
- Fix for cached 0 TTL records to not trigger prefetching when
serve-expired-client-timeout is set.
28 June 2022: Wouter
- Fix test program dohclient close to use portability routine.
23 June 2022: Tom
- Clarify -v flag manpage entry (#705)
22 June 2022: Philip
- Fix #663: use after free issue with edns options.
21 June 2022: Philip
- Fix for loading locally stored zones that have lines with blanks or
blanks and comments.
20 June 2022: George
- Remove unused LDNS function check for GOST Engine unloading.
14 June 2022: George
- Merge PR #688: Rpz url notify issue.
- Note in the unbound.conf text that NOTIFY is allowed from the url:
addresses for auth and rpz zones.
3 June 2022: George
- Fix for edns client subnet to respect not looking in its cache when
instructed to do so (e.g., prefetch).
3 June 2022: Wouter
- makedist.sh picks up 32bit libssp-0.dll when 32bit compile.
27 May 2022: Wouter
- Fix #684: [FTBS] configure script error with libmnl on openSUSE 15.3 (and possibly other distributions)
- Version is set to 1.16.0 for release. Release tag 1.16.0rc1. This
became release 1.16.0 on 2 June 2022. The source code branch
continues with version 1.16.1 under development.
20 May 2022: Wouter
- Fix to silence test for ede error output to the console from the
test setup script.
- Fix ede test to not use default pidfile, and use local interface.
- Fix some lint type warnings.
18 May 2022: George
- Fix typos in config_set_option for the 'num-threads' and
'ede-serve-expired' options.
15 May 2022: George
- Fix #678: [FR] modify behaviour of unbound-control rpz_enable zone,
by updating unbound-control's documentation.
12 May 2022: George
- Fix #417: prefetch and ECS causing cache corruption when used
together.
12 May 2022: Wouter
- Merge #677: Allow using system certificates not only on Windows,
from pemensik.
- For #677: Added tls-system-cert to config parser and documentation.
11 May 2022: Wouter
- Fix #673: DNS over TLS: error: SSL_handshake syscall: No route to
host.
10 May 2022: George
- Fix Python build in non-source directory; based on patch by
Michael Tokarev.
6 May 2022: Tom
- Merge PR #604: Add basic support for EDE (RFC8914).
28 April 2022: Wouter
- Fix #670: SERVFAIL problems with unbound 1.15.0 running on
OpenBSD 7.1.
8 April 2022: Wouter
- Fix zonemd check to allow unsupported algorithms to load.
If there are only unsupported algorithms, or unsupported schemes,
and no failed or successful other ZONEMD records, or malformed
or bad ZONEMD records, the unsupported records allow the zone load.
- Fix zonemd unsupported algo check.
- Fix zonemd unsupported algo check reason to not copy to next record,
and check for success for debug printout.
- Fix zonemd unsupported algo check to print unsupported reason before
zeroing it.
- Fix zonemd unsupported algo check to set reason to NULL before the
check routine, but after malformed checks, to get the correct NULL
output when the digest matches.
25 March 2022: Wouter
- Fix spelling error in comment in sldns_str2wire_svcparam_key_lookup.
23 March 2022: Wouter
- Fix #651: [FR] Better logging for refused queries.
18 March 2022: George
- Merge PR #648 from eaglegai: fix -q doesn't work when use with
'unbound-control stats_shm'.
17 March 2022: Wouter
- Fix to describe auth-zone and other configuration at the local-zone
configuration option, to allow for more broadly view of the options.
16 March 2022: Wouter
- Fix to ensure uniform handling of spaces and tabs when parsing RRs.
9 March 2022: Wouter
- Merge #644: Make `install-lib` make target install the pkg-config
file.
7 March 2022: Wouter
- Fix configure for python to use sysutils, because distutils is
deprecated. It uses sysutils when available, distutils otherwise.
3 March 2022: Wouter
- Fix #637: Integer Overflow in sldns_str2period function.
- Fix for #637: fix integer overflow checks in sldns_str2period.
2 March 2022: George
- Merge PR #632 from scottrw93: Match cnames in ipset.
- Various fixes for #632: variable initialisation, convert the qinfo
to str once, accept trailing dot in the local-zone ipset option.
2 March 2022: Wouter
- Fix compile warnings for printf ll format on mingw compile.
1 March 2022: Wouter
- Fix pythonmod for change in iter_dp_is_useless function prototype.
28 February 2022: George
- Fix #630: Unify the RPZ log messages.
- Merge #623 from rex4539: Fix typos.
28 February 2022: Wouter
- Fix #633: Document unix domain socket support for unbound-control.
- Fix for #633: updated fix with new text.
- Fix edns client subnet to add the option based on the option list,
so that it is not state dependent, after the state fix of #605 for
double EDNS options.
- Fix for edns client subnet option add fix in removal code, from review.
25 February 2022: Wouter
- Fix to detect that no IPv6 support means that IPv6 addresses are
useless for delegation point lookups.
- update Makefile dependencies.
- Fix check interface existence for support detection in remote lookup.
18 February 2022: Wouter
- Fix that address not available is squelched from the logs for
udp connect failures. It is visible on verbosity 4 and more.
- Merge #631 from mollyim: Replace OpenSSL's ERR_PACK with
ERR_GET_REASON.
16 February 2022: Wouter
- Fix for #628: fix rpz-passthru for qname trigger by localzone type.
15 February 2022: Wouter
- Fix #628: A rpz-passthru action is not ending RPZ zone processing.
11 February 2022: Wouter
- Fix #624: Unable to stop Unbound in Windows console (does not
respond to CTRL+C command).
- Fix #618: enabling interface-automatic disables DNS-over-TLS.
Adds the option to list interface-automatic-ports.
- Remove debug info from #618 fix.
7 February 2022: Wouter
- Fix that TCP interface does not use TLS when TLS is also configured.
4 February 2022: Wouter
- Fix #412: cache invalidation issue with CNAME+A.
3 February 2022: Wouter
- Fix for #611: Integer overflow in sldns_wire2str_pkt_scan.
- Tag for 1.15.0rc1 created. That became 1.15.0 on 10 feb 2022.
The repository continues with version 1.15.1.
2 February 2022: George
- Merge PR #532 from Shchelk: Fix: buffer overflow bug.
- Merge PR #616: Update ratelimit logic. It also introduces
ratelimit-backoff and ip-ratelimit-backoff configuration options.
- Change aggressive-nsec default to yes.
- Merge PR #617: Update stub/forward-host notation to accept port and
tls-auth-name.
- Update stream_ssl.tdir test to also use the new forward-host
notation.
2 February 2022: Wouter
- Update version number in repo to 1.15.0 for upcoming release,
since it changes the aggressive-nsec default and the ratelimit change.
- Fix header comment for doxygen for authextstrtoaddr.
- please clang analyzer for loop in test code.
- Fix docker splint test to use more portable uname.
- Update contrib/aaaa-filter-iterator.patch with diff for current
software version.
1 February 2022: George
- Merge PR #603 from fobser: Use OpenSSL 1.1 API to access DSA and RSA
internals.
31 January 2022: George
- Fix review comment for use-after-free when failing to send UDP out.
31 January 2022: Wouter
- iana portlist update.
29 January 2022: George
- Fix tls-* and ssl-* documented alternate syntax to also be available
through remote-control and unbound-checkconf.
- Better cleanup on failed DoT/DoH listening socket creation.
26 January 2022: George
- Fix #599: [FR] RFC 9156 (obsoletes RFC 7816), by noting the new RFC
document.
26 January 2022: Wouter
- Test for NSID in SERVFAIL response due to DNSSEC bogus.
25 January 2022: George
- Fix #588: Unbound 1.13.2 crashes due to p->pc is NULL in
serviced_udp_callback.
- Merge PR #612: TCP race condition.
25 January 2022: Wouter
- Fix #610: Undefine-shift in sldns_str2wire_hip_buf.
19 January 2022: George
- For dnstap, do not wakeupnow right there. Instead zero the timer to
force the wakeup callback asap.
14 January 2022: George
- Merge PR #605:
- Fix EDNS to upstream where the same option could be attached
more than once.
- Add a region to serviced_query for allocations.
14 January 2022: Wouter
- Add rpz: for-downstream: yesno option, where the RPZ zone is
authoritatively answered for, so the RPZ zone contents can be
checked with DNS queries directed at the RPZ zone.
- For #602: Allow the module-config "subnetcache validator cachedb
iterator".
11 January 2022: George
- Fix prematurely terminated TCP queries when a reply has the same ID.
7 January 2022: Wouter
- Merge #600 from pemensik: Change file mode before changing file
owner.
5 January 2022: Wouter
- Fix for #596: fix that rpz return message is returned and not just
the rcode from the iterator return path. This fixes signal unset RA
after a CNAME.
- Fix unit tests for rpz now that the AA flag returns successfully from
the iterator loop.
- Fix for #596: add unit test for nsdname trigger and signal unset RA.
- Fix for #596: add unit test for nsip trigger and signal unset RA.
- Fix #598: Fix unbound-checkconf fatal error: module conf
'respip dns64 validator iterator' is not known to work.
- Fix for #596: Fix rpz-signal-nxdomain-ra to work for clientip
triggered operation.
4 January 2022: Wouter
- Fix #596: unset the RA bit when a query is blocked by an unbound
RPZ nxdomain reply. The option rpz-signal-nxdomain-ra allows to
signal that a domain is externally blocked to clients when it
is blocked with NXDOMAIN by unsetting RA.
- Fix to add test for rpz-signal-nxdomain-ra.
- Fix #596: only unset RA when NXDOMAIN is signalled.
- Fix that RPZ does not set RD flag on replies, it should be copied
from the query.
22 December 2021: George
- contrib/aaaa-filter-iterator.patch file renewed diff content to
apply cleanly to the current coderepo for the current code version.
20 December 2021: George
- Fix #591: Unbound-anchor manpage links to non-existent license file.
13 December 2021: George
- Add missing configure flags for optional features in the
documentation.
- Fix Unbound capitalization in the documentation.
13 December 2021: Wouter
- Fix to pick up other class local zone information before unlock.
10 December 2021: George
- Allow local-data for classes other than IN to inherit a configured
local-zone's type if possible, instead of defaulting to type
transparent as per the implicit rule.
10 December 2021: Wouter
- Add code similar to fix for ldns for tab between strings, for
consistency, the test case was not broken.
6 December 2021: Wouter
- Merge PR #581 from fobser: Fix -Wmissing-prototypes and -Wshadow
warnings in rpz.
- Fix validator debug output about DS support, print correct algorithm.
3 December 2021: Wouter
- Fix compile warning for if_nametoindex on windows 64bit.
1 December 2021: Wouter
- configure is set to 1.14.0, and release branch.
This was released as version 1.14.0 on 9 Dec 2021, with the doxygen
fix below included. The main branch continues as 1.14.1.
- Fix doc/unbound.doxygen to remove obsolete tag warning.
1 December 2021: George
- Merge PR #511 from yan12125: Reduce unnecessary linking.
- Merge PR #493 from Jaap: Fix generation of libunbound.pc.
- Merge PR #555 from fobser: Allow interface names as scope-id in IPv6
link-local addresses.
- Merge PR #562 from Willem: Reset keepalive per new tcp session.
- Merge PR #522 from sibeream: memory management violations fixed.
- Merge PR #530 from Shchelk: Fix: dereferencing a null pointer.
- Fix #454: listen_dnsport.c:825: error: ‘IPV6_TCLASS’ undeclared.
- Fix #574: Review fixes for size allocation.
30 November 2021: Wouter
- Fix to remove git tracking and ci information from release tarballs.
- iana portlist update.
29 November 2021: Wouter
- Merge PR #570 from rex4539: Fix typos.
- Fix for #570: regen aclocal.m4, fix configure.ac for spelling.
- Fix to make python module opt_list use opt_list_in.
- Fix #574: unbound-checkconf reports fatal error if interface names
are used as value for interfaces:
- Fix #574: Review fixes for it.
- Fix #576: [FR] UB_* error codes in unbound.h
- Fix #574: Review fix for spelling.
15 November 2021: Tom
- Improve EDNS option handling, now also works for synthesised
responses such as local-data and server.id CH TXT responses.
5 November 2021: George
- Fix for #558: fix loop in comm_point->tcp_free when a comm_point is
reclaimed more than once during callbacks.
- Fix for #558: clear the UB_EV_TIMEOUT bit before adding an event.
5 November 2021: Wouter
- Fix that forward-zone name is documented as the full name of the
zone. It is not relative but a fully qualified domain name.
- Fix analyzer review failure in rpz action override code to not
crash on unlocking the local zone lock.
- Fix to remove unused code from rpz resolve client and action
function.
- Merge #565: unbound.service.in: Disable ProtectKernelTunables again.
2 November 2021: Wouter
- Fix #552: Unbound assumes index.html exists on RPZ host.
11 October 2021: Wouter
- Fix chaos replies to have truncation for short message lengths,
or long reply strings.
- Fix to protect custom regional create against small values.
4 October 2021: Wouter
- Fix to add example.conf note for outbound-msg-retry.
27 September 2021: Wouter
- Implement RFC8375: Special-Use Domain 'home.arpa.'.
21 September 2021: Wouter
- For crosscompile on windows, detect 64bit stackprotector library.
- Fix crosscompile shell syntax.
- Fix crosscompile windows to use libssp when it exists.
- For the windows compile script disable gost.
- Fix that on windows, use BIO_set_callback_ex instead of deprecated
BIO_set_callback.
- Fix crosscompile script for the shared build flags.
20 September 2021: Wouter
- Fix crosscompile on windows to work with openssl 3.0.0 the
link with ws2_32 needs -l:libssp.a for __strcpy_chk.
Also copy results from lib64 directory if needed.
10 September 2021: Wouter
- Fix initialisation errors reported by gcc sanitizer.
- Fix lock debug code for gcc sanitizer reports.
- Fix more initialisation errors reported by gcc sanitizer.
8 September 2021: Wouter
- Merged #41 from Moritz Schneider: made outbound-msg-retry
configurable.
- Small fixes for #41: changelog, conflicts resolved,
processQueryResponse takes an iterator env argument like other
functions in the iterator, no colon in string for set_option,
and some whitespace style, to make it similar to the rest.
- Fix for #41: change outbound retry to int to fix signed comparison
warnings.
- Fix root_anchor test to check with new icannbundle date.
3 September 2021: Wouter
- Fix #538: Fix subnetcache statistics.
1 September 2021: Wouter
- Fix tcp fastopen failure when disabled, try normal connect instead.
27 August 2021: Wouter
- Fix #533: Negative responses get cached even when setting
cache-max-negative-ttl: 1
25 August 2021: Wouter
- Merge #401: RPZ triggers. This add additional RPZ triggers,
unbound supports a full set of rpz triggers, and this now
includes nsdname, nsip and clientip triggers. Also actions
are fully supported, and this now includes the tcp-only action.
- Fix #536: error: RPZ: name of record (drop.spamhaus.org.rpz.local.)
to insert into RPZ.
- Fix the stream wait stream_wait_count_lock and http2 buffer locks
setup and desetup from race condition.
- Fix RPZ locks. Do not unlock zones lock if requested and rpz find
zone does not find the zone. Readlock the clientip that is found
for ipbased triggers. Unlock the nsdname zone lock when done.
Unlock zone and ip in rpz nsip and nsdname callback. Unlock
authzone and localzone if clientip found in rpz worker call.
- Fix compile warning in libunbound for listen desetup routine.
- Fix asynclook unit test for setup of lockchecks before log.
20 August 2021: Wouter
- Fix #529: Fix: log_assert does nothing if UNBOUND_DEBUG is
undefined.
- Fix #531: Fix: passed to proc after free.
17 August 2021: Wouter
- Fix that --with-ssl can use "/usr/include/openssl11" to pass the
location of a different openssl version.
- Fix #527: not sending quad9 cert to syslog (and may be more).
- Fix sed script in ssldir split handling.
16 August 2021: George
- Merge PR #528 from fobser: Make sldns_str2wire_svcparam_buf()
static.
16 August 2021: Wouter
- Fix to support harden-algo-downgrade for ZONEMD dnssec checks.
13 August 2021: Wouter
- Support using system-wide crypto policies.
- Fix for #431: Squelch permission denied errors for udp connect,
and udp send, they are visible at higher verbosity settings.
- Fix zonemd verification of key that is not in DNS but in the zone
and needs a chain of trust.
- zonemd, fix order of bogus printout string manipulation.
12 August 2021: George
- Merge PR #514, from ziollek: Docker environment for run tests.
- For #514: generate configure.
12 August 2021: Wouter
- And 1.13.2rc1 became the 1.13.2 with the fix for the python module
build. The current code repository continues with version 1.13.3.
- Add test tool readzone to .gitignore.
- Merge #521: Update mini_event.c.
- Merge #523: fix: free() call more than once with the same pointer.
- Merge #519: Support for selective enabling tcp-upstream for
stub/forward zones.
- For #519: note stub-tcp-upstream and forward-tcp-upstream in
the example configuration file.
- For #519: yacc and lex. And fix python bindings, and test program
unbound-dnstap-socket.
- For #519: fix comments for doxygen.
- Fix to print error from unbound-anchor for writing to the key
file, also when not verbose.
5 August 2021: Wouter
- Tag for 1.13.2rc1 release.
- Fix #520: Unbound 1.13.2rc1 fails to build python module.
4 August 2021: George
- Merge PR #415 from sibeream: Use
/proc/sys/net/ipv4/ip_local_port_range to determine available outgoing
ports. (New --enable-linux-ip-local-port-range configuration option)
- Bump MAX_RESTART_COUNT to 11 from 8; in relation to #438. This
allows longer CNAME chains in Unbound.
4 August 2021: Wouter
- In unit test use openssl set security level to allow keys in test.
- Fix static analysis warnings about localzone locks that are unused.
- Fix missing locks in zonemd unit test.
- Fix readzone compile under debug config.
- Fix out of sourcedir run of zonemd unit tests.
- Fix libnettle zonemd unit test.
- Fix unit test zonemd_reload for use in run_vm.
3 August 2021: George
- Listen to read or write events after the SSL handshake.
Sticky events on windows would stick on read when write was needed.
3 August 2021: Wouter
- Merge PR #517 from dyunwei: #420 breaks the mesh reply list
function that need to reuse the dns answer.
- Annotate assertion into error printout; we think it may be an
error, but the situation looks harmless.
- Fix sign comparison warning on FreeBSD.
2 August 2021: Wouter
- Prepare for OpenSSL 3.0.0 provider API usage, move the sldns
keyraw functions to produce EVP_PKEY results.
- Move RSA and DSA to use OpenSSL 3.0.0 API.
- Move ECDSA functions to use OpenSSL 3.0.0 API.
- iana portlist update.
- Fix verbose printout failure in tcp reuse unit test.
30 July 2021: Wouter
- Fix #515: Compilation against openssl 3.0.0 beta2 is failing to
build unbound.
- For #515: Fix compilation with openssl 3.0.0 beta2, lib64 dir and
SSL_get_peer_certificate.
- Move acx_nlnetlabs.m4 to version 41, with lib64 openssl dir check.
26 July 2021: George
- Merge #513: Stream reuse, attempt to fix #411, #439, #469. This
introduces a couple of fixes for the stream reuse functionality
that could result in broken internal structures.
26 July 2021: Wouter
- Merge #512: unbound.service.in: upgrade hardening to latest
standards.
- Fix readzone unknown type print for memory resize.
21 July 2021: Wouter
- Fix that ldns_zone_new_frm_fp_l counts the line number for an empty
line after a comment.
16 July 2021: George
- Introduce 'http-user-agent:' and 'hide-http-user-agent:' options.
16 July 2021: Wouter
- Merge #510 from ndptech: Don't call a function which hasn't been
defined.
- Fix for #510: in depth, use ifdefs for windows api event calls.
- Fix spelling in doc/unbound.doxygen comment.
- Fix spelling in localzone.h comment.
- Fix unbound-control local_data and local_datas to print detailed
syntax errors.
- review fix to remove duplicate error printout.
- Insert header into testcode/readzone.c, it was missing.
- Fix from lint for ignored return value.
- Fix for older parsers for function call in serve expired get cached.
6 July 2021: Wouter
- iana portlist update.
5 July 2021: George
- Fix compiler warnings for #491.
- Fix clang-analysis warnings for testcode/readzone.c.
4 July 2021: George
- Fix Wunused-result compile warnings.
2 July 2021: Tom
- Merge PR #491: Add SVCB and HTTPS types and handling according to
draft-ietf-dnsop-svcb-https.
2 July 2021: Wouter
- Fix #506: Python Module Seems to Leak Memory if it Experiences an
Unhandled Exception.
25 June 2021: Wouter
- Fix up permissions on rpl data file in tests.
- Fix testbound newline treatment in moment_read and tempfile write.
- Fix configure grep for reuseport default for failure.
- Fix compat ctime_r return value
- Fix configure does not require pkg-config if not needed.
- Fix unit test in the ctime_r calls for autotrust and in testbound.
- Fix auth zone download on windows to unlink before rename.
24 June 2021: Wouter
- Add analyzer and port compile github workflow.
23 June 2021: Wouter
- Fix #503: DNS over HTTPS response truncated.
- Fix warnings reported by the gcc analyzer.
21 June 2021: George
- Fix #495: Documentation or implementation of "verbosity" option.
18 June 2021: Wouter
- Fix a number of warnings reported by the gcc analyzer.
15 June 2021: George
- Merge #440 by kimheino: Various fixes to contrib/unbound_munin_ file.
14 June 2021: Wouter
- Fix configure nonblocking test and onmingw test to use host.
10 June 2021: Wouter
- Fix #500: SPEC file in version 1.13.1 references version 1.4;
unable to build RPM from source.
- Fix contrib/unbound.spec, fixed url and comment.
9 June 2021: George
- Merge #486 by fobster: Make VAL_MAX_RESTART_COUNT configurable.
- Generated lexer and parser for #486; updated example.conf.
- Fix #413 (based on patch by k-ronny): unbound: does not compile
on macOS 11.1-x86_64 host.
- Use host_os instead of target_os in configure for Darwin8 build.
8 June 2021: George
- Fix unused variable warning when compiling with --enable-dnstap.
7 June 2021: George
- Merge #448 from shoeper: Update unbound-control.8.in, fix
rpz_disable typo.
- Fix #425: Document auth-zone supports communication with DNS
primary on nondefault port.
1 June 2021: George
- Fix test for zonemd-check option.
27 May 2021: Wouter
- Merge #496 from banburybill: Use build system endianness if
available, otherwise try to work it out.
- zonemd-check: yesno option, default no, enables the processing
of ZONEMD records for that zone.
25 May 2021: Wouter
- Move the NSEC3 max iterations count in line with the 150 value
used by BIND, Knot and PowerDNS. This sets the default value
for it in the configuration to 150 for all key sizes.
- Fix #492: module-config respip missing in unbound.conf.5.in man
page. Merges #494 from he32.
- For #492: Fix font highlighting for the man page on emacs.
21 May 2021: Wouter
- Test code has -q option for quiet output.
19 May 2021: George
- Fix for #411, #439, #469: Reset the DNS message ID when moving queries
between TCP streams.
- Refactor for uniform way to produce random DNS message IDs.
17 May 2021: Wouter
- Fix #489: Compile using MSYS2 MinGW 64-bit.
12 May 2021: Wouter
- Fix that auth-zone zonefiles use last TTL if no TTL is specified.
10 May 2021: Wouter
- Merge PR #487: ifdef RLIMIT_AS in recently added check.
7 May 2021: Wouter
- Fix #485: Unbound occasionally reports broken stats.
- Add ./configure --with-deprecate-rsa-1024 that turns off RSA 1024.
- Remove case fallthrough from deprecate-rsa-1024 code.
4 May 2021: George
- Fix for #367: only attempt to get the interface for queries that are no
longer on the tcp_waiting_list.
- Add more logging for out-of-memory cases.
4 May 2021: Wouter
- Merge #478: Allow configuration of TCP timeout while waiting for
response.
- Fix to squelch tcp socket bind failures when the interface is gone.
- Rerun flex and bison.
3 May 2021: Wouter
- Fix #481: Fix comment in configuration file.
29 April 2021: Wouter
- Add that log-servfail prints an IP address and more information
about one of the last failures for that query.
28 April 2021: George
- Fix compiler warning for signed/unsigned comparison for
max_reuse_tcp_queries.
28 April 2021: Wouter
- Fix #474: always_null and others inside view.
26 April 2021: Wouter
- Merge #470 from edevil: Allow configuration of persistent TCP
connections.
22 April 2021: Wouter
- Merge #466 from FGasper: Support OpenSSLs that lack
SSL_get0_alpn_selected.
- Fix #468: OpenSSL 1.0.1 can no longer build Unbound.
- Further fix for #468: detect SSL_CTX_set_alpn_protos for build with
OpenSSL 1.0.1.
- Fix that testcode dohclient has OpenSSL initialisation calls.
13 April 2021: George
- Fix documentation comment for files previously residing in checkconf/.
- Remove unused functions worker_handle_reply and libworker_handle_reply.
13 April 2021: Wouter
- Fix that nxdomain synthesis does not happen above the stub or
forward definition.
12 April 2021: George
- Fix (increase) verbosity level for iterator error log in
processQueryTargets().
12 April 2021: Wouter
- Fix permission denied sendto log, squelch the log messages
unless high verbosity is set.
9 April 2021: Wouter
- rebuild configure to set EXTRALINK to libunbound.la for #460.
7 April 2021: Wouter
- Fix for #411: Depth protect for crash on deleted element timeout.
1 April 2021: Wouter
- Merge #460 from orbea: build: Link with the libtool archive.
- Fix to stop IPv6 PMTU discovery.
31 March 2021: George
- Clean makedist.sh.
31 March 2021: Wouter
- Fix stack-protector change to not override other CFLAGS options.
30 March 2021: George
- Disable the use of stack-protector for cross compiled 32-bit windows
builds; relates to #444.
25 March 2021: Wouter
- Fix #429: Also fix end of transfer for http download of auth zones.
24 March 2021: Wouter
- Fix deprecation test to work for iOS TVOS and WatchOS, it uses
CFLAGS and CPPFLAGS and also checks if the item is unavailable.
- Travis, fix script to fail when tasks fail.
- Travis, fix warning in ubsan compile.
- Fix configure Targetconfiditionals.h header check, to use compile.
- Fix that cachedb does not produce empty object files when disabled.
23 March 2021: Wouter
- Travis enable all tests again. Clang analyzer only a couple times,
when there is a difference. homebrew updates disabled, so it does
not hang. removed trailing slashes from configure paths. Moved iOS
tests to allow-failure.
- travis, analyzer disabled on test without debug, that does not
run anway. Turn off failing tests except one. Update iOS test
to xcode image 12.2.
22 March 2021: George
- Fix unused-function warning when compiling with --enable-dnscrypt.
- Fix for #367: fix memory leak when cannot bind to listening port.
- Reformat pythonmod/pythonmod_utils.{c,h}.
22 March 2021: Wouter
- Merge #449 from orbea: build: Add missing linker flags.
- iana portlist update.
- Comment out nonworking OSX and IOS travis tests, vm fails to start.
- Fix compile error in listen_dnsport on Android.
- Fix memory leak reported by asan in rpz SOA record query name.
19 March 2021: Wouter
- Fix for #447: squelch connection refused tcp connection failures
from the log, unless verbosity is high.
17 March 2021: Wouter
- Fix #441: Minimal NSEC range not accepted for top level domains.
11 March 2021: Wouter
- Fix parse of LOC RR type for decimetres.
5 March 2021: Wouter
- Workaround for #439: prevent loops in the reuse rbtree.
- Debug output for #411 and #439: printout internal error and details.
4 March 2021: Wouter
- iana portlist update.
- Fix spurious errors about "Could not generate request: out of
memory". The mesh detect cycle routine no longer wrongly stops
the check when the calling mesh state is unique.
26 February 2021: George
- Fix for #367: rc_ports don't have ub_sock; skip cleaning up.
26 February 2021: Wouter
- Fix: Resolve interface names on control-interface too.
25 February 2021: Wouter
- Merge PR #367 : DNSTAP log local address. With code from PR #365
and fixes #368 : dnstap does not log the DNS message ID for
FORWARDER_QUERY.
- Fix to allow rpz with wildcard that applies to all TLDs at once.
24 February 2021: George
- Fix #384: (1) A minor request to improve the log (2) A minor bug in one
log message.
- ipsecmod: Better logging for detecting a cycle when attaching the
A/AAAA subquery.
24 February 2021: Wouter
- On startup of unbound it checks if rlimits on memory size look
sufficient for the configured cache size, and logs warning if not.
- Fix function documentation.
- Fix unit test for added ulimit checks.
- spelling fix in header.
23 February 2021: Wouter
- Fix for zonemd, that domain-insecure zones work without dnssec.
- Fix for zonemd, do not reject insecure result from trust anchor
validation step in dnssec chain of trust.
22 February 2021: Wouter
- Fix #431: Squelch permission denied errors for tcp connect
and udp connect from the logs, unless at high verbosity.
- Fix for zonemd, that nxdomain for the chain of trust is allowed
for island zones, it is treated as an insecure zone for verification.
18 February 2021: Wouter
- Merge PR #317: ZONEMD Zone Verification, with RFC 8976 support.
ZONEMD records are checked for zones loaded as auth-zone,
with DNSSEC if available. There is an added option
zonemd-permissive-mode that makes it log but not fail wrong zones.
With zonemd-reject-absence for an auth-zone the presence of a
zonemd can be mandated for specific zones.
- Fix doxygen and pydoc warnings.
- Fix #429: rpz: url: with https: broken (regression in 1.13.1).
- rpz skip nsec3param records, and nicer log for unsupported actions.
15 February 2021: Wouter
- Fix #422: IPv6 fallback issues when IPv6 is not properly
enabled/configured.
- Fix to make tests work with support indicators set for iterator.
- Fix build on Python 3.10.
10 February 2021: Wouter
- Merge PR #420 from dyunwei: DOH not responsing with
"http2_query_read_done failure" logged.
9 February 2021: Wouter
- Fix for Python 3.9, no longer use deprecated functions of
PyEval_CallObject (now PyObject_Call), PyEval_InitThreads (now
none), PyParser_SimpleParseFile (now Py_CompileString).
4 February 2021: Wouter
- release 1.13.1rc2 tag on branch-1.13.1 with added changes of 2 feb.
This became 1.13.1 release tag on 9 feb. The main branch is set
to version 1.13.2.
2 February 2021: Wouter
- branch-1.13.1 is created, with release-1.13.1rc1 tag.
- Fix dynlibmod link on rhel8 for -ldl inclusion.
- Fix windows dependency on libssp.dll because of default stack
protector in mingw.
- Fix indentation of root anchor for use by windows install script.
1 February 2021: George
- Attempt to fix NULL keys in the reuse_tcp tree; relates to #411.
29 January 2021: Wouter
- Fix for doxygen 1.8.20 compatibility.
28 January 2021: Wouter
- Annotate that we ignore the return value of if_indextoname.
- Fix to use correct type for label count in rpz routine.
- Fix empty clause warning in config_file nsid parse.
- Fix to use correct type for label count in ipdnametoaddr rpz routine.
- Fix empty clause warning in edns pass for padding.
- Fix fwd ancil test post script when not supported.
26 January 2021: George
- Merge PR #408 from fobser: Prevent a few more yacc clashes.
- Merge PR #275 from Roland van Rijswijk-Deij: Add feature to return the
original instead of a decrementing TTL ('serve-original-ttl')
- Merge PR #355 from noloader: Make ICANN Update CA and DS Trust Anchor
static data.
- Ignore cache blacklisting when trying to reply with expired data from
cache (#394).
26 January 2021: Wouter
- Fix compile of unbound-dnstap-socket without dnstap installed.
22 January 2021: Willem
- Padding of queries and responses with DNS over TLS as specified in
RFC7830 and RFC8467.
22 January 2021: George
- Fix TTL of SOA record for negative answers (localzone and
authzone data) to be the minimum of the SOA TTL and the SOA.MINIMUM.
19 January 2021: Willem
- Support for RFC5001: DNS Name Server Identifier (NSID) Option
with the nsid: option in unbound.conf
18 January 2021: Wouter
- Fix #404: DNS query with small edns bufsize fail.
- Fix declaration before statement and signed comparison warning in
dns64.
15 January 2021: Wouter
- Merge #402 from fobser: Implement IPv4-Embedded addresses according
to RFC6052.
14 January 2021: Wouter
- Fix for #93: dynlibmodule import library is named libunbound.dll.a.
13 January 2021: Wouter
- Merge #399 from xiangbao227: The lock of lruhash table should
unlocked after markdel entry.
- Fix for #93: dynlibmodule link fix for Windows.
12 January 2021: Wouter
- Fix #397: [Feature request] add new type always_null to local-zone
similar to always_nxdomain.
- Fix so local zone types always_nodata and always_deny can be used
from the config file.
8 January 2021: Wouter
- Merge PR #391 from fhriley: Add start_time to reply callbacks so
modules can compute the response time.
- For #391: use struct timeval* start_time for callback information.
- For #391: fix indentation.
- For #391: more double casts in python start time calculation.
- Add comment documentation.
- Fix clang analysis warning.
6 January 2021: Wouter
- Fix #379: zone loading over HTTP appears to have buffer issues.
- Merge PR #395 from mptre: add missing null check.
- Fix #387: client-subnet-always-forward seems to effectively bypass
any caching?
5 January 2021: Wouter
- Fix #385: autoconf 2.70 impacts unbound build
- Merge PR #375 by fhriley: Add rpz_enable and rpz_disable commands
to unbound-control.
4 January 2021: Wouter
- For #376: Fix that comm point event is not double removed or double
added to event map.
- iana portlist updated.
16 December 2020: George
- Fix error cases when udp-connect is set and send() returns an error
(modified patch from Xin Li @delphij).
11 December 2020: Wouter
- Fix #371: unbound-control timeout when Unbound is not running.
- Fix to squelch permission denied and other errors from remote host,
they are logged at higher verbosity but not on low verbosity.
- Merge PR #335 from fobser: Sprinkle in some static to prevent
missing prototype warnings.
- Merge PR #373 from fobser: Warning: arithmetic on a pointer to void
is a GNU extension.
- Fix missing prototypes in the code.
3 December 2020: Wouter
- make depend.
- iana portlist updated.
2 December 2020: Wouter
- Fix #360: for the additionally reported TCP Fast Open makes TCP
connections fail, in that case we print a hint that this is
happening with the error in the logs.
- Fix #356: deadlock when listening tcp.
- Fix unbound-dnstap-socket to not use log routine from interrupt
handler and not print so frequently when invoked in sequence.
- Fix on windows to ignore connection failure on UDP, unless verbose.
- Fix for #283: fix stream reuse and tcp fast open.
- Fix update, with write event check with streamreuse and fastopen.
1 December 2020: Wouter
- Fix #358: Squelch udp connect 'no route to host' errors on low
verbosity.
30 November 2020: Wouter
- Fix assertion failure on double callback when iterator loses
interest in query at head of line that then has the tcp stream
not kept for reuse.
- tag for the 1.13.0rc4 release. This also became the 1.13.0
release version on 3 dec 2020 with the streamreuse and fastopen
fix from 2 dec 2020. The code repo continues for 1.13.1 in
development.
27 November 2020: Wouter
- Fix compile warning for type cast in http2_submit_dns_response.
- Fix when use free buffer to initialize rbtree for stream reuse.
- Fix compile warnings for windows.
- Fix compile warnings in rpz initialization.
- Fix contrib/metrics.awk for FreeBSD awk compatibility.
- tag for the 1.13.0rc3 release.
26 November 2020: Wouter
- Fix to omit UDP receive errors from log, if verbosity low.
These happen because of udp-connect.
- For #352: contrib/metrics.awk for Prometheus style metrics output.
- Fix that after failed read, the readagain cannot activate.
- Clear readagain upon decommission of pending tcp structure.
25 November 2020: Wouter
- with udp-connect ignore connection refused with UDP timeouts.
- Fix udp-connect on FreeBSD, do send calls on connected UDP socket.
- Better fix for reuse tree comparison for is-tls sockets. Where
the tree key identity is preserved after cleanup of the TLS state.
- Remove debug commands from reuse tests.
- Fix memory leak for edns client tag opcode config element.
- Attempt fix for libevent state in tcp reuse cases after a packet
is written.
- Fix readagain and writeagain callback functions for comm point
cleanup.
- tag for the 1.13.0rc2 release.
24 November 2020: Wouter
- Merge PR #283 : Stream reuse. This implements upstream stream
reuse for performing several queries over the same TCP or TLS
channel.
- set version of main branch to 1.13.0 for upcoming release.
- iana portlist updated.
- Fix one port unit test for udp-connect.
- tag for the 1.13.0rc1 release.
- Fix crash when TLS connection is closed prematurely, when
reuse tree comparison is not properly identical to insertion.
- Fix padding of struct regional for 32bit systems.
23 November 2020: George
- Merge PR #313 from Ralph Dolmans: Replace edns-client-tag with
edns-client-string option.
23 November 2020: Wouter
- Merge #351 from dvzrv: Add AF_NETLINK to set of allowed socket
address families.
- Fix #350: with the AF_NETLINK permission, to fix 1.12.0 error:
failed to list interfaces: getifaddrs: Address family not
supported by protocol.
- Fix #347: IP_DONTFRAG broken on Apple xcode 12.2.
- Option to toggle udp-connect, default is enabled.
- Fix for #303 CVE-2020-28935 : Fix that symlink does not interfere
with chown of pidfile.
- Further fix for it and retvalue 0 fix for it.
12 November 2020: Wouter
- Fix to connect() to UDP destinations, default turned on,
this lowers vulnerability to ICMP side channels.
- Retry for interfaces with unused ports if possible.
10 November 2020: Wouter
- Fix #341: fixing a possible memory leak.
- Fix memory leak after fix for possible memory leak failure.
- Fix #343: Fail to build --with-libnghttp2 with error: 'SSIZE_MAX'
undeclared.
27 October 2020: Wouter
- In man page note that tls-cert-bundle is read before permission
drop and chroot.
22 October 2020: Wouter
- Fix #333: Unbound Segmentation Fault w/ log_info Functions From
Python Mod.
- Fix that minimal-responses does not remove addresses from a priming
query response.
21 October 2020: George
- Fix #327: net/if.h check fails on some darwin versions; contribution by
Joshua Root.
- Fix #320: potential memory corruption due to size miscomputation upton
custom region alloc init.
21 October 2020: Wouter
- Merge PR #228 : infra-keep-probing option to probe hosts that are
down. Add infra-keep-probing: yes option. Hosts that are down are
probed more frequently.
With the option turned on, it probes about every 120 seconds,
eventually after exponential backoff, and that keeps that way. If
traffic keeps up for the domain. It probes with one at a time, eg.
one query is allowed to probe, other queries within that 120 second
interval are turned away.
19 October 2020: George
- Merge PR #324 from James Renken: Add modern X.509v3 extensions to
unbound-control TLS certificates.
- Fix for PR #324 to attach the x509v3 extensions to the client
certificate.
19 October 2020: Ralph
- local-zone regional allocations outside of chunk
19 October 2020: Wouter
- Fix that http settings have colon in set_option, for
http-endpoint, http-max-streams, http-query-buffer-size,
http-response-buffer-size, and http-nodelay.
- Fix memory leak of https port string when reading config.
- Fix #330: [Feature request] Add unencrypted DNS over HTTPS support.
This adds the option http-notls-downstream: yesno to change that,
and the dohclient test code has the -n option.
- Fix python documentation warning on functions.rst inplace_cb_reply.
- Fix dnstap test to wait for log timer to see if queries are logged.
- Log ip address when http session recv fails, eg. due to tls fail.
- Fix to set the tcp handler event toggle flag back to default when
the handler structure is reused.
- Clean the fix for out of order TCP processing limits on number
of queries. It was tested to work.
16 October 2020: Wouter
- Fix that the out of order TCP processing does not limit the
number of outstanding queries over a connection.
15 October 2020: George
- Fix that if there are reply callbacks for the given rcode, those
are called per reply and a new message created if that was modified
by the call.
- Pass the comm_reply information to the inplace_cb_reply* functions
during the mesh state and update the documentation on that.
15 October 2020: Wouter
- Merge PR #326 from netblue30: DoH: implement content-length
header field
- DoH content length, simplify code, remove declaration after
statement and fix cast warning.
14 October 2020: Wouter
- Fix for python reply callback to see mesh state reply_list member,
it only removes it briefly for the commpoint call so that it does
not drop it and attempt to modify the reply list during reply.
- Fix that if there are on reply callbacks, those are called per
reply and a new message created if that was modified by the call.
- Free up auth zone parse region after use for lookup of host
13 October 2020: Wouter
- Fix #323: unbound testsuite fails on mock build in systemd-nspawn
if systemd support is build.
9 October 2020: Wouter
- Fix dnstap socket and the chroot not applied properly to the dnstap
socket path.
- Fix warning in libnss compile, nss_buf2dsa is not used without DSA.
8 October 2020: Wouter
- Tag for 1.12.0 release.
- Current repo is version 1.12.1 in development.
- Fix #319: potential memory leak on config failure, in rpz config.
1 October 2020: Wouter
- Current repo is version 1.12.0 for release. Tag for 1.12.0rc1.
30 September 2020: Wouter
- Fix doh tests when not compiled in.
- Add dohclient test executable to gitignore.
- Fix stream_ssl, ssl_req_order and ssl_req_timeout tests for
alloc check debug output.
- Easier kill of unbound-dnstap-socket tool in test.
- Fix memory leak of edns tags at libunbound context delete.
- Fix double loopexit for unbound-dnstap-socket after sigterm.
29 September 2020: Ralph
- DNS Flag Day 2020: change edns-buffer-size default to 1232.
28 September 2020: Wouter
- Fix unit test for dnstap changes, so that it waits for the timer.
23 September 2020: Wouter
- Fix #305: dnstap logging significantly affects unbound performance
(regression in 1.11).
- Fix #305: only wake up thread when threshold reached.
- Fix to ifdef fptr wlist item for dnstap.
23 September 2020: Ralph
- Fix edns-client-tags get_option typo
- Add edns-client-tag-opcode option
- Use inclusive language in configuration
21 September 2020: Ralph
- Fix #304: dnstap logging not recovering after dnstap process restarts
21 September 2020: Wouter
- Merge PR #311 by luismerino: Dynlibmod leak.
- Error message is logged for dynlibmod malloc failures.
- iana portlist updated.
18 September 2020: Wouter
- Fix that prefer-ip4 and prefer-ip6 can be get and set with
unbound-control, with libunbound and the unbound-checkconf option
output function.
- iana portlist updated.
15 September 2020: George
- Introduce test for statistics.
15 September 2020: Wouter
- Spelling fix.
11 September 2020: Wouter
- Remove x file mode on ipset/ipset.c and h files.
9 September 2020: Wouter
- Fix num.expired statistics output.
31 August 2020: Wouter
- Merge PR #293: Add missing prototype. Also refactor to use the new
shorthand function to clean up the code.
- Refactor to use sock_strerr shorthand function.
- Fix #296: systemd nss-lookup.target is reached before unbound can
successfully answer queries. Changed contrib/unbound.service.in.
27 August 2020: Wouter
- Similar to NSD PR#113, implement that interface names can be used,
eg. something like interface: eth0 is resolved at server start and
uses the IP addresses for that named interface.
- Review fix, doxygen and assign null in case of error free.
26 August 2020: George
- Update documentation in python example code.
24 August 2020: Wouter
- Fix that dnstap reconnects do not spam the log with the repeated
attempts. Attempts on the timer are only logged on high verbosity,
if they produce a connection failure error.
- Fix to apply chroot to dnstap-socket-path, if chroot is enabled.
- Change configure to use EVP_sha256 instead of HMAC_Update for
openssl-3.0.0.
20 August 2020: Ralph
- Fix stats double count issue (#289).
13 August 2020: Ralph
- Create and init edns tags data for libunbound.
10 August 2020: Ralph
- Merge (modified) PR #277, use EVP_MAC_CTX_set_params if available,
by Vítězslav Čížek.
10 August 2020: Wouter
- Fix #287: doc typo: "Additionaly".
- Rerun autoconf
6 August 2020: Wouter
- Merge PR #284 and Fix #246: Remove DLV entirely from Unbound.
The DLV has been decommisioned and in unbound 1.5.4, in 2015, there
was advise to stop using it. The current code base does not contain
DLV code any more. The use of dlv options displays a warning.
5 August 2020: Wouter
- contrib/aaaa-filter-iterator.patch file renewed diff content to
apply cleanly to the current coderepo for the current code version.
5 August 2020: Ralph
- Merge PR #272: Add EDNS client tag functionality.
4 August 2020: George
- Improve error log message when inserting rpz RR.
- Merge PR #280, Make tvOS & watchOS checks verify truthiness as well as
definedness, by Felipe Gasper.
4 August 2020: Wouter
- Fix mini_event.h on OpenBSD cannot find fd_set.
31 July 2020: Wouter
- Fix doxygen comment for no ssl for tls session ticket key callback
routine.
27 July 2020: George
- Merge PR #268, draft-ietf-dnsop-serve-stale-10 has become RFC 8767 on
March 2020, by and0x000.
27 July 2020: Ralph
- Merge PR #269, Fix python module len() implementations, by Torbjörn
Lönnemark
27 July 2020: Wouter
- branch now named 1.11.1. 1.11.0rc1 became the 1.11.0 release.
- Merge PR #270 from cgzones: munin plugin: always exit 0 in autoconf
20 July 2020: Wouter
- Fix streamtcp to print packet data to stdout. This makes the
stdout and stderr not mix together lines, when parsing its output.
- Fix contrib/fastrpz.patch to apply cleanly. It fixes for changes
due to added libdynmod, but it does not compile, it conflicts with
new rpz code.
- branch now named 1.11.0 and 1.11.0rc1 tag.
17 July 2020: Wouter
- Fix libnettle compile for session ticket key callback function
changes.
- Fix lock dependency cycle in rpz zone config setup.
17 July 2020: Ralph
- Merge PR #234 - Ensure proper alignment of cmsg buffers by Jérémie
Courrèges-Anglas.
- Fix PR #234 log_assert sizeof to use union buffer.
16 July 2020: Wouter
- Fix check conf test for referencing installation paths.
- Fix unused variable warning for clang analyzer.
16 July 2020: George
- Introduce 'include-toplevel:' configuration option.
16 July 2020: Ralph
- Add bidirectional frame streams support.
8 July 2020: Wouter
- Fix add missing DSA header, for compilation without deprecated
OpenSSL APIs.
- Fix to use SSL_CTX_set_tlsext_ticket_key_evp_cb in OpenSSL
3.0.0-alpha4.
- Longer keys for the test set, this avoids weak crypto errors.
7 July 2020: Wouter
- Fix #259: Fix unbound-checkconf does not check view existence.
unbound-checkconf checks access-control-view, access-control-tags,
access-control-tag-actions and access-control-tag-datas.
- Fix offset of error printout for access-control-tag-datas.
- Review fixes for checkconf #259 change.
6 July 2020: Wouter
- run_vm cleanup better and removes trailing slash on single argument.
29 June 2020: Wouter
- Move reply list clean for serve expired mesh callback to after
the reply is sent, so that script callbacks have reply_info.
- Also move reply list clean for mesh callbacks to the scrip callback
can see the reply_info.
- Fix for mesh accounting if the reply list already empty to begin
with.
- Fix for mesh accounting when rpz decides to drop a reply with a
tcp stream waiting for it.
- Review fix for number of detached states due to use of variable
after end of loop.
- Fix tcp req info drop due to size call into mesh accounting
removal of mesh state during mesh send reply.
24 June 2020: Wouter
- iana portlist updated.
- doxygen file comments for dynlibmodule.
17 June 2020: Wouter
- Fix default explanation in man page for qname-minimisation-strict.
- Fix display of event loop method with libev.
8 June 2020: Wouter
- Mention tls name possible when tls is enabled for stub-addr in the
man page.
27 May 2020: George
- Merge PR #241 by Robert Edmonds: contrib/libunbound.pc.in: Do not use
"Requires:".
25 May 2020: George
- Update contrib/aaaa-filter-iterator.patch for the recent
generate_sub_request() change and to apply cleanly.
21 May 2020: George
- Fix for integer overflow when printing RDF_TYPE_TIME.
19 May 2020: Wouter
- CVE-2020-12662 Unbound can be tricked into amplifying an incoming
query into a large number of queries directed to a target.
- CVE-2020-12663 Malformed answers from upstream name servers can be
used to make Unbound unresponsive.
- Release 1.10.1 is 1.10.0 with fixes, code repository continues,
including those fixes, towards the next release. Configure has
version 1.10.2 version number in it.
- For PR #93: windows compile warnings removal
- windows compile warnings removal for ip dscp option code.
- For PR #93: unit test for dynlib module.
18 May 2020: Wouter
- For PR #93: dynlibmod can handle reloads and deinit and inits again,
with dlclose and dlopen of the library again. Also for multiple
modules. Fix memory leak by not closing dlopened content. Fix
to allow one dynlibmod instance by unbound-checkconf.
- For PR #93: checkconf allows multiple dynlib in module-config, for
a couple cases.
- For PR #93: checkconf allows python dynlib in module-config, for
a couple cases.
- For PR #93: man page spelling reference fix.
- For PR #93: fix link of other executables for dynlibmod dependency.
15 May 2020: Wouter
- Merge PR #93: Add dynamic library support.
- Fixed conflicts for PR #93 and make configure, yacc, lex.
- For PR #93: Fix warnings for dynlibmodule.
15 May 2020: Ralph
- Cache ECS answers with longest scope of CNAME chain.
22 April 2020: George
- Explicitly use 'rrset-roundrobin: no' for test cases.
21 April 2020: Wouter
- Merge #225 from akhait: KSK-2010 has been revoked. It removes the
KSK-2010 from the default list in unbound-anchor, now that the
revocation period is over. KSK-2017 is the only trust anchor in
the shipped default now.
21 April 2020: George
- Change default value for 'rrset-roundrobin' to yes.
- Fix tests for new rrset-roundrobin default.
20 April 2020: Wouter
- Fix #222: --enable-rpath, fails to rpath python lib.
- Fix for count of reply states in the mesh.
- Remove unneeded was_mesh_reply check.
17 April 2020: George
- Add SNI support on more TLS connections (fixes #193).
- Add SNI support to unbound-anchor.
16 April 2020: George
- Add doxygen documentation for DSCP.
16 April 2020: Wouter
- Fix help return code in unbound-control-setup script.
- Fix for posix shell syntax for trap in nsd-control-setup.
- Fix for posix shell syntax for trap in run_msg.sh test script.
15 April 2020: George
- Fix #220: auth-zone section in config may lead to segfault.
7 April 2020: Wouter
- Merge PR #214 from gearnode: unbound-control-setup recreate
certificates. With the -r option the certificates are created
again, without it, only the files that do not exist are created.
6 April 2020: Ralph
- Keep track of number of timeouts. Use this counter to determine if
capsforid fallback should be started.
6 April 2020: George
- More documentation for redis-expire-records option.
1 April 2020: George
- Merge PR #206: Redis TTL, by Talkabout.
30 March 2020: Wouter
- Merge PR #207: Clarify if-automatic listens on 0.0.0.0 and ::
- Merge PR #208: Fix uncached CLIENT_RESPONSE'es on stateful
transports.
27 March 2020: Wouter
- Merge PR #203 from noloader: Update README-Travis.md with current
procedures.
27 March 2020: Ralph
- Make unbound-control error returned on missing domain name more user
friendly.
26 March 2020: Ralph
- Fix RPZ concurrency issue when using auth_zone_reload.
25 March 2020: George
- Merge PR #201 from noloader: Fix OpenSSL cross-compaile warnings.
- Fix on #201.
24 March 2020: Wouter
- Merge PR #200 from yarikk: add ip-dscp option to specify the DSCP
tag for outgoing packets.
- Fixes on #200.
- Travis fix for ios by omitting tools from install.
23 March 2020: Wouter
- Fix compile on Solaris for unbound-checkconf.
20 March 2020: George
- Merge PR #198 from fobser: Declare lz_enter_rr_into_zone() static, it's
only used in this file.
20 March 2020: Wouter
- Merge PR #197 from fobser: Make log_ident_revert_to_default() a
proper prototype.
19 March 2020: Ralph
- Merge PR#191: Update iOS testing on Travis, by Jeffrey Walton.
- Fix #158: open tls-session-ticket-keys as binary, for Windows. By
Daisuke HIGASHI.
- Merge PR#134, Allow the kernel to provide random source ports. By
Florian Obser.
- Log warning when using outgoing-port-permit and outgoing-port-avoid
while explicit port randomisation is disabled.
- Merge PR#194: Add libevent testing to Travis, by Jeffrey Walton.
- Fix .travis.yml error, missing 'env' option.
16 March 2020: Wouter
- Fix #192: In the unbound-checkconf tool, the module config of
dns64 subnetcache respip validator iterator is whitelisted, it was
reported it seems to work.
12 March 2020: Wouter
- Fix compile of test tools without protobuf.
11 March 2020: Ralph
- Add check to make sure RPZ records are subdomains of configured
zone origin.
11 March 2020: George
- Fix #189: mini_event.h:142:17: error: field 'ev_timeout' has incomplete
type, by noloader.
- Changelog entry for (Fix #189, Merge PR #190).
11 March 2020: Wouter
- Fix #188: unbound-control.c:882:6: error: 'execlp' is
unavailable: not available on tvOS.
6 March 2020: George
- Merge PR #186, fix #183: Fix unrecognized 'echo -n' option on OS X, by
noloader
5 March 2020: Wouter
- Fix PR #182 from noloader: Add iOS testing to Travis.
4 March 2020: Ralph
- Update README-Travis.md (from PR #179), by Jeffrey Walton.
4 March 2020: George
- Merge PR #181 from noloader: Fix OpenSSL -pie warning on Android.
4 March 2020: Wouter
- Merge PR #180 from noloader: Avoid calling exit in Travis script.
3 March 2020: George
- Upgrade config.guess(2020-01-01) and config.sub(2020-01-01).
2 March 2020: Ralph
- Fix #175, Merge PR #176: fix link error when OpenSSL is configured
with no-engine, thanks noloader.
2 March 2020: George
- Fix compiler warning in dns64/dns64.c
- Merge PR #174: Add Android to Travis testing, by noloader.
- Move android build scripts to contrib/ and allow android tests to fail.
2 March 2020: Wouter
- Fix #177: dnstap does not build on macOS.
28 February 2020: Ralph
- Merge PR #172: Add IBM s390x arch for testing, by noloader.
28 February 2020: Wouter
- Merge PR #173: updated makedist.sh for config.guess and
config.sub and sha256 digest for gpg, by noloader.
- Merge PR #164: Framestreams, this branch implements dnstap
unidirectional connectivity in unbound. This has a number of
new features.
The dependency on libfstrm is removed. The fstrm protocol code
resides in dnstap/dnstap_fstrm.h and dnstap/dnstap_fstrm.c. This
contains a brief definition of what unbound needs.
The make unbound-dnstap-socket builds a debug tool,
unbound-dnstap-socket. It can listen, accept multiple DNSTAP
streams and print information. Commandline options control it.
Unbound can reconnect if the unix domain socket file socket is
closed. This uses exponential backoff after which it uses a
one second timer to throttle cpu down. There is also support
to use TCP and TLS for connecting to the log server. There
are new config options to turn them on, in the dnstap section
in the man page and example config file. dnstap-ip with IP
address of server for TCP or TLS use. dnstap-tls to turn
on TLS. And dnstap-tls-server-name, dnstap-tls-cert-bundle,
dnstap-tls-client-key-file and dnstap-tls-client-cert-file
to configure the certificates for server authentication and
client authentication, or leave at "" to not use that.
27 February 2020: George
- Merge PR #171: Add additional compilers and platforms to Travis
testing, by noloader.
27 February 2020: Wouter
- Fix #169: Fix warning for daemon/remote.c output may be truncated
from snprintf.
- Fix #170: Fix gcc undefined sanitizer signed integer overflow
warning in signature expiry RFC1982 serial number arithmetic.
- Fix more undefined sanitizer issues, in respip copy_rrset null
dname, and in the client_info_compare routine for null memcmp.
26 February 2020: Wouter
- iana portlist updated.
25 February 2020: Wouter
- Fix #165: Add prefer-ip4: yesno config option to prefer ipv4 for
using ipv4 filters, because the hosts ip6 netblock /64 is not owned
by one operator, and thus reputation is shared.
24 February 2020: George
- Merge PR #166: Fix typo in unbound.service.in, by glitsj16.
20 February 2020: Wouter
- Updated contrib/unbound_smf23.tar.gz with Solaris SMF service for
Unbound from Yuri Voinov.
- master branch has 1.10.1 version.
18 February 2020: Wouter
- protect X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS with ifdef for
different openssl versions.
17 February 2020: Wouter
- changelog point where the tag for 1.10.0rc2 release is. And with
the unbound_smf23 commit added to it, that is the 1.10.0 release.
17 February 2020: Ralph
- Add respip to supported module-config options in unbound-checkconf.
17 February 2020: George
- Remove unused variable.
17 February 2020: Wouter
- contrib/drop2rpz: perl script that converts the Spamhaus DROP-List
in RPZ-Format, contributed by Andreas Schulze.
14 February 2020: Wouter
- Fix spelling in unbound.conf.5.in.
- Stop unbound-checkconf from insisting that auth-zone and rpz
zonefiles have to exist. They can not exist, and download later.
13 February 2020: Wouter
- tag for 1.10.0rc1 release.
12 February 2020: Wouter
- Fix with libnettle make test with dsa disabled.
- Fix contrib/fastrpz.patch to apply cleanly. Fix for serve-stale
fixes, but it does not compile, conflicts with new rpz code.
- Fix to clean memory leak of respip_addr.lock when ip_tree deleted.
- Fix compile warning when threads disabled.
- updated version number to 1.10.0.
10 February 2020: George
- Document 'ub_result.was_ratelimited' in libunbound.
- Fix use after free on log-identity after a reload; Fixes #163.
6 February 2020: George
- Fix num_reply_states and num_detached_states counting with
serve_expired_callback.
- Cleaner code in mesh_serve_expired_lookup.
- Document in unbound.conf manpage that configuration clauses can be
repeated in the configuration file.
6 February 2020: Wouter
- Fix num_reply_addr counting in mesh and tcp drop due to size
after serve_stale commit.
- Fix to create and destroy rpz_lock in auth_zones structure.
- Fix to lock zone before adding rpz qname trigger.
- Fix to lock and release once in mesh_serve_expired_lookup.
- Fix to put braces around empty if body when threading is disabled.
5 February 2020: George
- Added serve-stale functionality as described in
draft-ietf-dnsop-serve-stale-10. `serve-expired-*` options can be used
to configure the behavior.
- Updated cachedb to honor `serve-expired-ttl`; Fixes #107.
- Renamed statistic `num.zero_ttl` to `num.expired` as expired replies
come with a configurable TTL value (`serve-expired-reply-ttl`).
- Fixed stats when replying with cached, cname-aliased records.
- Added missing default values for redis cachedb backend.
3 February 2020: Ralph
- Add assertion to please static analyzer
31 January 2020: Wouter
- Fix fclose on error in TLS session ticket code.
30 January 2020: Ralph
- Fix memory leak in error condition remote.c
- Fix double free in error condition view.c
- Fix memory leak in do_auth_zone_transfer on success
- Merge RPZ support into master. Only QNAME and Response IP triggers are
supported.
- Stop working on socket when socket() call returns an error.
- Check malloc return values in TLS session ticket code
30 January 2020: Wouter
- Fix subnet tests for disabled DSA algorithm by default.
- Update contrib/fastrpz.patch for clean diff with current code.
- Merge PR#151: Fixes for systemd units, by Maryse47, Edmonds
and Frzk. Updates the unbound.service systemd file and adds
a portable systemd service file.
- updated .gitignore for added contrib file.
- Add build rule for ipset to Makefile
- Add getentropy_freebsd.o to Makefile dependencies.
29 January 2020: Ralph
- Merge PR#156 from Alexander Berkes; Added unbound-control
view_local_datas_remove command.
29 January 2020: Wouter
- Fix #157: undefined reference to `htobe64'.
28 January 2020: Ralph
- Merge PR#147; change rfc reference for reserved top level dns names.
28 January 2020: Wouter
- iana portlist updated.
- Fix to silence the tls handshake errors for broken pipe and reset
by peer, unless verbosity is set to 2 or higher.
27 January 2020: Ralph
- Merge PR#154; Allow use of libbsd functions with configure option
--with-libbsd. By Robert Edmonds and Steven Chamberlain.
- Merge PR#148; Add some TLS stats to unbound_munin_. By Fredrik Pettai.
27 January 2020: Wouter
- Merge PR#155 from Robert Edmonds: contrib/libunbound.pc.in: Fixes
to Libs/Requires for crypto library dependencies.
- Fix #153: Disable validation for DSA algorithms. RFC 8624
compliance.
23 January 2020: Wouter
- Merge PR#150 from Frzk: Systemd unit without chroot. It add
contrib/unbound_nochroot.service.in, a systemd file for use with
chroot: "", see comments in the file, it uses systemd protections
instead.
14 January 2020: Wouter
- Removed the dnscrypt_queries and dnscrypt_queries_chacha tests,
because dnscrypt-proxy (2.0.36) does not support the test setup
any more, and also the config file format does not seem to have
the appropriate keys to recreate that setup.
- Fix crash after reload where a stats lookup could reference old key
cache and neg cache structures.
- Fix for memory leak when edns subnet config options are read when
compiled without edns subnet support.
- Fix auth zone support for NSEC3 records without salt.
10 January 2020: Wouter
- Fix the relationship between serve-expired and prefetch options,
patch from Saksham Manchanda from Secure64.
- Fix unreachable code in ssl set options code.
8 January 2020: Ralph
- Fix #138: stop binding pidfile inside chroot dir in systemd service
file.
8 January 2020: Wouter
- Fix 'make test' to work for --disable-sha1 configure option.
- Fix out-of-bounds null-byte write in sldns_bget_token_par while
parsing type WKS, reported by Luis Merino from X41 D-Sec.
- Updated sldns_bget_token_par fix for also space for the zero
delimiter after the character. And update for more spare space.
6 January 2020: George
- Downgrade compat/getentropy_solaris.c to version 1.4 from OpenBSD.
The dl_iterate_phdr() function introduced in newer versions raises
compilation errors on solaris 10.
- Changes to compat/getentropy_solaris.c for,
ifdef stdint.h inclusion for older systems.
ifdef sha2.h inclusion for older systems.
6 January 2020: Wouter
- Merge #135 from Florian Obser: Use passed in neg and key cache
if non-NULL.
- Fix #140: Document slave not downloading new zonefile upon update.
16 December 2019: George
- Update mailing list URL.
12 December 2019: Ralph
- Master is 1.9.7 in development.
- Fix typo to let serve-expired-ttl work with ub_ctx_set_option(), by
Florian Obser
10 December 2019: Wouter
- Fix to make auth zone IXFR to fallback to AXFR if a single
response RR is received over TCP with the SOA in it.
6 December 2019: Wouter
- Fix ipsecmod compile.
- Fix Makefile.in for ipset module compile, from Adi Prasaja.
- release-1.9.6 tag, which became the 1.9.6 release
5 December 2019: Wouter
- unbound-fuzzers.tar.bz2: three programs for fuzzing, that are 1:1
replacements for unbound-fuzzme.c that gets created after applying
the contrib/unbound-fuzzme.patch. They are contributed by
Eric Sesterhenn from X41 D-Sec.
- tag for 1.9.6rc1.
4 December 2019: Wouter
- Fix lock type for memory purify log lock deletion.
- Fix testbound for alloccheck runs, memory purify and lock checks.
- update contrib/fastrpz.patch to apply more cleanly.
- Fix Make Test Fails when Configured With --enable-alloc-nonregional,
reported by X41 D-Sec.
3 December 2019: Wouter
- Merge pull request #124 from rmetrich: Changed log lock
from 'quick' to 'basic' because this is an I/O lock.
- Fix text around serial arithmatic used for RRSIG times to refer
to correct RFC number.
- Fix Assert Causing DoS in synth_cname(),
reported by X41 D-Sec.
- Fix similar code in auth_zone synth cname to add the extra checks.
- Fix Assert Causing DoS in dname_pkt_copy(),
reported by X41 D-Sec.
- Fix OOB Read in sldns_wire2str_dname_scan(),
reported by X41 D-Sec.
- Fix Out of Bounds Write in sldns_str2wire_str_buf(),
reported by X41 D-Sec.
- Fix Out of Bounds Write in sldns_b64_pton(),
fixed by check in sldns_str2wire_int16_data_buf(),
reported by X41 D-Sec.
- Fix Insufficient Handling of Compressed Names in dname_pkt_copy(),
reported by X41 D-Sec.
- Fix Out of Bound Write Compressed Names in rdata_copy(),
reported by X41 D-Sec.
- Fix Hang in sldns_wire2str_pkt_scan(),
reported by X41 D-Sec.
This further lowers the max to 256.
- Fix snprintf() supports the n-specifier,
reported by X41 D-Sec.
- Fix Bad Indentation, in dnscrypt.c,
reported by X41 D-Sec.
- Fix Client NONCE Generation used for Server NONCE,
reported by X41 D-Sec.
- Fix compile error in dnscrypt.
- Fix _vfixed not Used, removed from sbuffer code,
reported by X41 D-Sec.
- Fix Hardcoded Constant, reported by X41 D-Sec.
- make depend
2 December 2019: Wouter
- Merge pull request #122 from he32: In tcp_callback_writer(),
don't disable time-out when changing to read.
22 November 2019: George
- Fix compiler warnings.
22 November 2019: Wouter
- Fix dname loop maximum, reported by Eric Sesterhenn from X41 D-Sec.
- Add make distclean that removes everything configure produced,
and make maintainer-clean that removes bison and flex output.
20 November 2019: Wouter
- Fix Out of Bounds Read in rrinternal_get_owner(),
reported by X41 D-Sec.
- Fix Race Condition in autr_tp_create(),
reported by X41 D-Sec.
- Fix Shared Memory World Writeable,
reported by X41 D-Sec.
- Adjust unbound-control to make stats_shm a read only operation.
- Fix Weak Entropy Used For Nettle,
reported by X41 D-Sec.
- Fix Randomness Error not Handled Properly,
reported by X41 D-Sec.
- Fix Out-of-Bounds Read in dname_valid(),
reported by X41 D-Sec.
- Fix Config Injection in create_unbound_ad_servers.sh,
reported by X41 D-Sec.
- Fix Local Memory Leak in cachedb_init(),
reported by X41 D-Sec.
- Fix Integer Underflow in Regional Allocator,
reported by X41 D-Sec.
- Upgrade compat/getentropy_linux.c to version 1.46 from OpenBSD.
- Synchronize compat/getentropy_win.c with version 1.5 from
OpenBSD, no changes but makes the file, comments, identical.
- Upgrade compat/getentropy_solaris.c to version 1.13 from OpenBSD.
- Upgrade compat/getentropy_osx.c to version 1.12 from OpenBSD.
- Changes to compat/getentropy files for,
no link to openssl if using nettle, and hence config.h for
HAVE_NETTLE variable.
compat definition of MAP_ANON, for older systems.
ifdef stdint.h inclusion for older systems.
ifdef sha2.h inclusion for older systems.
- Fixed Compat Code Diverging from Upstream, reported by X41 D-Sec.
- Fix compile with --enable-alloc-checks, reported by X41 D-Sec.
- Fix Terminating Quotes not Written, reported by X41 D-Sec.
- Fix Useless memset() in validator, reported by X41 D-Sec.
- Fix Unrequired Checks, reported by X41 D-Sec.
- Fix Enum Name not Used, reported by X41 D-Sec.
- Fix NULL Pointer Dereference via Control Port,
reported by X41 D-Sec.
- Fix Bad Randomness in Seed, reported by X41 D-Sec.
- Fix python examples/calc.py for eval, reported by X41 D-Sec.
- Fix comments for doxygen in dns64.
19 November 2019: Wouter
- Fix CVE-2019-18934, shell execution in ipsecmod.
- 1.9.5 is 1.9.4 with bugfix, trunk is 1.9.6 in development.
- Fix authzone printout buffer length check.
- Fixes to please lint checks.
- Fix Integer Overflow in Regional Allocator,
reported by X41 D-Sec.
- Fix Unchecked NULL Pointer in dns64_inform_super()
and ipsecmod_new(), reported by X41 D-Sec.
- Fix Out-of-bounds Read in rr_comment_dnskey(),
reported by X41 D-Sec.
- Fix Integer Overflows in Size Calculations,
reported by X41 D-Sec.
- Fix Integer Overflow to Buffer Overflow in
sldns_str2wire_dname_buf_origin(), reported by X41 D-Sec.
- Fix Out of Bounds Read in sldns_str2wire_dname(),
reported by X41 D-Sec.
- Fix Out of Bounds Write in sldns_bget_token_par(),
reported by X41 D-Sec.
18 November 2019: Wouter
- In unbound-host use separate variable for get_option to please
code checkers.
- update to bison output of 3.4.1 in code repository.
- Provide a prototype for compat malloc to remove compile warning.
- Portable grep usage for reuseport configure test.
- Check return type of HMAC_Init_ex for openssl 0.9.8.
- gitignore .source tempfile used for compatible make.
13 November 2019: Wouter
- iana portlist updated.
- contrib/fastrpz.patch updated to apply for current code.
- fixes for splint cleanliness, long vs int in SSL set_mode.
11 November 2019: Wouter
- Fix #109: check number of arguments for stdin-pipes in
unbound-control and fail if too many arguments.
- Merge #102 from jrtc27: Add getentropy emulation for FreeBSD.
24 October 2019: Wouter
- Fix #99: Memory leak in ub_ctx (event_base will never be freed).
23 October 2019: George
- Add new configure option `--enable-fully-static` to enable full static
build if requested; in relation to #91.
23 October 2019: Wouter
- Merge #97: manpage: Add missing word on unbound.conf,
from Erethon.
22 October 2019: Wouter
- drop-tld.diff: adds option drop-tld: yesno that drops 2 label
queries, to stop random floods. Apply with
patch -p1 < contrib/drop-tld.diff and compile.
From Saksham Manchanda (Secure64). Please note that we think this
will drop DNSKEY and DS lookups for tlds and hence break DNSSEC
lookups for downstream clients.
7 October 2019: Wouter
- Add doxygen comments to unbound-anchor source address code, in #86.
3 October 2019: Wouter
- Merge #90 from vcunat: fix build with nettle-3.5.
- Merge 1.9.4 release with fix for vulnerability CVE-2019-16866.
- Continue with development of 1.9.5.
- Merge #86 from psquarejho: Added -b source address option to
smallapp/unbound-anchor.c, from Lukas Wunner.
26 September 2019: Wouter
- Merge #87 from hardfalcon: Fix contrib/unbound.service.in,
Drop CAP_KILL, use + prefix for ExecReload= instead.
25 September 2019: Wouter
- The unbound.conf includes are sorted ascending, for include
statements with a '*' from glob.
23 September 2019: Wouter
- Merge #85 for #84 from sam-lunt: Add kill capability to systemd
service file to fix that systemctl reload fails.
20 September 2019: Wouter
- Merge #82 from hardfalcon: Downgrade CAP_NET_ADMIN to CAP_NET_RAW
in unbound.service.
- Merge #81 from Maryse47: Consistently use /dev/urandom instead
of /dev/random in scripts and docs.
- Merge #83 from Maryse47: contrib/unbound.service.in: do not fork
into the background.
19 September 2019: Wouter
- Fix #78: Memory leak in outside_network.c.
- Merge pull request #76 from Maryse47: Improvements and fixes for
systemd unbound.service.
- oss-fuzz badge on README.md.
- Fix fix for #78 to also free service callback struct.
- Fix for oss-fuzz build warning.
- Fix wrong response ttl for prepended short CNAME ttls, this would
create a wrong zero_ttl response count with serve-expired enabled.
- Merge #80 from stasic: Improve wording in man page.
11 September 2019: Wouter
- Use explicit bzero for wiping clear buffer of hash in cachedb,
reported by Eric Sesterhenn from X41 D-Sec.
9 September 2019: Wouter
- Fix #72: configure --with-syslog-facility=LOCAL0-7 with default
LOG_DAEMON (as before) can set the syslog facility that the server
uses to log messages.
4 September 2019: Wouter
- Fix #71: fix openssl error squelch commit compilation error.
3 September 2019: Wouter
- squelch DNS over TLS errors 'ssl handshake failed crypto error'
on low verbosity, they show on verbosity 3 (query details), because
there is a high volume and the operator cannot do anything for the
remote failure. Specifically filters the high volume errors.
2 September 2019: Wouter
- ipset module #28: log that an address is added, when verbosity high.
- ipset: refactor long routine into three smaller ones.
- updated Makefile dependencies.
23 August 2019: Wouter
- Fix contrib/fastrpz.patch asprintf return value checks.
22 August 2019: Wouter
- Fix that pkg-config is setup before --enable-systemd needs it.
- 1.9.3rc2 release candidate tag. And this became the 1.9.3 release.
Master is 1.9.4 in development.
21 August 2019: Wouter
- Fix log_dns_msg to log irrespective of minimal responses config.
19 August 2019: Ralph
- Document limitation of pidfile removal outside of chroot directory.
16 August 2019: Wouter
- Fix unittest valgrind false positive uninitialised value report,
where if gcc 9.1.1 uses -O2 (but not -O1) then valgrind 3.15.0
issues an uninitialised value for the token buffer at the str2wire.c
rrinternal_get_owner() strcmp with the '@' value. Rewritten to use
straight character comparisons removes the false positive. Also
valgrinds --expensive-definedness-checks=yes can stop this false
positive.
- Please doxygen's parser for "@" occurrence in doxygen comment.
- Fixup contrib/fastrpz.patch
- Remove warning about unknown cast-function-type warning pragma.
15 August 2019: Wouter
- iana portlist updated.
- Fix autotrust temp file uniqueness windows compile.
- avoid warning about upcast on 32bit systems for autotrust.
- escape commandline contents for -V.
- Fix character buffer size in ub_ctx_hosts.
- 1.9.3rc1 release candidate tag.
- Option -V prints if TCP fastopen is available.
14 August 2019: George
- Fix #59, when compiled with systemd support check that we can properly
communicate with systemd through the `NOTIFY_SOCKET`.
14 August 2019: Wouter
- Generate configlexer with newer flex.
- Fix warning for unused variable for compilation without systemd.
12 August 2019: George
- Introduce `-V` option to print the version number and build options.
Previously reported build options like linked libs and linked modules
are now moved from `-h` to `-V` as well for consistency.
- PACKAGE_BUGREPORT now also includes link to GitHub issues.
1 August 2019: Wouter
- For #52 #53, second context does not close logfile override.
- Fix #52 #53, fix for example fail program.
- Fix to return after failed auth zone http chunk write.
- Fix to remove unused test for task_probe existance.
- Fix to timeval_add for remaining second in microseconds.
- Check repinfo in worker_handle_request, if null, drop it.
29 July 2019: Wouter
- Add verbose log message when auth zone file is written, at level 4.
- Add hex print of trust anchor pointer to trust anchor file temp
name to make it unique, for libunbound created multiple contexts.
23 July 2019: Wouter
- Fix question section mismatch in local zone redirect.
19 July 2019: Wouter
- Fix #49: Set no renegotiation on the SSL context to stop client
session renegotiation.
12 July 2019: Wouter
- Fix #48: Unbound returns additional records on NODATA response,
if minimal-responses is enabled, also the additional for negative
responses is removed.
9 July 2019: Ralph
- Fix in respip addrtree selection. Absence of addr_tree_init_parents()
call made it impossible to go up the tree when the matching netmask is
too specific.
5 July 2019: Ralph
- Fix for possible assertion failure when answering respip CNAME from
cache.
25 June 2019: Wouter
- For #45, check that 127.0.0.1 and ::1 are not used in unbound.conf
when do-not-query-localhost is turned on, or at default on,
unbound-checkconf prints a warning if it is found in forward-addr or
stub-addr statements.
24 June 2019: Wouter
- Fix memleak in unit test, reported from the clang 8.0 static analyzer.
18 June 2019: Wouter
- PR #28: IPSet module, by Kevin Chou. Created a module to support
the ipset that could add the domain's ip to a list easily.
Needs libmnl, and --enable-ipset and config it, doc/README.ipset.md.
- Fix to omit RRSIGs from addition to the ipset.
- Fix to make unbound-control with ipset, remove unused variable,
use unsigned type because of comparison, and assign null instead
of compare with it. Remade lex and yacc output.
- make depend
- Added documentation to the ipset files (for doxygen output).
- Merge PR #6: Python module: support multiple instances
- Merge PR #5: Python module: define constant MODULE_RESTART_NEXT
- Merge PR #4: Python module: assign something useful to the
per-query data store 'qdata'
- Fix python dict reference and double free in config.
17 June 2019: Wouter
- Master contains version 1.9.3 in development.
- Fix #39: In libunbound, leftover logfile is close()d unpredictably.
- Fix for #24: Fix abort due to scan of auth zone masters using old
address from previous scan.
12 June 2019: Wouter
- Fix another spoolbuf storage code point, in prefetch.
- 1.9.2rc3 release candidate tag. Which became the 1.9.2 release
on 17 June 2019.
11 June 2019: Wouter
- Fix that fixes the Fix that spoolbuf is not used to store tcp
pipelined response between mesh send and callback end, this fixes
error cases that did not use the correct spoolbuf.
- 1.9.2rc2 release candidate tag.
6 June 2019: Wouter
- 1.9.2rc1 release candidate tag.
4 June 2019: Wouter
- iana portlist updated.
29 May 2019: Wouter
- Fix to guard _OPENBSD_SOURCE from redefinition.
28 May 2019: Wouter
- Fix to define _OPENBSD_SOURCE to get reallocarray on NetBSD.
- gitignore config.h.in~.
27 May 2019: Wouter
- Fix double file close in tcp pipelined response code.
24 May 2019: Wouter
- Fix that spoolbuf is not used to store tcp pipelined response
between mesh send and callback end.
20 May 2019: Wouter
- Note that so-reuseport at extreme load is better turned off,
otherwise queries are not distributed evenly, on Linux 4.4.x.
16 May 2019: Wouter
- Fix #31: swig 4.0 and python module.
13 May 2019: Wouter
- Squelch log messages from tcp send about connection reset by peer.
They can be enabled with verbosity at higher values for diagnosing
network connectivity issues.
- Attempt to fix malformed tcp response.
9 May 2019: Wouter
- Revert fix for oss-fuzz, error is in that build script that
unconditionally includes .o files detected by configure, also
when the machine architecture uses different LIBOBJS files.
8 May 2019: Wouter
- Attempt to fix build failure in oss-fuzz because of reallocarray.
https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=14648.
Does not omit compile flags from commandline.
7 May 2019: Wouter
- Fix edns-subnet locks, in error cases the lock was not unlocked.
- Fix doxygen output error on readme markdown vignettes.
6 May 2019: Wouter
- Fix #29: Solaris 11.3 and missing symbols be64toh, htobe64.
- Fix #30: AddressSanitizer finding in lookup3.c. This sets the
hash function to use a slower but better auditable code that does
not read beyond array boundaries. This makes code better security
checkable, and is better for security. It is fixed to be slower,
but not read outside of the array.
2 May 2019: Wouter
- contrib/fastrpz.patch updated for code changes, and with git diff.
- Fix .gitignore, add pythonmod and dnstap generated files.
And unit test generated files, and generated doc files.
1 May 2019: Wouter
- Update makedist for git.
- Nicer travis output for clang analysis.
- PR #16: XoT support, AXFR over TLS, turn it on with
master: <ip>#<authname> in unbound.conf. This uses TLS to
download the AXFR (or IXFR).
25 April 2019: Wouter
- Fix wrong query name in local zone redirect answers with a CNAME,
the copy of the local alias is in unpacked form.
18 April 2019: Ralph
- Scrub RRs from answer section when reusing NXDOMAIN message for
subdomain answers.
- For harden-below-nxdomain: do not consider a name to be non-exitent
when message contains a CNAME record.
18 April 2019: Wouter
- travis build file.
16 April 2019: Wouter
- Better braces in if statement in TCP fastopen code.
- iana portlist updated.
15 April 2019: Wouter
- Fix tls write event for read state change to re-call SSL_write and
not resume the TLS handshake.
11 April 2019: George
- Update python documentation for init_standard().
- Typos.
11 April 2019: Wouter
- Fix that auth zone uses correct network type for sockets for
SOA serial probes. This fixes that probes fail because earlier
probe addresses are unreachable.
- Fix that auth zone fails over to next master for timeout in tcp.
- Squelch SSL read and write connection reset by peer and broken pipe
messages. Verbosity 2 and higher enables them.
8 April 2019: Wouter
- Fix to use event_assign with libevent for thread-safety.
- verbose information about auth zone lookup process, also lookup
start, timeout and fail.
- Fix #17: Add python module example from Jan Janak, that is a
plugin for the Unbound DNS resolver to resolve DNS records in
multicast DNS [RFC 6762] via Avahi. The plugin communicates
with Avahi via DBus. The comment section at the beginning of
the file contains detailed documentation.
- Fix to wipe ssl ticket keys from memory with explicit_bzero,
if available.
5 April 2019: Wouter
- Fix to reinit event structure for accepted TCP (and TLS) sockets.
4 April 2019: Wouter
- Fix spelling error in log output for event method.
3 April 2019: Wouter
- Move goto label in answer_from_cache to the end of the function
where it is more visible.
- Fix auth-zone NSEC3 response for wildcard nodata answers,
include the closest encloser in the answer.
2 April 2019: Wouter
- Fix auth-zone NSEC3 response for empty nonterminals with exact
match nsec3 records.
- Fix for out of bounds integers, thanks to OSTIF audit. It is in
allocation debug code.
- Fix for auth zone nsec3 ent fix for wildcard nodata.
25 March 2019: Wouter
- Fix that tls-session-ticket-keys: "" on its own in unbound.conf
disables the tls session ticker key calls into the OpenSSL API.
- Fix crash if tls-servic-pem not filled in when necessary.
21 March 2019: Wouter
- Fix #4240: Fix whitespace cleanup in example.conf.
19 March 2019: Wouter
- add type CAA to libpyunbound (accessing libunbound from python).
18 March 2019: Wouter
- Add log message, at verbosity 4, that says the query is encrypted
with TLS, if that is enabled for the query.
- Fix #4239: set NOTIMPL when deny-any is enabled, for RFC8482.
7 March 2019: Wouter
- Fix for #4233: guard use of NDEBUG, so that it can be passed in
CFLAGS into configure.
5 March 2019: Wouter
- Tag release 1.9.1rc1. Which became 1.9.1 on 12 March 2019. Trunk
has 1.9.2 in development.
1 March 2019: Wouter
- output forwarder log in ssl_req_order test.
28 February 2019: Wouter
- Remove memory leak on pythonmod python2 script file init.
- Remove swig gcc8 python function cast warnings, they are ignored.
- Print correct module that failed when module-config is wrong.
27 February 2019: Wouter
- Fix #4229: Unbound man pages lack information, about access-control
order and local zone tags, and elements in views.
- Fix #14: contrib/unbound.init: Fix wrong comparison judgment
before copying.
- Fix for python module on Windows, fix fopen.
25 February 2019: Wouter
- Fix #4227: pair event del and add for libevent for tcp_req_info.
21 February 2019: Wouter
- Fix the error for unknown module in module-config is understandable,
and explains it was not compiled in and where to see the list.
- In example.conf explain where to put cachedb module in module-config.
- In man page and example config explain that most modules have to
be listed at the start of module-config.
20 February 2019: Wouter
- Fix pythonmod include and sockaddr_un ifdefs for compile on
Windows, and for libunbound.
18 February 2019: Wouter
- Print query name with ip_ratelimit exceeded log lines.
- Spaces instead of tabs in that log message.
- Print query name and IP address when domain rate limit exceeded.
14 February 2019: Wouter
- Fix capsforid canonical sort qsort callback.
11 February 2019: Wouter
- Note default for module-config in man page.
- Fix recursion lame test for qname minimisation asked queries,
that were not present in the set of prepared answers.
- Fix #13: Remove left-over requirements on OpenSSL >= 1.1.0 for
cert name matching, from man page.
- make depend, with newer gcc, nicer layout.
7 February 2019: Wouter
- Fix #4206: OpenSSL 1.0.2 hostname verification for FreeBSD 11.2.
- Fix that qname minimisation does not skip a label when missing
nameserver targets need to be fetched.
- Fix #4225: clients seem to erroneously receive no answer with
DNS-over-TLS and qname-minimisation.
4 February 2019: Wouter
- Fix that log-replies prints the correct name for local-alias
names, for names that have a CNAME in local-data configuration.
It logs the original query name, not the target of the CNAME.
- Add local-zone type inform_redirect, which logs like type inform,
and redirects like type redirect.
- Perform canonical sort for 0x20 capsforid compare of replies,
this sorts rrsets in the authority and additional section before
comparison, so that out of order rrsets do not cause failure.
31 January 2019: Wouter
- Set ub_ctx_set_tls call signature in ltrace config file for
libunbound in contrib/libunbound.so.conf.
- improve documentation for tls-service-key and forward-first.
- #10: fixed pkg-config operations, PKG_PROG_PKG_CONFIG moved out of
conditional section, fixes systemd builds, from Enrico Scholz.
- #9: For openssl 1.0.2 use the CRYPTO_THREADID locking callbacks,
still supports the set_id_callback previous API. And for 1.1.0
no locking callbacks are needed.
- #8: Fix OpenSSL without ENGINE support compilation.
- Wipe TLS session key data from memory on exit.
30 January 2019: Ralph
- Fix case in which query timeout can result in marking delegation
as edns_lame_known.
29 January 2019: Wouter
- Fix spelling of tls-ciphers in example.conf.in.
- Fix #4224: auth_xfr_notify.rpl test broken due to typo
- Fix locking for libunbound context setup with broken port config.
28 January 2019: Wouter
- ub_ctx_set_tls call for libunbound that enables DoT for the machines
set with ub_ctx_set_fwd. Patch from Florian Obser.
- Set build system for added call in the libunbound API.
- List example config for root zone copy locally hosted with auth-zone
as suggested from draft-ietf-dnsop-7706-bis-02. But with updated
B root address.
- set version to 1.9.0 for release. And this was released with the
spelling for tls-ciphers fix as 1.9.0 on Feb 5. Trunk has 1.9.1 in
development.
25 January 2019: Wouter
- Fix that tcp for auth zone and outgoing does not remove and
then gets the ssl read again applied to the deleted commpoint.
- updated contrib/fastrpz.patch to cleanly diff.
- no lock when threads disabled in tcp request buffer count.
- remove compile warnings from libnettle compile.
- output of newer lex 2.6.1 and bison 3.0.5.
24 January 2019: Wouter
- Newer aclocal and libtoolize used for generating configure scripts,
aclocal 1.16.1 and libtoolize 2.4.6.
- Fix unit test for python 3.7 new keyword 'async'.
- clang analysis fixes, assert arc4random buffer in init,
no check for already checked delegation pointer in iterator,
in testcode check for NULL packet matches, in perf do not copy
from NULL start list when growing capacity. Adjust host and file
only when present in test header read to please checker. In
testcode for unknown macro operand give zero result. Initialise the
passed argv array in test code. In test code add EDNS data
segment copy only when nonempty.
- Patch from Florian Obser fixes some compiler warnings:
include mini_event.h to have a prototype for mini_ev_cmp
include edns.h to have a prototype for apply_edns_options
sldns_wire2str_edns_keepalive_print is only called in the wire2str,
module declare it static to get rid of compiler warning:
no previous prototype for function
infra_find_ip_ratedata() is only called in the infra module,
declare it static to get rid of compiler warning:
no previous prototype for function
do not shadow local variable buf in authzone
auth_chunks_delete and az_nsec3_findnode are only called in the
authzone module, declare them static to get rid of compiler warning:
no previous prototype for function...
copy_rrset() is only called in the respip module, declare it
static to get rid of compiler warning:
no previous prototype for function 'copy_rrset'
no need for another variable "r"; gets rid of compiler warning:
declaration shadows a local variable in libunbound.c
no need for another variable "ns"; gets rid of compiler warning:
declaration shadows a local variable in iterator.c
- Moved includes and make depend.
23 January 2019: Wouter
- Patch from Manabu Sonoda with tls-ciphers and tls-ciphersuites
options for unbound.conf.
- Fixes for the patch, and man page entry.
- Fix configure to detect SSL_CTX_set_ciphersuites, for better
library compatibility when compiling.
- Patch for TLS session resumption from Manabu Sonoda,
enable with tls-session-ticket-keys in unbound.conf.
- Fixes for patch (includes, declarations, warnings). Free at end
and keep config options in order read from file to keep the first
one as the first one.
- Fix for IXFR fallback to reset counter when IXFR does not timeout.
22 January 2019: Wouter
- Fix space calculation for tcp req buffer size.
- Doc for stream-wait-size and unit test.
- unbound-control stats has mem.streamwait that counts TCP and TLS
waiting result buffers.
- Fix for #4219: secondaries not updated after serial change, unbound
falls back to AXFR after IXFR gives several timeout failures.
- Fix that auth zone after IXFR fallback tries the same master.
21 January 2019: Wouter
- Fix tcp idle timeout test, for difference in the tcp reply code.
- Unit test for tcp request reorder and timeouts.
- Unit tests for ssl out of order processing.
- Fix that multiple dns fragments can be carried in one TLS frame.
- Add stream-wait-size: 4m config option to limit the maximum
memory used by waiting tcp and tls stream replies. This avoids
a denial of service where these replies use up all of the memory.
17 January 2019: Wouter
- For caps-for-id fallback, use the whitelist to avoid timeout
starting a fallback sequence for it.
- increase mesh max activation count for capsforid long fetches.
16 January 2019: Ralph
- Get ready for the DNS flag day: remove EDNS lame procedure, do not
re-query without EDNS after timeout.
15 January 2019: Wouter
- In the out of order processing, reset byte count for (potential)
partial read.
- Review fixes in out of order processing.
14 January 2019: Wouter
- streamtcp option -a send queries consecutively and prints answers
as they arrive.
- Fix for out of order processing administration quit cleanup.
- unit test for tcp out of order processing.
11 January 2019: Wouter
- Initial commit for out-of-order processing for TCP and TLS.
9 January 2019: Wouter
- Log query name for looping module errors.
8 January 2019: Wouter
- Fix syntax in comment of local alias processing.
- Fix NSEC3 record that is returned in wildcard replies from
auth-zone zones with NSEC3 and wildcards.
7 January 2019: Wouter
- On FreeBSD warn if systcl settings do not allow server TCP FASTOPEN,
and server tcp fastopen is enabled at compile time.
- Document interaction between the tls-upstream option in the server
section and forward-tls-upstream option in the forward-zone sections.
- Add contrib/unbound-fuzzme.patch from Jacob Hoffman-Andrews,
the patch adds a program used for fuzzing.
12 December 2018: Wouter
- Fix for crash in dns64 module if response is null.
10 December 2018: Wouter
- Fix config parser memory leaks.
- ip-ratelimit-factor of 1 allows all traffic through, instead of the
previous blocking everything.
- Fix for FreeBSD port make with dnscrypt and dnstap enabled.
- Fix #4206: support openssl 1.0.2 for TLS hostname verification,
alongside the 1.1.0 and later support that is already there.
- Fixup openssl 1.0.2 compile
6 December 2018: Wouter
- Fix dns64 allocation in wrong region for returned internal queries.
3 December 2018: Wouter
- Fix icon, no ragged edges and nicer resolutions available, for eg.
Win 7 and Windows 10 display.
- cache-max-ttl also defines upperbound of initial TTL in response.
30 November 2018: Wouter
- Patch for typo in unbound.conf man page.
- log-tag-queryreply: yes in unbound.conf tags the log-queries and
log-replies in the log file for easier log filter maintenance.
29 November 2018: Wouter
- iana portlist updated.
- Fix chroot auth-zone fix to remove chroot prefix.
- tag for 1.8.2rc1, which became 1.8.2 on 4 dec 2018, with icon
updated. Trunk contains 1.8.3 in development.
Which became 1.8.3 on 11 december with only the dns64 fix of 6 dec.
Trunk then became 1.8.4 in development.
- Fix that unbound-checkconf does not complains if the config file
is not placed inside the chroot.
- Refuse to start with no ports.
- Remove clang analysis warnings.
28 November 2018: Wouter
- Fix leak in chroot fix for auth-zone.
- Fix clang analysis for outside directory build test.
27 November 2018: Wouter
- Fix DNS64 to not store intermediate results in cache, this avoids
other threads from picking up the wrong data. The module restores
the previous no_cache_store setting when the the module is finished.
- Fix #4208: 'stub-no-cache' and 'forward-no-cache' not work.
- New and better fix for Fix #4193: Fix that prefetch failure does
not overwrite valid cache entry with SERVFAIL.
- auth-zone give SERVFAIL when expired, fallback activates when
expired, and this is documented in the man page.
- stat count SERVFAIL downstream auth-zone queries for expired zones.
- Put new logos into windows installer.
- Fix windows compile for new rrset roundrobin fix.
- Update contrib fastrpz patch for latest release.
26 November 2018: Wouter
- Fix to not set GLOB_NOSORT so the unbound.conf include: files are
sorted and in a predictable order.
- Fix #4193: Fix that prefetch failure does not overwrite valid cache
entry with SERVFAIL.
- Add unbound-control view_local_datas command, like local_datas.
- Fix that unbound-control can send file for view_local_datas.
22 November 2018: Wouter
- With ./configure --with-pyunbound --with-pythonmodule
PYTHON_VERSION=3.6 or with 2.7 unbound can compile and unit tests
succeed for the python module.
- pythonmod logs the python error and traceback on failure.
- ignore debug python module for test in doxygen output.
- review fixes for python module.
- Fix #4209: Crash in libunbound when called from getdns.
- auth zone zonefiles can be in a chroot, the chroot directory
components are removed before use.
- Fix that empty zonefile means the zonefile is not set and not used.
- make depend.
21 November 2018: Wouter
- Scrub NS records from NODATA responses as well.
20 November 2018: Wouter
- Scrub NS records from NXDOMAIN responses to stop fragmentation
poisoning of the cache.
- Add patch from Jan Vcelak for pythonmod,
add sockaddr_storage getters, add support for query callbacks,
allow raw address access via comm_reply and update API documentation.
- Removed compile warnings in pythonmod sockaddr routines.
19 November 2018: Wouter
- Support SO_REUSEPORT_LB in FreeBSD 12 with the so-reuseport: yes
option in unbound.conf.
6 November 2018: Ralph
- Bugfix min-client-subnet-ipv6
25 October 2018: Ralph
- Add min-client-subnet-ipv6 and min-client-subnet-ipv4 options.
25 October 2018: Wouter
- Fix #4191: NXDOMAIN vs SERVFAIL during dns64 PTR query.
- Fix #4190: Please create a "ANY" deny option, adds the option
deny-any: yes in unbound.conf. This responds with an empty message
to queries of type ANY.
- Fix #4141: More randomness to rrset-roundrobin.
- Fix #4132: Openness/closeness of RANGE intervals in rpl files.
- Fix #4126: RTT_band too low on VSAT links with 600+ms latency,
adds the option unknown-server-time-limit to unbound.conf that
can be increased to avoid the problem.
- remade makefile dependencies.
- Fix #4152: Logs shows wrong time when using log-time-ascii: yes.
24 October 2018: Ralph
- Add markdel function to ECS slabhash.
- Limit ECS scope returned to client to the scope used for caching.
- Make lint like previous #4154 fix.
22 October 2018: Wouter
- Fix #4192: unbound-control-setup generates keys not readable by
group.
- check that the dnstap socket file can be opened and exists, print
error if not.
- Fix #4154: make ECS_MAX_TREESIZE configurable, with
the max-ecs-tree-size-ipv4 and max-ecs-tree-size-ipv6 options.
22 October 2018: Ralph
- Change fast-server-num default to 3.
8 October 2018: Ralph
- Add fast-server-permil and fast-server-num options.
- Deprecate low-rtt and low-rtt-permil options.
8 October 2018: Wouter
- Squelch log of failed to tcp initiate after TCP Fastopen failure.
5 October 2018: Wouter
- Squelch EADDRNOTAVAIL errors when the interface goes away,
this omits 'can't assign requested address' errors unless
verbosity is set to a high value.
- Set default for so-reuseport to no for FreeBSD. It is enabled
by default for Linux and DragonFlyBSD. The setting can
be configured in unbound.conf to override the default.
- iana port update.
2 October 2018: Wouter
- updated contrib/fastrpz.patch to apply for this version
- dnscrypt.c removed sizeof to get array bounds.
- Fix testlock code to set noreturn on error routine.
- Remove unused variable from contrib fastrpz/rpz.c and
remove unused diagnostic pragmas that themselves generate warnings
- clang analyze test is used only when assertions are enabled.
1 October 2018: Wouter
- tag for release 1.8.1rc1. Became release 1.8.1 on 8 oct, with
fastrpz.patch fix included. Trunk has 1.8.2 in development.
27 September 2018: Wouter
- Fix #4188: IPv6 forwarders without ipv6 result in SERVFAIL, fixes
qname minimisation with a forwarder when connectivity has issues
from rejecting responses.
25 September 2018: Wouter
- Perform TLS SNI indication of the host that is being contacted
for DNS over TLS service. It sets the configured tls auth name.
This is useful for hosts that apart from the DNS over TLS services
also provide other (web) services.
- Fix #4149: Add SSL cleanup for tcp timeout.
17 September 2018: Wouter
- Fix compile on Mac for unbound, provide explicit_bzero when libc
does not have it.
- Fix unbound for openssl in FIPS mode, it uses the digests with
the EVP call contexts.
- Fix that with harden-below-nxdomain and qname minisation enabled
some iterator states for nonresponsive domains can get into a
state where they waited for an empty list.
- Stop UDP to TCP failover after timeouts that causes the ping count
to be reset by the TCP time measurement (that exists for TLS),
because that causes the UDP part to not be measured as timeout.
- Fix #4156: Fix systemd service manager state change notification.
13 September 2018: Wouter
- Fix seed for random backup code to use explicit zero when wiped.
- exit log routine is annotated as noreturn function.
- free memory leaks in config strlist and str2list insert functions.
- do not move unused argv variable after getopt.
- Remove unused if clause in testcode.
- in testcode, free async ids, initialise array, and check for null
pointer during test of the test. And use exit for return to note
irregular program stop.
- Free memory leak in config strlist append.
- make sure nsec3 comparison salt is initialized.
- unit test has clang analysis.
- remove unused variable assignment from iterator scrub routine.
- check for null in delegation point during iterator refetch
in forward zone.
- neater pointer cast in libunbound context quit routine.
- initialize statistics totals for printout.
- in authzone check that node exists before adding rrset.
- in unbound-anchor, use readwrite memory BIO.
- assertion in autotrust that packed rrset is formed correctly.
- Fix memory leak when message parse fails partway through copy.
- remove unused udpsize assignment in message encode.
- nicer bio free code in unbound-anchor.
- annotate exit functions with noreturn in unbound-control.
11 September 2018: Wouter
- Fixed unused return value warnings in contrib/fastrpz.patch for
asprintf.
- Fix to squelch respip warning in unit test, it is printed at
higher verbosity settings.
- Fix spelling errors.
- Fix initialisation in remote.c
10 September 2018: Wouter
- 1.8.1 in svn trunk. (changes from 4,5,.. sep apply).
- iana port update.
5 September 2018: Wouter
- Fix spelling error in header, from getdns commit by Andreas Gelmini.
4 September 2018: Ralph
- More explicitly mention the type of ratelimit when applying
ip-ratelimit.
4 September 2018: Wouter
- Tag for 1.8.0rc1 release, became 1.8.0 release on 10 Sep 2018.
31 August 2018: Wouter
- Disable minimal-responses in subnet unit tests.
30 August 2018: Wouter
- Fix that a local-zone with a local-zone-type that is transparent
in a view with view-first, makes queries check for answers from the
local-zones defined outside of views.
28 August 2018: Ralph
- Disable minimal-responses in ipsecmod unit tests.
- Added serve-expired-ttl and serve-expired-ttl-reset options.
27 August 2018: Wouter
- Set defaults to yes for a number of options to increase speed and
resilience of the server. The so-reuseport, harden-below-nxdomain,
and minimal-responses options are enabled by default. They used
to be disabled by default, waiting to make sure they worked. They
are enabled by default now, and can be disabled explicitly by
setting them to "no" in the unbound.conf config file. The reuseport
and minimal options increases speed of the server, and should be
otherwise harmless. The harden-below-nxdomain option works well
together with the recently default enabled qname minimisation, this
causes more fetches to use information from the cache.
- next release is called 1.8.0.
- Fix lintflags for lint on FreeBSD.
22 August 2018: George
- #4140: Expose repinfo (comm_reply) to the inplace_callbacks. This
gives access to reply information for the client's communication
point when the callback is called before the mesh state (modules).
Changes to C and Python's inplace_callback signatures were also
necessary.
21 August 2018: Wouter
- log-local-actions: yes option for unbound.conf that logs all the
local zone actions, a patch from Saksham Manchanda (Secure64).
- #4146: num.query.subnet and num.query.subnet_cache counters.
- Fix only misc failure from log-servfail when val-log-level is not
enabled.
17 August 2018: Ralph
- Fix classification for QTYPE=CNAME queries when QNAME minimisation is
enabled.
17 August 2018: Wouter
- Set libunbound to increase current, because the libunbound change
to the event callback function signature. That needs programs,
that use it, to recompile against the new header definition.
- print servfail info to log as error.
- added more servfail printout statements, to the iterator.
- log-servfail: yes prints log lines that say why queries are
returning SERVFAIL to clients.
16 August 2018: Wouter
- Fix warning on compile without threads.
- Fix contrib/fastrpz.patch.
15 August 2018: Wouter
- Fix segfault in auth-zone read and reorder of RRSIGs.
14 August 2018: Wouter
- Fix that printout of error for cycle targets is a verbosity 4
printout and does not wrongly print it is a memory error.
- Upgraded crosscompile script to include libunbound DLL in the
zipfile.
10 August 2018: Wouter
- Fix #4144: dns64 module caches wrong (negative) information.
9 August 2018: Wouter
- unbound-checkconf checks if modules exist and prints if they are
not compiled in the name of the wrong module.
- document --enable-subnet in doc/README.
- Patch for stub-no-cache and forward-no-cache options that disable
caching for the contents of that stub or forward, for when you
want immediate changes visible, from Bjoern A. Zeeb.
7 August 2018: Ralph
- Make capsforid fallback QNAME minimisation aware.
7 August 2018: Wouter
- Fix #4142: unbound.service.in: improvements and fixes.
Add unit dependency ordering (based on systemd-resolved).
Add 'CAP_SYS_RESOURCE' to 'CapabilityBoundingSet' (fixes warnings
about missing privileges during startup). Add 'AF_INET6' to
'RestrictAddressFamilies' (without it IPV6 can't work). From
Guido Shanahan.
- Patch to implement tcp-connection-limit from Jim Hague (Sinodun).
This limits the number of simultaneous TCP client connections
from a nominated netblock.
- make depend, yacc, lex, doc, headers. And log the limit exceeded
message only on high verbosity, so as to not spam the logs when
it is busy.
6 August 2018: Wouter
- Fix for #4136: Fix to unconditionally call destroy in daemon.c.
3 August 2018: George
- Expose if a query (or a subquery) was ratelimited (not src IP
ratelimiting) to libunbound under 'ub_result.was_ratelimited'.
This also introduces a change to 'ub_event_callback_type' in
libunbound/unbound-event.h.
- Tidy pylib tests.
3 August 2018: Wouter
- Revert previous change for #4136: because it introduces build
problems.
- New fix for #4136: This one ignores lex without without
yylex_destroy.
1 August 2018: Wouter
- Fix to remove systemd sockaddr function check, that is not
always present. Make socket activation more lenient. But not
different when socket activation is not used.
- iana port list update.
31 July 2018: Wouter
- Patches from Jim Hague (Sinodun) for EDNS KeepAlive.
- Sort out test runs when the build directory isn't the project
root directory.
- Add config tcp-idle-timeout (default 30s). This applies to
client connections only; the timeout on TCP connections upstream
is unaffected.
- Error if EDNS Keepalive received over UDP.
- Add edns-tcp-keepalive and edns-tcp-keepalive timeout options
and implement option in client responses.
- Correct and expand manual page entries for keepalive and idle timeout.
- Implement progressive backoff of TCP idle/keepalive timeout.
- Fix 'make depend' to work when build dir is not project root.
- Add delay parameter to streamtcp, -d secs.
To be used when testing idle timeout.
- From Wouter: make depend, the dependencies in the patches did not
apply cleanly. Also remade yacc and lex.
- Fix mesh.c incompatible pointer pass.
- Please doxygen so it passes.
- Fix #4139: Fix unbound-host leaks memory on ANY.
30 July 2018: Wouter
- Fix #4136: insufficiency from mismatch of FLEX capability between
released tarball and build host.
27 July 2018: Wouter
- Fix man page, say that chroot is enabled by default.
26 July 2018: Wouter
- Fix #4135: 64-bit Windows Installer Creates Entries Under The
Wrong Registry Key, reported by Brian White.
23 July 2018: Wouter
- Fix use-systemd readiness signalling, only when use-systemd is yes
and not in signal handler.
20 July 2018: Wouter
- Fix #4130: print text describing -dd and unbound-checkconf on
config file read error at startup, the errors may have been moved
away by the startup process.
- Fix #4131: for solaris, error YY_CURRENT_BUFFER undeclared.
19 July 2018: Wouter
- Fix #4129 unbound-control error message with wrong cert permissions
is too cryptic.
17 July 2018: Wouter
- Fix #4127 unbound -h does not list -p help.
- Print error if SSL name verification configured but not available
in the ssl library.
- Fix that ratelimit and ip-ratelimit are applied after reload of
changed config file.
- Resize ratelimit and ip-ratelimit caches if changed on reload.
16 July 2018: Wouter
- Fix qname minimisation NXDOMAIN validation lookup failures causing
error_supers assertion fails.
- Squelch can't bind socket errors with Permission denied unless
verbosity is 4 or higher, for UDP outgoing sockets.
12 July 2018: Wouter
- Fix to improve systemd socket activation code file descriptor
assignment.
- Fix for 4126 that the #define for UNKNOWN_SERVER_NICENESS can be more
easily changed to adjust default rtt assumptions.
10 July 2018: Wouter
- Note in documentation that the cert name match code needs
OpenSSL 1.1.0 or later to be enabled.
6 July 2018: Wouter
- Fix documentation ambiguity for tls-win-cert in tls-upstream and
forward-tls-upstream docs.
- iana port update.
- Note RFC8162 support. SMIMEA record type can be read in by the
zone record parser.
- Fix round robin for failed addresses with prefer-ip6: yes
4 July 2018: Wouter
- Fix #4112: Fix that unbound-anchor -f /etc/resolv.conf will not pass
if DNSSEC is not enabled. New option -R allows fallback from
resolv.conf to direct queries.
3 July 2018: Wouter
- Better documentation for unblock-lan-zones and insecure-lan-zones
config statements.
- Fix permission denied printed for auth zone probe random port nrs.
2 July 2018: Wouter
- Fix checking for libhiredis printout in configure output.
- Fix typo on man page in ip-address description.
- Update libunbound/python/examples/dnssec_test.py example code to
also set the 20326 trust anchor for the root in the example code.
29 June 2018: Wouter
- dns64-ignore-aaaa: config option to list domain names for which the
existing AAAA is ignored and dns64 processing is used on the A
record.
28 June 2018: Wouter
- num.queries.tls counter for queries over TLS.
- log port number with err_addr logs.
27 June 2018: Wouter
- #4109: Fix that package config depends on python unconditionally.
- Patch, do not export python from pkg-config, from Petr Menšík.
26 June 2018: Wouter
- Partial fix for permission denied on IPv6 address on FreeBSD.
- Fix that auth-zone master reply with current SOA serial does not
stop scan of masters for an updated zone.
- Fix that auth-zone does not start the wait timer without checking
if the wait timer has already been started.
21 June 2018: Wouter
- #4108: systemd reload hang fix.
- Fix usage printout for unbound-host, hostname has to be last
argument on BSDs and Windows.
19 June 2018: Wouter
- Fix for unbound-control on Windows and set TCP socket parameters
more closely.
This fix is part of 1.7.3.
- Windows example service.conf edited with more windows specific
configuration.
- Fix windows unbound-control no cert bad file descriptor error.
This fix is part of 1.7.3.
18 June 2018: Wouter
- Fix that control-use-cert: no works for 127.0.0.1 to disable certs.
This fix is part of 1.7.3rc2.
- Fix unbound-checkconf for control-use-cert.
This fix is part of 1.7.3.
15 June 2018: Wouter
- tag for 1.7.3rc1.
- trunk has 1.7.4.
- unbound-control auth_zone_reload _zone_ option rereads the zonefile.
- unbound-control auth_zone_transfer _zone_ option starts the probe
sequence for a master to transfer the zone from and transfers when
a new zone version is available.
14 June 2018: Wouter
- #4103: Fix that auth-zone does not insist on SOA record first in
file for url downloads.
- Fix that first control-interface determines if TLS is used. Warn
when IP address interfaces are used without TLS.
- Fix nettle compile.
12 June 2018: Ralph
- Don't count CNAME response types received during qname minimisation as
query restart.
12 June 2018: Wouter
- #4102 for NSD, but for Unbound. Named unix pipes do not use
certificate and key files, access can be restricted with file and
directory permissions. The option control-use-cert is no longer
used, and ignored if found in unbound.conf.
- Rename tls-additional-ports to tls-additional-port, because every
line adds one port.
- Fix buffer size warning in unit test.
- remade dependencies in the Makefile.
6 June 2018: Wouter
- Patch to fix openwrt for mac os build darwin detection in configure.
5 June 2018: Wouter
- Fix crash if ratelimit taken into use with unbound-control
instead of with unbound.conf.
4 June 2018: Wouter
- Fix deadlock caused by incoming notify for auth-zone.
- tag for 1.7.2rc1, became 1.7.2 release on 11 June 2018,
trunk is 1.7.3 in development from this point.
- #4100: Fix stub reprime when it becomes useless.
1 June 2018: Wouter
- Rename additional-tls-port to tls-additional-ports.
The older name is accepted for backwards compatibility.
30 May 2018: Wouter
- Patch from Syzdek: Add ability to ignore RD bit and treat all
requests as if the RD bit is set.
29 May 2018: Wouter
- in compat/arc4random call getentropy_urandom when getentropy fails
with ENOSYS.
- Fix that fallback for windows port.
28 May 2018: Wouter
- Fix windows tcp and tls spin on events.
- Add routine from getdns to add windows cert store to the SSL_CTX.
- tls-win-cert option that adds the system certificate store for
authenticating DNS-over-TLS connections. It can be used instead
of the tls-cert-bundle option, or with it to add certificates.
25 May 2018: Wouter
- For TCP and TLS connections that don't establish, perform address
update in infra cache, so future selections can exclude them.
- Fix that tcp sticky events are removed for closed fd on windows.
- Fix close events for tcp only.
24 May 2018: Wouter
- Fix that libunbound can do DNS-over-TLS, when configured.
- Fix that windows unbound service can use DNS-over-TLS.
- unbound-host initializes ssl (for potential DNS-over-TLS usage
inside libunbound), when ssl upstream or a cert-bundle is configured.
23 May 2018: Wouter
- Use accept4 to speed up incoming TCP (and TLS) connections,
available on Linux, FreeBSD and OpenBSD.
17 May 2018: Ralph
- Qname minimisation default changed to yes.
15 May 2018: Wouter
- Fix low-rtt-pct to low-rtt-permil, as it is parts in one thousand.
11 May 2018: Wouter
- Fix contrib/libunbound.pc for libssl libcrypto references,
from https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=226914
7 May 2018: Wouter
- Fix windows to not have sticky TLS events for TCP.
- Fix read of DNS over TLS length and data in one read call.
- Fix mesh state assertion failure due to callback removal.
3 May 2018: Wouter
- Fix that configure --with-libhiredis also turns on cachedb.
- Fix gcc 8 buffer warning in testcode.
- Fix function type cast warning in libunbound context callback type.
2 May 2018: Wouter
- Fix fail to reject dead peers in forward-zone, with ssl-upstream.
1 May 2018: Wouter
- Fix that unbound-control reload frees the rrset keys and returns
the memory pages to the system.
30 April 2018: Wouter
- Fix spelling error in man page and note defaults as no instead of
off.
26 April 2018: Wouter
- Fix for crash in daemon_cleanup with dnstap during reload,
from Saksham Manchanda.
- Also that for dnscrypt.
- tag for 1.7.1rc1 release. Became 1.7.1 release on 3 May, trunk
is from here 1.7.2 in development.
25 April 2018: Ralph
- Fix memory leak when caching wildcard records for aggressive NSEC use
24 April 2018: Wouter
- Fix contrib/fastrpz.patch for this release.
- Fix auth https for libev.
24 April 2018: Ralph
- Added root-key-sentinel support
23 April 2018: Wouter
- makedist uses bz2 for expat code, instead of tar.gz.
- Fix #4092: libunbound: use-caps-for-id lacks colon in
config_set_option.
- auth zone http download stores exact copy of downloaded file,
including comments in the file.
- Fix sldns parse failure for CDS alternate delete syntax empty hex.
- Attempt for auth zone fix; add of callback in mesh gets from
callback does not skip callback of result.
- Fix cname classification with qname minimisation enabled.
- list_auth_zones unbound-control command.
20 April 2018: Wouter
- man page documentation for dns-over-tls forward-addr '#' notation.
- removed free from failed parse case.
- Fix #4091: Fix that reload of auth-zone does not merge the zonefile
with the previous contents.
- Delete auth zone when removed from config.
19 April 2018: Wouter
- Can set tls authentication with forward-addr: IP#tls.auth.name
And put the public cert bundle in tls-cert-bundle: "ca-bundle.pem".
such as forward-addr: 9.9.9.9@853#dns.quad9.net or
1.1.1.1@853#cloudflare-dns.com
- Fix #658: unbound using TLS in a forwarding configuration does not
verify the server's certificate (RFC 8310 support).
- For addr with #authname and no @port notation, the default is 853.
18 April 2018: Wouter
- Fix auth-zone retry timer to be on schedule with retry timeout,
with backoff. Also time a refresh at the zone expiry.
17 April 2018: Wouter
- auth zone notify work.
- allow-notify: config statement for auth-zones.
- unit test for allow-notify
16 April 2018: Wouter
- Fix auth zone target lookup iterator.
- auth zone notify with prefix
- auth zone notify work.
13 April 2018: Wouter
- Fix for max include depth for authzones.
- Fix memory free on fail for $INCLUDE in authzone.
- Fix that an internal error to look up the wrong rr type for
auth zone gets stopped, before trying to send there.
- auth zone notify work.
10 April 2018: Ralph
- num.query.aggressive.NOERROR and num.query.aggressive.NXDOMAIN
statistics counters.
10 April 2018: Wouter
- documentation for low-rtt and low-rtt-pct.
- auth zone notify work.
9 April 2018: Wouter
- Fix that flush_zone sets prefetch ttl expired, so that with
serve-expired enabled it'll start prefetching those entries.
- num.query.authzone.up and num.query.authzone.down statistics counters.
- Fix downstream auth zone, only fallback when auth zone fails to
answer and fallback is enabled.
- Accept both option names with and without colon for get_option
and set_option.
- low-rtt and low-rtt-pct in unbound.conf enable the server selection
of fast servers for some percentage of the time.
5 April 2018: Wouter
- Combine write of tcp length and tcp query for dns over tls.
- nitpick fixes in example.conf.
- Fix above stub queries for type NS and useless delegation point.
- Fix unbound-control over pipe with openssl 1.1.1, the TLSv1.3
tls_choose_sigalg routine does not allow the ciphers for the pipe,
so use TLSv1.2.
- ED448 support.
3 April 2018: Wouter
- Fix #4043: make test fails due to v6 presentation issue in macOS.
- Fix unable to resolve after new WLAN connection, due to auth-zone
failing with a forwarder set. Now, auth-zone is only used for
answers (not referrals) when a forwarder is set.
29 March 2018: Ralph
- Check "result" in dup_all(), by Florian Obser.
23 March 2018: Ralph
- Fix unbound-control get_option aggressive-nsec
21 March 2018: Ralph
- Do not use cached NSEC records to generate negative answers for
domains under DNSSEC Negative Trust Anchors.
19 March 2018: Wouter
- iana port update.
16 March 2018: Wouter
- corrected a minor typo in the changelog.
- move htobe64/be64toh portability code to cachedb.c.
15 March 2018: Wouter
- Add --with-libhiredis, unbound support for a new cachedb backend
that uses a Redis server as the storage. This implementation
depends on the hiredis client library (https://redislabs.com/lp/hiredis/).
And unbound should be built with both --enable-cachedb and
--with-libhiredis[=PATH] (where $PATH/include/hiredis/hiredis.h
should exist). Patch from Jinmei Tatuya (Infoblox).
- Fix #3817: core dump happens in libunbound delete, when queued
servfail hits deleted message queue.
- Create additional tls service interfaces by opening them on other
portnumbers and listing the portnumbers as additional-tls-port: nr.
13 March 2018: Wouter
- Fix typo in documentation.
- Fix #3736: Fix 0 TTL domains stuck on SERVFAIL unless manually
flushed with serve-expired on.
12 March 2018: Wouter
- Added documentation for aggressive-nsec: yes.
- tag 1.7.0rc3. That became the 1.7.0 release on 15 Mar, trunk
now has 1.7.1 in development.
- Fix #3727: Protocol name is TLS, options have been renamed but
documentation is not consistent.
- Check IXFR start serial.
9 March 2018: Wouter
- Fix #3598: Fix swig build issue on rhel6 based system.
configure --disable-swig-version-check stops the swig version check.
8 March 2018: Wouter
- tag 1.7.0rc2.
7 March 2018: Wouter
- Fixed contrib/fastrpz.patch, even though this already applied
cleanly for me, now also for others.
- patch to log creates keytag queries, from A. Schulze.
- patch suggested by Debian lintian: allow to -> allow one to, from
A. Schulze.
- Attempt to remove warning about trailing whitespace.
6 March 2018: Wouter
- Reverted fix for #3512, this may not be the best way forward;
although it could be changed at a later time, to stay similar to
other implementations.
- svn trunk contains 1.7.0, this is the number for the next release.
- Fix for windows compile.
- tag 1.7.0rc1.
5 March 2018: Wouter
- Fix to check define of DSA for when openssl is without deprecated.
- iana port update.
- Fix #3582: Squelch address already in use log when reuseaddr option
causes same port to be used twice for tcp connections.
27 February 2018: Wouter
- Fixup contrib/fastrpz.patch so that it applies.
- Fix compile without threads, and remove unused variable.
- Fix compile with staticexe and python module.
- Fix nettle compile.
22 February 2018: Ralph
- Save wildcard RRset from answer with original owner for use in
aggressive NSEC.
21 February 2018: Wouter
- Fix #3512: unbound incorrectly reports SERVFAIL for CAA query
when there is a CNAME loop.
- Fix validation for CNAME loops. When it detects a cname loop,
by finding the cname, cname in the existing list, it returns
the partial result with the validation result up to then.
- more robust cachedump rrset routine.
19 February 2018: Wouter
- Fix #3505: Documentation for default local zones references
wrong RFC.
- Fix #3494: local-zone noview can be used to break out of the view
to the global local zone contents, for queries for that zone.
- Fix for more maintainable code in localzone.
16 February 2018: Wouter
- Fixes for clang static analyzer, the missing ; in
edns-subnet/addrtree.c after the assert made clang analyzer
produce a failure to analyze it.
13 February 2018: Ralph
- Aggressive NSEC tests
13 February 2018: Wouter
- tls-cert-bundle option in unbound.conf enables TLS authentication.
- iana port update.
12 February 2018: Wouter
- Unit test for auth zone https url download.
12 February 2018: Ralph
- Added tests with wildcard expanded NSEC records (CVE-2017-15105 test)
- Processed aggressive NSEC code review remarks Wouter
8 February 2018: Ralph
- Aggressive use of NSEC implementation. Use cached NSEC records to
generate NXDOMAIN, NODATA and positive wildcard answers.
8 February 2018: Wouter
- iana port update.
- auth zone url config.
5 February 2018: Wouter
- Fix #3451: dnstap not building when you have a separate build dir.
And removed protoc warning, set dnstap.proto syntax to proto2.
- auth-zone provides a way to configure RFC7706 from unbound.conf,
eg. with auth-zone: name: "." for-downstream: no for-upstream: yes
fallback-enabled: yes and masters or a zonefile with data.
2 February 2018: Wouter
- Fix unfreed locks in log and arc4random at exit of unbound.
- unit test with valgrind
- Fix lock race condition in dns cache dname synthesis.
- lock subnet new item before insertion to please checklocks,
no modification of critical regions outside of lock region.
1 February 2018: Wouter
- fix unaligned structure making a false positive in checklock
unitialised memory.
29 January 2018: Ralph
- Use NSEC with longest ce to prove wildcard absence.
- Only use *.ce to prove wildcard absence, no longer names.
25 January 2018: Wouter
- ltrace.conf file for libunbound in contrib.
23 January 2018: Wouter
- Fix that unbound-checkconf -f flag works with auto-trust-anchor-file
for startup scripts to get the full pathname(s) of anchor file(s).
- Print fatal errors about remote control setup before log init,
so that it is printed to console.
22 January 2018: Wouter
- Accept tls-upstream in unbound.conf, the ssl-upstream keyword is
also recognized and means the same. Also for tls-port,
tls-service-key, tls-service-pem, stub-tls-upstream and
forward-tls-upstream.
- Fix #3397: Fix that cachedb could return a partial CNAME chain.
- Fix #3397: Fix that when the cache contains an unsigned DNAME in
the middle of a cname chain, a result without the DNAME could
be returned.
19 January 2018: Wouter
- tag 1.6.8 for release with CVE fix.
- trunk has 1.6.9 with fix and previous commits.
- patch for CVE-2017-15105: vulnerability in the processing of
wildcard synthesized NSEC records.
- iana port update.
- make depend: code dependencies updated in Makefile.
4 January 2018: Ralph
- Copy query and correctly set flags on REFUSED answers when cache
snooping is not allowed.
3 January 2018: Ralph
- Fix queries being leaked above stub when refetching glue.
2 January 2017: Wouter
- Fix that DS queries with referral replies are answered straight
away, without a repeat query picking the DS from cache.
The correct reply should have been an answer, the reply is fixed
by the scrubber to have the answer in the answer section.
- Remove clang optimizer disable,
Fix that expiration date checks don't fail with clang -O2.
15 December 2017: Wouter
- Fix timestamp failure because of clang optimizer failure, by
disabling -O2 when the compiler --version is clang.
- iana port update.
- Also disable -flto for clang, to make incep-expi signature check
work.
12 December 2017: Ralph
- Fix qname-minimisation documentation (A QTYPE, not NS)
12 December 2017: Wouter
- authzone work, transfer connect.
7 December 2017: Ralph
- Check whether --with-libunbound-only is set when using --with-nettle
or --with-nss.
4 December 2017: Wouter
- Fix link failure on OmniOS.
1 December 2017: Wouter
- auth zone work.
30 November 2017: Wouter
- Fix #3299 - forward CNAME daisy chain is not working
14 November 2017: Wouter
- Fix #2882: Unbound behaviour changes (wrong) when domain-insecure is
set for stub zone. It no longer searches for DNSSEC information.
- auth xfer work on probe timer and lookup.
13 November 2017: Wouter
- Fix #2801: Install libunbound.pc.
- Fix qname minimisation to send AAAA queries at zonecut like type A.
- reverted AAAA change.
7 November 2017: Wouter
- Fix #2492: Documentation libunbound.
3 November 2017: Wouter
- Fix #2362: TLS1.3/openssl-1.1.1 not working.
- Fix #2034 - Autoconf and -flto.
- Fix #2141 - for libsodium detect lack of entropy in chroot, print
a message and exit.
2 November 2017: Wouter
- Fix #1913: ub_ctx_config is under circumstances thread-safe.
- make ip-transparent option work on OpenBSD.
31 October 2017: Wouter
- Document that errno is left informative on libunbound config read
fail.
- lexer output.
- iana port update.
25 October 2017: Ralph
- Fixed libunbound manual typo.
- Fix #1949: [dnscrypt] make provider name mismatch more obvious.
- Fix #2031: Double included headers
24 October 2017: Ralph
- Update B root ipv4 address.
19 October 2017: Wouter
- authzone work, probe timer setup.
18 October 2017: Wouter
- lint for recent authzone commit.
17 October 2017: Wouter
- Fix #1749: With harden-referral-path: performance drops, due to
circular dependency in NS and DS lookups.
- [dnscrypt] prevent dnscrypt-secret-key, dnscrypt-provider-cert
duplicates
- [dnscrypt] introduce dnscrypt-provider-cert-rotated option,
from Manu Bretelle.
This option allows handling multiple cert/key pairs while only
distributing some of them.
In order to reliably match a client magic with a given key without
strong assumption as to how those were generated, we need both key and
cert. Likewise, in order to know which ES version should be used.
On the other hand, when rotating a cert, it can be desirable to only
serve the new cert but still be able to handle clients that are still
using the old certs's public key.
The `dnscrypt-provider-cert-rotated` allow to instruct unbound to not
publish the cert as part of the DNS's provider_name's TXT answer.
- Better documentation for cache-max-negative-ttl.
- Work on local root zone code.
10 October 2017: Wouter
- tag 1.6.7
- trunk has version 1.6.8.
6 October 2017: Wouter
- Fix spelling in unbound-control man page.
5 October 2017: Wouter
- Fix trust-anchor-signaling works in libunbound.
- Fix some more crpls in testdata for different signaling default.
- tag 1.6.7rc1
5 October 2017: Ralph
- Set trust-anchor-signaling default to yes
- Use RCODE from A query on DNS64 synthesized answer.
2 October 2017: Wouter
- Fix param unused warning for windows exportsymbol compile.
25 September 2017: Ralph
- Fix #1450: Generate again patch contrib/aaaa-filter-iterator.patch
(by Danilo G. Baio).
21 September 2017: Ralph
- Log name of looping module
19 September 2017: Wouter
- use a cachedb answer even if it's "expired" when serve-expired is yes
(patch from Jinmei Tatuya).
- trigger refetching of the answer in that case (this will bypass
cachedb lookup)
- allow storing a 0-TTL answer from cachedb in the in-memory message
cache when serve-expired is yes
- Fix DNSCACHE_STORE_ZEROTTL to be bigger than 0xffff.
18 September 2017: Ralph
- Fix #1400: allowing use of global cache on ECS-forwarding unless
always-forward.
18 September 2017: Wouter
- tag 1.6.6 (is 1.6.6rc2)
- Fix that looping modules always stop the query, and don't pass
control.
- Fix #1435: Please allow UDP to be disabled separately upstream and
downstream.
- Fix #1440: [dnscrypt] client nonce cache.
15 September 2017: Wouter
- Fix unbound-host to report error for DNSSEC state of failed lookups.
- Spelling fixes, from Josh Soref.
13 September 2017: Wouter
- tag 1.6.6rc2, became 1.6.6 on 18 sep. trunk 1.6.7 in development.
12 September 2017: Wouter
- Add dns64 for client-subnet in unbound-checkconf.
4 September 2017: Ralph
- Fix #1412: QNAME minimisation strict mode not honored
- Fix #1434: Fix windows openssl 1.1.0 linking.
4 September 2017: Wouter
- tag 1.6.6rc1
- makedist fix for windows binaries, with openssl 1.1.0 windres fix,
and expat 2.2.4 install target fix.
1 September 2017: Wouter
- Recommend 1472 buffer size in unbound.conf
31 August 2017: Wouter
- Fix #1424: cachedb:testframe is not thread safe.
- For #1417: escape ; in dnscrypt tests.
- but reverted that, tests fails with that escape.
- Fix #1417: [dnscrypt] shared secret cache counters, and works when
dnscrypt is not enabled. And cache size configuration option.
- make depend
- Fix #1418: [ip ratelimit] initialize slabhash using
ip-ratelimit-slabs.
30 August 2017: Wouter
- updated contrib/fastrpz.patch to apply with configparser changes.
- Fix 1416: qname-minimisation breaks TLSA lookups with CNAMEs.
29 August 2017: Wouter
- Fix #1414: fix segfault on parse failure and log_replies.
- zero qinfo in handle_request, this zeroes local_alias and also the
qname member.
- new keys and certs for dnscrypt tests.
- fixup WKS test on buildhost without servicebyname.
28 August 2017: Wouter
- Fix #1415: patch to free dnscrypt environment on reload.
- iana portlist update
- Fix #1415: [dnscrypt] shared secret cache, patch from
Manu Bretelle.
- Small fixes for the shared secret cache patch.
- Fix WKS records on kvm autobuild host, with default protobyname
entries for udp and tcp.
23 August 2017: Wouter
- Fix #1407: Add ECS options check to unbound-checkconf.
- make depend
- Fix to reclaim tcp handler when it is closed due to dnscrypt buffer
allocation failure.
22 August 2017: Wouter
- Fix install of trust anchor when two anchors are present, makes both
valid. Checks hash of DS but not signature of new key. This fixes
the root.key file if created when unbound is installed between
sep11 and oct11 2017.
- tag 1.6.5 with pointrelease 1.6.5 (1.6.4 plus 5011 fix).
- trunk version 1.6.6 in development.
- Fix issue on macOX 10.10 where TCP fast open is detected but not
implemented causing TCP to fail. The fix allows fallback to regular
TCP in this case and is also more robust for cases where connectx()
fails for some reason.
- Fix #1402: squelch invalid argument error for fd_set_block on windows.
10 August 2017: Wouter
- Patch to show DNSCrypt status in help output, from Carsten
Strotmann.
8 August 2017: Wouter
- Fix #1398: make cachedb secret configurable.
- Remove spaces from Makefile.
7 August 2017: Wouter
- Fix #1397: Recursive DS lookups for AS112 zones names should recurse.
3 August 2017: Ralph
- Remove unused iter_env member (ip6arpa_dname)
- Do not reset rrset.bogus stats when called using stats_noreset.
- Added stats for queries that have been ratelimited by domain
recursion.
- Do not add rrset_bogus and query ratelimiting stats per thread, these
module stats are global.
3 August 2017: Wouter
- Fix #1394: mix of serve-expired and response-ip could cause a crash.
24 July 2017: Wouter
- upgrade aclocal(pkg.m4 0.29.1), config.guess(2016-10-02),
config.sub(2016-09-05).
- annotate case statement fallthrough for gcc 7.1.1.
- flex output from flex 2.6.1.
- snprintf of thread number does not warn about truncated string.
- squelch TCP fast open error on FreeBSD when kernel has it disabled,
unless verbosity is high.
- remove warning from windows compile.
- Fix compile with libnettle
- Fix DSA configure switch (--disable dsa) for libnettle and libnss.
- Fix #1365: Add Ed25519 support using libnettle.
- iana portlist update
17 July 2017: Wouter
- Fix #1350: make cachedb backend configurable (from JINMEI Tatuya).
- Fix #1349: allow suppression of pidfiles (from Daniel Kahn Gillmor).
With the -p option unbound does not create a pidfile.
11 July 2017: Wouter
- Fix #1344: RFC6761-reserved domains: test. and invalid.
- Redirect all localhost names to localhost address for RFC6761.
6 July 2017: Wouter
- Fix tests to use .tdir (from Manu Bretelle) instead of .tpkg.
- Fix svn hooks for tdir (selected if testcode/mini_tdir.sh exists)..
4 July 2017: Wouter
- Fix 1332: Bump verbosity of failed chown'ing of the control socket.
3 July 2017: Wouter
- Fix for unbound-checkconf, check ipsecmod-hook if ipsecmod is turned
on.
- Fix #1331: libunbound segfault in threaded mode when context is
deleted.
- Fix pythonmod link line option flag.
- Fix openssl 1.1.0 load of ssl error strings from ssl init.
29 June 2017: Wouter
- Fix python example0 return module wait instead of error for pass.
- iana portlist update
- enhancement for hardened-tls for DNS over TLS. Removed duplicated
security settings.
27 June 2017: Wouter
- Tag 1.6.4 is created with the 1.6.4rc2 contents.
- Trunk contains 1.6.5, with changes from 26, 27 june.
- Remove signed unsigned warning from authzone.
- Fix that infra cache host hash does not change after reconfig.
26 June 2017: Wouter
- (for 1.6.5)
Better fixup of dnscrypt_cert_chacha test for different escapes.
- First fix for zero b64 and hex text zone format in sldns.
- unbound-control dump_infra prints port number for address if not 53.
23 June 2017: Wouter
- (for 1.6.5): fixup of dnscrypt_cert_chacha test (from Manu Bretelle).
22 June 2017: Wouter
- Tag 1.6.4rc2
22 June 2017: Ralph
- Added fastrpz patch to contrib
21 June 2017: Wouter
- Fix #1316: heap read buffer overflow in parse_edns_options.
20 June 2017: Wouter
- Fix warning in pythonmod under clang compiler.
- Tag 1.6.4rc1
- Fix lintian typo.
16 June 2017: Ralph
- Fix #1277: disable domain ratelimit by setting value to 0.
16 June 2017: Wouter
- Fix #1301: memory leak in respip and tests.
- Free callback in edns-subnetmod on exit and restart.
- Fix memory leak in sldns_buffer_new_frm_data.
- Fix memory leak in dnscrypt config read.
- Fix dnscrypt chacha cert support ifdefs.
- Fix dnscrypt chacha cert unit test escapes in grep.
- Remove asynclook tests that cause test and purifier problems.
- Fix to unlock view in view test.
15 June 2017: Wouter
- Fix stub zone queries leaking to the internet for
harden-referral-path ns checks.
- Fix query for refetch_glue of stub leaking to internet.
13 June 2017: Wouter
- Fix #1279: Memory leak on reload when python module is enabled.
- Fix #1280: Unbound fails assert when response from authoritative
contains malformed qname. When 0x20 caps-for-id is enabled, when
assertions are not enabled the malformed qname is handled correctly.
- 1.6.3 tag created, with only #1280 fix, trunk is 1.6.4 development.
- More fixes in depth for buffer checks in 0x20 qname checks.
12 June 2017: Wouter
- Fix #1278: Incomplete wildcard proof.
8 June 2017: Ralph
- Added domain name based ECS whitelist.
8 June 2017: Wouter
- Detect chacha for dnscrypt at configure time.
- dnscrypt unit tests with chacha.
7 June 2017: Wouter
- Fix that unbound-control can set val_clean_additional and val_permissive_mode.
- Add dnscrypt XChaCha20 tests.
6 June 2017: Wouter
- Add an explicit type cast for TCP FASTOPEN fix.
- renumbering B-Root's IPv6 address to 2001:500:200::b.
- Fix #1275: cached data in cachedb is never used.
- Fix #1276: [dnscrypt] add XChaCha20-Poly1305 cipher.
1 June 2017: Ralph
- Fix #1274: automatically trim chroot path from dnscrypt key/cert paths
(from Manu Bretelle).
1 June 2017: Wouter
- Fix fastopen EPIPE fallthrough to perform connect.
31 May 2017: Ralph
- Also use global local-zones when there is a matching view that does
not have any local-zone specified.
31 May 2017: Wouter
- Fix #1273: cachedb.c doesn't compile with -Wextra.
- If MSG_FASTOPEN gives EPIPE fallthrough to try normal tcp write.
30 May 2017: Ralph
- Fix #1269: inconsistent use of built-in local zones with views.
- Add defaults for new local-zone trees added to views using
unbound-control.
30 May 2017: Wouter
- Support for openssl EVP_DigestVerify.
- Support for the ED25519 algorithm with openssl (from openssl 1.1.1).
29 May 2017: Wouter
- Fix assertion for low buffer size and big edns payload when worker
overrides udpsize.
26 May 2017: Ralph
- Added redirect-bogus.patch to contrib directory.
26 May 2017: Wouter
- Fix #1270: unitauth.c doesn't compile with higher warning level
and optimization
- exec_prefix is by default equal to prefix.
- printout localzone for duplicate local-zone warnings.
24 May 2017: Wouter
- authzone cname chain, no rrset duplicates, wildcard doesn't change
rrsets added for cname chain.
23 May 2017: Wouter
- first services/authzone check in, it compiles and reads and writes
zonefiles.
- iana portlist update
22 May 2017: Wouter
- Fix #1268: SIGSEGV after log_reopen.
18 May 2017: Wouter
- Fix #1265 to use /bin/kill.
- Fix #1267: Libunbound validator/val_secalgo.c uses obsolete APIs,
and compatibility with BoringSSL.
17 May 2017: Wouter
- Fix #1265: contrib/unbound.service contains hardcoded path.
17 May 2017: George
- Use qstate's region for IPSECKEY rrset (ipsecmod).
16 May 2017: George
- Implemented opportunistic IPsec support module (ipsecmod).
- Some whitespace fixup.
16 May 2017: Wouter
- updated dependencies in the makefile.
- document trust-anchor-signaling in example config file.
- updated configure, dependencies and flex output.
- better module memory lookup, fix of unbound-control shm names for
module memory printout of statistics.
- Fix type AVC sldns rrdef.
12 May 2017: Wouter
- Adjust servfail by iterator to not store in cache when serve-expired
is enabled, to avoid overwriting useful information there.
- Fix queries for nameservers under a stub leaking to the internet.
9 May 2017: Ralph
- Add 'c' to getopt() in testbound.
- iana portlist update
8 May 2017: Wouter
- Fix tcp-mss failure printout text.
- Set SO_REUSEADDR on outgoing tcp connections to fix the bind before
connect limited tcp connections. With the option tcp connections
can share the same source port (for different destinations).
2 May 2017: Ralph
- Added mesh_add_sub to add detached mesh entries.
- Use mesh_add_sub for key tag signaling query.
2 May 2017: Wouter
- Added test for leak of stub information.
- Fix sldns wire2str printout of RR type CAA tags.
- Fix sldns int16_data parse.
- Fix sldns parse and printout of TSIG RRs.
- sldns SMIMEA and AVC definitions, same as getdns definitions.
1 May 2017: Wouter
- Fix #1259: "--disable-ecdsa" argument overwritten
by "#ifdef SHA256_DIGEST_LENGTH@daemon/remote.c".
- iana portlist update
- Fix #1258: Windows 10 X64 unbound 1.6.2 service will not start.
and fix that 64bit getting installed in C:\Program Files (x86).
26 April 2017: Ralph
- Implemented trust anchor signaling using key tag query.
26 April 2017: Wouter
- Based on #1257: check parse limit before t increment in sldns RR
string parse routine.
24 April 2017: Wouter
- unbound-checkconf -o allows query of dnstap config variables.
Also unbound-control get_option. Also for dnscrypt.
- trunk contains 1.6.3 version number (changes from 1.6.2 back from
when the 1.6.2rc1 tag has been created).
21 April 2017: Ralph
- Fix #1254: clarify ratelimit-{for,below}-domain (from Manu Bretelle).
- iana portlist update
18 April 2017: Ralph
- Fix #1252: more indentation inconsistencies.
- Fix #1253: unused variable in edns-subnet/addrtree.c:getbit().
13 April 2017: Ralph
- Added ECS unit test (from Manu Bretelle).
- ECS documentation fix (from Manu Bretelle).
13 April 2017: Wouter
- Fix #1250: inconsistent indentation in services/listen_dnsport.c.
- tag for 1.6.2rc1
- (for 1.6.3:) unbound.h exports the shm stats structures. They use
type long long and no ifdefs, and ub_ before the typenames.
12 April 2017: Wouter
- subnet mem value is available in shm, also when not enabled,
to make the struct easier to memmap by other applications,
independent of the configuration of unbound.
12 April 2017: Ralph
- Fix #1247: unbound does not shorten source prefix length when
forwarding ECS.
- Properly check for allocation failure in local_data_find_tag_datas.
- Fix #1249: unbound doesn't return FORMERR to bogus ECS.
- Set SHM ECS memory usage to 0 when module not loaded.
11 April 2017: Ralph
- Display ECS module memory usage.
10 April 2017: Wouter
- harden-algo-downgrade: no also makes unbound more lenient about
digest algorithms in DS records.
10 April 2017: Ralph
- Remove ECS option after REFUSED answer.
- Fix small memory leak in edns_opt_copy_alloc.
- Respip dereference after NULL check.
- Zero initialize addrtree allocation.
- Use correct identifier for SHM destroy.
7 April 2017: George
- Fix pythonmod for cb changes.
- Some whitespace fixup.
7 April 2017: Ralph
- Unlock view in respip unit test
6 April 2017: Ralph
- Generalise inplace callback (de)registration
- (de)register inplace callbacks for module id
- No unbound-control set_option for ECS options
- Deprecated client-subnet-opcode config option
- Introduced client-subnet-always-forward config option
- Changed max-client-subnet-ipv6 default to 56 (as in RFC)
- Removed extern ECS config options
- module_restart_next now calls clear on all following modules
- Also create ECS module qstate on module_event_pass event
- remove malloc from inplace_cb_register
6 April 2017: Wouter
- Small fixup for documentation.
- iana portlist update
- Fix respip for braces when locks arent used.
- Fix pythonmod for cb changes.
4 April 2017: Wouter
- Fix #1244: document that use of chroot requires trust anchor file to
be under chroot.
- iana portlist update
3 April 2017: Ralph
- Do not add current time twice to TTL before ECS cache store.
- Do not touch rrset cache after ECS cache message generation.
- Use LDNS_EDNS_CLIENT_SUBNET as default ECS opcode.
3 April 2017: Wouter
- Fix #1217: Add metrics to unbound-control interface showing
crypted, cert request, plaintext and malformed queries (from
Manu Bretelle).
- iana portlist update
27 March 2017: Wouter
- Remove (now unused) event2 include from dnscrypt code.
24 March 2017: George
- Fix to prevent non-referal query from being cached as referal when the
no_cache_store flag was set.
23 March 2017: Wouter
- Fix #1239: configure fails to find python distutils if python
prints warning.
22 March 2017: Wouter
- Fix #1238: segmentation fault when adding through the remote
interface a per-view local zone to a view with no previous
(configured) local zones.
- Fix #1229: Systemd service sandboxing, options in wrong sections.
21 March 2017: Ralph
- Merge EDNS Client subnet implementation from feature branch into main
branch, using new EDNS processing framework.
21 March 2017: Wouter
- Fix doxygen for dnscrypt files.
20 March 2017: Wouter
- #1217. DNSCrypt support, with --enable-dnscrypt, libsodium and then
enabled in the config file from Manu Bretelle.
- make depend, autoconf, remove warnings about statement before var.
- lru_demote and lruhash_insert_or_retrieve functions for getdns.
- fixup for lruhash (whitespace and header file comment).
- dnscrypt tests.
17 March 2017: Wouter
- Patch for view functionality for local-data-ptr from Björn Ketelaars.
- Fix #1237 - Wrong resolving in chain, for norec queries that get
SERVFAIL returned.
16 March 2017: Wouter
- Fix that SHM is not inited if not enabled.
- Add trustanchor.unbound CH TXT that gets a response with a number
of TXT RRs with a string like "example.com. 2345 1234" with
the trust anchors and their keytags.
- Fix that looped DNAMEs do not cause unbound to spend effort.
- trustanchor tags are sorted. reusable routine to fetch taglist.
13 March 2017: Wouter
- testbound understands Deckard MATCH rcode question answer commands.
- Fix #1235: Fix too long DNAME expansion produces SERVFAIL instead
of YXDOMAIN + query loop, reported by Petr Spacek.
10 March 2017: Wouter
- Fix #1234: shortening DNAME loop produces duplicate DNAME records
in ANSWER section.
9 March 2017: Wouter
- --disable-sha1 disables SHA1 support in RRSIG, so from DNSKEY and
DS records. NSEC3 is not disabled.
- fake-sha1 test option; print warning if used. To make unit tests.
- unbound-control list local zone and data commands listed in the
help output.
8 March 2017: Wouter
- make depend for build dependencies.
- swig version 2.0.1 required.
- fix enum conversion warnings
7 March 2017: Wouter
- Fix #1230: swig version 2.0.0 is required for pythonmod, with
1.3.40 it crashes when running repeatly unbound-control reload.
- Response actions based on IP address from Jinmei Tatuya (Infoblox).
6 March 2017: Wouter
- Fix #1229: Systemd service sandboxing in contrib/unbound.service.
- iana portlist update
28 February 2017: Ralph
- Fix testpkts.c, check if DO bit is set, not only if there is an OPT
record.
28 February 2017: Wouter
- For #1227: if we have sha256, set the cipher list to have no
known vulns.
27 February 2017: Wouter
- Fix #1227: Fix that Unbound control allows weak ciphersuits.
- Fix #1226: provide official 32bit binary for windows.
24 February 2017: Wouter
- include sys/time.h for new shm code on NetBSD.
23 February 2017: Wouter
- Fix doc/CNAME-basedRedirectionDesignNotes.pdf zone static to
redirect.
- Patch from Luiz Fernando Softov for Stats Shared Memory.
- unbound-control stats_shm command prints stats using shared memory,
which uses less cpu.
- make depend, autoconf, doxygen and lint fixed up.
22 February 2017: Wouter
- Fix #1224: Fix that defaults should not fall back to "Program Files
(x86) if Unbound is 64bit by default on windows.
21 February 2017: Wouter
- iana portlist update
16 February 2017: Wouter
- sldns updated for vfixed and buffer resize indication from getdns.
15 February 2017: Wouter
- sldns has ED25519 and ED448 algorithm number and name for display.
14 February 2017: Wouter
- tag 1.6.1rc3. -- which became 1.6.1 on 21feb, trunk has 1.6.2
13 February 2017: Wouter
- Fix autoconf of systemd check for lack of pkg-config.
10 February 2017: Wouter
- Fix pythonmod for typedef changes.
- Fix dnstap for warning of set but not used.
- tag 1.6.1rc2.
9 February 2017: Wouter
- tag 1.6.1rc1.
8 February 2017: Wouter
- Fix for type name change and fix warning on windows compile.
7 February 2017: Wouter
- Include root trust anchor id 20326 in unbound-anchor.
6 February 2017: Wouter
- Fix compile on solaris of the fix to use $host detect.
4 February 2017: Wouter
- fix root_anchor test for updated icannbundle.pem lower certificates.
26 January 2017: Wouter
- Fix 1211: Fix can't enable interface-automatic if no IPv6 with
more helpful error message.
20 January 2017: Wouter
- Increase MAX_MODULE to 16.
19 January 2017: Wouter
- Fix to Rename ub_callback_t to ub_callback_type, because POSIX
reserves _t typedefs.
- Fix to rename internally used types from _t to _type, because _t
type names are reserved by POSIX.
- iana portlist update
12 January 2017: Wouter
- Fix to also block meta types 128 through to 248 with formerr.
- Fix #1206: Some view-related commands are missing from 'unbound-control -h'
9 January 2017: Wouter
- Fix #1202: Fix code comment that packed_rrset_data is not always
'packed'.
6 January 2017: Wouter
- Fix #1201: Fix missing unlock in answer_from_cache error condition.
5 January 2017: Wouter
- Fix to return formerr for queries for meta-types, to avoid
packet amplification if this meta-type is sent on to upstream.
- Fix #1184: Log DNS replies. This includes the same logging
information that DNS queries and response code and response size,
patch from Larissa Feng.
- Fix #1187: Source IP rate limiting, patch from Larissa Feng.
3 January 2017: Wouter
- configure --enable-systemd and lets unbound use systemd sockets if
you enable use-systemd: yes in unbound.conf.
Also there are contrib/unbound.socket and contrib/unbound.service:
systemd files for unbound, install them in /usr/lib/systemd/system.
Contributed by Sami Kerola and Pavel Odintsov.
- Fix reload chdir failure when also chrooted to that directory.
2 January 2017: Wouter
- Fix #1194: Cross build fails when $host isn't `uname` for getentropy.
23 December 2016: Ralph
- Fix #1190: Do not echo back EDNS options in local-zone error response.
- iana portlist update
21 December 2016: Ralph
- Fix #1188: Unresolved symbol 'fake_dsa' in libunbound.so when built
with Nettle
19 December 2016: Ralph
- Fix #1191: remove comment about view deletion.
15 December 2016: Wouter
- iana portlist update
- 64bit is default for windows builds.
- Fix inet_ntop and inet_pton warnings in windows compile.
14 December 2016: Wouter
- Fix #1178: attempt to fix setup error at end, pop result values
at end of install.
13 December 2016: Wouter
- Fix #1182: Fix Resource leak (socket), at startup.
- Fix unbound-control and ipv6 only.
9 December 2016: Wouter
- Fix #1176: stack size too small for Alpine Linux.
8 December 2016: Wouter
- Fix downcast warnings from visual studio in sldns code.
- tag 1.6.0rc1 which became 1.6.0 on 15 dec, and trunk is 1.6.1.
7 December 2016: Ralph
- Add DSA support for OpenSSL 1.1.0
- Fix remote control without cert for LibreSSL
6 December 2016: George
- Added generic EDNS code for registering known EDNS option codes,
bypassing the cache response stage and uniquifying mesh states. Four EDNS
option lists were added to module_qstate (module_qstate.edns_opts_*) to
store EDNS options from/to front/back side.
- Added two flags to module_qstate (no_cache_lookup, no_cache_store) that
control the modules' cache interactions.
- Added code for registering inplace callback functions. The registered
functions can be called just before replying with local data or Chaos,
replying from cache, replying with SERVFAIL, replying with a resolved
query, sending a query to a nameserver. The functions can inspect the
available data and maybe change response/query related data (i.e. append
EDNS options).
- Updated Python module for the above.
- Updated Python documentation.
5 December 2016: Ralph
- Fix #1173: differ local-zone type deny from unset
tag_actions element.
5 December 2016: Wouter
- Fix #1170: document that 'inform' local-zone uses local-data.
1 December 2016: Ralph
- hyphen as minus fix, by Andreas Schulze
30 November 2016: Ralph
- Added local-zones and local-data bulk addition and removal
functionality in unbound-control (local_zones, local_zones_remove,
local_datas and local_datas_remove).
- iana portlist update
29 November 2016: Wouter
- version 1.6.0 is in the development branch.
- braces in view.c around lock statements.
28 November 2016: Wouter
- new install-sh.
25 November 2016: Wouter
- Fix that with openssl 1.1 control-use-cert: no uses less cpu, by
using no encryption over the unix socket.
-22 Novenber 2016: Ralph
+22 November 2016: Ralph
- Make access-control-tag-data RDATA absolute. This makes the RDATA
origin consistent between local-data and access-control-tag-data.
- Fix NSEC ENT wildcard check. Matching wildcard does not have to be a
subdomain of the NSEC owner.
- QNAME minimisation uses QTYPE=A, therefore always check cache for
this type in harden-below-nxdomain functionality.
- Added unit test for QNAME minimisation + harden below nxdomain
synergy.
22 November 2016: Wouter
- iana portlist update.
- Fix unit tests for DS hash processing for fake-dsa test option.
- patch from Dag-Erling Smorgrav that removes code that relies
on sbrk().
21 November 2016: Wouter
- Fix #1158: reference RFC 8020 "NXDOMAIN: There Really Is Nothing
Underneath" for the harden-below-nxdomain option.
10 November 2016: Ralph
- Fix #1155: test status code of unbound-control in 04-checkconf,
not the status code from the tee command.
4 November 2016: Ralph
- Added stub-ssl-upstream and forward-ssl-upstream options.
4 November 2016: Wouter
- configure detects ssl security level API function in the autoconf
manner. Every function on its own, so that other libraries (eg.
LibreSSL) can develop their API without hindrance.
- Fix #1154: segfault when reading config with duplicate zones.
- Note that for harden-below-nxdomain the nxdomain must be secure,
this means nsec3 with optout is insufficient.
3 November 2016: Ralph
- Set OpenSSL security level to 0 when using aNULL ciphers.
3 November 2016: Wouter
- .gitattributes line for githubs code language display.
- log-identity: config option to set sys log identity, patch from
"Robin H. Johnson" <robbat2@gentoo.org>
2 November 2016: Wouter
- iana portlist update.
31 October 2016: Wouter
- Fix failure to build on arm64 with no sbrk.
- iana portlist update.
28 October 2016: Wouter
- Patch for server.num.zero_ttl stats for count of expired replies,
from Pavel Odintsov.
26 October 2016: Wouter
- Fix unit tests for openssl 1.1, with no DSA, by faking DSA, enabled
with the undocumented switch 'fake-dsa'. It logs a warning.
25 October 2016: Wouter
- Fix #1134: unbound-control set_option -- val-override-date: -1 works
immediately to ignore datetime, or back to 0 to enable it again.
The -- is to ignore the '-1' as an option flag.
24 October 2016: Wouter
- serve-expired config option: serve expired responses with TTL 0.
- g.root-servers.net has AAAA address.
21 October 2016: Wouter
- Ported tests for local_cname unit test to testbound framework.
20 October 2016: Wouter
- suppress compile warning in lex files.
- init lzt variable, for older gcc compiler warnings.
- fix --enable-dsa to work, instead of copying ecdsa enable.
- Fix DNSSEC validation of query type ANY with DNAME answers.
- Fixup query_info local_alias init.
19 October 2016: Wouter
- Fix #1130: whitespace in example.conf.in more consistent.
18 October 2016: Wouter
- Patch that resolves CNAMEs entered in local-data conf statements that
point to data on the internet, from Jinmei Tatuya (Infoblox).
- Removed patch comments from acllist.c and msgencode.c
- Added documentation doc/CNAME-basedRedirectionDesignNotes.pdf,
from Jinmei Tatuya (Infoblox).
- Fix #1125: unbound could reuse an answer packet incorrectly for
clients with different EDNS parameters, from Jinmei Tatuya.
- Fix #1118: libunbound.pc sets strange Libs, Libs.private values.
- Added Requires line to libunbound.pc
- Please doxygen by modifying mesh.h
17 October 2016: Wouter
- Re-fix #839 from view commit overwrite.
- Fixup const void cast warning.
12 October 2016: Ralph
- Free view config elements.
11 October 2016: Ralph
- Added qname-minimisation-strict config option.
- iana portlist update.
- fix memoryleak logfile when in debug mode.
5 October 2016: Ralph
- Added views functionality.
- Fix #1117: spelling errors, from Robert Edmonds.
30 September 2016: Wouter
- Fix Nits for 1.5.10 reported by Dag-Erling Smorgrav.
29 September 2016: Wouter
- Fix #838: 1.5.10 cannot be built on Solaris, undefined PATH_MAX.
- Fix #839: Memory grows unexpectedly with large RPZ files.
- Fix #840: infinite loop in unbound_munin_ plugin on unowned lockfile.
- Fix #841: big local-zone's make it consume large amounts of memory.
27 September 2016: Wouter
- tag for 1.5.10 release
- trunk contains 1.5.11 in development.
- Fix dnstap relaying "random" messages instead of resolver/forwarder
responses, from Nikolay Edigaryev.
- Fix #836: unbound could echo back EDNS options in an error response.
20 September 2016: Wouter
- iana portlist update.
- Fix #835: fix --disable-dsa with nettle verify.
- tag for 1.5.10rc1 release.
15 September 2016: Wouter
- Fix 883: error for duplicate local zone entry.
- Test for openssl init_crypto and init_ssl functions.
15 September 2016: Ralph
- fix potential memory leak in daemon/remote.c and nullpointer
dereference in validator/autotrust.
- iana portlist update.
13 September 2016: Wouter
- Silenced flex-generated sign-unsigned warning print with gcc
diagnostic pragma.
- Fix for new splint on FreeBSD. Fix cast for sockaddr_un.sun_len.
9 September 2016: Wouter
- Fix #831: workaround for spurious fread_chk warning against petal.c
5 September 2016: Ralph
- Take configured minimum TTL into consideration when reducing TTL
to original TTL from RRSIG.
5 September 2016: Wouter
- Fix #829: doc of sldns_wire2str_rdata_buf() return value has an
off-by-one typo, from Jinmei Tatuya (Infoblox).
- Fix incomplete prototypes reported by Dag-Erling Smørgrav.
- Fix #828: missing type in access-control-tag-action redirect results
in NXDOMAIN.
2 September 2016: Wouter
- Fix compile with openssl 1.1.0 with api=1.1.0.
1 September 2016: Wouter
- RFC 7958 is now out, updated docs for unbound-anchor.
- Fix for compile without warnings with openssl 1.1.0.
- Fix #826: Fix refuse_non_local could result in a broken response.
- iana portlist update.
29 August 2016: Wouter
- Fix #777: OpenSSL 1.1.0 compatibility, patch from Sebastian A.
Siewior.
- Add default root hints for IPv6 E.ROOT-SERVERS.NET, 2001:500:a8::e.
25 August 2016: Ralph
- Clarify local-zone-override entry in unbound.conf.5
25 August 2016: Wouter
- 64bit build option for makedist windows compile, -w64.
24 August 2016: Ralph
- Fix #820: set sldns_str2wire_rr_buf() dual meaning len parameter
in each iteration in find_tag_datas().
- unbound.conf.5 entries for define-tag, access-control-tag,
access-control-tag-action, access-control-tag-data, local-zone-tag,
and local-zone-override.
23 August 2016: Wouter
- Fix #804: unbound stops responding after outage. Fixes queries
that attempt to wait for an empty list of subqueries.
- Fix #804: lower num_target_queries for iterator also for failed
lookups.
8 August 2016: Wouter
- Note that OPENPGPKEY type is RFC 7929.
4 August 2016: Wouter
- Fix #807: workaround for possible some "unused" function parameters
in test code, from Jinmei Tatuya.
3 August 2016: Wouter
- use sendmsg instead of sendto for TFO.
28 July 2016: Wouter
- Fix #806: wrong comment removed.
26 July 2016: Wouter
- nicer ratelimit-below-domain explanation.
22 July 2016: Wouter
- Fix #801: missing error condition handling in
daemon_create_workers().
- Fix #802: workaround for function parameters that are "unused"
without log_assert.
- Fix #803: confusing (and incorrect) code comment in daemon_cleanup().
20 July 2016: Wouter
- Fix typo in unbound.conf.
18 July 2016: Wouter
- Fix #798: Client-side TCP fast open fails (Linux).
14 July 2016: Wouter
- TCP Fast open patch from Sara Dickinson.
- Fixed unbound.doxygen for 1.8.11.
7 July 2016: Wouter
- access-control-tag-data implemented. verbose(4) prints tag debug.
5 July 2016: Wouter
- Fix dynamic link of anchor-update.exe on windows.
- Fix detect of mingw for MXE package build.
- Fixes for 64bit windows compile.
- Fix #788 for nettle 3.0: Failed to build with Nettle >= 3.0 and
--with-libunbound-only --with-nettle.
4 July 2016: Wouter
- For #787: prefer-ip6 option for unbound.conf prefers to send
upstream queries to ipv6 servers.
- Fix #787: outgoing-interface netblock/64 ipv6 option to use linux
freebind to use 64bits of entropy for every query with random local
part.
30 June 2016: Wouter
- Document always_transparent, always_refuse, always_nxdomain types.
29 June 2016: Wouter
- Fix static compile on windows missing gdi32.
28 June 2016: Wouter
- Create a pkg-config file for libunbound in contrib.
27 June 2016: Wouter
- Fix #784: Build configure assumess that having getpwnam means there
is endpwent function available.
- Updated repository with newer flex and bison output.
24 June 2016: Ralph
- Possibility to specify local-zone type for an acl/tag pair
- Possibility to specify (override) local-zone type for a source address
block
16 June 2016: Ralph
- Decrease dp attempts at each QNAME minimisation iteration
16 June 2016: Wouter
- Fix tcp timeouts in tv.usec.
15 June 2016: Wouter
- TCP_TIMEOUT is specified in milliseconds.
- If more than half of tcp connections are in use, a shorter timeout
is used (200 msec, vs 2 minutes) to pressure tcp for new connects.
14 June 2016: Ralph
- QNAME minimisation unit test for dropped QTYPE=A queries.
14 June 2016: Wouter
- Fix 775: unbound-host and unbound-anchor crash on windows, ignore
null delete for wsaevent.
- Fix spelling in freebind option man page text.
- Fix windows link of ssl with crypt32.
- Fix 779: Union casting is non-portable.
- Fix 780: MAP_ANON not defined in HP-UX 11.31.
- Fix 781: prealloc() is an HP-UX system library call.
13 June 2016: Ralph
- Use QTYPE=A for QNAME minimisation.
- Keep track of number of time-outs when performing QNAME minimisation.
Stop minimising when number of time-outs for a QNAME/QTYPE pair is
more than three.
13 June 2016: Wouter
- Fix #778: unbound 1.5.9: -h segfault (null deref).
- Fix directory: fix for unbound-checkconf, it restores cwd.
10 June 2016: Wouter
- And delete service.conf.shipped on uninstall.
- In unbound.conf directory: dir immediately changes to that directory,
so that include: file below that is relative to that directory.
With chroot, make the directory an absolute path inside chroot.
- keep debug symbols in windows build.
- do not delete service.conf on windows uninstall.
- document directory immediate fix and allow EXECUTABLE syntax in it
on windows.
9 June 2016: Wouter
- Trunk is called 1.5.10 (with previous fixes already in there to 2
june).
- Revert fix for NetworkService account on windows due to breakage
it causes.
- Fix that windows install will not overwrite existing service.conf
file (and ignore gui config choices if it exists).
7 June 2016: Ralph
- Lookup localzones by taglist from acl.
- Possibility to lookup local_zone, regardless the taglist.
- Added local_zone/taglist/acl unit test.
7 June 2016: Wouter
- Fix #773: Non-standard Python location build failure with pyunbound.
- Improve threadsafety for openssl 0.9.8 ecdsa dnssec signatures.
6 June 2016: Wouter
- Better help text from -h (from Ray Griffith).
- access-control-tag config directive.
- local-zone-override config directive.
- access-control-tag-action and access-control-tag-data config
directives.
- free acl-tags, acltag-action and acltag-data config lists during
initialisation to free up memory for more entries.
3 June 2016: Wouter
- Fix to not ignore return value of chown() in daemon startup.
2 June 2016: Wouter
- Fix libubound for edns optlist feature.
- Fix distinction between free and CRYPTO_free in dsa and ecdsa alloc.
- Fix #752: retry resource temporarily unavailable on control pipe.
- un-document localzone tags.
- tag for release 1.5.9rc1.
And this also became release 1.5.9.
- Fix (for 1.5.10): Fix unbound-anchor.exe file location defaults to
Program Files with (x86) appended.
- re-documented localzone tags in example.conf.
31 May 2016: Wouter
- Fix windows service to be created run with limited rights, as a
network service account, from Mario Turschmann.
- compat strsep implementation.
- generic edns option parse and store code.
- and also generic edns options for upstream messages (and replies).
after parse use edns_opt_find(edns.opt_list, LDNS_EDNS_NSID),
to insert use edns_opt_append(edns, region, code, len, bindata) on
the opt_list passed to send_query, or in edns_opt_inplace_reply.
30 May 2016: Wouter
- Fix time in case answer comes from cache in ub_resolve_event().
- Attempted fix for #765: _unboundmodule missing for python3.
27 May 2016: Wouter
- Fix #770: Small subgroup attack on DH used in unix pipe on localhost
if unbound control uses a unix local named pipe.
- Document write permission to directory of trust anchor needed.
- Fix #768: Unbound Service Sometimes Can Not Shutdown
Completely, WER Report Shown Up. Close handle before closing WSA.
26 May 2016: Wouter
- Updated patch from Charles Walker.
24 May 2016: Wouter
- disable-dnssec-lame-check config option from Charles Walker.
- remove memory leak from lame-check patch.
- iana portlist update.
23 May 2016: Wouter
- Fix #767: Reference to an expired Internet-Draft in
harden-below-nxdomain documentation.
20 May 2016: Ralph
- No QNAME minimisation fall-back for NXDOMAIN answers from DNSSEC
signed zones.
- iana portlist update.
19 May 2016: Wouter
- Fix #766: dns64 should synthesize results on timeout/errors.
18 May 2016: Wouter
- Fix #761: DNSSEC LAME false positive resolving nic.club.
17 May 2016: Wouter
- trunk updated with output of flex 2.6.0.
6 May 2016: Wouter
- Fix memory leak in out-of-memory conditions of local zone add.
29 April 2016: Wouter
- Fix sldns with static checking fixes copied from getdns.
28 April 2016: Wouter
- Fix #759: 0x20 capsforid no longer checks type PTR, for
compatibility with cisco dns guard. This lowers false positives.
18 April 2016: Wouter
- Fix some malformed responses to edns queries get fallback to nonedns.
15 April 2016: Wouter
- cachedb module event handling design.
14 April 2016: Wouter
- cachedb module framework (empty).
- iana portlist update.
12 April 2016: Wouter
- Fix #753: document dump_requestlist is for first thread.
24 March 2016: Wouter
- Document permit-small-holddown for 5011 debug.
- Fix #749: unbound-checkconf gets SIGSEGV when use against a
malformatted conf file.
23 March 2016: Wouter
- OpenSSL 1.1.0 portability, --disable-dsa configure option.
21 March 2016: Wouter
- Fix compile of getentropy_linux for SLES11 servicepack 4.
- Fix dnstap-log-resolver-response-messages, from Nikolay Edigaryev.
- Fix test for openssl to use HMAC_Update for 1.1.0.
- acx_nlnetlabs.m4 to v33, with HMAC_Update.
- acx_nlnetlabs.m4 to v34, with -ldl -pthread test for libcrypto.
- ERR_remove_state deprecated since openssl 1.0.0.
- OPENSSL_config is deprecated, removing.
18 March 2016: Ralph
- Validate QNAME minimised NXDOMAIN responses.
- If QNAME minimisation is enabled, do cache lookup for QTYPE NS in
harden-below-nxdomain.
17 March 2016: Ralph
- Limit number of QNAME minimisation iterations.
17 March 2016: Wouter
- Fix #746: Fix unbound sets CD bit on all forwards.
If no trust anchors, it'll not set CD bit when forwarding to another
server. If a trust anchor, no CD bit on the first attempt to a
forwarder, but CD bit thereafter on repeated attempts to get DNSSEC.
- iana portlist update.
16 March 2016: Wouter
- Fix ip-transparent for ipv6 on FreeBSD, thanks to Nick Hibma.
- Fix ip-transparent for tcp on freebsd.
15 March 2016: Wouter
- ip_freebind: yesno option in unbound.conf sets IP_FREEBIND for
binding to an IP address while the interface or address is down.
14 March 2016: Wouter
- Fix warnings in ifdef corner case, older or unknown libevent.
- Fix compile for ub_event code with older libev.
11 March 2016: Wouter
- Remove warning about unused parameter in event_pluggable.c.
- Fix libev usage of dispatch return value.
- No side effects in tolower() call, in case it is a macro.
- For test put free in pluggable api in parenthesis.
10 March 2016: Wouter
- Fixup backend2str for libev.
09 March 2016: Willem
- User defined pluggable event API for libunbound
- Fixup of compile fix for pluggable event API from P.Y. Adi
Prasaja.
09 March 2016: Wouter
- Updated configure and ltmain.sh.
- Updated L root IPv6 address.
07 March 2016: Wouter
- Fix #747: assert in outnet_serviced_query_stop.
- iana ports fetched via https.
- iana portlist update.
03 March 2016: Wouter
- configure tests for the weak attribute support by the compiler.
02 March 2016: Wouter
- 1.5.8 release tag
- trunk contains 1.5.9 in development.
- iana portlist update.
- Fix #745: unbound.py - idn2dname throws UnicodeError when idnname
contains trailing dot.
24 February 2016: Wouter
- Fix OpenBSD asynclook lock free that gets used later (fix test code).
- Fix that NSEC3 negative cache is used when there is no salt.
23 February 2016: Wouter
- ub_ctx_set_stub() function for libunbound to config stub zones.
- sorted ubsyms.def file with exported libunbound functions.
19 February 2016: Wouter
- Print understandable debug log when unusable DS record is seen.
- load gost algorithm if digest is seen before key algorithm.
- iana portlist update.
17 February 2016: Wouter
- Fix that "make install" fails due to "text file busy" error.
16 February 2016: Wouter
- Set IPPROTO_IP6 for ipv6 sockets otherwise invalid argument error.
15 February 2016: Wouter
- ip-transparent option for FreeBSD with IP_BINDANY socket option.
- wait for sendto to drain socket buffers when they are full.
9 February 2016: Wouter
- Test for type OPENPGPKEY.
- insecure-lan-zones: yesno config option, patch from Dag-Erling
Smørgrav.
8 February 2016: Wouter
- Fix patch typo in prevuous commit for 734 from Adi Prasaja.
- RR Type CSYNC support RFC 7477, in debug printout and config input.
- RR Type OPENPGPKEY support (draft-ietf-dane-openpgpkey-07).
29 January 2016: Wouter
- Neater cmdline_verbose increment patch from Edgar Pettijohn.
27 January 2016: Wouter
- Made netbsd sendmsg test nonfatal, in case of false positives.
- Fix #741: log message for dnstap socket connection is more clear.
26 January 2016: Wouter
- Fix #734: chown the pidfile if it resides inside the chroot.
- Use arc4random instead of random in tests (because it is
available, possibly as compat, anyway).
- Fix cmsg alignment for argument to sendmsg on NetBSD.
- Fix that unbound complains about unimplemented IP_PKTINFO for
sendmsg on NetBSD (for interface-automatic).
25 January 2016: Wouter
- Fix #738: Swig should not be invoked with CPPFLAGS.
19 January 2016: Wouter
- Squelch 'cannot assign requested address' log messages unless
verbosity is high, it was spammed after network down.
14 January 2016: Wouter
- Fix to simplify empty string checking from Michael McConville.
- iana portlist update.
12 January 2016: Wouter
- Fix #734: Do not log an error when the PID file cannot be chown'ed.
Patch from Simon Deziel.
11 January 2016: Wouter
- Fix test if -pthreads unused to use better grep for portability.
06 January 2016: Wouter
- Fix mingw crosscompile for recent mingw.
- Update aclocal, autoconf output with new versions (1.15, 2.4.6).
05 January 2016: Wouter
- #731: tcp-mss, outgoing-tcp-mss options for unbound.conf, patch
from Daisuke Higashi.
- Support RFC7686: handle ".onion" Special-Use Domain. It is blocked
by default, and can be unblocked with "nodefault" localzone config.
04 January 2016: Wouter
- Define DEFAULT_SOURCE together with BSD_SOURCE when that is defined,
for Linux glibc 2.20.
- Fixup contrib/aaaa-filter-iterator.patch for moved contents in the
source code, so it applies cleanly again. Removed unused variable
warnings.
15 December 2015: Ralph
- Fix #729: omit use of escape sequences in echo since they are not
portable (unbound-control-setup).
11 December 2015: Wouter
- remove NULL-checks before free, patch from Michael McConville.
- updated ax_pthread.m4 to version 21 with clang support, this
removes a warning from compilation.
- OSX portability, detect if sbrk is deprecated.
- OSX clang, stop -pthread unused during link stage warnings.
- OSX clang new flto check.
10 December 2015: Wouter
- 1.5.7 release
- trunk has 1.5.8 in development.
8 December 2015: Wouter
- Fixup 724 for unbound-control.
7 December 2015: Ralph
- Do not minimise forwarded requests.
4 December 2015: Wouter
- Removed unneeded whitespace from example.conf.
3 December 2015: Ralph
- (after rc1 tag)
- Committed fix to qname minimisation and unit test case for it.
3 December 2015: Wouter
- iana portlist update.
- 1.5.7rc1 prerelease tag.
2 December 2015: Wouter
- Fixup 724: Fix PCA prompt for unbound-service-install.exe.
re-enable stdout printout.
- For 724: Add Changelog to windows binary dist.
1 December 2015: Ralph
- Qname minimisation review fixes
1 December 2015: Wouter
- Fixup 724 fix for fname_after_chroot() calls.
- Remove stdout printout for unbound-service-install.exe
- .gitignore for git users.
30 November 2015: Ralph
- Implemented qname minimisation
30 November 2015: Wouter
- Fix for #724: conf syntax to read files from run dir (on Windows).
25 November 2015: Wouter
- Fix for #720, fix unbound-control-setup windows batch file.
24 November 2015: Wouter
- Fix #720: add windows scripts to zip bundle.
- iana portlist update.
20 November 2015: Wouter
- Added assert on rrset cache correctness.
- Fix that malformed EDNS query gets a response without malformed EDNS.
18 November 2015: Wouter
- newer acx_nlnetlabs.m4.
- spelling fixes from Igor Sobrado Delgado.
17 November 2015: Wouter
- Fix #594. libunbound: optionally use libnettle for crypto.
Contributed by Luca Bruno. Added --with-nettle for use with
--with-libunbound-only.
- refactor nsec3 hash implementation to be more library-portable.
- iana portlist update.
- Fixup DER encoded DSA signatures for libnettle.
16 November 2015: Wouter
- Fix for lenient accept of reverse order DNAME and CNAME.
6 November 2015: Wouter
- Change example.conf: ftp.internic.net to https://www.internic.net
5 November 2015: Wouter
- ACX_SSL_CHECKS no longer adds -ldl needlessly.
3 November 2015: Wouter
- Fix #718: Fix unbound-control-setup with support for env
without HEREDOC bash support.
29 October 2015: Wouter
- patch from Doug Hogan for SSL_OP_NO_SSLvx options.
- Fix #716: nodata proof with empty non-terminals and wildcards.
28 October 2015: Wouter
- Fix checklock testcode for linux threads on exit.
27 October 2015: Wouter
- isblank() compat implementation.
- detect libexpat without xml_StopParser function.
- portability fixes.
- portability, replace snprintf if return value broken.
23 October 2015: Wouter
- Fix #714: Document config to block private-address for IPv4
mapped IPv6 addresses.
22 October 2015: Wouter
- Fix #712: unbound-anchor appears to not fsync root.key.
20 October 2015: Wouter
- 1.5.6 release.
- trunk tracks development of 1.5.7.
15 October 2015: Wouter
- Fix segfault in the dns64 module in the formaterror error path.
- Fix sldns_wire2str_rdata_scan for malformed RRs.
- tag for 1.5.6rc1 release.
14 October 2015: Wouter
- ANY responses include DNAME records if present, as per Evan Hunt's
remark in dnsop.
- Fix manpage to suggest using SIGTERM to terminate the server.
9 October 2015: Wouter
- Default for ssl-port is port 853, the temporary port assignment
for secure domain name system traffic.
If you used to rely on the older default of port 443, you have
to put a clause in unbound.conf for that. The new value is likely
going to be the standardised port number for this traffic.
- iana portlist update.
6 October 2015: Wouter
- 1.5.5 release.
- trunk tracks the development of 1.5.6.
28 September 2015: Wouter
- MAX_TARGET_COUNT increased to 64, to fix up sporadic resolution
failures.
- tag for 1.5.5rc1 release.
- makedist.sh: pgp sig echo commands.
25 September 2015: Wouter
- Fix unbound-control flush that does not succeed in removing data.
22 September 2015: Wouter
- Fix config globbed include chroot treatment, this fixes reload of
globs (patch from Dag-Erling Smørgrav).
- iana portlist update.
- Fix #702: New IPs for for h.root-servers.net.
- Remove confusion comment from canonical_compare() function.
- Fix #705: ub_ctx_set_fwd() return value mishandled on windows.
- testbound selftest also works in non-debug mode.
- Fix minor error in unbound.conf.5.in
- Fix unbound.conf(5) access-control description for precedence
and default.
31 August 2015: Wouter
- changed windows setup compression to be more transparent.
28 August 2015: Wouter
- Fix #697: Get PY_MAJOR_VERSION failure at configure for python
2.4 to 2.6.
- Feature #699: --enable-pie option to that builds PIE binary.
- Feature #700: --enable-relro-now option that enables full read-only
relocation.
24 August 2015: Wouter
- Fix deadlock for local data add and zone add when unbound-control
list_local_data printout is interrupted.
- iana portlist update.
- Change default of harden-algo-downgrade to off. This is lenient
for algorithm rollover.
13 August 2015: Wouter
- 5011 implementation does not insist on all algorithms, when
harden-algo-downgrade is turned off.
- Reap the child process that libunbound spawns.
11 August 2015: Wouter
- Fix #694: configure script does not detect LibreSSL 2.2.2
4 August 2015: Wouter
- Document that local-zone nodefault matches exactly and transparent
can be used to release a subzone.
3 August 2015: Wouter
- Document in the manual more text about configuring locally served
zones.
- Fix 5011 anchor update timer after reload.
- Fix mktime in unbound-anchor not using UTC.
30 July 2015: Wouter
- please afl-gcc (llvm) for uninitialised variable warning.
- Added permit-small-holddown config to debug fast 5011 rollover.
24 July 2015: Wouter
- Fix #690: Reload fails when so-reuseport is yes after changing
num-threads.
- iana portlist update.
21 July 2015: Wouter
- Fix configure to detect SSL_CTX_set_ecdh_auto.
- iana portlist update.
20 July 2015: Wouter
- Enable ECDHE for servers. Where available, use
SSL_CTX_set_ecdh_auto() for TLS-wrapped server configurations to
enable ECDHE. Otherwise, manually offer curve p256.
Client connections should automatically use ECDHE when available.
(thanks Daniel Kahn Gillmor)
18 July 2015: Willem
- Allow certificate chain files to allow for intermediate certificates.
(thanks Daniel Kahn Gillmor)
13 July 2015: Wouter
- makedist produces sha1 and sha256 files for created binaries too.
9 July 2015: Wouter
- 1.5.4 release tag
- trunk has 1.5.5 in development.
- Fix #681: Setting forwarders with unbound-control forward
implicitly turns on forward-first.
29 June 2015: Wouter
- iana portlist update.
- Fix alloc with log for allocation size checks.
26 June 2015: Wouter
- Fix #677 Fix DNAME responses from cache that failed internal chain
test.
- iana portlist update.
22 June 2015: Wouter
- Fix #677 Fix CNAME corresponding to a DNAME was checked incorrectly
and was therefore always synthesized (thanks to Valentin Dietrich).
4 June 2015: Wouter
- RFC 7553 RR type URI support, is now enabled by default.
2 June 2015: Wouter
- Fix #674: Do not free pointers given by getenv.
29 May 2015: Wouter
- Fix that unparseable error responses are ratelimited.
- SOA negative TTL is capped at minimumttl in its rdata section.
- cache-max-negative-ttl config option, default 3600.
26 May 2015: Wouter
- Document that ratelimit works with unbound-control set_option.
21 May 2015: Wouter
- iana portlist update.
- documentation proposes ratelimit of 1000 (closer to what upstream
servers expect from us).
20 May 2015: Wouter
- DLV is going to be decommissioned. Advice to stop using it, and
put text in the example configuration and man page to that effect.
10 May 2015: Wouter
- Change syntax of particular validator error to be easier for
machine parse, swap rrset and ip adres info so it looks like:
validation failure <www.example.nl. TXT IN>: signature crypto
failed from 2001:DB8:7:bba4::53 for <*.example.nl. NSEC IN>
1 May 2015: Wouter
- caps-whitelist in unbound.conf allows whitelist of loadbalancers
that cannot work with caps-for-id or its fallback.
30 April 2015: Wouter
- Unit test for type ANY synthesis.
22 April 2015: Wouter
- Removed contrib/unbound_unixsock.diff, because it has been
integrated, use control-interface: /path in unbound.conf.
- iana portlist update.
17 April 2015: Wouter
- Synthesize ANY responses from cache. Does not search exhaustively,
but MX,A,AAAA,SOA,NS also CNAME.
- Fix leaked dns64prefix configuration string.
16 April 2015: Wouter
- Add local-zone type inform_deny, that logs query and drops answer.
- Ratelimit does not apply to prefetched queries, and ratelimit-factor
is default 10. Repeated normal queries get resolved and with
prefetch stay in the cache.
- Fix bug#664: libunbound python3 related fixes (from Tomas Hozza)
Use print_function also for Python2.
libunbound examples: produce sorted output.
libunbound-Python: libldns is not used anymore.
Fix issue with Python 3 mapping of FILE* using file_py3.i from ldns.
10 April 2015: Wouter
- unbound-control ratelimit_list lists high rate domains.
- ratelimit feature, ratelimit: 100, or some sensible qps, can be
used to turn it on. It ratelimits recursion effort per zone.
For particular names you can configure exceptions in unbound.conf.
- Fix that get_option for cache-sizes does not print double newline.
- Fix#663: ssl handshake fails when using unix socket because dh size
is too small.
8 April 2015: Wouter
- Fix crash in dnstap: Do not try to log TCP responses after timeout.
7 April 2015: Wouter
- Libunbound skips dos-line-endings from etc/hosts.
- Unbound exits with a fatal error when the auto-trust-anchor-file
fails to be writable. This is seconds after startup. You can
load a readonly auto-trust-anchor-file with trust-anchor-file.
The file has to be writable to notice the trust anchor change,
without it, a trust anchor change will be unnoticed and the system
will then become inoperable.
- unbound-control list_insecure command shows the negative trust
anchors currently configured, patch from Jelte Jansen.
2 April 2015: Wouter
- Fix #660: Fix interface-automatic broken in the presence of
asymmetric routing.
26 March 2015: Wouter
- remote.c probedelay line is easier to read.
- rename ldns subdirectory to sldns to avoid name collision.
25 March 2015: Wouter
- Fix #657: libunbound(3) recommends deprecated
CRYPTO_set_id_callback.
- If unknown trust anchor algorithm, and libressl is used, error
message encourages upgrade of the libressl package.
23 March 2015: Wouter
- Fix segfault on user not found at startup (from Maciej Soltysiak).
20 March 2015: Wouter
- Fixed to add integer overflow checks on allocation (defense in depth).
19 March 2015: Wouter
- Add ip-transparent config option for bind to non-local addresses.
17 March 2015: Wouter
- Use reallocarray for integer overflow protection, patch submitted
by Loganaden Velvindron.
16 March 2015: Wouter
- Fixup compile on cygwin, more portable openssl thread id.
12 March 2015: Wouter
- Updated default keylength in unbound-control-setup to 3k.
10 March 2015: Wouter
- Fix lintian warning in unbound-checkconf man page (from Andreas
Schulze).
- print svnroot when building windows dist.
- iana portlist update.
- Fix warning on sign compare in getentropy_linux.
9 March 2015: Wouter
- Fix #644: harden-algo-downgrade option, if turned off, fixes the
reported excessive validation failure when multiple algorithms
are present. It allows the weakest algorithm to validate the zone.
- iana portlist update.
5 March 2015: Wouter
- contrib/unbound_smf22.tar.gz: Solaris SMF installation/removal
scripts. Contributed by Yuri Voinov.
- Document that incoming-num-tcp increase is good for large servers.
- stats reports tcp usage, of incoming-num-tcp buffers.
4 March 2015: Wouter
- Patch from Brad Smith that syncs compat/getentropy_linux with
OpenBSD's version (2015-03-04).
- 0x20 fallback improved: servfail responses do not count as missing
comparisons (except if all responses are errors),
inability to find nameservers does not fail equality comparisons,
many nameservers does not try to compare more than max-sent-count,
parse failures start 0x20 fallback procedure.
- store caps_response with best response in case downgrade response
happens to be the last one.
- Document windows 8 tests.
3 March 2015: Wouter
- tag 1.5.3rc1
[ This became 1.5.3 on 10 March, trunk is 1.5.4 in development ]
2 March 2015: Wouter
- iana portlist update.
20 February 2015: Wouter
- Use the getrandom syscall introduced in Linux 3.17 (from Heiner
Kallweit).
- Fix #645 Portability to Solaris 10, use AF_LOCAL.
- Fix #646 Portability to Solaris, -lrt for getentropy_solaris.
- Fix #647 crash in 1.5.2 because pwd.db no longer accessible after
reload.
19 February 2015: Wouter
- 1.5.2 release tag.
- svn trunk contains 1.5.3 under development.
13 February 2015: Wouter
- Fix #643: doc/example.conf.in: unnecessary whitespace.
12 February 2015: Wouter
- tag 1.5.2rc1
11 February 2015: Wouter
- iana portlist update.
10 February 2015: Wouter
- Fix scrubber with harden-glue turned off to reject NS (and other
not-address) records.
9 February 2015: Wouter
- Fix validation failure in case upstream forwarder (ISC BIND) does
not have the same trust anchors and decides to insert unsigned NS
record in authority section.
2 February 2015: Wouter
- infra-cache-min-rtt patch from Florian Riehm, for expected long
uplink roundtrip times.
30 January 2015: Wouter
- Fix 0x20 capsforid fallback to omit gratuitous NS and additional
section changes.
- Portability fix for Solaris ('sun' is not usable for a variable).
29 January 2015: Wouter
- Fix pyunbound byte string representation for python3.
26 January 2015: Wouter
- Fix unintended use of gcc extension for incomplete enum types,
compile with pedantic c99 compliance (from Daniel Dickman).
23 January 2015: Wouter
- windows port fixes, no AF_LOCAL, no chown, no chmod(grp).
16 January 2015: Wouter
- unit test for local unix connection. Documentation and log_addr
does not inspect port for AF_LOCAL.
- unbound-checkconf -f prints chroot with pidfile path.
13 January 2015: Wouter
- iana portlist update.
12 January 2015: Wouter
- Cast sun_len sizeof to socklen_t.
- Fix pyunbound ord call, portable for python 2 and 3.
7 January 2015: Wouter
- Fix warnings in pythonmod changes.
6 January 2015: Wouter
- iana portlist update.
- patch for remote control over local sockets, from Dag-Erling
Smorgrav, Ilya Bakulin. Use control-interface: /path/sock and
control-use-cert: no.
- Fixup that patch and uid lookup (only for daemon).
- coded the default of control-use-cert, to yes.
5 January 2015: Wouter
- getauxval test for ppc64 linux compatibility.
- make strip works for unbound-host and unbound-anchor.
- patch from Stephane Lapie that adds to the python API, that
exposes struct delegpt, and adds the find_delegation function.
- print query name when max target count is exceeded.
- patch from Stuart Henderson that fixes DESTDIR in
unbound-control-setup for installs where config is not in
the prefix location.
- Fix #634: fix fail to start on Linux LTS 3.14.X, ignores missing
IP_MTU_DISCOVER OMIT option (fix from Remi Gacogne).
- Updated contrib warmup.cmd/sh to support two modes - load
from pre-defined list of domains or (with filename as argument)
load from user-specified list of domains, and updated contrib
unbound_cache.sh/cmd to support loading/save/reload cache to/from
default path or (with secondary argument) arbitrary path/filename,
from Yuri Voinov.
- Patch from Philip Paeps to contrib/unbound_munin_ that uses
type ABSOLUTE. Allows munin.conf: [idleserver.example.net]
unbound_munin_hits.graph_period minute
9 December 2014: Wouter
- svn trunk has 1.5.2 in development.
- config.guess and config.sub update from libtoolize.
- local-zone: example.com inform makes unbound log a message with
client IP for queries in that zone. Eg. for finding infected hosts.
8 December 2014: Wouter
- Fix CVE-2014-8602: denial of service by making resolver chase
endless series of delegations.
1 December 2014: Wouter
- Fix bug#632: unbound fails to build on AArch64, protects
getentropy compat code from calling sysctl if it is has been removed.
29 November 2014: Wouter
- Add include to getentropy_linux.c, hopefully fixing debian build.
28 November 2014: Wouter
- Fix makefile for build from noexec source tree.
26 November 2014: Wouter
- Fix libunbound undefined symbol errors for main.
Referencing main does not seem to be possible for libunbound.
24 November 2014: Wouter
- Fix log at high verbosity and memory allocation failure.
- iana portlist update.
21 November 2014: Wouter
- Fix crash on multiple thread random usage on systems without
arc4random.
20 November 2014: Wouter
- fix compat/getentropy_win.c check if CryptGenRandom works and no
immediate exit on windows.
19 November 2014: Wouter
- Fix cdflag dns64 processing.
18 November 2014: Wouter
- Fix that CD flag disables DNS64 processing, returning the DNSSEC
signed AAAA denial.
- iana portlist update.
17 November 2014: Wouter
- Fix #627: SSL_CTX_load_verify_locations return code not properly
checked.
14 November 2014: Wouter
- parser with bison 2.7
13 November 2014: Wouter
- Patch from Stephane Lapie for ASAHI Net that implements aaaa-filter,
added to contrib/aaaa-filter-iterator.patch.
12 November 2014: Wouter
- trunk has 1.5.1 in development.
- Patch from Robert Edmonds to build pyunbound python module
differently. No versioninfo, with -shared and without $(LIBS).
- Patch from Robert Edmonds fixes hyphens in unbound-anchor man page.
- Removed 'increased limit open files' log message that is written
to console. It is only written on verbosity 4 and higher.
This keeps system bootup console cleaner.
- Patch from James Raftery, always print stats for rcodes 0..5.
11 November 2014: Wouter
- iana portlist update.
- Fix bug where forward or stub addresses with same address but
different port number were not tried.
- version number in svn trunk is 1.5.0
- tag 1.5.0rc1
- review fix from Ralph.
7 November 2014: Wouter
- dnstap fixes by Robert Edmonds:
dnstap/dnstap.m4: cosmetic fixes
dnstap/: Remove compiled protoc-c output files
dnstap/dnstap.m4: Error out if required libraries are not found
dnstap: Fix ProtobufCBufferSimple usage that is incorrect as of
protobuf-c 1.0.0
dnstap/: Adapt to API changes in latest libfstrm (>= 0.2.0)
4 November 2014: Wouter
- Add ub_ctx_add_ta_autr function to add a RFC5011 automatically
tracked trust anchor to libunbound.
- Redefine internal minievent symbols to unique symbols that helps
linking on platforms where the linker leaks names across modules.
27 October 2014: Wouter
- Disabled use of SSLv3 in remote-control and ssl-upstream.
- iana portlist update.
16 October 2014: Wouter
- Documented dns64 configuration in unbound.conf man page.
13 October 2014: Wouter
- Fix #617: in ldns in unbound, lowercase WKS services.
- Fix ctype invocation casts.
10 October 2014: Wouter
- Fix unbound-checkconf check for module config with dns64 module.
- Fix unbound capsforid fallback, it ignores TTLs in comparison.
6 October 2014: Wouter
- Fix #614: man page variable substitution bug.
6 October 2014: Willem
- Whitespaces after $ORIGIN are not part of the origin dname (ldns).
- $TTL's value starts at position 5 (ldns).
1 October 2014: Wouter
- fix #613: Allow tab ws in var length last rdfs (in ldns str2wire).
29 September 2014: Wouter
- Fix #612: create service with service.conf in present directory and
auto load it.
- Fix for mingw compile openssl ranlib.
25 September 2014: Wouter
- updated configure and aclocal with newer autoconf 1.13.
22 September 2014: Wouter
- Fix swig and python examples for Python 3.x.
- Fix for mingw compile with openssl-1.0.1i.
19 September 2014: Wouter
- improve python configuration detection to build on Fedora 22.
18 September 2014: Wouter
- patches to also build with Python 3.x (from Pavel Simerda).
16 September 2014: Wouter
- Fix tcp timer waiting list removal code.
- iana portlist update.
- Updated the TCP_BACLOG from 5 to 256, so that the tcp accept queue
is longer and more tcp connections can be handled.
15 September 2014: Wouter
- Fix unit test for CDS typecode.
5 September 2014: Wouter
- type CDS and CDNSKEY types in sldns.
25 August 2014: Wouter
- Fixup checklock code for log lock and its mutual initialization
dependency.
- iana portlist update.
- Removed necessity for pkg-config from the dnstap.m4, new are
the --with-libfstrm and --with-protobuf-c configure options.
19 August 2014: Wouter
- Update unbound manpage with more explanation (from Florian Obser).
18 August 2014: Wouter
- Fix #603: unbound-checkconf -o <option> should skip verification
checks.
- iana portlist update.
- Fixup doc/unbound.doxygen to remove obsolete 1.8.7 settings.
5 August 2014: Wouter
- dnstap support, with a patch from Farsight Security, written by
Robert Edmonds. The --enable-dnstap needs libfstrm and protobuf-c.
It is BSD licensed (see dnstap/dnstap.c).
Building with --enable-dnstap needs pkg-config with this patch.
- Noted dnstap in doc/README and doc/CREDITS.
- Changes to the dnstap patch.
- lint fixes.
- dnstap/dnstap_config.h should not have been added to the repo,
because is it generated.
1 August 2014: Wouter
- Patch add msg, rrset, infra and key cache sizes to stats command
from Maciej Soltysiak.
- iana portlist update.
31 July 2014: Wouter
- DNS64 from Viagenie (BSD Licensed), written by Simon Perrault.
Initial commit of the patch from the FreeBSD base (with its fixes).
This adds a module (for module-config in unbound.conf) dns64 that
performs DNS64 processing, see README.DNS64.
- Changes from DNS64:
strcpy changed to memmove.
arraybound check fixed from prefix_net/8/4 to prefix_net/8+4.
allocation of result consistently in the correct region.
time_t is now used for ttl in unbound (since the patch's version).
- testdata/dns64_lookup.rpl for unit test for dns64 functionality.
29 July 2014: Wouter
- Patch from Dag-Erling Smorgrav that implements feature, unbound -dd
does not fork in the background and also logs to stderr.
21 July 2014: Wouter
- Fix endian.h include for OpenBSD.
16 July 2014: Wouter
- And Fix#596: Bail out of unbound-control dump_infra when ssl
write fails.
15 July 2014: Wouter
- Fix #596: Bail out of unbound-control list_local_zones when ssl
write fails.
- iana portlist update.
13 July 2014: Wouter
- Configure tests if main can be linked to from getentropy compat.
12 July 2014: Wouter
- Fix getentropy compat code, function refs were not portable.
- Fix to check openssl version number only for OpenSSL.
- LibreSSL provides compat items, check for that in configure.
- Fix bug in fix for log locks that caused deadlock in signal handler.
- update compat/getentropy and arc4random to the most recent ones from OpenBSD.
11 July 2014: Matthijs
- fake-rfc2553 patch (thanks Benjamin Baier).
11 July 2014: Wouter
- arc4random in compat/ and getentropy, explicit_bzero, chacha for
dependencies, from OpenBSD. arc4_lock and sha512 in compat.
This makes arc4random available on all platforms, except when
compiled with LIBNSS (it uses libNSS crypto random).
- fix strptime implicit declaration error on OpenBSD.
- arc4random, getentropy and explicit_bzero compat for Windows.
4 July 2014: Wouter
- Fix #593: segfault or crash upon rotating logfile.
3 July 2014: Wouter
- DLV tests added.
- signit tool fixup for compile with libldns library.
- iana portlist updated.
27 June 2014: Wouter
- so-reuseport is available on BSDs(such as FreeBSD 10) and OS/X.
26 June 2014: Wouter
- unbound-control status reports if so-reuseport was successful.
- iana portlist updated.
24 June 2014: Wouter
- Fix caps-for-id fallback, and added fallback attempt when servers
drop 0x20 perturbed queries.
- Fixup testsetup for VM tests (run testcode/run_vm.sh).
17 June 2014: Wouter
- iana portlist updated.
3 June 2014: Wouter
- Add AAAA for B root server to default root hints.
2 June 2014: Wouter
- Remove unused define from iterator.h
30 May 2014: Wouter
- Fixup sldns_enum_edns_option typedef definition.
28 May 2014: Wouter
- Code cleanup patch from Dag-Erling Smorgrav, with compiler issue
fixes from FreeBSD's copy of Unbound, he notes:
Generate unbound-control-setup.sh at build time so it respects
prefix and sysconfdir from the configure script. Also fix the
umask to match the comment, and the comment to match the umask.
Add const and static where needed. Use unions instead of
playing pointer poker. Move declarations that are needed in
multiple source files into a shared header. Move sldns_bgetc()
from parse.c to buffer.c where it belongs. Introduce a new
header file, worker.h, which declares the callbacks that
all workers must define. Remove those declarations from
libworker.h. Include the correct headers in the correct places.
Fix a few dummy callbacks that don't match their prototype.
Fix some casts. Hide the sbrk madness behind #ifdef HAVE_SBRK.
Remove a useless printf which breaks reproducible builds.
Get rid of CONFIGURE_{TARGET,DATE,BUILD_WITH} now that they're
no longer used. Add unbound-control-setup.sh to the list of
generated files. The prototype for libworker_event_done_cb()
needs to be moved from libunbound/libworker.h to
libunbound/worker.h.
- Fixup out-of-directory compile with unbound-control-setup.sh.in.
- make depend.
23 May 2014: Wouter
- unbound-host -D enabled dnssec and reads root trust anchor from
the default root key file that was compiled in.
20 May 2014: Wouter
- Feature, unblock-lan-zones: yesno that you can use to make unbound
perform 10.0.0.0/8 and other reverse lookups normally, for use if
unbound is running service for localhost on localhost.
16 May 2014: Wouter
- Updated create_unbound_ad_servers and unbound_cache scripts from
Yuri Voinov in the source/contrib directory. Added
warmup.cmd (and .sh): warm up the DNS cache with your MRU domains.
9 May 2014: Wouter
- Implement draft-ietf-dnsop-rfc6598-rfc6303-01.
- iana portlist updated.
8 May 2014: Wouter
- Contrib windows scripts from Yuri Voinov added to src/contrib:
create_unbound_ad_servers.cmd: enters anti-ad server lists.
unbound_cache.cmd: saves and loads the cache.
- Added unbound-control-setup.cmd from Yuri Voinov to the windows
unbound distribution set. It requires openssl installed in %PATH%.
6 May 2014: Wouter
- Change MAX_SENT_COUNT from 16 to 32 to resolve some cases easier.
5 May 2014: Wouter
- More #567: remove : from output of stub and forward lists, this is
easier to parse.
29 April 2014: Wouter
- iana portlist updated.
- Add unbound-control flush_negative that flushed nxdomains, nodata,
and errors from the cache. For dnssec-trigger and NetworkManager,
fixes cases where network changes have localdata that was already
negatively cached from the previous network.
23 April 2014: Wouter
- Patch from Jeremie Courreges-Anglas to use arc4random_uniform
if available on the OS, it gets entropy from the OS.
15 April 2014: Wouter
- Fix compile with libevent2 on FreeBSD.
11 April 2014: Wouter
- Fix #502: explain that do-ip6 disable does not stop AAAA lookups,
but it stops the use of the ipv6 transport layer for DNS traffic.
- iana portlist updated.
10 April 2014: Wouter
- iana portlist updated.
- Patch from Hannes Frederic Sowa for Linux 3.15 fragmentation
option for DNS fragmentation defense.
- Document that dump_requestlist only prints queries from thread 0.
- unbound-control stats prints num.query.tcpout with number of TCP
outgoing queries made in the previous statistics interval.
- Fix #567: unbound lists if forward zone is secure or insecure with
+i annotation in output of list_forwards, also for list_stubs
(for NetworkManager integration.)
- Fix #554: use unsigned long to print 64bit statistics counters on
64bit systems.
- Fix #558: failed prefetch lookup does not remove cached response
but delays next prefetch (in lieu of caching a SERVFAIL).
- Fix #545: improved logging, the ip address of the error is printed
on the same log-line as the error.
8 April 2014: Wouter
- Fix #574: make test fails on Ubuntu 14.04. Disabled remote-control
in testbound scripts.
- iana portlist updated.
7 April 2014: Wouter
- C.ROOT-SERVERS.NET has an IPv6 address, and we updated the root
hints (patch from Anand Buddhdev).
- Fix #572: Fix unit test failure for systems with different
/etc/services.
28 March 2014: Wouter
- Fix #569: do_tcp is do-tcp in unbound.conf man page.
25 March 2014: Wouter
- Patch from Stuart Henderson to build unbound-host man from .1.in.
24 March 2014: Wouter
- Fix print filename of encompassing config file on read failure.
12 March 2014: Wouter
- tag 1.4.22
- trunk has 1.4.23 in development.
10 March 2014: Wouter
- Fix bug#561: contrib/cacti plugin did not report SERVFAIL rcodes
because of spelling. Patch from Chris Coates.
27 February 2014: Wouter
- tag 1.4.22rc1
21 February 2014: Wouter
- iana portlist updated.
20 February 2014: Matthijs
- Be lenient when a NSEC NameError response with RCODE=NXDOMAIN is
received. This is okay according 4035, but not after revising
existence in 4592. NSEC empty non-terminals exist and thus the
RCODE should have been NOERROR. If this occurs, and the RRsets
are secure, we set the RCODE to NOERROR and the security status
of the response is also considered secure.
14 February 2014: Wouter
- Works on Minix (3.2.1).
11 February 2014: Wouter
- Fix parse of #553(NSD) string in sldns, quotes without spaces.
7 February 2014: Wouter
- iana portlist updated.
- add body to ifstatement if locks disabled.
- add TXT string"string" test case to unit test.
- Fix #551: License change "Regents" to "Copyright holder", matching
the BSD license on opensource.org.
6 February 2014: Wouter
- sldns has type HIP.
- code documentation on the module interface.
5 February 2014: Wouter
- Fix sldns parse tests on osx.
3 February 2014: Wouter
- Detect libevent2 install automatically by configure.
- Fixup link with lib/event2 subdir.
- Fix parse in sldns of quoted parenthesized text strings.
31 January 2014: Wouter
- unit test for ldns wire to str and back with zones, root, nlnetlabs
and types.sidnlabs.
- Fix for hex to string in unknown, atma and nsap.
- fixup nss compile (no ldns in it).
- fixup warning in unitldns
- fixup WKS and rdata type service to print unsigned because strings
are not portable; they cannot be read (for sure) on other computers.
- fixup type EUI48 and EUI64, type APL and type IPSECKEY in string
parse sldns.
30 January 2014: Wouter
- delay-close does not act if there are udp-wait queries, so that
it does not make a socketdrain DoS easier.
28 January 2014: Wouter
- iana portlist updated.
- iana portlist test updated so it does not touch the source
if there are no changes.
- delay-close: msec option that delays closing ports for which
the UDP reply has timed out. Keeps the port open, only accepts
the correct reply. This correct reply is not used, but the port
is open so that no port-denied ICMPs are generated.
27 January 2014: Wouter
- reuseport is attempted, then fallback to without on failure.
24 January 2014: Wouter
- Change unbound-event.h to use void* buffer, length idiom.
- iana portlist updated.
- unbound-event.h is installed if you configure --enable-event-api.
- speed up unbound (reports say it could be up to 10%), by reducing
lock contention on localzones.lock. It is changed to an rwlock.
- so-reuseport: yesno option to distribute queries evenly over
threads on Linux (Thanks Robert Edmonds).
- made lint clean.
21 January 2014: Wouter
- Fix #547: no trustanchor written if filesystem full, fclose checked.
17 January 2014: Wouter
- Fix isprint() portability in sldns, uses unsigned int.
- iana portlist updated.
16 January 2014: Wouter
- fix #544: Fixed +i causes segfault when running with module conf
"iterator".
- Windows port, adjust %lld to %I64d, and warning in win_event.c.
14 January 2014: Wouter
- iana portlist updated.
5 Dec 2013: Wouter
- Fix bug in cachedump that uses sldns.
- update pythonmod for ldns_ to sldns_ name change.
3 Dec 2013: Wouter
- Fix sldns to use sldns_ prefix for all ldns_ variables.
- Fix windows compile to compile with sldns.
30 Nov 2013: Wouter
- Fix sldns to make globals use sldns_ prefix. This fixes
linking with libldns that uses global variables ldns_ .
13 Nov 2013: Wouter
- Fix bug#537: compile python plugin without ldns library.
12 Nov 2013: Wouter
- Fix bug#536: acl_deny_non_local and refuse_non_local added.
5 Nov 2013: Wouter
- Patch from Neel Goyal to fix async id assignment if callback
is called by libunbound in the mesh attach.
- Accept ip-address: as an alternative for interface: for
consistency with nsd.conf syntax.
4 Nov 2013: Wouter
- Patch from Neel Goyal to fix callback in libunbound.
3 Nov 2013: Wouter
- if configured --with-libunbound-only fix make install.
31 Oct 2013: Wouter
- Fix #531: Set SO_REUSEADDR so that the wildcard interface and a
more specific interface port 53 can be used at the same time, and
one of the daemons is unbound.
- iana portlist update.
- separate ldns into core ldns inside ldns/ subdirectory. No more
--with-ldns is needed and unbound does not rely on libldns.
- portability fixes for new USE_SLDNS ldns subdir codebase.
22 Oct 2013: Wouter
- Patch from Neel Goyal: Add an API call to set an event base on an
existing ub_ctx. This basically just destroys the current worker and
sets the event base to the current. And fix a deadlock in
ub_resolve_event – the cfglock is held when libworker_create is
called. This ends up trying to acquire the lock again in
context_obtain_alloc in the call chain.
- Fix #528: if very high logging (4 or more) segfault on allow_snoop.
26 Sep 2013: Wouter
- unbound-event.h is installed if configured --with-libevent. It
contains low-level library calls, that use libevent's event_base
and an ldns_buffer for the wire return packet to perform async
resolution in the client's eventloop.
19 Sep 2013: Wouter
- 1.4.21 tag created.
- trunk has 1.4.22 number inside it.
- iana portlist updated.
- acx_nlnetlabs.m4 to 26; improve FLTO help text.
16 Sep 2013: Wouter
- Fix#524: max-udp-size not effective to non-EDNS0 queries, from
Daisuke HIGASHI.
10 Sep 2013: Wouter
- MIN_TTL and MAX_TTL also in time_t.
- tag 1.4.21rc1 made again.
26 Aug 2013: Wouter
- More fixes for bug#519: for the threaded case test if the bg
thread has been killed, on ub_ctx_delete, to avoid hangs.
22 Aug 2013: Wouter
- more fixes that I overlooked.
- review fixes from Willem.
21 Aug 2013: Wouter
- Fix#520: Errors found by static analysis from Tomas Hozza(redhat).
20 Aug 2013: Wouter
- Fix for 2038, with time_t instead of uint32_t.
19 Aug 2013: Wouter
- Fix#519 ub_ctx_delete may hang in some scenarios (libunbound).
14 Aug 2013: Wouter
- Fix uninit variable in fix#516.
8 Aug 2013: Wouter
- Fix#516 dnssec lameness detection for answers that are improper.
30 Jun 2013: Wouter
- tag 1.4.21rc1
29 Jun 2013: Wouter
- Fix#512 memleak in testcode for testbound (if it fails).
- Fix#512 NSS returned arrays out of setup function to be statics.
26 Jun 2013: Wouter
- max include of 100.000 files (depth and globbed at one time).
This is to preserve system memory in bug cases, or endless cases.
- iana portlist updated.
19 Jun 2013: Wouter
- streamtcp man page, contributed by Tomas Hozza.
- iana portlist updated.
- libunbound documentation on how to avoid openssl race conditions.
25 Jun 2013: Wouter
- Squelch sendto-permission denied errors when the network is
not connected, to avoid spamming syslog.
- configure --disable-flto option (from Robert Edmonds).
18 Jun 2013: Wouter
- Fix for const string literals in C++ for libunbound, from Karel
Slany.
- iana portlist updated.
17 Jun 2013: Wouter
- Fixup manpage syntax.
14 Jun 2013: Wouter
- get_option and set_option support for log-time-ascii, python-script
val-sig-skew-min and val-sig-skew-max. log-time-ascii takes effect
immediately. The others are mostly useful for libunbound users.
13 Jun 2013: Wouter
- get_option, set_option, unbound-checkconf -o and libunbound
getoption and setoption support cache-min-ttl and cache-max-ttl.
10 Jun 2013: Wouter
- Fix#501: forward-first does not recurse, when forward name is ".".
- iana portlist update.
- Max include depth is unlimited.
27 May 2013: Wouter
- Update acx_pthreads.m4 to ax_pthreads.4 (2013-03-29), and apply
patch to it to not fail when -Werror is also specified, from the
autoconf-archives.
- iana portlist update.
21 May 2013: Wouter
- Explain bogus and secure flags in libunbound more.
16 May 2013: Wouter
- Fix#499 use-after-free in out-of-memory handling code (thanks Jake
Montgomery).
- Fix#500 use on non-initialised values on socket bind failures.
15 May 2013: Wouter
- Fix round-robin doesn't work with some Windows clients (from Ilya
Bakulin).
3 May 2013: Wouter
- update acx_nlnetlabs.m4 to v23, sleep w32 fix.
26 April 2013: Wouter
- add unbound-control insecure_add and insecure_remove for the
administration of negative trust anchors.
25 April 2013: Wouter
- Implement max-udp-size config option, default 4096 (thanks
Daisuke Higashi).
- Robust checks on dname validity from rdata for dname compare.
- updated iana portlist.
19 April 2013: Wouter
- Fixup snprintf return value usage, fixed libunbound_get_option.
18 April 2013: Wouter
- fix bug #491: pick program name (0th argument) as syslog identity.
- own implementation of compat/snprintf.c.
15 April 2013: Wouter
- Fix so that for a configuration line of include: "*.conf" it is not
an error if there are no files matching the glob pattern.
- unbound-anchor review: BIO_write can return 0 successfully if it
has successfully appended a zero length string.
11 April 2013: Wouter
- Fix queries leaking up for stubs and forwards, if the configured
nameservers all fail to answer.
10 April 2013: Wouter
- code improve for minimal responses, small speed increase.
9 April 2013: Wouter
- updated iana portlist.
- Fix crash in previous private address fixup of 22 March.
28 March 2013: Wouter
- Make reverse zones easier by documenting the nodefault statements
commented-out in the example config file.
26 March 2013: Wouter
- more fixes to lookup3.c endianness detection.
25 March 2013: Wouter
- #492: Fix endianness detection, revert to older lookup3.c detection
and put new detect lines after previous tests, to avoid regressions
but allow new detections to succeed.
And add detection for machine/endian.h to it.
22 March 2013: Wouter
- Fix resolve of names that use a mix of public and private addresses.
- iana portlist update.
- Fix makedist for new svn for -d option.
- unbound.h header file has UNBOUND_VERSION_MAJOR define.
- Fix windows RSRC version for long version numbers.
21 March 2013: Wouter
- release 1.4.20
- trunk has 1.4.21
- committed libunbound version 4:1:2 for binary API updated in 1.4.20
- install copy of unbound-control.8 man page for unbound-control-setup
14 March 2013: Wouter
- iana portlist update.
- tag 1.4.20rc1
12 March 2013: Wouter
- Fixup makedist.sh for windows compile.
11 March 2013: Wouter
- iana portlist update.
- testcode/ldns-testpkts.c check for makedist is informational.
15 February 2013: Wouter
- fix defines in lookup3 for bigendian bsd alpha
11 February 2013: Wouter
- Fixup openssl_thread init code to only run if compiled with SSL.
7 February 2013: Wouter
- detect endianness in lookup3 on BSD.
- add libunbound.ttl at end of result structure, version bump for
libunbound and binary backwards compatible, but 1.4.19 is not
forward compatible with 1.4.20.
- update iana port list.
30 January 2013: Wouter
- includes and have_ssl fixes for nss.
29 January 2013: Wouter
- printout name of zone with duplicate fwd and hint errors.
28 January 2013: Wouter
- updated fwd_zero for newer nc. Updated common.sh for newer netstat.
17 January 2013: Wouter
- unbound-anchors checks the emailAddress of the signer of the
root.xml file, default is dnssec@iana.org. It also checks that
the signer has the correct key usage for a digital signature.
- update iana port list.
3 January 2013: Wouter
- Test that unbound-control checks client credentials.
- Test that unbound can handle a CNAME at an intermediate node in
the chain of trust (where it seeks a DS record).
- Check the commonName of the signer of the root.xml file in
unbound-anchor, default is dnssec@iana.org.
2 January 2013: Wouter
- Fix openssl lock free on exit (reported by Robert Fleischman).
- iana portlist updated.
- Tested that unbound implements the RFC5155 Technical Errata id 3441.
Unbound already implements insecure classification of an empty
nonterminal in NSEC3 optout zone.
20 December 2012: Wouter
- Fix unbound-anchor xml parse of entity declarations for safety.
19 December 2012: Wouter
- iana portlist updated.
18 December 2012: Wouter
- iana portlist updated.
14 December 2012: Wouter
- Change of D.ROOT-SERVERS.NET A address in default root hints.
12 December 2012: Wouter
- 1.4.19 release.
- trunk has 1.4.20 under development.
5 December 2012: Wouter
- note support for AAAA RR type RFC.
4 December 2012: Wouter
- 1.4.19rc1 tag.
30 November 2012: Wouter
- bug 481: fix python example0.
- iana portlist updated.
27 November 2012: Wouter
- iana portlist updated.
9 November 2012: Wouter
- Fix unbound-control forward disables configured stubs below it.
7 November 2012: Wouter
- Fixup ldns-testpkts, identical to ldns/examples.
- iana portlist updated.
30 October 2012: Wouter
- Fix bug #477: unbound-anchor segfaults if EDNS is blocked.
29 October 2012: Matthijs
- Fix validation for responses with both CNAME and wildcard
expanded CNAME records in answer section.
8 October 2012: Wouter
- update ldns-testpkts.c to ldns 1.6.14 version.
- fix build of pythonmod in objdir, for unbound.py.
- make clean and makerealclean remove generated python and docs.
5 October 2012: Wouter
- fix build of pythonmod in objdir (thanks Jakob Schlyter).
3 October 2012: Wouter
- fix text in unbound-anchor man page.
1 October 2012: Wouter
- ignore trusted-keys globs that have no files (from Paul Wouters).
27 September 2012: Wouter
- include: directive in config file accepts wildcards. Patch from
Paul Wouters. Suggested use: include: "/etc/unbound.d/conf.d/*"
- unbound-control -q option is quiet, patch from Mariano Absatz.
- iana portlist updated.
- updated contrib/unbound.spec, patch from Valentin Bud.
21 September 2012: Wouter
- chdir to / after chroot call (suggested by Camiel Dobbelaar).
17 September 2012: Wouter
- patch_rsamd5_enable.diff: this patch enables RSAMD5 validation
otherwise it is treated as insecure. The RSAMD5 algorithm is
deprecated (RFC6725). The MD5 hash is considered weak for some
purposes, if you want to sign your zone, then RSASHA256 is an
uncontested hash.
30 August 2012: Wouter
- RFC6725 deprecates RSAMD5: this DNSKEY algorithm is disabled.
- iana portlist updated.
29 August 2012: Wouter
- Nicer comments outgoing-port-avoid, thanks Stu (bug #465).
22 August 2012: Wouter
- Fallback to 1472 and 1232, one fragment size without headers.
21 August 2012: Wouter
- Fix timeouts so that when a server has been offline for a while
and is probed to see it works, it becomes fully available for
server selection again.
17 August 2012: Wouter
- Add documentation to libunbound for default nonuse of resolv.conf.
2 August 2012: Wouter
- trunk has 1.4.19 under development (fixes from 1 aug and 31 july
are for 1.4.19).
- iana portlist updated.
1 August 2012: Wouter
- Fix openssl race condition, initializes openssl locks, reported
by Einar Lonn and Patrik Wallstrom.
31 July 2012: Wouter
- Improved forward-first and stub-first documentation.
- Fix that enables modules to register twice for the same
serviced_query, without race conditions or administration issues.
This should not happen with the current codebase, but it is robust.
- Fix forward-first option where it sets the RD flag wrongly.
- added manpage links for libunbound calls (Thanks Paul Wouters).
30 July 2012: Wouter
- tag 1.4.18rc2 (became 1.4.18 release at 2 august 2012).
27 July 2012: Wouter
- unbound-host works with libNSS
- fix bogus nodata cname chain not reported as bogus by validator,
(Thanks Peter van Dijk).
26 July 2012: Wouter
- iana portlist updated.
- tag 1.4.18rc1.
25 July 2012: Wouter
- review fix for libnss, check hash prefix allocation size.
23 July 2012: Wouter
- fix missing break for GOST DS hash function.
- implemented forward_first for the root.
20 July 2012: Wouter
- Fix bug#452 and another assertion failure in mesh.c, makes
assertions in mesh.c resist duplicates. Fixes DS NS search to
not generate duplicate sub queries.
19 July 2012: Willem
- Fix bug#454: Remove ACX_CHECK_COMPILER_FLAG from configure.ac,
if CFLAGS is specified at configure time then '-g -O2' is not
appended to CFLAGS, so that the user can override them.
18 July 2012: Willem
- Fix libunbound report of errors when in background mode.
11 July 2012: Willem
- updated iana ports list.
9 July 2012: Willem
- Add flush_bogus option for unbound-control
6 July 2012: Wouter
- Fix validation of qtype DS queries that result in no data for
non-optout NSEC3 zones.
4 July 2012: Wouter
- compile libunbound with libnss on Suse, passes regression tests.
3 July 2012: Wouter
- FIPS_mode openssl does not use arc4random but RAND_pseudo_bytes.
2 July 2012: Wouter
- updated iana ports list.
29 June 2012: Wouter
- patch for unbound_munin_ script to handle arbitrary thread count by
Sven Ulland.
28 June 2012: Wouter
- detect if openssl has FIPS_mode.
- code review: return value of cache_store can be ignored for better
performance in out of memory conditions.
- fix edns-buffer-size and msg-buffer-size manpage documentation.
- updated iana ports list.
25 June 2012: Wouter
- disable RSAMD5 if in FIPS mode (for openssl and for libnss).
22 June 2012: Wouter
- implement DS records, NSEC3 and ECDSA for compile with libnss.
21 June 2012: Wouter
- fix error handling of alloc failure during rrsig verification.
- nss check for verification failure.
- nss crypto works for RSA and DSA.
20 June 2012: Wouter
- work on --with-nss build option (for now, --with-libunbound-only).
19 June 2012: Wouter
- --with-libunbound-only build option, only builds the library and
not the daemon and other tools.
18 June 2012: Wouter
- code review.
15 June 2012: Wouter
- implement log-time-ascii on windows.
- The key-cache bad key ttl is now 60 seconds.
- updated iana ports list.
- code review.
11 June 2012: Wouter
- bug #452: fix crash on assert in mesh_state_attachment.
30 May 2012: Wouter
- silence warning from swig-generated code (md set but not used in
swig initmodule, due to ifdefs in swig-generated code).
27 May 2012: Wouter
- Fix debian-bugs-658021: Please enable hardened build flags.
25 May 2012: Wouter
- updated iana ports list.
24 May 2012: Wouter
- tag for 1.4.17 release.
- trunk is 1.4.18 in development.
18 May 2012: Wouter
- Review comments, removed duplicate memset to zero in delegpt.
16 May 2012: Wouter
- Updated doc/FEATURES with RFCs that are implemented but not listed.
- Protect if statements in val_anchor for compile without locks.
- tag for 1.4.17rc1.
15 May 2012: Wouter
- fix configure ECDSA support in ldns detection for windows compile.
- fix possible uninitialised variable in windows pipe implementation.
9 May 2012: Wouter
- Fix alignment problem in util/random on sparc64/freebsd.
8 May 2012: Wouter
- Fix for accept spinning reported by OpenBSD.
- iana portlist updated.
2 May 2012: Wouter
- Fix validation of nodata for DS query in NSEC zones, reported by
Ondrej Mikle.
13 April 2012: Wouter
- ECDSA support (RFC 6605) by default. Use --disable-ecdsa for older
openssl.
10 April 2012: Wouter
- Applied patch from Daisuke HIGASHI for rrset-roundrobin and
minimal-responses features.
- iana portlist updated.
5 April 2012: Wouter
- fix bug #443: --with-chroot-dir not honoured by configure.
- fix bug #444: setusercontext was called too late (thanks Bjorn
Ketelaars).
27 March 2012: Wouter
- fix bug #442: Fix that Makefile depends on pythonmod headers
even using --without-pythonmodule.
22 March 2012: Wouter
- contrib/validation-reporter follows rotated log file (patch from
Augie Schwer).
21 March 2012: Wouter
- new approach to NS fetches for DS lookup that works with
cornercases, and is more robust and considers forwarders.
19 March 2012: Wouter
- iana portlist updated.
- fix to locate nameservers for DS lookup with NS fetches.
16 March 2012: Wouter
- Patch for access to full DNS packet data in unbound python module
from Ondrej Mikle.
9 March 2012: Wouter
- Applied line-buffer patch from Augie Schwer to validation.reporter.sh.
2 March 2012: Wouter
- flush_infra cleans timeouted servers from the cache too.
- removed warning from --enable-ecdsa.
1 March 2012: Wouter
- forward-first option. Tries without forward if a query fails.
Also stub-first option that is similar.
28 February 2012: Wouter
- Fix from code review, if EINPROGRESS not defined chain if statement
differently.
27 February 2012: Wouter
- Fix bug#434: on windows check registry for config file location
for unbound-control.exe, and unbound-checkconf.exe.
23 February 2012: Wouter
- Fix to squelch 'network unreachable' errors from tcp connect in
logs, high verbosity will show them.
16 February 2012: Wouter
- iter_hints is now thread-owned in module env, and thus threadsafe.
- Fix prefetch and sticky NS, now the prefetch works. It picks
nameservers that 'would be valid in the future', and if this makes
the NS timeout, it updates that NS by asking delegation from the
parent again. If child NS has longer TTL, that TTL does not get
refreshed from the lookup to the child nameserver.
15 February 2012: Wouter
- Fix forward-zone memory, uses malloc and frees original root dp.
- iter hints (stubs) uses malloc inside for more dynamicity.
- unbound-control forward_add, forward_remove, stub_add, stub_remove
can modify stubs and forwards for running unbound (on mobile computer)
they can also add and remove domain-insecure for the zone.
14 February 2012: Wouter
- Fix sticky NS (ghost domain problem) if prefetch is yes.
- iter forwards uses malloc inside for more dynamicity.
13 February 2012: Wouter
- RT#2955. Fix for cygwin compilation.
- iana portlist updated.
10 February 2012: Wouter
- Slightly smaller critical region in one case in infra cache.
- Fix timeouts to keep track of query type, A, AAAA and other, if
another has caused timeout blacklist, different type can still probe.
- unit test fix for nomem_cnametopos.rpl race condition.
9 February 2012: Wouter
- Fix AHX_BROKEN_MEMCMP for autoheader mess up of #undef in config.h.
8 February 2012: Wouter
- implement draft-ietf-dnsext-ecdsa-04; which is in IETF LC; This
implementation is experimental at this time and not recommended
for use on the public internet (the protocol numbers have not
been assigned). Needs recent ldns with --enable-ecdsa.
- fix memory leak in errorcase for DSA signatures.
- iana portlist updated.
- workaround for openssl 0.9.8 ecdsa sha2 and evp problem.
3 February 2012: Wouter
- fix for windows, rename() is not posix compliant on windows.
2 February 2012: Wouter
- 1.4.16 release tag.
- svn trunk is 1.4.17 in development.
- iana portlist updated.
1 February 2012: Wouter
- Fix validation failures (like: validation failure xx: no NSEC3
closest encloser from yy for DS zz. while building chain of trust,
because of a bug in the TTL-fix in 1.4.15, it picked the wrong rdata
for an NSEC3. Now it does not change rdata, and fixes TTL.
30 January 2012: Wouter
- Fix version-number in libtool to be version-info so it produces
libunbound.so.2 like it should.
26 January 2012: Wouter
- Tag 1.4.15 (same as 1.4.15rc1), for 1.4.15 release.
- trunk 1.4.16; includes changes memset testcode, #424 openindiana,
and keyfile write fixup.
- applied patch to support outgoing-interface with ub_ctx_set_option.
23 January 2012: Wouter
- Fix memset in test code.
20 January 2012: Wouter
- Fix bug #424: compile on OpenIndiana OS with gcc 4.6.2.
19 January 2012: Wouter
- Fix to write key files completely to a temporary file, and if that
succeeds, replace the real key file. So failures leave a useful file.
18 January 2012: Wouter
- tag 1.4.15rc1 created
- updated libunbound/ubsyms.def and remade tag 1.4.15rc1.
17 January 2012: Wouter
- Fix bug where canonical_compare of RRSIG did not downcase the
signer-name. This is mostly harmless because RRSIGs do not have
to be sorted in canonical order, usually.
12 January 2012: Wouter
- bug#428: add ub_version() call to libunbound. API version increase,
with (binary) backwards compatibility for the previous version.
10 January 2012: Wouter
- Fix bug #425: unbound reports wrong TTL in reply, it reports a TTL
that would be permissible by the RFCs but it is not the TTL in the
cache.
- iana portlist updated.
- uninitialised variable in reprobe for rtt blocked domains fixed.
- lintfix and new flex output.
2 January 2012: Wouter
- Fix to randomize hash function, based on 28c3 congress, reported
by Peter van Dijk.
24 December 2011: Wouter
- Fix for memory leak (about 20 bytes when a tcp or udp send operation
towards authority servers failed, takes about 50.000 such failures to
leak one Mb, such failures are also usually logged), reported by
Robert Fleischmann.
- iana portlist updated.
19 December 2011: Wouter
- Fix for VU#209659 CVE-2011-4528: Unbound denial of service
vulnerabilities from nonstandard redirection and denial of existence
http://www.unbound.net/downloads/CVE-2011-4528.txt
- robust checks for next-closer NSEC3s.
- tag 1.4.14 created.
- trunk has 1.4.15 in development.
15 December 2011: Wouter
- remove uninit warning from cachedump code.
- Fix parse error on negative SOA RRSIGs if badly ordered in the packet.
13 December 2011: Wouter
- iana portlist updated.
- svn tag 1.4.14rc1
- fix infra cache comparison.
- Fix to constrain signer_name to be a parent of the lookupname.
5 December 2011: Wouter
- Fix getaddrinfowithincludes on windows with fedora16 mingw32-gcc.
- Fix warnings with gcc 4.6 in compat/inet_ntop.c.
- Fix warning unused in compat/strptime.c.
- Fix malloc detection and double definition.
2 December 2011: Wouter
- configure generated with autoconf 2.68.
30 November 2011: Wouter
- Fix for tcp-upstream and ssl-upstream for if a laptop sleeps, causes
SERVFAILs. Also fixed for UDP (but less likely).
28 November 2011: Wouter
- Fix quartile time estimate, it was too low, (thanks Jan Komissar).
- iana ports updated.
11 November 2011: Wouter
- Makefile compat with SunOS make, BSD make and GNU make.
- iana ports updated.
10 November 2011: Wouter
- Makefile changed for BSD make compatibility.
9 November 2011: Wouter
- added unit test for SSL service and SSL-upstream.
8 November 2011: Wouter
- can configure ssl service to one port number, and not on others.
- fixup windows compile with ssl support.
- Fix double free in unbound-host, reported by Steve Grubb.
- iana portlist updated.
1 November 2011: Wouter
- dns over ssl support as a client, ssl-upstream yes turns it on.
It performs an SSL transaction for every DNS query (250 msec).
- documentation for new options: ssl-upstream, ssl-service-key and
ssl-service.pem.
- iana portlist updated.
- fix -flto detection on Lion for llvm-gcc.
31 October 2011: Wouter
- dns over ssl support, ssl-service-pem and ssl-service-key files
can be given and then TCP queries are serviced wrapped in SSL.
27 October 2011: Wouter
- lame-ttl and lame-size options no longer exist, it is integrated
with the host info. They are ignored (with verbose warning) if
encountered to keep the config file backwards compatible.
- fix iana-update for changing gzip compression of results.
- fix export-all-symbols on OSX.
26 October 2011: Wouter
- iana portlist updated.
- Infra cache stores information about ping and lameness per IP, zone.
This fixes bug #416.
- fix iana_update target for gzipped file on iana site.
24 October 2011: Wouter
- Fix resolve of partners.extranet.microsoft.com with a fix for the
server selection for choosing out of a (particular) list of bad
choices. (bug#415)
- Fix make_new_space function so that the incoming query is not
overwritten if a jostled out query causes a waiting query to be
resumed that then fails and sends an error message. (Thanks to
Matthew Lee).
21 October 2011: Wouter
- fix --enable-allsymbols, fptr wlist is disabled on windows with this
option enabled because of memory layout exe vs dll.
19 October 2011: Wouter
- fix unbound-anchor for broken strptime on OSX lion, detected
in configure.
- Detect if GOST really works, openssl1.0 on OSX fails.
- Implement ipv6%interface notation for scope_id usage.
17 October 2011: Wouter
- better documentation for inform_super (Thanks Yang Zhe).
14 October 2011: Wouter
- Fix for out-of-memory condition in libunbound (thanks
Robert Fleischman).
13 October 2011: Wouter
- Fix --enable-allsymbols, it depended on link specifics of the
target platform, or fptr_wlist assertion failures could occur.
12 October 2011: Wouter
- updated contrib/unbound_munin_ to family=auto so that it works with
munin-node-configure automatically (if installed as
/usr/local/share/munin/plugins/unbound_munin_ ).
27 September 2011: Wouter
- unbound.exe -w windows option for start and stop service.
23 September 2011: Wouter
- TCP-upstream calculates tcp-ping so server selection works if there
are alternatives.
20 September 2011: Wouter
- Fix classification of NS set in answer section, where there is a
parent-child server, and the answer has the AA flag for dir.slb.com.
Thanks to Amanda Constant from Secure64.
16 September 2011: Wouter
- fix bug #408: accept patch from Steve Snyder that comments out
unused functions in lookup3.c.
- iana portlist updated.
- fix EDNS1480 change memleak and TCP fallback.
- fix various compiler warnings (reported by Paul Wouters).
- max sent count. EDNS1480 only for rtt < 5000. No promiscuous
fetch if sentcount > 3, stop query if sentcount > 16. Count is
reset when referral or CNAME happens. This makes unbound better
at managing large NS sets, they are explored when there is continued
interest (in the form of queries).
15 September 2011: Wouter
- release 1.4.13.
- trunk contains 1.4.14 in development.
- Unbound probes at EDNS1480 if there an EDNS0 timeout.
12 September 2011: Wouter
- Reverted dns EDNS backoff fix, it did not help and needs
fragmentation fixes instead.
- tag 1.4.13rc2
7 September 2011: Wouter
- Fix operation in ipv6 only (do-ip4: no) mode.
6 September 2011: Wouter
- fedora specfile updated.
5 September 2011: Wouter
- tag 1.4.13rc1
2 September 2011: Wouter
- iana portlist updated.
26 August 2011: Wouter
- Fix num-threads 0 does not segfault, reported by Simon Deziel.
- Fix validation failures due to EDNS backoff retries, the retry
for fetch of data has want_dnssec because the iter_indicate_dnssec
function returns true when validation failure retry happens, and
then the serviced query code does not fallback to noEDNS, even if
the cache says it has this. This helps for DLV deployment when
the DNSSEC status is not known for sure before the lookup concludes.
24 August 2011: Wouter
- Applied patch from Karel Slany that fixes a memory leak in the
unbound python module, in string conversions.
22 August 2011: Wouter
- Fix validation of qtype ANY responses with CNAMEs (thanks Cathy
Zhang and Luo Ce). Unbound responds with the RR types that are
available at the name for qtype ANY and validates those RR types.
It does not test for completeness (i.e. with NSEC or NSEC3 query),
and it does not follow the CNAME or DNAME to another name (with
even more data for the already large response).
- Fix that internally, CNAMEs with NXDOMAIN have that as rcode.
- Documented the options that work with control set_option command.
- tcp-upstream yes/no option (works with set_option) for tunnels.
18 August 2011: Wouter
- fix autoconf call in makedist crosscompile to RC or snapshot.
17 August 2011: Wouter
- Fix validation of . DS query.
- new xml format at IANA, new awk for iana_update.
- iana portlist updated.
10 August 2011: Wouter
- Fix python site-packages path to /usr/lib64.
- updated patch from Tom.
- fix memory and fd leak after out-of-memory condition.
9 August 2011: Wouter
- patch from Tom Hendrikx fixes load of python modules.
8 August 2011: Wouter
- make clean had ldns-src reference, removed.
1 August 2011: Wouter
- Fix autoconf 2.68 warnings
14 July 2011: Wouter
- Unbound implements RFC6303 (since version 1.4.7).
- tag 1.4.12rc1 is released as 1.4.12 (without the other fixes in the
meantime, those are for 1.4.13).
- iana portlist updated.
13 July 2011: Wouter
- Quick fix for contrib/unbound.spec example, no ldns-builtin any more.
11 July 2011: Wouter
- Fix wildcard expansion no-data reply under an optout NSEC3 zone is
validated as insecure, reported by Jia Li (lijia@cnnic.cn).
4 July 2011: Wouter
- 1.4.12rc1 tag created.
1 July 2011: Wouter
- version number in example config file.
- fix that --enable-static-exe does not complain about it unknown.
30 June 2011: Wouter
- tag relase 1.4.11, trunk is 1.4.12 development.
- iana portlist updated.
- fix bug#395: id bits of other query may leak out under conditions
- fix replyaddr count wrong after jostled queries, which leads to
eventual starvation where the daemon has no replyaddrs left to use.
- fix comment about rndc port, that referred to the old port number.
- fix that the listening socket is not closed when too many remote
control connections are made at the same time.
- removed ldns-src tarball inside the unbound tarball.
23 June 2011: Wouter
- Changed -flto check to support clang compiler.
- tag 1.4.11rc3 created.
17 June 2011: Wouter
- tag 1.4.11rc1 created.
- remove warning about signed/unsigned from flex (other flex version).
- updated aclocal.m4 and libtool to match.
- tag 1.4.11rc2 created.
16 June 2011: Wouter
- log-queries: yesno option, default is no, prints querylog.
- version is 1.4.11.
14 June 2011: Wouter
- Use -flto compiler flag for link time optimization, if supported.
- iana portlist updated.
12 June 2011: Wouter
- IPv6 service address for d.root-servers.net (2001:500:2D::D).
10 June 2011: Wouter
- unbound-control has version number in the header,
UBCT[version]_space_ is the header sent by the client now.
- Unbound control port number is registered with IANA:
ub-dns-control 8953/tcp unbound dns nameserver control
This is the new default for the control-port config setting.
- statistics-interval prints the number of jostled queries to log.
30 May 2011: Wouter
- Fix Makefile for U in environment, since wrong U is more common than
deansification necessity.
- iana portlist updated.
- updated ldns tarball to 1.6.10rc2 snapshot of today.
25 May 2011: Wouter
- Fix assertion failure when unbound generates an empty error reply
in response to a query, CVE-2011-1922 VU#531342.
- This fix is in tag 1.4.10.
- defense in depth against the above bug, an error is printed to log
instead of an assertion failure.
10 May 2011: Wouter
- bug#386: --enable-allsymbols option links all binaries to libunbound
and reduces install size significantly.
- feature, ignore-cd-flag: yesno to provide dnssec to legacy servers.
- iana portlist updated.
- Fix TTL of SOA so negative TTL is separately cached from normal TTL.
14 April 2011: Wouter
- configure created with newer autoconf 2.66.
12 April 2011: Wouter
- bug#378: Fix that configure checks for ldns_get_random presence.
8 April 2011: Wouter
- iana portlist updated.
- queries with CD flag set cause DNSSEC validation, but the answer is
not withheld if it is bogus. Thus, unbound will retry if it is bad
and curb the TTL if it is bad, thus protecting the cache for use by
downstream validators.
- val-override-date: -1 ignores dates entirely, for NTP usage.
29 March 2011: Wouter
- harden-below-nxdomain: changed so that it activates when the
cached nxdomain is dnssec secure. This avoids backwards
incompatibility because those old servers do not have dnssec.
24 March 2011: Wouter
- iana portlist updated.
- release 1.4.9.
- trunk is 1.5.0
17 March 2011: Wouter
- bug#370: new unbound.spec for CentOS 5.x from Harold Jones.
Applied but did not do the --disable-gost.
10 March 2011: Wouter
- tag 1.4.9 release candidate 1 created.
3 March 2011: Wouter
- updated ldns to today.
1 March 2011: Wouter
- Fix no ADflag for NXDOMAIN in NSEC3 optout. And wildcard in optout.
- give config parse error for multiple names on a stub or forward zone.
- updated ldns tarball to 1.6.9(todays snapshot).
24 February 2011: Wouter
- bug #361: Fix, time.elapsed variable not reset with stats_noreset.
23 February 2011: Wouter
- iana portlist updated.
- common.sh to version 3.
18 February 2011: Wouter
- common.sh in testdata updated to version 2.
15 February 2011: Wouter
- Added explicit note on unbound-anchor usage:
Please note usage of unbound-anchor root anchor is at your own risk
and under the terms of our LICENSE (see that file in the source).
11 February 2011: Wouter
- iana portlist updated.
- tpkg updated with common.sh for common functionality.
7 February 2011: Wouter
- Added regression test for addition of a .net DS to the root, and
cache effects with different TTL for glue and DNSKEY.
- iana portlist updated.
28 January 2011: Wouter
- Fix remove private address does not throw away entire response.
24 January 2011: Wouter
- release 1.4.8
19 January 2011: Wouter
- fix bug#349: no -L/usr for ldns.
18 January 2011: Wouter
- ldns 1.6.8 tarball included.
- release 1.4.8rc1.
17 January 2011: Wouter
- add get and set option for harden-below-nxdomain feature.
- iana portlist updated.
14 January 2011: Wouter
- Fix so a changed NS RRset does not get moved name stuck on old
server, for type NS the TTL is not increased.
13 January 2011: Wouter
- Fix prefetch so it does not get stuck on old server for moved names.
12 January 2011: Wouter
- iana portlist updated.
11 January 2011: Wouter
- Fix insecure CNAME sequence marked as secure, reported by Bert
Hubert.
10 January 2011: Wouter
- faster lruhash get_mem routine.
4 January 2011: Wouter
- bug#346: remove ITAR scripts from contrib, the service is discontinued, use the root.
- iana portlist updated.
23 December 2010: Wouter
- Fix in infra cache that could cause rto larger than TOP_TIMEOUT kept.
21 December 2010: Wouter
- algorithm compromise protection using the algorithms signalled in
the DS record. Also, trust anchors, DLV, and RFC5011 receive this,
and thus, if you have multiple algorithms in your trust-anchor-file
then it will now behave different than before. Also, 5011 rollover
for algorithms needs to be double-signature until the old algorithm
is revoked.
It is not an option, because I see no use to turn the security off.
- iana portlist updated.
17 December 2010: Wouter
- squelch 'tcp connect: bla' in logfile, (set verbosity 2 to see them).
- fix validation in this case: CNAME to nodata for co-hosted opt-in
NSEC3 insecure delegation, was bogus, fixed to be insecure.
16 December 2010: Wouter
- Fix our 'BDS' license (typo reported by Xavier Belanger).
10 December 2010: Wouter
- iana portlist updated.
- review changes for unbound-anchor.
2 December 2010: Wouter
- feature typetransparent localzone, does not block other RR types.
1 December 2010: Wouter
- Fix bug#338: print address when socket creation fails.
30 November 2010: Wouter
- Fix storage of EDNS failures in the infra cache.
- iana portlist updated.
18 November 2010: Wouter
- harden-below-nxdomain option, default off (because very old
software may be incompatible). We could enable it by default in
the future.
17 November 2010: Wouter
- implement draft-vixie-dnsext-resimprove-00, we stop on NXDOMAIN.
- make test output nicer.
15 November 2010: Wouter
- silence 'tcp connect: broken pipe' and 'net down' at low verbosity.
- iana portlist updated.
- so-sndbuf option for very busy servers, a bit like so-rcvbuf.
9 November 2010: Wouter
- unbound-anchor compiles with openssl 0.9.7.
8 November 2010: Wouter
- release tag 1.4.7.
- trunk is version 1.4.8.
- Be lenient and accept imgw.pl malformed packet (like BIND).
5 November 2010: Wouter
- do not synthesize a CNAME message from cache for qtype DS.
4 November 2010: Wouter
- Use central entropy to seed threads.
3 November 2010: Wouter
- Change the rtt used to probe EDNS-timeout hosts to 1000 msec.
2 November 2010: Wouter
- tag 1.4.7rc1.
- code review.
1 November 2010: Wouter
- GOST code enabled by default (RFC 5933).
27 October 2010: Wouter
- Fix uninit value in dump_infra print.
- Fix validation failure for parent and child on same server with an
insecure childzone and a CNAME from parent to child.
- Configure detects libev-4.00.
26 October 2010: Wouter
- dump_infra and flush_infra commands for unbound-control.
- no timeout backoff if meanwhile a query succeeded.
- Change of timeout code. No more lost and backoff in blockage.
At 12sec timeout (and at least 2x lost before) one probe per IP
is allowed only. At 120sec, the IP is blocked. After 15min, a
120sec entry has a single retry packet.
25 October 2010: Wouter
- Configure errors if ldns is not found.
22 October 2010: Wouter
- Windows 7 fix for the installer.
21 October 2010: Wouter
- Fix bug where fallback_tcp causes wrong roundtrip and edns
observation to be noted in cache. Fix bug where EDNSprobe halted
exponential backoff if EDNS status unknown.
- new unresponsive host method, exponentially increasing block backoff.
- iana portlist updated.
20 October 2010: Wouter
- interface automatic works for some people with ip6 disabled.
Therefore the error check is removed, so they can use the option.
19 October 2010: Wouter
- Fix for request list growth, if a server has long timeout but the
lost counter is low, then its effective rtt is the one without
exponential backoff applied. Because the backoff is not working.
The lost counter can then increase and the server is blacklisted,
or the lost counter does not increase and the server is working
for some queries.
18 October 2010: Wouter
- iana portlist updated.
13 October 2010: Wouter
- Fix TCP so it uses a random outgoing-interface.
- unbound-anchor handles ADDPEND keystate.
11 October 2010: Wouter
- Fix bug when DLV below a trust-anchor that uses NSEC3 optout where
the zone has a secure delegation hosted on the same server did not
verify as secure (it was insecure by mistake).
- iana portlist updated.
- ldns tarball updated (for reading cachedumps with bad RR data).
1 October 2010: Wouter
- test for unbound-anchor. fix for reading certs.
- Fix alloc_reg_release for longer uptime in out of memory conditions.
28 September 2010: Wouter
- unbound-anchor working, it creates or updates a root.key file.
Use it before you start the validator (e.g. at system boot time).
27 September 2010: Wouter
- iana portlist updated.
24 September 2010: Wouter
- bug#329: in example.conf show correct ipv4 link-local 169.254/16.
23 September 2010: Wouter
- unbound-anchor app, unbound requires libexpat (xml parser library).
22 September 2010: Wouter
- compliance with draft-ietf-dnsop-default-local-zones-14, removed
reverse ipv6 orchid prefix from builtin list.
- iana portlist updated.
17 September 2010: Wouter
- DLV has downgrade protection again, because the RFC says so.
- iana portlist updated.
16 September 2010: Wouter
- Algorithm rollover operational reality intrudes, for trust-anchor,
5011-store, and DLV-anchor if one key matches it's good enough.
- iana portlist updated.
- Fix reported validation error in out of memory condition.
15 September 2010: Wouter
- Abide RFC5155 section 9.2: no AD flag for replies with NSEC3 optout.
14 September 2010: Wouter
- increased mesh-max-activation from 1000 to 3000 for crazy domains
like _tcp.slb.com with 262 servers.
- iana portlist updated.
13 September 2010: Wouter
- bug#327: Fix for cannot access stub zones until the root is primed.
9 September 2010: Wouter
- unresponsive servers are not completely blacklisted (because of
firewalls), but also not probed all the time (because of the request
list size it generates). The probe rate is 1%.
- iana portlist updated.
20 August 2010: Wouter
- openbsd-lint fixes: acl_list_get_mem used if debug-alloc enabled.
iterator get_mem includes priv_get_mem. delegpt nodup removed.
listen_pushback, query_info_allocqname, write_socket, send_packet,
comm_point_set_cb_arg and listen_resume removed.
19 August 2010: Wouter
- Fix bug#321: resolution of rs.ripe.net artifacts with 0x20.
Delegpt structures checked for duplicates always.
No more nameserver lookups generated when depth is full anyway.
- example.conf notes how to do DNSSEC validation and track the root.
- iana portlist updated.
18 August 2010: Wouter
- Fix bug#322: configure does not respect CFLAGS on Solaris.
Pass CFLAGS="-xO4 -xtarget=generic" on the configure command line
if use sun-cc, but some systems need different flags.
16 August 2010: Wouter
- Fix acx_nlnetlabs.m4 configure output for autoconf-2.66 AS_TR_CPP
changes, uses m4_bpatsubst now.
- make test (or make check) should be more portable and run the unit
test and testbound scripts. (make longtest has special requirements).
13 August 2010: Wouter
- More pleasant remote control command parsing.
- documentation added for return values reported by doxygen 1.7.1.
- iana portlist updated.
9 August 2010: Wouter
- Fix name of rrset printed that failed validation.
5 August 2010: Wouter
- Return NXDOMAIN after chain of CNAMEs ends at name-not-found.
4 August 2010: Wouter
- Fix validation in case a trust anchor enters into a zone with
unsupported algorithms.
3 August 2010: Wouter
- updated ldns tarball with bugfixes.
- release tag 1.4.6.
- trunk becomes 1.4.7 develop.
- iana portlist updated.
22 July 2010: Wouter
- more error details on failed remote control connection.
15 July 2010: Wouter
- rlimit adjustments for select and ulimit can happen at the same time.
14 July 2010: Wouter
- Donation text added to README.
- Fix integer underflow in prefetch ttl creation from cache. This
fixes a potential negative prefetch ttl.
12 July 2010: Wouter
- Changed the defaults for num-queries-per-thread/outgoing-range.
For builtin-select: 512/960, for libevent 1024/4096 and for
windows 24/48 (because of win api). This makes the ratio this way
to improve resilience under heavy load. For high performance, use
libevent and possibly higher numbers.
10 July 2010: Wouter
- GOST enabled if SSL is recent and ldns has GOST enabled too.
- ldns tarball updated.
9 July 2010: Wouter
- iana portlist updated.
- Fix validation of qtype DNSKEY when a key-cache entry exists but
no rr-cache entry is used (it expired or prefetch), it then goes
back up to the DS or trust-anchor to validate the DNSKEY.
7 July 2010: Wouter
- Neat function prototypes, unshadowed local declarations.
6 July 2010: Wouter
- failure to chown the pidfile is not fatal any more.
- testbound uses UTC timezone.
- ldns tarball updated (ports and works on Minix 3.1.7). On Minix, add
/usr/gnu/bin to PATH, use ./configure AR=/usr/gnu/bin/gar and gmake.
5 July 2010: Wouter
- log if a server is skipped because it is on the donotquery list,
at verbosity 4, to enable diagnosis why no queries to 127.0.0.1.
- added feature to print configure date, target and options with -h.
- added feature to print event backend system details with -h.
- wdiff is not actually required by make test, updated requirements.
1 July 2010: Wouter
- Fix RFC4035 compliance with 2.2 statement that the DNSKEY at apex
must be signed with all algorithms from the DS rrset at the parent.
This is now checked and becomes bogus if not.
28 June 2010: Wouter
- Fix jostle list bug found by Vince (luoce@cnnic), it caused the qps
in overload situations to be about 5 qps for the class of shortly
serviced queries.
The capacity of the resolver is then about (numqueriesperthread / 2)
/ (average time for such long queries) qps for long queries.
And about (numqueriesperthread / 2)/(jostletimeout in whole seconds)
qps for short queries, per thread.
- Fix the max number of reply-address count to be applied for duplicate
queries, and not for new query list entries. This raises the memory
usage to a max of (16+1)*numqueriesperthread reply addresses.
25 June 2010: Wouter
- Fix handling of corner case reply from lame server, follows rfc2308.
It could lead to a nodata reply getting into the cache if the search
for a non-lame server turned up other misconfigured servers.
- unbound.h has extern "C" statement for easier include in c++.
23 June 2010: Wouter
- iana portlist updated.
- makedist upgraded cross compile openssl option, like this:
./makedist.sh -s -wssl openssl-1.0.0a.tar.gz -w --enable-gost
22 June 2010: Wouter
- Unbound reports libev or libevent correctly in logs in verbose mode.
- Fix to unload gost dynamic library module for leak testing.
18 June 2010: Wouter
- iana portlist updated.
17 June 2010: Wouter
- Add AAAA to root hints for I.ROOT-SERVERS.NET.
16 June 2010: Wouter
- Fix assertion failure reported by Kai Storbeck from XS4ALL, the
assertion was wrong.
- updated ldns tarball.
15 June 2010: Wouter
- tag 1.4.5 created.
- trunk contains 1.4.6 in development.
- Fix TCPreply on systems with no writev, if just 1 byte could be sent.
- Fix to use one pointer less for iterator query state store_parent_NS.
- makedist crosscompile to windows uses builtin ldns not host ldns.
- Max referral count from 30 to 130, because 128 one character domains
is valid DNS.
- added documentation for the histogram printout to syslog.
11 June 2010: Wouter
- When retry to parent the retrycount is not wiped, so failed
nameservers are not tried again.
- iana portlist updated.
10 June 2010: Wouter
- Fix bug where a long loop could be entered, now cycle detection
has a loop-counter and maximum search amount.
4 June 2010: Wouter
- iana portlist updated.
- 1.4.5rc1 tag created.
3 June 2010: Wouter
- ldns tarball updated, 1.6.5.
- review comments, split dependency cycle tracking for parentside
last resort lookups for A and AAAA so there are more lookup options.
2 June 2010: Wouter
- Fix compile warning if compiled without threads.
- updated ldns-tarball with current ldns svn (pre 1.6.5).
- GOST disabled-by-default, the algorithm number is allocated but the
RFC is still has to pass AUTH48 at the IETF.
1 June 2010: Wouter
- Ignore Z flag in incoming messages too.
- Fix storage of negative parent glue if that last resort fails.
- libtoolize 2.2.6b, autoconf 2.65 applied to configure.
- new splint flags for newer splint install.
31 May 2010: Wouter
- Fix AD flag handling, it could in some cases mistakenly copy the AD
flag from upstream servers.
- alloc_special_obtain out of memory is not a fatal error any more,
enabling unbound to continue longer in out of memory conditions.
- parentside names are dispreferred but not said to be dnssec-lame.
- parentside check for cached newname glue.
- fix parentside and querytargets modulestate, for dump_requestlist.
- unbound-control-setup makes keys -rw-r--- so not all users permitted.
- fix parentside from cache to be marked dispreferred for bad names.
28 May 2010: Wouter
- iana portlist updated.
- parent-child disagreement approach altered. Older fixes are
removed in place of a more exhaustive search for misconfigured data
available via the parent of a delegation.
This is designed to be throttled by cache entries, with TTL from the
parent if possible. Additionally the loop-counter is used.
It also tests for NS RRset differences between parent and child.
The fetch of misconfigured data should be more reliable and thorough.
It should work reliably even with no or only partial data in cache.
Data received from the child (as always) is deemed more
authoritative than information received from the delegation parent.
The search for misconfigured data is not performed normally.
26 May 2010: Wouter
- Contribution from Migiel de Vos (Surfnet): nagios patch for
unbound-host, in contrib/ (in the source tarball). Makes
unbound-host suitable for monitoring dnssec(-chain) status.
21 May 2010: Wouter
- EDNS timeout code will not fire if EDNS status already known.
- EDNS failure not stored if EDNS status known to work.
19 May 2010: Wouter
- Fix resolution for domains like safesvc.com.cn. If the iterator
can not recurse further and it finds the delegation in a state
where it would otherwise have rejected it outhand if so received
from a cache lookup, then it can try to ask higherup (with loop
protection).
- Fix comments in iter_utils:dp_is_useless.
18 May 2010: Wouter
- Fix various compiler warnings from the clang llvm compiler.
- iana portlist updated.
6 May 2010: Wouter
- Fix bug#308: spelling error in variable name in parser and lexer.
4 May 2010: Wouter
- Fix dnssec-missing detection that was turned off by server selection.
- Conforms to draft-ietf-dnsop-default-local-zones-13. Added default
reverse lookup blocks for IPv4 test nets 100.51.198.in-addr.arpa,
113.0.203.in-addr.arpa and Orchid prefix 0.1.1.0.0.2.ip6.arpa.
29 April 2010: Wouter
- Fix for dnssec lameness detection to use the key cache.
- infra cache entries that are expired are wiped clean. Previously
it was possible to not expire host data (if accessed often).
28 April 2010: Wouter
- ldns tarball updated and GOST support is detected and then enabled.
- iana portlist updated.
- Fix detection of gost support in ldns (reported by Chris Smith).
27 April 2010: Wouter
- unbound-control get_option domain-insecure shows config file items.
- fix retry sequence if prime hints are recursion-lame.
- autotrust anchor file can be initialized with a ZSK key as well.
- harden-referral-path does not result in failures due to max-depth.
You can increase the max-depth by adding numbers (' 0') after the
target-fetch-policy, this increases the depth to which is checked.
26 April 2010: Wouter
- Compile fix using Sun Studio 12 compiler on Solaris 5.9, use
CPPFLAGS during configure process.
- if libev is installed on the base system (not libevent), detect
it from the event.h header file and link with -lev.
- configlexer.lex gets config.h, and configyyrename.h added by make,
no more double include.
- More strict scrubber (Thanks to George Barwood for the idea):
NS set must be pertinent to the query (qname subdomain nsname).
- Fix bug#307: In 0x20 backoff fix fallback so the number of
outstanding queries does not become -1 and block the request.
Fixed handling of recursion-lame in combination with 0x20 fallback.
Fix so RRsets are compared canonicalized and sorted if the immediate
comparison fails, this makes it work around round-robin sites.
23 April 2010: Wouter
- Squelch log message: sendto failed permission denied for
255.255.255.255, it is visible in VERB_DETAIL (verbosity 2).
- Fix to fetch data as last resort more tenaciously. When cycle
targets cause the server selection to believe there are more options
when they really are not there, the server selection is reinitiated.
- Fix fetch from blacklisted dnssec lame servers as last resort. The
server's IP address is then given in validator errors as well.
- Fix local-zone type redirect that did not use the query name for
the answer rrset.
22 April 2010: Wouter
- tag 1.4.4.
- trunk contains 1.4.5 in development.
- Fix validation failure for qtype ANY caused by a RRSIG parse failure.
The validator error message was 'no signatures from ...'.
16 April 2010: Wouter
- more portability defines for CMSG_SPACE, CMSG_ALIGN, CMSG_LEN.
- tag 1.4.4rc1.
15 April 2010: Wouter
- ECC-GOST algorithm number 12 that is assigned by IANA. New test
example key and signatures for GOST. GOST requires openssl-1.0.0.
GOST is still disabled by default.
9 April 2010: Wouter
- Fix bug#305: pkt_dname_tolower could read beyond end of buffer or
get into an endless loop, if 0x20 was enabled, and buffers are small
or particular broken packets are received.
- Fix chain of trust with CNAME at an intermediate step, for the DS
processing proof.
8 April 2010: Wouter
- Fix validation of queries with wildcard names (*.example).
6 April 2010: Wouter
- Fix EDNS probe for .de DNSSEC testbed failure, where the infra
cache timeout coincided with a server update, the current EDNS
backoff is less sensitive, and does not cache the backoff unless
the backoff actually works and the domain is not expecting DNSSEC.
- GOST support with correct algorithm numbers.
1 April 2010: Wouter
- iana portlist updated.
24 March 2010: Wouter
- unbound control flushed items are not counted when flushed again.
23 March 2010: Wouter
- iana portlist updated.
22 March 2010: Wouter
- unbound-host disables use-syslog from config file so that the
config file for the main server can be used more easily.
- fix bug#301: unbound-checkconf could not parse interface
'0.0.0.0@5353', even though unbound itself worked fine.
19 March 2010: Wouter
- fix fwd_ancil test to pass if the socket options are not supported.
18 March 2010: Wouter
- Fixed random numbers for port, interface and server selection.
Removed very small bias.
- Refer to the listing in unbound-control man page in the extended
statistics entry in the unbound.conf man page.
16 March 2010: Wouter
- Fix interface-automatic for OpenBSD: msg.controllen was too small,
also assertions on ancillary data buffer.
- check for IP_SENDSRCADDR for interface-automatic or IP_PKTINFO.
- for NSEC3 check if signatures are cached.
15 March 2010: Wouter
- unit test for util/regional.c.
12 March 2010: Wouter
- Reordered configure checks so fork and -lnsl -lsocket checks are
earlier, and thus later checks benefit from and do not hinder them.
- iana portlist updated.
- ldns tarball updated.
- Fix python use when multithreaded.
- Fix solaris python compile.
- Include less in config.h and include per code file for ldns, ssl.
11 March 2010: Wouter
- another memory allocation option: --enable-alloc-nonregional.
exposes the regional allocations to other memory purifiers.
- fix for memory alignment in struct sock_list allocation.
- Fix for MacPorts ldns without ssl default, unbound checks if ldns
has dnssec functionality and uses the builtin if not.
- Fix daemonize on Solaris 10, it did not detach from terminal.
- tag 1.4.3 created.
- trunk is 1.4.4 in development.
- spelling fix in validation error involving cnames.
10 March 2010: Wouter
- --enable-alloc-lite works with test set.
- portability in the testset: printf format conversions, prototypes.
9 March 2010: Wouter
- tag 1.4.2 created.
- trunk is 1.4.3 in development.
- --enable-alloc-lite debug option.
8 March 2010: Wouter
- iana portlist updated.
4 March 2010: Wouter
- Fix crash in control channel code.
3 March 2010: Wouter
- better casts in pipe code, brackets placed wrongly.
- iana portlist updated.
1 March 2010: Wouter
- make install depends on make all.
- Fix 5011 auto-trust-anchor-file initial read to skip RRSIGs.
- --enable-checking: enables assertions but does not look nonproduction.
- nicer VERB_DETAIL (verbosity 2, unbound-host -d) output, with
nxdomain and nodata distinguished.
- ldns tarball updated.
- --disable-rpath fixed for libtool not found errors.
- new fedora specfile from Fedora13 in contrib from Paul Wouters.
26 February 2010: Wouter
- Fixup prototype for lexer cleanup in daemon code.
- unbound-control list_stubs, list_forwards, list_local_zones and
list_local_data.
24 February 2010: Wouter
- Fix scrubber bug that potentially let NS records through. Reported
by Amanda Constant.
- Also delete potential poison references from additional.
- Fix: no classification of a forwarder as lame, throw away instead.
23 February 2010: Wouter
- libunbound ub_ctx_get_option() added.
- unbound-control set_option and get_option commands.
- iana portlist updated.
18 February 2010: Wouter
- A little more strict DS scrubbing.
- No more blacklisting of unresponsive servers, a 2 minute timeout
is backed off to.
- RD flag not enabled for dnssec-blacklisted tries, unless necessary.
- pickup ldns compile fix, libdl for libcrypto.
- log 'tcp connect: connection timed out' only in high verbosity.
- unbound-control log_reopen command.
- moved get_option code from unbound-checkconf to util/config_file.c
17 February 2010: Wouter
- Disregard DNSKEY from authority section for chain of trust.
DS records that are irrelevant to a referral scrubbed. Anti-poison.
- iana portlist updated.
16 February 2010: Wouter
- Check for 'no space left on device' (or other errors) when
writing updated autotrust anchors and print errno to log.
15 February 2010: Wouter
- Fixed the requery protection, the TTL was 0, it is now 900 seconds,
hardcoded. We made the choice to send out more conservatively,
protecting against an aggregate effect more than protecting a
single user (from their own folly, perhaps in case of misconfig).
12 February 2010: Wouter
- Re-query pattern changed on validation failure. To protect troubled
authority servers, unbound caches a failure for the DNSKEY or DS
records for the entire zone, and only retries that 900 seconds later.
This implies that only a handful of packets are sent extra to the
authority if the zone fails.
11 February 2010: Wouter
- ldns tarball update for long label length syntax error fix.
- iana portlist updated.
9 February 2010: Wouter
- Fixup in compat snprintf routine, %f 1.02 and %g support.
- include math.h for testbound test compile portability.
2 February 2010: Wouter
- Updated url of IANA itar, interim trust anchor repository, in script.
1 February 2010: Wouter
- iana portlist updated.
- configure test for memcmp portability.
27 January 2010: Wouter
- removed warning on format string in validator error log statement.
- iana portlist updated.
22 January 2010: Wouter
- libtool finish the install of unbound python dynamic library.
21 January 2010: Wouter
- acx_nlnetlabs.m4 synchronised with nsd's version.
20 January 2010: Wouter
- Fixup lookup trouble for parent-child domains on the first query.
14 January 2010: Wouter
- Fixup ldns detection to also check for header files.
13 January 2010: Wouter
- prefetch-key option that performs DNSKEY queries earlier in the
validation process, and that could halve the latency on DNSSEC
queries. It takes some extra processing (CPU, a cache is needed).
12 January 2010: Wouter
- Fix unbound-checkconf for auto-trust-anchor-file present checks.
8 January 2010: Wouter
- Fix for parent-child disagreement code which could have trouble
when (a) ipv6 was disabled and (b) the TTL for parent and child
were different. There were two bugs, the parent-side information
is fixed to no longer block lookup of child side information and
the iterator is fixed to no longer attempt to get ipv6 when it is
not enabled and then give up in failure.
- test and fixes to make prefetch actually store the answer in the
cache. Considers some rrsets 'already expired' but does not allow
overwriting of rrsets considered more secure.
7 January 2010: Wouter
- Fixup python documentation (thanks Leo Vandewoestijne).
- Work on cache prefetch feature.
- Stats for prefetch, in log print stats, unbound-control stats
and in unbound_munin plugin.
6 January 2010: Wouter
- iana portlist updated.
- bug#291: DNS wireformat max is 255. dname_valid allowed 256 length.
- verbose output includes parent-side-address notion for lameness.
- documented val-log-level: 2 setting in example.conf and man page.
- change unbound-control-setup from 1024(sha1) to 1536(sha256).
1 January 2010: Wouter
- iana portlist updated.
22 December 2009: Wouter
- configure with newer libtool 2.2.6b.
17 December 2009: Wouter
- review comments.
- tag 1.4.1.
- trunk to version 1.4.2.
15 December 2009: Wouter
- Answer to qclass=ANY queries, with class IN contents.
Test that validation also works.
- updated ldns snapshot tarball with latest fixes (parsing records).
11 December 2009: Wouter
- on IPv4 UDP turn off DF flag.
10 December 2009: Wouter
- requirements.txt updated with design choice explanations.
- Reading fixes: fix to set unlame when child confirms parent glue,
and fix to avoid duplicate addresses in delegation point.
- verify_rrsig routine checks expiration last.
9 December 2009: Wouter
- Fix Bug#287(reopened): update of ldns tarball with fix for parse
errors generated for domain names like '.example.com'.
- Fix SOA excluded from negative DS responses. Reported by Hauke
Lampe. The negative cache did not include proper SOA records for
negative qtype DS responses which makes BIND barf on it, such
responses are now only used internally.
- Fix negative cache lookup of closestencloser check of DS type bit.
8 December 2009: Wouter
- Fix for lookup of parent-child disagreement domains, where the
parent-side glue works but it does not provide proper NS, A or AAAA
for itself, fixing domains such as motorcaravanners.eu.
- Feature: you can specify a port number in the interface: line, so
you can bind the same interface multiple times at different ports.
7 December 2009: Wouter
- Bug#287: Fix segfault when unbound-control remove nonexistent local
data. Added check to tests.
1 December 2009: Wouter
- Fix crash with module-config "iterator".
- Added unit test that has "iterator" module-config.
30 November 2009: Wouter
- bug#284: fix parse of # without end-of-line at end-of-file.
26 November 2009: Wouter
- updated ldns with release candidate for version 1.6.3.
- tag for 1.4.0 release.
- 1.4.1 version in trunk.
- Fixup major libtool version to 2 because of why_bogus change.
It was 1:5:0 but should have been 2:0:0.
23 November 2009: Wouter
- Patch from David Hubbard for libunbound manual page.
- Fixup endless spinning in unbound-control stats reported by
Attila Nagy. Probably caused by clock reversal.
20 November 2009: Wouter
- contrib/split-itar.sh contributed by Tom Hendrikx.
19 November 2009: Wouter
- better argument help for unbound-control.
- iana portlist updated.
17 November 2009: Wouter
- noted multiple entries for multiple domain names in example.conf.
- iana portlist updated.
16 November 2009: Wouter
- Fixed signer detection of CNAME responses without signatures.
- Fix#282 libunbound memleak on error condition by Eric Sesterhenn.
- Tests for CNAMEs to deeper trust anchors, secure and bogus.
- svn tag 1.4.0rc1 made.
13 November 2009: Wouter
- Fixed validation failure for CNAME to optout NSEC3 nodata answer.
- unbound-host does not fail on type ANY.
- Fixed wireparse failure to put RRSIGs together with data in some
long ANY mix cases, which fixes validation failures.
12 November 2009: Wouter
- iana portlist updated.
- fix manpage errors reported by debian lintian.
- review comments.
- fixup very long vallog2 level error strings.
11 November 2009: Wouter
- ldns tarball updated (to 1.6.2).
- review comments.
10 November 2009: Wouter
- Thanks to Surfnet found bug in new dnssec-retry code that failed
to combine well when combined with DLV and a particular failure.
- Fixed unbound-control -h output about argument optionality.
- review comments.
5 November 2009: Wouter
- lint fixes and portability tests.
- better error text for multiple domain keys in one autotrust file.
2 November 2009: Wouter
- Fix bug where autotrust does not work when started with a DS.
- Updated GOST unit tests for unofficial algorithm number 249
and DNSKEY-format changes in draft version -01.
29 October 2009: Wouter
- iana portlist updated.
- edns-buffer-size option, default 4096.
- fixed do-udp: no.
28 October 2009: Wouter
- removed abort on prealloc failure, error still printed but softfail.
- iana portlist updated.
- RFC 5702: RSASHA256 and RSASHA512 support enabled by default.
- ldns tarball updated (which also enables rsasha256 support).
27 October 2009: Wouter
- iana portlist updated.
8 October 2009: Wouter
- please doxygen
- add val-log-level print to corner case (nameserver.epost.bg).
- more detail to errors from insecure delegation checks.
- Fix double time subtraction in negative cache reported by
Amanda Constant and Hugh Mahon.
- Made new validator error string available from libunbound for
applications. It is in result->why_bogus, a zero-terminated string.
unbound-host prints it by default if a result is bogus.
Also the errinf is public in module_qstate (for other modules).
7 October 2009: Wouter
- retry for validation failure in DS and prime results. Less mem use.
unit test. Provisioning in other tests for requeries.
- retry for validation failure in DNSKEY in middle of chain of trust.
unit test.
- retry for empty non terminals in chain of trust and unit test.
- Fixed security bug where the signatures for NSEC3 records were not
checked when checking for absence of DS records. This could have
enabled the substitution of an insecure delegation.
- moved version number to 1.4.0 because of 1.3.4 release with only
the NSEC3 patch from the entry above.
- val-log-level: 2 shows extended error information for validation
failures, but still one (longish) line per failure. For example:
validation failure <example.com. DNSKEY IN>: signature expired from
192.0.2.4 for trust anchor example.com. while building chain of trust
validation failure <www.example.com. A IN>: no signatures from
192.0.2.6 for key example.com. while building chain of trust
6 October 2009: Wouter
- Test set updated to provide additional ns lookup result.
The retry would attempt to fetch the data from other nameservers
for bogus data, and this needed to be provisioned in the tests.
5 October 2009: Wouter
- first validation failure retry code. Retries for data failures.
And unit test.
2 October 2009: Wouter
- improve 5011 modularization.
- fix unbound-host so -d can be given before -C.
- iana portlist updated.
28 September 2009: Wouter
- autotrust-anchor-file can read multiline input and $ORIGIN.
- prevent integer overflow in holddown calculation. review fixes.
- fixed race condition in trust point revocation. review fix.
- review fixes to comments, removed unused code.
25 September 2009: Wouter
- so-rcvbuf: 4m option added. Set this on large busy servers to not
drop the occasional packet in spikes due to full socket buffers.
netstat -su keeps a counter of UDP dropped due to full buffers.
- review of validator/autotrust.c, small fixes and comments.
23 September 2009: Wouter
- 5011 query failed counts verification failures, not lookup failures.
- 5011 probe failure handling fixup.
- test unbound reading of original autotrust data.
The metadata per-key, such as key state (PENDING, MISSING, VALID) is
picked up, otherwise performs initial probe like usual.
22 September 2009: Wouter
- autotrust test with algorithm rollover, new ordering of checks
assists in orderly rollover.
- autotrust test with algorithm rollover to unknown algorithm.
checks if new keys are supported before adding them.
- autotrust test with trust point revocation, becomes unsigned.
- fix DNSSEC-missing-signature detection for minimal responses
for qtype DNSKEY (assumes DNSKEY occurs at zone apex).
18 September 2009: Wouter
- autotrust tests, fix trustpoint timer deletion code.
fix count of valid anchors during missing remove.
- autotrust: pick up REVOKE even if not signed with known other keys.
17 September 2009: Wouter
- fix compile of unbound-host when --enable-alloc-checks.
- Fix lookup problem reported by Koh-ichi Ito and Jaap Akkerhuis.
- Manual page fixes reported by Tony Finch.
16 September 2009: Wouter
- Fix memory leak reported by Tao Ma.
- Fix memstats test tool for log-time-ascii log format.
15 September 2009: Wouter
- iana portlist updated.
10 September 2009: Wouter
- increased MAXSYSLOGLEN so .bg key can be printed in debug output.
- use linebuffering for log-file: output, this can be significantly
faster than the previous fflush method and enable some class of
resolvers to use high verbosity (for short periods).
Not on windows, because line buffering does not work there.
9 September 2009: Wouter
- Fix bug where DNSSEC-bogus messages were marked with too high TTL.
The RRsets would still expire at the normal time, but this would
keep messages bogus in the cache for too long.
- regression test for that bug.
- documented that load_cache is meant for debugging.
8 September 2009: Wouter
- fixup printing errors when load_cache, they were printed to the
SSL connection which broke, now to the log.
- new ldns - with fixed parse of large SOA values.
7 September 2009: Wouter
- autotrust testbound scenarios.
- autotrust fix that failure count is written to file.
- autotrust fix that keys may become valid after add holddown time
alone, before the probe returns.
4 September 2009: Wouter
- Changes to make unbound work with libevent-2.0.3 alpha. (in
configure detection due to new ssl dependency in libevent)
- do not call sphinx for documentation when python is disabled.
- remove EV_PERSIST from libevent timeout code to make the code
compatible with the libevent-2.0. Works with older libevent too.
- fix memory leak in python code.
3 September 2009: Wouter
- Got a patch from Luca Bruno for libunbound support on windows to
pick up the system resolvconf nameservers and hosts there.
- included ldns updated (enum warning fixed).
- makefile fix for parallel makes.
- Patch from Zdenek Vasicek and Attila Nagy for using the source IP
from python scripts. See pythonmod/examples/resip.py.
- doxygen comment fixes.
2 September 2009: Wouter
- TRAFFIC keyword for testbound. Simplifies test generation.
${range lower val upper} to check probe timeout values.
- test with 5011-prepublish rollover and revocation.
- fix revocation of RR for autotrust, stray exclamation mark.
1 September 2009: Wouter
- testbound variable arithmetic.
- autotrust probe time is randomised.
- autotrust: the probe is active and does not fetch from cache.
31 August 2009: Wouter
- testbound variable processing.
28 August 2009: Wouter
- fixup unbound-control lookup to print forward and stub servers.
27 August 2009: Wouter
- autotrust: mesh answer callback is empty.
26 August 2009: Wouter
- autotrust probing.
- iana portlist updated.
25 August 2009: Wouter
- fixup memleak in trust anchor unsupported algorithm check.
- iana portlist updated.
- autotrust options: add-holddown, del-holddown, keep-missing.
- autotrust store revoked status of trust points.
- ctime_r compat definition.
- detect yylex_destroy() in configure.
- detect SSL_get_compression_methods declaration in configure.
- fixup DS lookup at anchor point with unsigned parent.
- fixup DLV lookup for DS queries to unsigned domains.
24 August 2009: Wouter
- cleaner memory allocation on exit. autotrust test routines.
- free all memory on program exit, fix for ssl and flex.
21 August 2009: Wouter
- autotrust: debug routines. Read,write and conversions work.
20 August 2009: Wouter
- autotrust: save and read trustpoint variables.
19 August 2009: Wouter
- autotrust: state table updates.
- iana portlist updated.
17 August 2009: Wouter
- autotrust: process events.
17 August 2009: Wouter
- Fix so that servers are only blacklisted if they fail to reply
to 16 queries in a row and the timeout gets above 2 minutes.
- autotrust work, split up DS verification of DNSKEYs.
14 August 2009: Wouter
- unbound-control lookup prints out infra cache information, like RTT.
- Fix bug in DLV lookup reported by Amanda from Secure64.
It could sometimes wrongly classify a domain as unsigned, which
does not give the AD bit on replies.
13 August 2009: Wouter
- autotrust read anchor files. locked trust anchors.
12 August 2009: Wouter
- autotrust import work.
11 August 2009: Wouter
- Check for openssl compatible with gost if enabled.
- updated unit test for GOST=211 code.
Nicer naming of test files.
- iana portlist updated.
7 August 2009: Wouter
- call OPENSSL_config() in unbound and unit test so that the
operator can use openssl.cnf for configuration options.
- removed small memory leak from config file reader.
6 August 2009: Wouter
- configure --enable-gost for GOST support, experimental
implementation of draft-dolmatov-dnsext-dnssec-gost-01.
- iana portlist updated.
- ldns tarball updated (with GOST support).
5 August 2009: Wouter
- trunk moved to 1.3.4.
4 August 2009: Wouter
- Added test that the examples from draft rsasha256-14 verify.
- iana portlist updated.
- tagged 1.3.3
3 August 2009: Wouter
- nicer warning when algorithm not supported, tells you to upgrade.
- iana portlist updated.
27 July 2009: Wouter
- Updated unbound-cacti contribution from Dmitriy Demidov, with
the queue statistics displayed in its own graph.
- iana portlist updated.
22 July 2009: Wouter
- Fix bug found by Michael Tokarev where unbound would try to
prime the root servers even though forwarders are configured for
the root.
- tagged 1.3.3rc1
21 July 2009: Wouter
- Fix server selection, so that it waits for open target queries when
faced with lameness.
20 July 2009: Wouter
- Ignore transient sendto errors, no route to host, and host, net down.
- contrib/update-anchor.sh has -r option for root-hints.
- feature val-log-level: 1 prints validation failures so you can
keep track of them during dnssec deployment.
16 July 2009: Wouter
- fix replacement malloc code. Used in crosscompile.
- makedist -w creates crosscompiled setup.exe on fedora11.
15 July 2009: Wouter
- dependencies for compat items, for crosscompile.
- mingw32 crosscompile changes, dependencies and zipfile creation.
and with System.dll from the windows NSIS you can make setup.exe.
- package libgcc_s_sjlj exception handler for NSISdl.dll.
14 July 2009: Wouter
- updated ldns tarball for solaris x64 compile assistance.
- no need to define RAND_MAX from config.h.
- iana portlist updated.
- configure changes and ldns update for mingw32 crosscompile.
13 July 2009: Wouter
- Fix for crash at start on windows.
- tag for release 1.3.2.
- trunk has version 1.3.3.
- Fix for ID bits on windows to use all 16. RAND_MAX was not
defined like you'd expect on mingw. Reported by Mees de Roo.
9 July 2009: Wouter
- tag for release 1.3.1.
- trunk has version 1.3.2.
7 July 2009: Wouter
- iana portlist updated.
6 July 2009: Wouter
- prettier error handling in SSL setup.
- makedist.sh uname fix (same as ldns).
- updated fedora spec file.
3 July 2009: Wouter
- fixup linking when ldnsdir is "".
30 June 2009: Wouter
- more lenient truncation checks.
29 June 2009: Wouter
- ldns trunk r2959 imported as tarball, because of solaris cc compile
support for c99. r2960 for better configure.
- better wrongly_truncated check.
- On Linux, fragment IPv6 datagrams to the IPv6 minimum MTU, to
avoid dropped packets at routers.
26 June 2009: Wouter
- Fix EDNS fallback when EDNS works for short answers but long answers
are dropped.
22 June 2009: Wouter
- fixup iter priv strict aliasing while preserving size of sockaddr.
- iana portlist updated. (one less port allocated, one more fraction
of a bit for security!)
- updated fedora specfile in contrib from Paul Wouters.
19 June 2009: Wouter
- Fixup strict aliasing warning in iter priv code.
and config_file code.
- iana portlist updated.
- harden-referral-path: handle cases where NS is in answer section.
18 June 2009: Wouter
- Fix of message parse bug where (specifically) an NSEC and RRSIG
in the wrong order would be parsed, but put wrongly into internal
structures so that later validation would fail.
- Extreme lenience for wrongly truncated replies where a positive
reply has an NS in the authority but no signatures. They are
turned into minimal responses with only the (secure) answer.
- autoconf 2.63 for configure.
- python warnings suppress. Keep python API away from header files.
17 June 2009: Wouter
- CREDITS entry for cz.nic, sponsoring a 'summer of code' that was
used for the python code in unbound. (http://www.nic.cz/vip/ in cz).
16 June 2009: Wouter
- Fixup opportunistic target query generation to it does not
generate queries that are known to fail.
- Touchup on munin total memory report.
- messages picked out of the cache by the iterator are checked
if their cname chain is still correct and if validation status
has to be reexamined.
15 June 2009: Wouter
- iana portlist updated.
14 June 2009: Wouter
- Fixed bug where cached responses would lose their security
status on second validation, which especially impacted dlv
lookups. Reported by Hauke Lampe.
13 June 2009: Wouter
- bug #254. removed random whitespace from example.conf.
12 June 2009: Wouter
- Fixup potential wrong NSEC picked out of the cache.
- If unfulfilled callbacks are deleted they are called with an error.
- fptr wlist checks for mesh callbacks.
- fwd above stub in configuration works.
11 June 2009: Wouter
- Fix queries for type DS when forward or stub zones are there.
They are performed to higherup domains, and thus treated as if
going to higher zones when looking up the right forward or stub
server. This makes a stub pointing to a local server that has
a local view of example.com signed with the same keys as are
publicly used work. Reported by Johan Ihren.
- Added build-unbound-localzone-from-hosts.pl to contrib, from
Dennis DeDonatis. It converts /etc/hosts into config statements.
- same thing fixed for forward-zone and DS, chain of trust from
public internet into the forward-zone works now. Added unit test.
9 June 2009: Wouter
- openssl key files are opened apache-style, when user is root and
before chrooting. This makes permissions on remote-control key
files easier to set up. Fixes bug #251.
- flush_type and flush_name remove msg cache entries.
- codereview - dp copy bogus setting fix.
8 June 2009: Wouter
- Removed RFC5011 REVOKE flag support. Partial 5011 support may cause
inadvertant behaviour.
- 1.3.0 tarball for release created.
- 1.3.1 development in svn trunk.
- iana portlist updated.
- fix lint from complaining on ldns/sha.h.
- help compiler figure out aliasing in priv_rrset_bad() routine.
- fail to configure with python if swig is not found.
- unbound_munin_ in contrib uses ps to show rss if sbrk does not work.
3 June 2009: Wouter
- fixup bad free() when wrongly encoded DSA signature is seen.
Reported by Paul Wouters.
- review comments from Matthijs.
2 June 2009: Wouter
- --enable-sha2 option. The draft rsasha256 changed its algorithm
numbers too often. Therefore it is more prudent to disable the
RSASHA256 and RSASHA512 support by default.
- ldns trunk included as new tarball.
- recreated the 1.3.0 tag in svn. rc1 tarball generated at this point.
29 May 2009: Wouter
- fixup doc bug in README reported by Matthew Dempsky.
28 May 2009: Wouter
- update iana port list
- update ldns lib tarball
27 May 2009: Wouter
- detect lack of IPv6 support on XP (with a different error code).
- Fixup a crash-on-exit which was triggered by a very long queue.
Unbound would try to re-use ports that came free, but this is
of course not really possible because everything is deleted.
Most easily triggered on XP (not Vista), maybe because of the
network stack encouraging large messages backlogs.
- change in debug statements.
- Fixed bug that could cause a crash if root prime failed when there
were message backlogs.
26 May 2009: Wouter
- Thanks again to Brett Carr, found an assertion that was not true.
Assertion checked if recursion parent query still existed.
29 April 2009: Wouter
- Thanks to Brett Carr, caught windows resource leak, use
closesocket() and not close() on sockets or else the network stack
starts to leak handles.
- Removed usage of windows Mutex because windows cannot handle enough
mutexes open. Provide own mutex implementation using primitives.
28 April 2009: Wouter
- created svn tag for 1.3.0.
27 April 2009: Wouter
- optimised cname from cache.
- ifdef windows functions in testbound.
23 April 2009: Wouter
- fix for threadsafety in solaris thr_key_create() in tests.
- iana portlist updated.
- fix pylib test for Darwin.
- fix pymod test for Darwin and a python threading bug in pymod init.
- check python >= 2.4 in configure.
- -ldl check for libcrypto 1.0.0beta.
21 April 2009: Wouter
- fix for build outside sourcedir.
- fix for configure script swig detection.
17 April 2009: Wouter
- Fix reentrant in minievent handler for unix. Could have resulted
in spurious event callbacks.
- timers do not take up a fd slot for winsock handler.
- faster fix for winsock reentrant check.
- fix rsasha512 unit test for new (interim) algorithm number.
- fix test:ldns doesn't like DOS line endings in keyfiles on unix.
- fix compile warning on ubuntu (configlexer fwrite return value).
- move python include directives into CPPFLAGS instead of CFLAGS.
16 April 2009: Wouter
- winsock event handler exit very quickly on signal, even if
under heavy load.
- iana portlist updated.
- fixup windows winsock handler reentrant problem.
14 April 2009: Wouter
- bug #245: fix munin plugin, perform cleanup of stale lockfiles.
- makedist.sh; better help text.
- cache-min-ttl option and tests.
- mingw detect error condition on TCP sockets (NOTCONN).
9 April 2009: Wouter
- Fix for removal of RSASHA256_NSEC3 protonumber from ldns.
- ldns tarball updated.
- iana portlist update.
- detect GOST support in openssl-1.0.0-beta1, and fix compile problem
because that openssl defines the name STRING for itself.
6 April 2009: Wouter
- windows compile fix.
- Detect FreeBSD jail without ipv6 addresses assigned.
- python libunbound wrapper unit test.
- installs the following files. Default is to not build them.
from configure --with-pythonmodule:
/usr/lib/python2.x/site-packages/unboundmodule.py
from configure --with-pyunbound:
/usr/lib/python2.x/site-packages/unbound.py
/usr/lib/python2.x/site-packages/_unbound.so*
The example python scripts (pythonmod/examples and
libunbound/python/examples) are not installed.
- python invalidate routine respects packed rrset ids and locks.
- clock skew checks in unbound, config statements.
- nxdomain ttl considerations in requirements.txt
3 April 2009: Wouter
- Fixed a bug that caused messages to be stored in the cache too
long. Hard to trigger, but NXDOMAINs for nameservers or CNAME
targets have been more vulnerable to the TTL miscalculation bug.
- documentation test fixed for python addition.
2 April 2009: Wouter
- pyunbound (libunbound python plugin) compiles using libtool.
- documentation for pythonmod and pyunbound is generated in doc/html.
- iana portlist updated.
- fixed bug in unbound-control flush_zone where it would not flush
every message in the target domain. This especially impacted
NXDOMAIN messages which could remain in the cache regardless.
- python module test package.
1 April 2009: Wouter
- suppress errors when trying to contact authority servers that gave
ipv6 AAAA records for their nameservers with ipv4 mapped contents.
Still tries to do so, could work when deployed in intranet.
Higher verbosity shows the error.
- new libunbound calls documented.
- pyunbound in libunbound/python. Removed compile warnings.
Makefile to make it.
30 March 2009: Wouter
- Fixup LDFLAGS from libevent sourcedir compile configure restore.
- Fixup so no non-absolute rpaths are added.
- Fixup validation of RRSIG queries, they are let through.
- read /dev/random before chroot
- checkconf fix no python checks when no python module enabled.
- fix configure, pthread first, so other libs do not change outcome.
27 March 2009: Wouter
- nicer -h output. report linked libraries and modules.
- prints modules in intuitive order (config file friendly).
- python compiles easily on BSD.
26 March 2009: Wouter
- ignore swig varargs warnings with gcc.
- remove duplicate example.conf text from python example configs.
- outofdir compile fix for python.
- pyunbound works.
- print modules compiled in on -h. manpage.
25 March 2009: Wouter
- initial import of the python contribution from Zdenek Vasicek and
Marek Vavrusa.
- pythonmod in Makefile; changes to remove warnings/errors for 1.3.0.
24 March 2009: Wouter
- more neat configure.ac. Removed duplicate config.h includes.
- neater config.h.in.
- iana portlist updated.
- fix util/configlexer.c and solaris -std=c99 flag.
- fix postcommit aclocal errors.
- spaces stripped. Makefile cleaner, /usr omitted from -I, -L, -R.
- swap order of host detect and libtool generation.
23 March 2009: Wouter
- added launchd plist example file for MacOSX to contrib.
- deprecation test for daemon(3).
- moved common configure actions to m4 include, prettier Makefile.
20 March 2009: Wouter
- bug #239: module-config entries order is important. Documented.
- build fix for test asynclook.
19 March 2009: Wouter
- winrc/README.txt dos-format text file.
- iana portlist updated.
- use _beginthreadex() when available (performs stack alignment).
- defaults for windows baked into configure.ac (used if on mingw).
18 March 2009: Wouter
- Added tests, unknown algorithms become insecure. fallback works.
- Fix for and test for unknown algorithms in a trust anchor
definition. Trust anchors with no supported algos are ignored.
This means a (higher)DS or DLV entry for them could succeed, and
otherwise they are treated as insecure.
- domain-insecure: "example.com" statement added. Sets domain
insecure regardless of chain of trust DSs or DLVs. The inverse
of a trust-anchor.
17 March 2009: Wouter
- unit test for unsupported algorithm in anchor warning.
- fixed so queries do not fail on opportunistic target queries.
16 March 2009: Wouter
- fixup diff error printout in contrib/update-itar.sh.
- added contrib/unbound_cacti for statistics support in cacti,
contributed by Dmitriy Demidov.
13 March 2009: Wouter
- doxygen and lex/yacc on linux.
- strip update-anchor on makedist -w.
- fix testbound on windows.
- default log to syslog for windows.
- uninstaller can stop unbound - changed text on it to reflect that.
- remove debugging from windows 'cron' actions.
12 March 2009: Wouter
- log to App.logs on windows prints executable identity.
- fixup tests.
- munin plugin fix benign locking error printout.
- anchor-update for windows, called every 24 hours; unbound reloads.
11 March 2009: Wouter
- winsock event handler resets WSAevents after signalled.
- winsock event handler tests if signals are really signalled.
- install and service with log to file works on XP and Vista on
default install location.
- on windows logging to the Application logbook works (as a service).
- fix RUN_DIR on windows compile setting in makedist.
- windows registry has Software\Unbound\ConfigFile element.
If does not exist, the default is used. The -c switch overrides it.
- fix makedist version cleanup function.
10 March 2009: Wouter
- makedist -w strips out old rc.. and snapshot info from version.
- setup.exe starts and stops unbound after install, before uninstall.
- unbound-checkconf recognizes absolute pathnames on windows (C:...).
9 March 2009: Wouter
- Nullsoft NSIS installer creation script.
5 March 2009: Wouter
- fixup memory leak introduced on 18feb in mesh reentrant fix.
3 March 2009: Wouter
- combined icon with 16x16(4) 32x32(4) 48x48(8) 64x64(8).
- service works on xp/vista, no config necessary (using defaults).
- windows registry settings.
2 March 2009: Wouter
- fixup --export-symbols to be -export-symbls for libtool.
This should fix extraneous symbols exported from libunbound.
Thanks to Ondrej Sury and Robert Edmonds for finding it.
- iana portlist updated.
- document FAQ entry on stub/forward zones and default blocking.
- fix asynclook test app for libunbound not exporting symbols.
- service install and remove utils that work with vista UAC.
27 February 2009: Wouter
- Fixup lexer, to not give warnings about fwrite. Appeared in
new lexer features.
- makedistro functionality for mingw. Has RC support.
- support spaces and backslashes in configured defaults paths.
- register, deregister in service control manager.
25 February 2009: Wouter
- windres usage for application resources.
24 February 2009: Wouter
- isc moved their dlv key download location.
- fixup warning on vista/mingw.
- makedist -w for window zip distribution first version.
20 February 2009: Wouter
- Fixup contrib/update-itar.sh, the exit codes 1 and 0 were swapped.
Nicer script layout. Added url to site in -h output.
19 February 2009: Wouter
- unbound-checkconf and unbound print warnings when trust anchors
have unsupported algorithms.
- added contrib/update-itar.sh This script is similar to
update-anchor.sh, and updates from the IANA ITAR repository.
You can provide your own PGP key and trust repo, or can use the
builtin. The program uses wget and gpg to work.
- iana portlist updated.
- update-itar.sh: using ftp:// urls because https godaddy certificate
is not available everywhere and then gives fatal errors. The
security is provided by pgp signature.
18 February 2009: Wouter
- more cycle detection. Also for target queries.
- fixup bug where during deletion of the mesh queries the callbacks
that were reentrant caused assertion failures. Keep the mesh in
a reentrant safe state. Affects libunbound, reload of server,
on quit and flush_requestlist.
- iana portlist updated.
13 February 2009: Wouter
- forwarder information now per-thread duplicated.
This keeps it read only for speed, with no locking necessary.
- forward command for unbound control to change forwarders to use
on the fly.
- document that unbound-host reads no config file by default.
- updated iana portlist.
12 February 2009: Wouter
- call setusercontext if available (on BSD).
- small refactor of stats clearing.
- #227: flush_stats feature for unbound-control.
- stats_noreset feature for unbound-control.
- flush_requestlist feature for unbound-control.
- libunbound version upped API (was changed 5 feb).
- unbound-control status shows if root forwarding is in use.
- slightly nicer memory management in iter-fwd code.
10 February 2009: Wouter
- keys with rfc5011 REVOKE flag are skipped and not considered when
validating data.
- iana portlist updated
- #226: dump_requestlist feature for unbound-control.
6 February 2009: Wouter
- contrib contains specfile for fedora 1.2.1 (from Paul Wouters).
- iana portlist updated.
- fixup EOL in include directive (reported by Paul Wouters).
You can no longer specify newlines in the names of included files.
- config parser changed. Gives some syntax errors closer to where they
occurred. Does not enforce a space after keyword anymore.
Does not allow literal newlines inside quoted strings anymore.
- verbosity level 5 logs customer IP for new requestlist entries.
- test fix, lexer and cancel test.
- new option log-time-ascii: yes if you enable it prints timestamps
in the log file as Feb 06 13:45:26 (like syslog does).
- detect event_base_new in libevent-1.4.1 and later and use it.
- #231 unbound-checkconf -o option prints that value from config file.
Useful for scripting in management scripts and the like.
5 February 2009: Wouter
- ldns 1.5.0 rc as tarball included.
- 1.3.0 development continues:
change in libunbound API: ub_cancel can return an error, that
the async_id did not exist, or that it was already delivered.
The result could have been delivered just before the cancel
routine managed to acquire the lock, so a caller may get the
result at the same time they call cancel. For this case,
ub_cancel tries to return an error code.
Fixes race condition in ub_cancel() libunbound function.
- MacOSX Leopard cleaner text output from configure.
- initgroups(3) is called to drop secondary group permissions, if
applicable.
- configure option --with-ldns-builtin forces the use of the
inluded ldns package with the unbound source. The -I include
is put before the others, so it avoids bad include files from
an older ldns install.
- daemon(3) posix call is used when available.
- testbound test for older fix added.
4 February 2009: Wouter
- tag for release 1.2.1.
- trunk setup for 1.3.0 development.
3 February 2009: Wouter
- noted feature requests in doc/TODO.
- printout more detailed errors on ssl certificate loading failures.
- updated IANA portlist.
16 January 2009: Wouter
- more quiet about ipv6 network failures, i.e. when ipv6 is not
available (network unreachable). Debug still printed on high
verbosity.
- unbound-host -4 and -6 options. Stops annoying ipv6 errors when
debugging with unbound-host -4 -d ...
- more cycle detection for NS-check, addr-check, root-prime and
stub-prime queries in the iterator. Avoids possible deadlock
when priming fails.
15 January 2009: Wouter
- bug #229: fixup configure checks for compilation with Solaris
Sun cc compiler, ./configure CC=/opt/SUNWspro/bin/cc
- fixup suncc warnings.
- fix bug where unbound could crash using libevent 1.3 and older.
- update testset for recent retry change.
14 January 2009: Wouter
- 1.2.1 feature: negative caching for failed queries.
Queries that failed are cached for 5 seconds (NORR_TTL).
If the failure is local, like out of memory, it is not cached.
- the TTL comparison for the cache used different comparisons,
causing many cache responses that used the iterator and validator
state machines unnecessarily.
- retry from 4 to 5 so that EDNS drop retry is part of the first
query resolve attempt, and cached error does not stop EDNS fallback.
- remove debug prints that protect against bad referrals.
- honor QUIET=no on make commandline (or QUIET=yes ).
13 January 2009: Wouter
- fixed bug in lameness marking, removed printouts.
- find NS rrset more cleanly for qtype NS.
- Moved changes to 1.2.0 for release. Thanks to Mark Zealey for
reporting and logs.
- 1.2.1 feature: stops resolving AAAAs promiscuously when they
are in the negative cache.
12 January 2009: Wouter
- fixed bug in infrastructure lameness cache, did not lowercase
name of zone to hash when setting lame.
- lameness debugging printouts.
9 January 2009: Wouter
- created svn tag for 1.2.0 release.
- svn trunk contains 1.2.1 version number.
- iana portlist updated for todays list.
- removed debug print.
8 January 2009: Wouter
- new version of ldns-trunk (today) included as tarball, fixed
bug #224, building with -j race condition.
- remove possible race condition in the test for race conditions.
7 January 2009: Wouter
- version 1.2.0 in preparation.
- feature to allow wildcards (*, ?, [], {}. ~) in trusted-keys-file
statements. (Adapted from patch by Paul Wouters).
- typo fix and iana portlist updated.
- porting testsuite; unused var warning, and type fixup.
6 January 2009: Wouter
- fixup packet-of-death when compiled with --enable-debug.
A malformed packet could cause an internal assertion failure.
- added test for HINFO canonicalisation behaviour.
- fixup reported problem with transparent local-zone data where
queries with different type could get nxdomain. Now queries
with a different name get resolved normally, with different type
get a correct NOERROR/NODATA answer.
- HINFO no longer downcased for validation, making unbound compatible
with bind and ldns.
- fix reading included config files when chrooted.
Give full path names for include files.
Relative path names work if the start dir equals the working dir.
- fix libunbound message transport when no packet buffer is available.
5 January 2009: Wouter
- fixup getaddrinfo failure handling for remote control port.
- added L.ROOT-SERVERS.NET. AAAA 2001:500:3::42 to builtin root hints.
- fixup so it works with libev-3.51 from http://dist.schmorp.de/libev/
- comm_timer_set performs base_set operation after event_add.
18 December 2008: Wouter
- fixed bug reported by Duane Wessels: error in DLV lookup, would make
some zones that had correct DLV keys as insecure.
- follows -rc makedist from ldns changes (no _rc).
- ldns tarball updated with 1.4.1rc for DLV unit test.
- verbose prints about recursion lame detection and server selection.
- fixup BSD port for infra host storage. It hashed wrongly.
- fixup makedist snapshot name generation.
- do not reopen syslog to avoid dev/log dependency.
17 December 2008: Wouter
- follows ldns makedist.sh. -rc option. autom4te dir removed.
- unbound-control status command.
- extended statistics has a number of ipv6 queries counter.
contrib/unbound_munin_ was updated to draw ipv6 in the hits graph.
16 December 2008: Wouter
- follow makedist improvements from ldns, for maintainers prereleases.
- snapshot version uses _ not - to help rpm distinguish the
version number.
11 December 2008: Wouter
- better fix for bug #219: use LOG_NDELAY with openlog() call.
Thanks to Tamas Tevesz.
9 December 2008: Wouter
- bug #221 fixed: unbound checkconf checks if key files exist if
remote control is enabled. Also fixed NULL printf when not chrooted.
- iana portlist updated.
3 December 2008: Wouter
- Fix problem reported by Jaco Engelbrecht where unbound-control stats
freezes up unbound if this was compiled without threading, and
was using multiple processes.
- iana portlist updated.
- test for remote control with interprocess communication.
- created command distribution mechanism so that remote control
commands other than 'stats' work on all processes in a nonthreaded
compiled version. dump/load cache work, on the first process.
- fixup remote control local_data addition memory corruption bug.
1 December 2008: Wouter
- SElinux policy files in contrib/selinux for the unbound daemon,
by Paul Wouters and Adam Tkac.
25 November 2008: Wouter
- configure complains when --without-ssl is given (bug #220).
- skip unsupported feature tests on vista/mingw.
- fixup testcode/streamtcp to work on vista/mingw.
- root-hints test checks version of dig required.
- blacklisted servers are polled at a low rate (1%) to see if they
come back up. But not if there is some other working server.
24 November 2008: Wouter
- document that the user of the server daemon needs read privileges
on the keys and certificates generated by unbound-control-setup.
This is different per system or distribution, usually, running the
script under the same username as the server uses suffices.
i.e. sudo -u unbound unbound-control-setup
- testset port to vista/mingw.
- tcp_sigpipe to freebsd port.
21 November 2008: Wouter
- fixed tcp accept, errors were printed when they should not.
- unbound-control-setup.sh removes read/write permissions other
from the keys it creates (as suggested by Dmitriy Demidov).
20 November 2008: Wouter
- fixup fatal error due to faulty error checking after tcp accept.
- add check in rlimit to avoid integer underflow.
- rlimit check with new formula; better estimate for number interfaces
- nicer comments in rlimit check.
- tag 1.1.1 created in svn.
- trunk label is 1.1.2
19 November 2008: Wouter
- bug #219: fixed so that syslog which delays opening until the first
log line is written, gets a log line while not chroot'ed yet.
18 November 2008: Wouter
- iana portlist updated.
- removed cast in unit test debug print that was not 64bit safe.
- trunk back to 1.1.0; copied to tags 1.1.0 release.
- trunk to has version number 1.1.1 again.
- in 1.1.1; make clean nicer. grammar in manpage.
17 November 2008: Wouter
- theoretical fix for problems reported on mailing list.
If a delegation point has no A but only AAAA and do-ip6 is no,
resolution would fail. Fixed to ask for the A and AAAA records.
It has to ask for both always, so that it can fail quietly, from
TLD perspective, when a zone is only reachable on one transport.
- test for above, only AAAA and doip6 is no. Fix causes A record
for nameserver to be fetched.
- fixup address duplication on cache fillup for delegation points.
- testset updated for new query answer requirements.
14 November 2008: Wouter
- created 1.1.0 release tag in svn.
- trunk moved to 1.1.1
- fixup unittest-neg for locking.
13 November 2008: Wouter
- added fedora init and specfile to contrib (by Paul Wouters).
- added configure check for ldns 1.4.0 (using its compat funcs).
- neater comments in worker.h.
- removed doc/plan and updated doc/TODO.
- silenced EHOSTDOWN (verbosity 2 or higher to see it).
- review comments from Jelte, Matthijs. Neater code.
12 November 2008: Wouter
- add unbound-control manpage to makedist replace list.
11 November 2008: Wouter
- unit test for negative cache, stress tests the refcounting.
- fix for refcounting error that could cause fptr_wlist fatal exit
in the negative cache rbtree (upcoming 1.1 feature). (Thanks to
Attila Nagy for testing).
- nicer comments in cachedump about failed RR to string conversion.
- fix 32bit wrap around when printing large (4G and more) mem usage
for extended statistics.
10 November 2008: Wouter
- fixup the getaddrinfo compat code rename.
8 November 2008: Wouter
- added configure check for eee build warning.
7 November 2008: Wouter
- fix bug 217: fixed, setreuid and setregid do not work on MacOSX10.4.
- detect nonblocking problems in network stack in configure script.
6 November 2008: Wouter
- dname_priv must decompress the name before comparison.
- iana portlist updated.
5 November 2008: Wouter
- fixed possible memory leak in key_entry_key deletion.
Would leak a couple bytes when trust anchors were replaced.
- if query and reply qname overlap, the bytes are skipped not copied.
- fixed file descriptor leak when messages were jostled out that
had outstanding (TCP) replies.
- DNAMEs used from cache have their synthesized CNAMEs initialized
properly.
- fixed file descriptor leak for localzone type deny (for TCP).
- fixed memleak at exit for nsec3 negative cached zones.
- fixed memleak for the keyword 'nodefault' when reading config.
- made verbosity of 'edns incapable peer' warning higher, so you
do not get spammed by it.
- caught elusive Bad file descriptor error bug, that would print the
error while unnecessarily try to listen to a closed fd. Fixed.
4 November 2008: Wouter
- fixed -Wwrite-strings warnings that result in better code.
3 November 2008: Wouter
- fixup build process for Mac OSX linker, use ldns b32 compat funcs.
- generated configure with autoconf-2.61.
- iana portlist updated.
- detect if libssl needs libdl. For static linking with libssl.
- changed to use new algorithm identifiers for sha256/sha512
from ldns 1.4.0 (need very latest version).
- updated the included ldns tarball.
- proper detection of SHA256 and SHA512 functions (not just sizes).
23 October 2008: Wouter
- a little more debug info for failure on signer names. prints names.
22 October 2008: Wouter
- CFLAGS are picked up by configure from the environment.
- iana portlist updated.
- updated ldns to use 1.4.0-pre20081022 so it picks up CFLAGS too.
- new stub-prime: yesno option. Default is off, so it does not prime.
can be turned on to get same behaviour as previous unbound release.
- made automated test that checks if builtin root hints are uptodate.
- finished draft-wijngaards-dnsext-resolver-side-mitigation
implementation. The unwanted-reply-threshold can be set.
- fixup so fptr_whitelist test in alloc.c works.
21 October 2008: Wouter
- fix update-anchors.sh, so it does not report different RR order
as an update. Sorts the keys in the file. Updated copyright.
- fixup testbound on windows, the command control pipe doesn't exist.
- skip 08hostlib test on windows, no fork() available.
- made unbound-remote work on windows.
20 October 2008: Wouter
- quench a log message that is debug only.
- iana portlist updated.
- do not query bogus nameservers. It is like nameservers that have
the NS or A or AAAA record bogus are listed as donotquery.
- if server selection is faced with only bad choices, it will
attempt to get more options to be fetched.
- changed bogus-ttl default value from 900 to 60 seconds.
In anticipation that operator caused failures are more likely than
actual attacks at this time. And thus repeated validation helps
the operators get the problem fixed sooner. It makes validation
failures go away sooner (60 seconds after the zone is fixed).
Also it is likely to try different nameserver targets every minute,
so that if a zone is bad on one server but not another, it is
likely to pick up the 'correct' one after a couple minutes,
and if the TTL is big enough that solves validation for the zone.
- fixup unbound-control compilation on windows.
17 October 2008: Wouter
- port Leopard/G5: fixup type conversion size_t/uint32.
please ranlib, stop file without symbols warning.
- harden referral path now also validates the root after priming.
It looks up the root NS authoritatively as well as the root servers
and attemps to validate the entries.
16 October 2008: Wouter
- Fixup negative TTL values appearing (reported by Attila Nagy).
15 October 2008: Wouter
- better documentation for 0x20; remove fallback TODO, it is done.
- harden-referral-path feature includes A, AAAA queries for glue,
as well as very careful NS caching (only when doing NS query).
A, AAAA use the delegation from the NS-query.
14 October 2008: Wouter
- fwd_three.tpkg test was flaky. If the three requests hit the
wrong threads by chance (or bad OS) then the test would fail.
Made less flaky by increasing number of retries.
- stub_udp.tpkg changed to work, give root hints. fixed ldns_dname_abs.
- ldns tarball is snapshot of ldns r2759 (1.4.0-pre-20081014).
Which includes the ldns_dname_absolute fix.
- fwd_three test remains flaky now that unbound does not stop
listening when full. Thus, removed timeout problem.
It may be serviced by three threads, or maybe by one.
Mostly only useful for lock-check testing now.
13 October 2008: Wouter
- fixed recursion servers deployed as authoritative detection, so
that as a last resort, a +RD query is sent there to get the
correct answer.
- iana port list update.
- ldns tarball is snapshot of ldns r2759 (1.4.0-pre-20081013).
10 October 2008: Wouter
- fixup tests - the negative cache contained the correct NSEC3s for
two tests that are supposed to fail to validate.
9 October 2008: Wouter
- negative cache caps max iterations of NSEC3 done.
- NSEC3 negative cache for qtype DS works.
8 October 2008: Wouter
- NSEC negative cache for DS.
6 October 2008: Wouter
- jostle-timeout option, so you can config for slow links.
- 0x20 fallback code. Tries 3xnumber of nameserver addresses
queries that must all be the same. Sent to random nameservers.
- documented choices for DoS, EDNS, 0x20.
2 October 2008: Wouter
- fixup unlink of pidfile.
- fixup SHA256 algorithm collation code.
- contrib/update-anchor.sh does not overwrite anchors if not needed.
exits 0 when a restart is needed, other values if not.
so, update-anchor.sh -d mydir && /etc/rc.d/unbound restart
can restart unbound exactly when needed.
30 September 2008: Wouter
- fixup SHA256 DS downgrade, no longer possible to downgrade to SHA1.
- tests for sha256 support and downgrade resistance.
- RSASHA256 and RSASHA512 support (using the draft in dnsext),
using the drafted protocol numbers.
- when using stub on localhost (127.0.0.1@10053) unbound works.
Like when running NSD to host a local zone, on the same machine.
The noprime feature. manpages more explanation. Added a test for it.
- shorthand for reverse PTR, local-data-ptr: "1.2.3.4 www.ex.com"
29 September 2008: Wouter
- EDNS lameness detection, if EDNS packets are dropped this is
detected, eventually.
- multiple query timeout rtt backoff does not backoff too much.
26 September 2008: Wouter
- tests for remote-control.
- small memory leak in exception during remote control fixed.
- fixup for lock checking but not unchecking in remote control.
- iana portlist updated.
23 September 2008: Wouter
- Msg cache is loaded. A cache load enables cache responses.
- unbound-control flush [name], flush_type and flush_zone.
22 September 2008: Wouter
- dump_cache and load_cache statements in unbound-control.
RRsets are dumped and loaded correctly.
Msg cache is dumped.
19 September 2008: Wouter
- locking on the localdata structure.
- add and remove local zone and data with unbound-control.
- ldns trunk snapshot updated, make tests work again.
18 September 2008: Wouter
- fixup error in time calculation.
- munin plugin improvements.
- nicer abbreviations for high query types values (ixfr, axfr, any...)
- documented the statistics output in unbound-control man page.
- extended statistics prints out histogram, over unbound-control.
17 September 2008: Wouter
- locking for threadsafe bogus rrset counter.
- ldns trunk no longer exports b32 functions, provide compat.
- ldns tarball updated.
- testcode/ldns-testpkts.c const fixups.
- fixed rcode stat printout.
- munin plugin in contrib.
- stats always printout uptime, because stats plugins need it.
16 September 2008: Wouter
- extended-statistics: yesno config option.
- unwanted replies spoof nearmiss detector.
- iana portlist updated.
15 September 2008: Wouter
- working start, stop, reload commands for unbound-control.
- test for unbound-control working; better exit value for control.
- verbosity control via unbound-control.
- unbound-control stats.
12 September 2008: Wouter
- removed browser control mentions. Proto speccy.
11 September 2008: Wouter
- set nonblocking on new TCP streams, because linux does not inherit
the socket options to the accepted socket.
- fix TCP timeouts.
- SSL protected connection between server and unbound-control.
10 September 2008: Wouter
- remove memleak in privacy addresses on reloads and quits.
- remote control work.
9 September 2008: Wouter
- smallapp/unbound-control-setup.sh script to set up certificates.
4 September 2008: Wouter
- scrubber scrubs away private addresses.
- test for private addresses. man page entry.
- code refactored for name and address tree lookups.
3 September 2008: Wouter
- options for 'DNS Rebinding' protection: private-address and
private-domain.
- dnstree for reuse of routines that help with domain, addr lookups.
- private-address and private-domain config option read, stored.
2 September 2008: Wouter
- DoS protection features. Queries are jostled out to make room.
- testbound can pass time, increasing the internal timer.
- do not mark unsigned additionals bogus, leave unchecked, which
is removed too.
1 September 2008: Wouter
- disallow nonrecursive queries for cache snooping by default.
You can allow is using access-control: <subnet> allow_snoop.
The defaults do allow access no authoritative data without RD bit.
- two tests for it and fixups of tests for nonrec refused.
29 August 2008: Wouter
- version 1.1 number in trunk.
- harden-referral-path option for query for NS records.
Default turns off expensive, experimental option.
28 August 2008: Wouter
- fixup logfile handling; it is created with correct permissions
again. (from bugfix#199).
Some errors are not written to logfile (pidfile writing, forking),
and these are only visible by using the -d commandline flag.
27 August 2008: Wouter
- daemon(3) is causing problems for people. Reverting the patch.
bug#200, and 199 and 203 contain sideline discussion on it.
- bug#199 fixed: pidfile can be outside chroot. openlog is done before
chroot and drop permissions.
- config option to set size of aggressive negative cache,
neg-cache-size.
- bug#203 fixed: dlv has been implemented.
26 August 2008: Wouter
- test for insecure zone when DLV is in use, also does negative cache.
- test for trustanchor when DLV is in use (the anchor works).
- test for DLV used for a zone below a trustanchor.
- added scrub filter for overreaching NSEC records and unit test.
- iana portlist update
- use of setresuid or setreuid when available.
- use daemon(3) if available.
25 August 2008: Wouter
- realclean patch from Robert Edmonds.
22 August 2008: Wouter
- nicer debuglogging of DLV.
- test with secure delegation inside the DLV repository.
21 August 2008: Wouter
- negative cache code linked into validator, for DLV use.
negative cache works for DLV.
- iana portlist update.
- dlv-anchor option for unit tests.
- fixup NSEC_AT_APEX classification for short typemaps.
- ldns-testns has subdomain checks, for unit tests.
20 August 2008: Wouter
- negative cache code, reviewed.
18 August 2008: Wouter
- changes info: in logfile to notice: info: or debug: depending on
the verbosity of the statements. Better logfile message
classification.
- bug #208: extra rc.d unbound flexibility for freebsd/nanobsd.
15 August 2008: Wouter
- DLV nsec code fixed for better detection of closest existing
enclosers from NSEC responses.
- DLV works, straight to the dlv repository, so not for production.
- Iana port update.
14 August 2008: Wouter
- synthesize DLV messages from the rrset cache, like done for DS.
13 August 2008: Wouter
- bug #203: nicer do-auto log message when user sets incompatible
options.
- bug #204: variable name ameliorated in log.c.
- bug #206: in iana_update, no egrep, but awk use.
- ldns snapshot r2699 taken (includes DLV type).
- DLV work, config file element, trust anchor read in.
12 August 2008: Wouter
- finished adjusting testset to provide qtype NS answers.
11 August 2008: Wouter
- Fixup rrset security updates overwriting 2181 trust status.
This makes validated to be insecure data just as worthless as
nonvalidated data, and 2181 rules prevent cache overwrites to them.
- Fix assertion fail on bogus key handling.
- dnssec lameness detection works on first query at trust apex.
- NS queries get proper cache and dnssec lameness treatment.
- fixup compilation without pthreads on linux.
8 August 2008: Wouter
- NS queries are done after every referral.
validator is used on those NS records (if anchors enabled).
7 August 2008: Wouter
- Scrubber more strict. CNAME chains, DNAMEs from cache, other
irrelevant rrsets removed.
- 1.0.2 released from 1.0 support branch.
- fixup update-anchor.sh to work both in BSD shell and bash.
5 August 2008: Wouter
- fixup DS test so apex nodata works again.
4 August 2008: Wouter
- iana port update.
- TODO update.
- fix bug 201: null ptr deref on cleanup while udp pkts wait for port.
- added explanatory text for outgoing-port-permit in manpage.
30 July 2008: Wouter
- fixup bug qtype DS for unsigned zone and signed parent validation.
25 July 2008: Wouter
- added original copyright statement of OpenBSD arc4random code.
- created tube signaling solution on windows, as a pipe replacement.
this makes background asynchronous resolution work on windows.
- removed very insecure socketpair compat code. It also did not
work with event_waiting. Solved by pipe replacement.
- unbound -h prints openssl version number as well.
22 July 2008: Wouter
- moved pipe actions to util/tube.c. easier porting and shared code.
- check _raw() commpoint callbacks with fptr_wlist.
- iana port update.
21 July 2008: Wouter
- #198: nicer entropy warning message. manpage OS hints.
19 July 2008: Wouter
- #198: fixup man page to suggest chroot entropy fix.
18 July 2008: Wouter
- branch for 1.0 support.
- trunk work on tube.c.
17 July 2008: Wouter
- fix bug #196, compile outside source tree.
- fix bug #195, add --with-username=user configure option.
- print error and exit if started with config that requires more
fds than the builtin minievent can handle.
16 July 2008: Wouter
- made svn tag 1.0.1, trunk now 1.0.2
- sha256 checksums enabled in makedist.sh
15 July 2008: Wouter
- Follow draft-ietf-dnsop-default-local-zones-06 added reverse
IPv6 example prefix to AS112 default blocklist.
- fixup lookup of DS records by client with trustanchor for same.
- libunbound ub_resolve, fix handling of error condition during setup.
- lowered log_hex blocksize to fit through BSD syslog linesize.
- no useless initialisation if getpwnam not available.
- iana, ldns snapshot updated.
3 July 2008: Wouter
- Matthijs fixed memory leaks in root hints file reading.
26 June 2008: Wouter
- fixup streamtcp bounds setting for udp mode, in the test framework.
- contrib item for updating trust anchors.
25 June 2008: Wouter
- fixup fwd_ancil test typos.
- Fix for newegg lameness : ok for qtype=A, but lame for others.
- fixup unit test for infra cache, test lame merging.
- porting to mingw, bind, listen, getsockopt and setsockopt error
handling.
24 June 2008: Wouter
- removed testcode/checklocks from production code compilation path.
- streamtcp can use UDP mode (connected UDP socket), for testing IPv6
on windows.
- fwd_ancil test fails if platform support is lacking.
23 June 2008: Wouter
- fixup minitpkg to cleanup on windows with its file locking troubles.
- minitpkg shows skipped tests in report.
- skip ipv6 tests on ipv4 only hosts (requires only ipv6 localhost not
ipv6 connectivity).
- winsock event handler keeps track of sticky TCP events, that have
not been fully handled yet. when interest in the event(s) resumes,
they are sent again. When WOULDBLOCK is returned events are cleared.
- skip tests that need signals when testing on mingw.
18 June 2008: Wouter
- open testbound replay files in binary mode, because fseek/ftell
do not work in ascii-mode on windows. The b does nothing on unix.
unittest and testbound tests work on windows (xp too).
- ioctlsocket prints nicer error message.
- fixed up some TCP porting for winsock.
- lack of IPv6 gives a warning, no fatal error.
- use WSAGetLastError() on windows instead of errno for some errors.
17 June 2008: Wouter
- outgoing num fds 32 by default on windows ; it supports less
fds for waiting on than unixes.
- winsock_event minievent handler for windows. (you could also
attempt to link with libevent/libev ports for windows).
- neater crypto check and gdi32 detection.
- unbound.exe works to resolve and validate www.nlnetlabs.nl on vista.
16 June 2008: Wouter
- on windows, use windows threads, mutex and thread-local-storage(Tls).
- detect if openssl needs gdi32.
- if no threading, THREADS_DISABLED is defined for use in the code.
- sets USE_WINSOCK if using ws2_32 on windows.
- wsa_strerror() function for more readable errors.
- WSA Startup and Cleanup called in unbound.exe.
13 June 2008: Wouter
- port mingw32, more signal ifdefs, detect sleep, usleep,
random, srandom (used inside the tests).
- signed or unsigned FD_SET is cast.
10 June 2008: Wouter
- fixup warnings compiling on eeepc xandros linux.
9 June 2008: Wouter
- in iteration response type code
* first check for SOA record (negative answer) before NS record
and lameness.
* check if no AA bit for non-forwarder, and thus lame zone.
In response to error report by Richard Doty for mail.opusnet.com.
- fixup unput warning from lexer on freeBSD.
- bug#183. pidfile, rundir, and chroot configure options. Also the
example.conf and manual pages get the configured defaults.
You can use: (or accept the defaults to /usr/local/etc/unbound/)
--with-conf-file=filename
--with-pidfile=filename
--with-run-dir=path
--with-chroot-dir=path
8 June 2008: Wouter
- if multiple CNAMEs, use the first one. Fixup akamai CNAME bug.
Reported by Robert Edmonds.
- iana port updated.
4 June 2008: Wouter
- updated libtool files with newer version.
- iana portlist updated.
3 June 2008: Wouter
- fixup local-zone: "30.172.in-addr.arpa." nodefault, so that the
trailing dot is not used during comparison.
2 June 2008: Wouter
- Jelte fixed bugs in my absence
- bug 178: fixed unportable shell usage in configure (relied on
bash shell).
- bug 180: fixed buffer overflow in unbound-checkconf use of strncat.
- bug 181: fixed buffer overflow in ldns (called by unbound to parse
config file parts).
- fixes by Wouter
- bug 177: fixed compilation failure on opensuse, the
--disable-static configure flag caused problems. (Patch from
Klaus Singvogel)
- bug 179: same fix as 177.
- bug 185: --disable-shared not passed along to ldns included with
unbound. Fixed so that configure parameters are passed to the
subdir configure script.
fixed that ./libtool is used always, you can still override
manually with ./configure libtool=mylibtool or set $libtool in
the environment.
- update of the ldns tarball to current ldns svn version (fix 181).
- bug 184: -r option for unbound-host, read resolv.conf for
forwarder. (Note that forwarder must support DNSSEC for validation
to succeed).
23 May 2008: Wouter
- mingw32 porting.
- test for sys/wait.h
- WSAEWOULDBLOCK test after nonblocking TCP connect.
- write_iov_buffer removed: unused and no struct iov on windows.
- signed/unsigned warning fixup mini_event.
- use ioctlsocket to set nonblocking I/O if fnctl is unavailable.
- skip signals that are not defined
- detect pwd.h.
- detect getpwnam, getrlimit, setsid, sbrk, chroot.
- default config has no chroot if chroot() unavailable.
- if no kill() then no pidfile is read or written.
- gmtime_r is replaced by nonthreadsafe alternative if unavail.
used in rrsig time validation errors.
22 May 2008: Wouter
- contrib unbound.spec from Patrick Vande Walle.
- fixup bug#175: call tzset before chroot to have correct timestamps
in system log.
- do not generate lex input and lex unput functions.
- mingw port. replacement functions labelled _unbound.
- fix bug 174 - check for tcp_sigpipe that ldns-testns is installed.
19 May 2008: Wouter
- fedora 9, check in6_pktinfo define in configure.
- CREDITS fixup of history.
- ignore ldns-1.2.2 if installed, use builtin 1.3.0-pre alternative.
16 May 2008: Wouter
- fixup for MacOSX hosts file reading (reported by John Dickinson).
- created 1.0.0 svn tag.
- trunk version 1.0.1.
14 May 2008: Wouter
- accepted patch from Ondrej Sury for library version libtool option.
- configure --disable-rpath fixes up libtool for rpath trouble.
Adapted from debian package patch file.
13 May 2008: Wouter
- Added root ipv6 addresses to builtin root hints.
- TODO modified for post 1.0 plans.
- trunk version set to 1.0.0.
- no unnecessary linking with librt (only when libevent/libev used).
7 May 2008: Wouter
- fixup no-ip4 problem with error callback in outside network.
25 April 2008: Wouter
- DESTDIR is honored by the Makefile for rpms.
- contrib files unbound.spec and unbound.init, builds working RPM
on FC7 Linux, a chrooted caching resolver, and libunbound.
- iana ports update.
24 April 2008: Wouter
- chroot checks improved. working directory relative to chroot.
checks if config file path is inside chroot. Documentation on it.
- nicer example.conf text.
- created 0.11 tag.
23 April 2008: Wouter
- parseunbound.pl contrib update from Kai Storbeck for threads.
- iana ports update
22 April 2008: Wouter
- ignore SIGPIPE.
- unit test for SIGPIPE ignore.
21 April 2008: Wouter
- FEATURES document.
- fixup reread of config file if it was given as a full path
and chroot was used.
16 April 2008: Wouter
- requirements doc, updated clean query returns.
- parseunbound.pl update from Kai Storbeck.
- sunos4 porting changes.
15 April 2008: Wouter
- fixup default rc.d pidfile location to /usr/local/etc.
- iana ports updated.
- copyright updated in ldns-testpkts to keep same as in ldns.
- fixup checkconf chroot tests a bit more, chdir must be inside
chroot dir.
- documented 'gcc: unrecognized -KPIC option' errors on Solaris.
- example.conf values changed to /usr/local/etc/unbound
- DSA test work.
- DSA signatures: unbound is compatible with both encodings found.
It will detect and convert when necessary.
14 April 2008: Wouter
- got update for parseunbound.pl statistics script from Kai Storbeck.
- tpkg tests for udp wait list.
- documented 0x20 status.
- fixup chroot and checkconf, it is much smarter now.
- fixup DSA EVP signature decoding. Solution that Jelte found copied.
- and check first sig byte for the encoding type.
11 April 2008: Wouter
- random port selection out of the configged ports.
- fixup threadsafety for libevent-1.4.3+ (event_base_get_method).
- removed base_port.
- created 256-port ephemeral space for the OS, 59802 available.
- fixup consistency of port_if out array during heavy use.
10 April 2008: Wouter
- --with-libevent works with latest libevent 1.4.99-trunk.
- added log file statistics perl script to contrib.
- automatic iana ports update from makefile. 60058 available.
9 April 2008: Wouter
- configure can detect libev(from its build directory) when passed
--with-libevent=/home/wouter/libev-3.2
libev-3.2 is a little faster than libevent-1.4.3-stable (about 5%).
- unused commpoints not listed in epoll list.
- statistics-cumulative option so that the values are not reset.
- config creates array of available ports, 61841 available,
it excludes <1024 and iana assigned numbers.
config statements to modify the available port numbers.
8 April 2008: Wouter
- unbound tries to set the ulimit fds when started as server.
if that does not work, it will scale back its requirements.
27 March 2008: Wouter
- documented /dev/random symlink from chrootdir as FAQ entry.
26 March 2008: Wouter
- implemented AD bit signaling. If a query sets AD bit (but not DO)
then the AD bit is set in the reply if the answer validated.
Without including DNSSEC signatures. Useful if you have a trusted
path from the client to the resolver. Follows dnssec-updates draft.
25 March 2008: Wouter
- implemented check that for NXDOMAIN and NOERROR answers a query
section must be present in the reply (by the scrubber). And it must
be equal to the question sent, at least lowercase folded.
Previously this feature happened because the cache code refused
to store such messages. However blocking by the scrubber makes
sure nothing gets into the RRset cache. Also, this looks like a
timeout (instead of an allocation failure) and this retries are
done (which is useful in a spoofing situation).
- RTT banding. Band size 400 msec, this makes band around zero (fast)
include unknown servers. This makes unbound explore unknown servers.
7 March 2008: Wouter
- -C config feature for harvest program.
- harvest handles CNAMEs too.
5 March 2008: Wouter
- patch from Hugo Koji Kobayashi for iterator logs spelling.
4 March 2008: Wouter
- From report by Jinmei Tatuya, rfc2181 trust value for remainder
of a cname trust chain is lower; not full answer_AA.
- test for this fix.
- default config file location is /usr/local/etc/unbound.
Thus prefix is used to determine the location. This is also the
chroot and pidfile default location.
3 March 2008: Wouter
- Create 0.10 svn tag.
- 0.11 version in trunk.
- indentation nicer.
29 February 2008: Wouter
- documentation update.
- fixup port to Solaris of perf test tool.
- updated ldns-tarball with decl-after-statement fixes.
28 February 2008: Wouter
- fixed memory leaks in libunbound (during cancellation and wait).
- libunbound returns the answer packet in full.
- snprintf compat update.
- harvest performs lookup.
- ldns-tarball update with fix for ldns_dname_label.
- installs to sbin by default.
- install all manual pages (unbound-host and libunbound too).
27 February 2008: Wouter
- option to use caps for id randomness.
- config file option use-caps-for-id: yes
- harvest debug tool
26 February 2008: Wouter
- delay utility delays TCP as well. If the server that is forwarded
to has a TCP error, the delay utility closes the connection.
- delay does REUSE_ADDR, and can handle a server that closes its end.
- answers use casing from query.
25 February 2008: Wouter
- delay utility works. Gets decent thoughput too (>20000).
22 February 2008: Wouter
- +2% for recursions, if identical queries (except for destination
and query ID) in the reply list, avoid re-encoding the answer.
- removed TODO items for optimizations that do not show up in
profile reports.
- default is now minievent - not libevent. As its faster and
not needed for regular installs, only for very large port ranges.
- loop check different speedup pkt-dname-reading, 1% faster for
nocache-recursion check.
- less hashing during msg parse, 4% for recursion.
- small speed fix for dname_count_size_labels, +1 or +2% recursion.
- some speed results noted:
optimization resulted in +40% for recursion (cache miss) and
+70 to +80 for cache hits, and +96% for version.bind.
zone nsec3 example, 100 NXDOMAIN queries, NSD 35182.8 Ub 36048.4
www.nlnetlabs.nl from cache: BIND 8987.99 Ub 31218.3
www with DO bit set : BIND 8269.31 Ub 28735.6 qps.
So, unbound can be about equal qps to NSD in cache hits.
And about 3.4x faster than BIND in cache performance.
- delay utility for testing.
21 February 2008: Wouter
- speedup of root-delegation message encoding by 15%.
- minor speedup of compress tree_lookup, maybe 1%.
- speedup of dname_lab_cmp and memlowercmp - the top functions in
profiler output, maybe a couple percent when it matters.
20 February 2008: Wouter
- setup speec_cache for need-ldns-testns in dotests.
- check number of queued replies on incoming queries to avoid overload
on that account.
- fptr whitelist checks are not disabled in optimize mode.
- do-daemonize config file option.
- minievent time share initializes time at start.
- updated testdata for nsec3 new algorithm numbers (6, 7).
- small performance test of packet encoding (root delegation).
19 February 2008: Wouter
- applied patch to unbound-host man page from Jan-Piet Mens.
- fix donotquery-localhost: yes default (it erroneously was switched
to default 'no').
- time is only gotten once and the value is shared across unbound.
- unittest cleans up crypto, so that it has no memory leaks.
- mini_event shares the time value with unbound this results in
+3% speed for cache responses and +9% for recursions.
- ldns tarball update with new NSEC3 sign code numbers.
- perform several reads per UDP operation. This improves performance
in DoS conditions, and costs very little in normal conditions.
improves cache response +50%, and recursions +10%.
- modified asynclook test. because the callback from async is not
in any sort of lock (and thus can use all library functions freely),
this causes a tiny race condition window when the last lock is
released for a callback and a new cancel() for that callback.
The only way to remove this is by putting callbacks into some
lock window. I'd rather have the small possibility of a callback
for a cancelled function then no use of library functions in
callbacks. Could be possible to only outlaw process(), wait(),
cancel() from callbacks, by adding another lock, but I'd rather not.
18 February 2008: Wouter
- patch to unbound-host from Jan-Piet Mens.
- unbound host prints errors if fails to configure context.
- fixup perf to resend faster, so that long waiting requests do
not hold up the queue, they become lost packets or SERVFAILs,
or can be sent a little while later (i.e. processing time may
take long, but throughput has to be high).
- fixup iterator operating in no cache conditions (RD flag unset
after a CNAME).
- streamlined code for RD flag setting.
- profiled code and changed dname compares to be faster.
The speedup is about +3% to +8% (depending on the test).
- minievent tests for eintr and eagain.
15 February 2008: Wouter
- added FreeBSD rc.d script to contrib.
- --prefix option for configure also changes directory: pidfile:
and chroot: defaults in config file.
- added cache speed test, for cache size OK and cache too small.
14 February 2008: Wouter
- start without a config file (will complain, but start with
defaults).
- perf test program works.
13 February 2008: Wouter
- 0.9 released.
- 1.0 development. Printout ldns version on unbound -h.
- start of perf tool.
- bugfix to read empty lines from /etc/hosts.
12 February 2008: Wouter
- fixup problem with configure calling itself if ldns-src tarball
is not present.
11 February 2008: Wouter
- changed library to use ub_ instead of ub_val_ as prefix.
- statistics output text nice.
- etc/hosts handling.
- library function to put logging to a stream.
- set any option interface.
8 February 2008: Wouter
- test program for multiple queries over a TCP channel.
- tpkg test for stream tcp queries.
- unbound replies to multiple TCP queries on a TCP channel.
- fixup misclassification of root referral with NS in answer
when validating a nonrec query.
- tag 0.9
- layout of manpages, spelling fix in header, manpages process by
makedist, list asynclook and tcpstream tests as ldns-testns
required.
7 February 2008: Wouter
- moved up all current level 2 to be level 3. And 3 to 4.
to make room for new debug level 2 for detailed information
for operators.
- verbosity level 2. Describes recursion and validation.
- cleaner configure script and fixes for libevent solaris.
- signedness for log output memory sizes in high verbosity.
6 February 2008: Wouter
- clearer explanation of threading configure options.
- fixup asynclook test for nothreading (it creates only one process
to do the extended test).
- changed name of ub_val_result_free to ub_val_resolve_free.
- removes warning message during library linking, renamed
libunbound/unbound.c -> libunbound.c and worker to libworker.
- fallback without EDNS if result is NOTIMPL as well as on FORMERR.
5 February 2008: Wouter
- statistics-interval: seconds option added.
- test for statistics option
- ignore errors making directories, these can occur in parallel builds
- fixup Makefile strip command and libunbound docs typo.
31 January 2008: Wouter
- bg thread/process reads and writes the pipe nonblocking all the time
so that even if the pipe is buffered or so, the bg thread does not
block, and services both pipes and queries.
30 January 2008: Wouter
- check trailing / on chrootdir in checkconf.
- check if root hints and anchor files are in chrootdir.
- no route to host tcp error is verbosity level 2.
- removed unused send_reply_iov. and its configure check.
- added prints of 'remote address is 1.2.3.4 port 53' to errors
from netevent; the basic socket errors.
28 January 2008: Wouter
- fixup uninit use of buffer by libunbound (query id, flags) for
local_zone answers.
- fixup uninit warning from random.c; also seems to fix sporadic
sigFPE coming out of openssl.
- made openssl entropy warning more silent for library use. Needs
verbosity 1 now.
- fixup forgotten locks for rbtree_searches on ctx->query tree.
- random generator cleanup - RND_STATE_SIZE removed, and instead
a super-rnd can be passed at init to chain init random states.
- test also does lock checks if available.
- protect config access in libworker_setup().
- libevent doesn't like comm_base_exit outside of runloop.
- close fds after removing commpoints only (for epoll, kqueue).
25 January 2008: Wouter
- added tpkg for asynclook and library use.
- allows localhost to be queried when as a library.
- fixup race condition between cancel and answer (in case of
really fast answers that beat the cancel).
- please doxygen, put doxygen comment in one place.
- asynclook -b blocking mode and test.
- refactor asynclook, nicer code.
- fixup race problems from opensll in rand init from library, with
a mutex around the rand init.
- fix pass async_id=NULL to _async resolve().
- rewrote _wait() routine, so that it is threadsafe.
- cancelation is threadsafe.
- asynclook extended test in tpkg.
- fixed two races where forked bg process waits for (somehow shared?)
locks, so does not service the query pipe on the bg side.
Now those locks are only held for fg_threads and for bg_as_a_thread.
24 January 2008: Wouter
- tested the cancel() function.
- asynclook -c (cancel) feature.
- fix fail to allocate context actions.
- make pipe nonblocking at start.
- update plane for retry mode with caution to limit bandwidth.
- fix Makefile for concurrent make of unbound-host.
- renamed ub_val_ctx_wait/poll/process/fd to ub_val*.
- new calls to set forwarding added to header and docs.
23 January 2008: Wouter
- removed debug prints from if-auto, verb-algo enables some.
- libunbound QUIT setup, remove memory leaks, when using threads
will share memory for passing results instead of writing it over
the pipe, only writes ID number over the pipe (towards the handler
thread that does process() ).
22 January 2008: Wouter
- library code for async in libunbound/unbound.c.
- fix link testbound.
- fixup exit bug in mini_event.
- background worker query enter and result functions.
- bg query test application asynclook, it looks up multiple
hostaddresses (A records) at the same time.
21 January 2008: Wouter
- libworker work, netevent raw commpoints, write_msg, serialize.
18 January 2008: Wouter
- touch up of manpage for libunbound.
- support for IP_RECVDSTADDR (for *BSD ip4).
- fix for BSD, do not use ip4to6 mapping, make two sockets, once
ip6 and once ip4, uses socket options.
- goodbye ip4to6 mapping.
- update ldns-testpkts with latest version from ldns-trunk.
- updated makedist for relative ldns pathnames.
- library API with more information inside the result structure.
- work on background resolves.
17 January 2008: Wouter
- fixup configure in case -lldns is installed.
- fixup a couple of doxygen warnings, about enum variables.
- interface-automatic now copies the interface address from the
PKT_INFO structure as well.
- manual page with library API, all on one page 'man libunbound'.
- rewrite of PKTINFO structure, it also captures IP4 PKTINFO.
16 January 2008: Wouter
- incoming queries to the server with TC bit on are replied FORMERR.
- interface-automatic replied the wrong source address on localhost
queries. Seems to be due to ifnum=0 in recvmsg PKTINFO. Trying
to use ifnum=-1 to mean 'no interface, use kernel route'.
15 January 2008: Wouter
- interface-automatic feature. experimental. Nice for anycast.
- tpkg test for ip6 ancillary data.
- removed debug prints.
- porting experience, define for Solaris, test refined for BSD
compatibility. The feature probably will not work on OpenBSD.
- makedist fixup for ldns-src in build-dir.
14 January 2008: Wouter
- in no debug sets NDEBUG to remove asserts.
- configure --enable-debug is needed for dependency generation
for assertions and for compiler warnings.
- ldns.tgz updated with ldns-trunk (where buffer.h is updated).
- fix lint, unit test in optimize mode.
- default access control allows ::ffff:127.0.0.1 v6mapped localhost.
11 January 2008: Wouter
- man page, warning removed.
- added text describing the use of stub zones for private zones.
- checkconf tests for bad hostnames (IP address), and for doubled
interface lines.
- memory sizes can be given with 'k', 'Kb', or M or G appended.
10 January 2008: Wouter
- typo in example.conf.
- made using ldns-src that is included the package more portable
by linking with .lo instead of .o files in the ldns package.
- nicer do-ip6: yes/no documentation.
- nicer linking of libevent .o files.
- man pages render correctly on solaris.
9 January 2008: Wouter
- fixup openssl RAND problem, when the system is not configured to
give entropy, and the rng needs to be seeded.
8 January 2008: Wouter
- print median and quartiles with extensive logging.
4 January 2008: Wouter
- document misconfiguration in private network.
2 January 2008: Wouter
- fixup typo in requirements.
- document that 'refused' is a better choice than 'drop' for
the access control list, as refused will stop retries.
7 December 2007: Wouter
- unbound-host has a -d option to show what happens. This can help
with debugging (why do I get this answer).
- fixup CNAME handling, on nodata, sets and display canonname.
- dot removed from CNAME display.
- respect -v for NXDOMAINs.
- updated ldns-src.tar.gz with ldns-trunk today (1.2.2 fixes).
- size_t to int for portability of the header file.
- fixup bogus handling.
- dependencies and lint for unbound-host.
6 December 2007: Wouter
- library resolution works in foreground mode, unbound-host app
receives data.
- unbound-host prints rdata using ldns.
- unbound-host accepts trust anchors, and prints validation
information when you give -v.
5 December 2007: Wouter
- locking in context_new() inside the function.
- setup of libworker.
4 December 2007: Wouter
- minor Makefile fixup.
- moved module-stack code out of daemon/daemon into services/modstack,
preparing for code-reuse.
- move context into own header file.
- context query structure.
- removed unused variable pwd from checkconf.
- removed unused assignment from outside netw.
- check timeval length of string.
- fixup error in val_utils getsigner.
- fixup same (*var) error in netblocktostr.
- fixup memleak on parse error in localzone.
- fixup memleak on packet parse error.
- put ; after union in parser.y.
- small hardening in iter_operate against iq==NULL.
- hardening, if error reply with rcode=0 (noerror) send servfail.
- fixup same (*var) error in find_rrset in msgparse, was harmless.
- check return value of evtimer_add().
- fixup lockorder in lruhash_reclaim(), building up a list of locked
entries one at a time. Instead they are removed and unlocked.
- fptr_wlist for markdelfunc.
- removed is_locked param from lruhash delkeyfunc.
- moved bin_unlock during bin_split purely to please.
3 December 2007: Wouter
- changed checkconf/ to smallapp/ to make room for more support tools.
(such as unbound-host).
- install dirs created with -m 755 because they need to be accessible.
- library extensive featurelist added to TODO.
- please doxygen, lint.
- library test application, with basic functionality.
- fix for building in a subdirectory.
- link lib fix for Leopard.
30 November 2007: Wouter
- makefile that creates libunbound.la, basic file or libunbound.a
when creating static executables (no libtool).
- more API setup.
29 November 2007: Wouter
- 0.9 public API start.
28 November 2007: Wouter
- Changeup plan for 0.8 - no complication needed, a simple solution
has been chosen for authoritative features.
- you can use single quotes in the config file, so it is possible
to specify TXT records in local data.
- fixup small memory problem in implicit transparent zone creation.
- test for implicit zone creation and multiple RR RRsets local data.
- local-zone nodefault test.
- show testbound testlist on commit.
- iterator normalizer changes CNAME chains ending in NXDOMAIN where
the packet got rcode NXDOMAIN into rcode NOERROR. (since the initial
domain exists).
- nicer verbosity: 0 and 1 levels.
- lower nonRDquery chance of eliciting wrongly typed validation
requiring message from the cache.
- fix for nonRDquery validation typing; nodata is detected when
SOA record in auth section (all validation-requiring nodata messages
have a SOA record in authority, so this is OK for the validator),
and NS record is needed to be a referral.
- duplicate checking when adding NSECs for a CNAME, and test.
- created svn tag 0.8, after completing testbed tests.
27 November 2007: Wouter
- per suggestion in rfc2308, replaced default max-ttl value with 1 day.
- set size of msgparse lookup table to 32, from 1024, so that its size
is below the 2048 regional large size threshold, and does not cause
a call to malloc when a message is parsed.
- update of memstats tool to print number of allocation calls.
This is what is taking time (not space) and indicates the avg size
of the allocations as well. region_alloc stat is removed.
22 November 2007: Wouter
- noted EDNS in-the-middle dropping trouble as a TODO.
At this point theoretical, no user trouble has been reported.
- added all default AS112 zones.
- answers from local zone content.
* positive answer, the rrset in question
* nodata answer (exist, but not that type).
* nxdomain answer (domain does not exist).
* empty-nonterminal answer.
* But not: wildcard, nsec, referral, rrsig, cname/dname,
or additional section processing, NS put in auth.
- test for correct working of static and transparent and couple
of important defaults (localhost, as112, reverses).
Also checks deny and refuse settings.
- fixup implicit zone generation and AA bit for NXDOMAIN on localdata.
21 November 2007: Wouter
- local zone internal data setup.
20 November 2007: Wouter
- 0.8 - str2list config support for double string config options.
- local-zone and local-data options, config storage and documentation.
19 November 2007: Wouter
- do not downcase NSEC and RRSIG for verification. Follows
draft-ietf-dnsext-dnssec-bis-updates-06.txt.
- fixup leaking unbound daemons at end of tests.
- README file updated.
- nice libevent not found error.
- README talks about gnu make.
- 0.8: unit test for addr_mask and fixups for it.
and unit test for addr_in_common().
- 0.8: access-control config file element.
and unit test rpl replay file.
- 0.8: fixup address reporting from netevent.
16 November 2007: Wouter
- privilege separation is not needed in unbound at this time.
TODO item marked as such.
- created beta-0.7 branch for support.
- tagged 0.7 for beta release.
- moved trunk to 0.8 for 0.8(auth features) development.
- 0.8: access control list setup.
15 November 2007: Wouter
- review fixups from Jelte.
14 November 2007: Wouter
- testbed script does not recreate configure, since its in svn now.
- fixup checkconf test so that it does not test
/etc/unbound/unbound.conf.
- tag 0.6.
13 November 2007: Wouter
- remove debug print.
- fixup testbound exit when LIBEVENT_SIGNAL_PROBLEM exists.
12 November 2007: Wouter
- fixup signal handling where SIGTERM could be ignored if a SIGHUP
arrives later on.
- bugreports to unbound-bugs@nlnetlabs.nl
- fixup testbound so it exits cleanly.
- cleanup the caches on a reload, so that rrsetID numbers won't clash.
9 November 2007: Wouter
- took ldns snapshot in repo.
- default config file is /etc/unbound/unbound.conf.
If it doesn't exist, it is installed with the doc/example.conf file.
The file is not deleted on uninstall.
- default listening is not all, but localhost interfaces.
8 November 2007: Wouter
- Fixup chroot and drop user privileges.
- new L root ip address in default hints.
1 November 2007: Wouter
- Fixup of crash on reload, due to anchors in env not NULLed after
dealloc during deinit.
- Fixup of chroot call. Happens after privileges are dropped, so
that checking the passwd entry still works.
- minor touch up of clear() hashtable function.
- VERB_DETAIL prints out what chdir, username, chroot is being done.
- when id numbers run out, caches are cleared, as in design notes.
Tested with a mock setup with very few bits in id, it worked.
- harden-dnssec-stripped: yes is now default. It insists on dnssec
data for trust anchors. Included tests for the feature.
31 October 2007: Wouter
- cache-max-ttl config option.
- building outside sourcedir works again.
- defaults more secure:
username: "unbound"
chroot: "/etc/unbound"
The operator can override them to be less secure ("") if necessary.
- fix horrible oversight in sorting rrset references in a message,
sort per reference key pointer, not on referencepointer itself.
- pidfile: "/etc/unbound/unbound.pid" is now the default.
- tests changed to reflect the updated default.
- created hashtable clear() function that respects locks.
30 October 2007: Wouter
- fixup assertion failure that relied on compressed names to be
smaller than uncompressed names. A packet from comrite.com was seen
to be compressed to a larger size. Added it as unit test.
- quieter logging at low verbosity level for common tcp messages.
- no greedy TTL update.
23 October 2007: Wouter
- fixup (grand-)parent problem for dnssec-lameness detection.
- fixup tests to do additional section processing for lame replies,
since the detection needs that.
- no longer trust in query section in reply during dnssec lame detect.
- dnssec lameness does not make the server never ever queried, but
non-preferred. If no other servers exist or answer, the dnssec lame
server is used; the fastest dnssec lame server is chosen.
- added test then when trust anchor cannot be primed (nodata), the
insecure mode from unbound works.
- Fixup max queries per thread, any more are dropped.
22 October 2007: Wouter
- added donotquerylocalhost config option. Can be turned off for
out test cases.
- ISO C compat changes.
- detect RA-no-AA lameness, as LAME.
- DNSSEC-lameness detection, as LAME.
See notes in requirements.txt for choices made.
- tests for lameness detection.
- added all to make test target; need unbound for fwd tests.
- testbound does not pollute /etc/unbound.
19 October 2007: Wouter
- added configure (and its files) to svn, so that the trunk is easier
to use. ./configure, config.guess, config.sub, ltmain.sh,
and config.h.in.
- added yacc/lex generated files, util/configlexer.c,
util/configparser.c util/configparser.h, to svn.
- without lex no attempt to use it.
- unsecure response validation collated into one block.
- remove warning about const cast of cfgfile name.
- outgoing-interfaces can be different from service interfaces.
- ldns-src configure is done during unbound configure and
ldns-src make is done during unbound make, and so inherits the
make arguments from the unbound make invocation.
- nicer error when libevent problem causes instant exit on signal.
- read root hints from a root hint file (like BIND does).
18 October 2007: Wouter
- addresses are logged with errors.
- fixup testcode fake event to remove pending before callback
since the callback may create new pending items.
- tests updated because retries are now in iterator module.
- ldns-testpkts code is checked for differences between unbound
and ldns by makedist.sh.
- ldns trunk from today added in svn repo for fallback in case
no ldns is installed on the system.
make download_ldns refreshes the tarball with ldns svn trunk.
- ldns-src.tar.gz is used if no ldns is found on the system, and
statically linked into unbound.
- start of regional allocator code.
- regional uses less memory and variables, simplified code.
- remove of region-allocator.
- alloc cache keeps a cache of recently released regional blocks,
up to a maximum.
- make unit test cleanly free memory.
17 October 2007: Wouter
- fixup another cycle detect and ns-addr timeout resolution bug.
This time by refusing delegations from the cache without addresses
when resolving a mandatory-glue nameserver-address for that zone.
We're going to have to ask a TLD server anyway; might as well be
the TLD server for this name. And this resolves a lot of cases where
the other nameserver names lead to cycles or are not available.
- changed random generator from random(3) clone to arc4random wrapped
for thread safety. The random generator is initialised with
entropy from the system.
- fix crash where failure to prime DNSKEY tried to print null pointer
in the log message.
- removed some debug prints, only verb_algo (4) enables them.
- fixup test; new random generator took new paths; such as one
where no scripted answer was available.
- mark insecure RRs as insecure.
- fixup removal of nonsecure items from the additional.
- reduced timeout values to more realistic, 376 msec (262 msec has
90% of roundtrip times, 512 msec has 99% of roundtrip times.)
- server selection failover to next server after timeout (376 msec).
16 October 2007: Wouter
- no malloc in log_hex.
- assertions around system calls.
- protect against gethostname without ending zero.
- ntop output is null terminated by unbound.
- pidfile content null termination
- various snprintf use sizeof(stringbuf) instead of fixed constant.
- changed loopdetect % 8 with & 0x7 since % can become negative for
weird negative input and particular interpretation of integer math.
- dname_pkt_copy checks length of result, to protect result buffers.
prints an error, this should not happen. Bad strings should have
been rejected earlier in the program.
- remove a size_t underflow from msgreply size func.
15 October 2007: Wouter
- nicer warning.
- fix IP6 TCP, wrong definition check. With test package.
- fixup the fact that the query section was not compressed to,
the code was there but was called by value instead of by reference.
And test for the case, uses xxd and nc.
- more portable ip6 check for sockaddr types.
8 October 2007: Wouter
- --disable-rpath option in configure for 64bit systems with
several dynamic lib dirs.
7 October 2007: Wouter
- fixup tests for no AD bit in non-DO queries.
- test that makes sure AD bit is not set on non-DO query.
6 October 2007: Wouter
- removed logfile open early. It did not have the proper permissions;
it was opened as root instead of the user. And we cannot change user
id yet, since chroot and bind ports need to be done.
- callback checks for event callbacks done from mini_event. Because
of deletions cannot do this from netevent. This means when using
libevent the protection does not work on event-callbacks.
- fixup too small reply (did not zero counts).
- fixup reply no longer AD bit when query without DO bit.
5 October 2007: Wouter
- function pointer whitelist.
4 October 2007: Wouter
- overwrite sensitive random seed value after use.
- switch to logfile very soon if not -d (console attached).
- error messages do not reveal the trustanchor contents.
- start work on function pointer whitelists.
3 October 2007: Wouter
- fix for multiple empty nonterminals, after multiple DSes in the
chain of trust.
- mesh checks if modules are looping, and stops them.
- refetch with CNAMEd nameserver address regression test added.
- fixup line count bug in testcode, so testbound prints correct line
number with parse errors.
- unit test for multiple ENT case.
- fix for cname out of validated unsec zone.
- fixup nasty id=0 reuse. Also added assertions to detect its
return (the assertion catches in the existing test cases).
1 October 2007: Wouter
- skip F77, CXX, objC tests in configure step.
- fixup crash in refetch glue after a CNAME.
and protection against similar failures (with error print).
28 September 2007: Wouter
- test case for unbound-checkconf, fixed so it also checks the
interface: statements.
26 September 2007: Wouter
- SIGHUP will reopen the log file.
- Option to log to syslog.
- please lint, fixup tests (that went to syslog on open, oops).
- config check program.
25 September 2007: Wouter
- tests for NSEC3. Fixup bitmap checks for NSEC3.
- positive ANY response needs to check if wildcard expansion, and
check that original data did not exist.
- tests for NSEC3 that wrong use of OPTOUT is bad. For insecure
delegation, for abuse of child zone apex nsec3.
- create 0.5 release tag.
24 September 2007: Wouter
- do not make test programs by default.
- But 'make test' will perform all of the tests.
- Advertise builtin select libevent alternative when no libevent
is found.
- signit can generate NSEC3 hashes, for generating tests.
- multiple nsec3 parameters in message test.
- too high nsec3 iterations becomes insecure test.
21 September 2007: Wouter
- fixup empty_DS_name allocated in wrong region (port DEC Alpha).
- fixup testcode lock safety (port FreeBSD).
- removes subscript has type char warnings (port Solaris 9).
- fixup of field with format type to int (port MacOS/X intel).
- added test for infinite loop case in nonRD answer validation.
It was a more general problem, but hard to reproduce. When an
unsigned rrset is being validated and the key fetched, the DS
sequence is followed, but if the final name has no DS, then no
proof is possible - the signature has been stripped off.
20 September 2007: Wouter
- fixup and test for NSEC wildcard with empty nonterminals.
- makedist.sh fixup for svn info.
- acl features request in plan.
- improved DS empty nonterminal handling.
- compat with ANS nxdomain for empty nonterminals. Attempts the nodata
proof anyway, which succeeds in ANS failure case.
- striplab protection in case it becomes -1.
- plans for static and blacklist config.
19 September 2007: Wouter
- comments about non-packed usage.
- plan for overload support in 0.6.
- added testbound tests for a failed resolution from the logs
and for failed prime when missing glue.
- fixup so useless delegation points are not returned from the
cache. Also the safety belt is used if priming fails to complete.
- fixup NSEC rdata not to be lowercased, bind compat.
18 September 2007: Wouter
- wildcard nsec3 testcases, and fixup to get correct wildcard name.
- validator prints subtype classification for debug.
17 September 2007: Wouter
- NSEC3 hash cache unit test.
- validator nsec3 nameerror test.
14 September 2007: Wouter
- nsec3 nodata proof, nods proof, wildcard proof.
- nsec3 support for cname chain ending in noerror or nodata.
- validator calls nsec3 proof routines if no NSECs prove anything.
- fixup iterator bug where it stored the answer to a cname under
the wrong qname into the cache. When prepending the cnames, the
qname has to be reset to the original qname.
13 September 2007: Wouter
- nsec3 find matching and covering, ce proof, prove namerror msg.
12 September 2007: Wouter
- fixup of manual page warnings, like for NSD bugreport.
- nsec3 work, config, max iterations, filter, and hash cache.
6 September 2007: Wouter
- fixup to find libevent on mac port install.
- fixup size_t vs unsigned portability in validator/sigcrypt.
- please compiler on different platforms, for unreachable code.
- val_nsec3 file.
- pthread_rwlock type is optional, in case of old pthread libs.
5 September 2007: Wouter
- cname, name error validator tests.
- logging of qtype ANY works.
- ANY type answers get RRSIG in answer section of replies (but not
in other sections, unless DO bit is on).
- testbound can replay a TCP query (set MATCH TCP in the QUERY).
- DS and noDS referral validation test.
- if you configure many trust anchors, parent trust anchors can
securely deny existence of child trust anchors, if validated.
- not all *.name NSECs are present because a wildcard was matched,
and *.name NSECs can prove nodata for empty nonterminals.
Also, for wildcard name NSECs, check they are not from the parent
zone (for wildcarded zone cuts), and check absence of CNAME bit,
for a nodata proof.
- configure option for memory allocation debugging.
- port configure option for memory allocation to solaris10.
4 September 2007: Wouter
- fixup of Leakage warning when serviced queries processed multiple
callbacks for the same query from the same server.
- testbound removes config file from /tmp on failed exit.
- fixup for referral cleanup of the additional section.
- tests for cname, referral validation.
- neater testbound tpkg output.
- DNAMEs no longer match their apex when synthesized from the cache.
- find correct signer name for DNAME responses.
- wildcarded DNAME test and fixup code to detect.
- prepend NSEC and NSEC3 rrsets in the iterator while chasing CNAMEs.
So that wildcarded CNAMEs get their NSEC with them to the answer.
- test for a CNAME to a DNAME to a CNAME to an answer, all from
different domains, for key fetching and signature checking of
CNAME'd messages.
3 September 2007: Wouter
- Fixed error in iterator that would cause assertion failure in
validator. CNAME to a NXDOMAIN response was collated into a response
with both a CNAME and the NXDOMAIN rcode. Added a test that the
rcode is changed to NOERROR (because of the CNAME).
- timeout on tcp does not lead to spurious leakage detect.
- account memory for name of lame zones, so that memory leakages does
not show lame cache growth as a leakage growth.
- config setting for lameness cache expressed in bytes, instead of
number of entries.
- tool too summarize allocations per code line.
31 August 2007: Wouter
- can read bind trusted-keys { ... }; files, in a compatibility mode.
- iterator should not detach target queries that it still could need.
the protection against multiple outstanding queries is moved to a
current_query num check.
- validator nodata, positive, referral tests.
- dname print can print '*' wildcard.
30 August 2007: Wouter
- fixup override date config option.
- config options to control memory usage.
- caught bad free of un-alloced data in worker_send error case.
- memory accounting for key cache (trust anchors and temporary cache).
- memory accounting fixup for outside network tcp pending waits.
- memory accounting fixup for outside network tcp callbacks.
- memory accounting for iterator fixed storage.
- key cache size and slabs config options.
- lib crypto cleanups at exit.
29 August 2007: Wouter
- test tool to sign rrsets for testing validator with.
- added RSA and DSA test keys, public and private pairs, 512 bits.
- default configuration is with validation enabled.
Only a trust-anchor needs to be configured for DNSSEC to work.
- do not convert to DER for DSA signature verification.
- validator replay test file, for a DS to DNSKEY DSA key prime and
positive response.
28 August 2007: Wouter
- removed double use for udp buffers, that could fail,
instead performs a malloc to do the backup.
- validator validates referral messages, by validating all the rrsets
and stores the rrsets in the cache. Further referral (nonRD queries)
replies are made from the rrset cache directly. Unless unchecked
rrsets are encountered, there are then validated.
- enforce that signing is done by a parent domain (or same domain).
- adjust TTL downwards if rrset TTL bigger than signature allows.
- permissive mode feature, sets AD bit for secure, but bogus does
not give servfail (bogus is changed into indeterminate).
- optimization of rrset verification. rr canonical sorting is reused,
for the same rrset. canonical rrset image in buffer is reused for
the same signature.
- if the rrset is too big (64k exactly + large owner name) the
canonicalization routine will fail if it does not fit in buffer.
- faster verification for large sigsets.
- verb_detail mode reports validation failures, but not the entire
algorithm for validation. Key prime failures are reported as
verb_ops level.
27 August 2007: Wouter
- do not garble the edns if a cache answer fails.
- answer norecursive from cache if possible.
- honor clean_additional setting when returning secure non-recursive
referrals.
- do not store referral in msg cache for nonRD queries.
- store verification status in the rrset cache to speed up future
verification.
- mark rrsets indeterminate and insecure if they are found to be so.
and store this in the cache.
24 August 2007: Wouter
- message is bogus if unsecure authority rrsets are present.
- val-clean-additional option, so you can turn it off.
- move rrset verification out of the specific proof types into one
routine. This makes the proof routines prettier.
- fixup cname handling in validator, cname-to-positive and cname-to-
nodata work.
- Do not synthesize DNSKEY and DS responses from the rrset cache if
the rrset is from the additional section. Signatures may have
fallen off the packet, and cause validation failure.
- more verbose signature date errors (with the date attached).
- increased default infrastructure cache size. It is important for
performance, and 1000 entries are only 212k (or a 400 k total cache
size). To 10000 entries (for 2M entries, 4M cache size).
23 August 2007: Wouter
- CNAME handling - move needs_validation to before val_new().
val_new() setups the chase-reply to be an edited copy of the msg.
new classification, and find signer can find for it.
removal of unsigned crap from additional, and query restart for
cname.
- refuse to follow wildcarded DNAMEs when validating.
But you can query for qtype ANY, or qtype DNAME and validate that.
22 August 2007: Wouter
- bogus TTL.
- review - use val_error().
21 August 2007: Wouter
- ANY response validation.
- store security status in cache.
- check cache security status and either send the query to be
validated, return the query to client, or send servfail to client.
Sets AD bit on validated replies.
- do not examine security status on an error reply in mesh_done.
- construct DS, DNSKEY messages from rrset cache.
- manual page entry for override-date.
20 August 2007: Wouter
- validate and positive validation, positive wildcard NSEC validation.
- nodata validation, nxdomain validation.
18 August 2007: Wouter
- process DNSKEY response in FINDKEY state.
17 August 2007: Wouter
- work on DS2KE routine.
- val_nsec.c for validator NSEC proofs.
- unit test for NSEC bitmap reading.
- dname iswild and canonical_compare with unit tests.
16 August 2007: Wouter
- DS sig unit test.
- latest release libevent 1.3c and 1.3d have threading fixed.
- key entry fixup data pointer and ttl absolute.
- This makes a key-prime succeed in validator, with DS or DNSKEY as
trust-anchor.
- fixup canonical compare byfield routine, fix bug and also neater.
- fixed iterator response type classification for queries of type
ANY and NS.
dig ANY gives sometimes NS rrset in AN and NS section, and parser
removes the NS section duplicate. dig NS gives sometimes the NS
in the answer section, as referral.
- validator FINDKEY state.
15 August 2007: Wouter
- crypto calls to verify signatures.
- unit test for rrsig verification.
14 August 2007: Wouter
- default outgoing ports changed to avoid port 2049 by default.
This port is widely blocked by firewalls.
- count infra lameness cache in memory size.
- accounting of memory improved
- outbound entries are allocated in the query region they are for.
- extensive debugging for memory allocations.
- --enable-lock-checks can be used to enable lock checking.
- protect undefs in config.h from autoheaders ministrations.
- print all received udp packets. log hex will print on multiple
lines if needed.
- fixed error in parser with backwards rrsig references.
- mark cycle targets for iterator did not have CD flag so failed
its task.
13 August 2007: Wouter
- fixup makefile, if lexer is missing give nice error and do not
mess up the dependencies.
- canonical compare routine updated.
- canonical hinfo compare.
- printout list of the queries that the mesh is working on.
10 August 2007: Wouter
- malloc and free overrides that track total allocation and frees.
for memory debugging.
- work on canonical sort.
9 August 2007: Wouter
- canonicalization, signature checks
- dname signature label count and unit test.
- added debug heap size print to memory printout.
- typo fixup in worker.c
- -R needed on solaris.
- validator override option for date check testing.
8 August 2007: Wouter
- ldns _raw routines created (in ldns trunk).
- sigcrypt DS digest routines
- val_utils uses sigcrypt to perform signature cryptography.
- sigcrypt keyset processing
7 August 2007: Wouter
- security status type.
- security status is copied when rdata is equal for rrsets.
- rrset id is updated to invalidate all the message cache entries
that refer to NSEC, NSEC3, DNAME rrsets that have changed.
- val_util work
- val_sigcrypt file for validator signature checks.
6 August 2007: Wouter
- key cache for validator.
- moved isroot and dellabel to own dname routines, with unit test.
3 August 2007: Wouter
- replanning.
- scrubber check section of lame NS set.
- trust anchors can be in config file or read from zone file,
DS and DNSKEY entries.
- unit test trust anchor storage.
- trust anchors converted to packed rrsets.
- key entry definition.
2 August 2007: Wouter
- configure change for latest libevent trunk version (needs -lrt).
- query_done and walk_supers are moved out of module interface.
- fixup delegation point duplicates.
- fixup iterator scrubber; lame NS set is let through the scrubber
so that the classification is lame.
- validator module exists, and does nothing but pass through,
with calling of next module and return.
- validator work.
1 August 2007: Wouter
- set version to 0.5
- module work for module to module interconnections.
- config of modules.
- detect cycle takes flags.
31 July 2007: Wouter
- updated plan
- release 0.4 tag.
30 July 2007: Wouter
- changed random state init, so that sequential process IDs are not
cancelled out by sequential thread-ids in the random number seed.
- the fwd_three test, which sends three queries to unbound, and
unbound is kept waiting by ldns-testns for 3 seconds, failed
because the retry timeout for default by unbound is 3 seconds too,
it would hit that timeout and fail the test. Changed so that unbound
is kept waiting for 2 seconds instead.
27 July 2007: Wouter
- removed useless -C debug option. It did not work.
- text edit of documentation.
- added doc/CREDITS file, referred to by the manpages.
- updated planning.
26 July 2007: Wouter
- cycle detection, for query state dependencies. Will attempt to
circumvent the cycle, but if no other targets available fails.
- unit test for AXFR, IXFR response.
- test for cycle detection.
25 July 2007: Wouter
- testbound read ADDRESS and check it.
- test for version.bind and friends.
- test for iterator chaining through several referrals.
- test and fixup for refetch for glue. Refetch fails if glue
is still not provided.
24 July 2007: Wouter
- Example section in config manual.
- Addr stored for range and moment in replay.
20 July 2007: Wouter
- Check CNAME chain before returning cache entry with CNAMEs.
- Option harden-glue, default is on. It will discard out of zone
data. If disabled, performance is faster, but spoofing attempts
become a possibility. Note that still normalize scrubbing is done,
and that the potentially spoofed data is used for infrastructure
and not returned to the client.
- if glue times out, refetch by asking parent of delegation again.
Much like asking for DS at the parent side.
- TODO items from forgery-resilience draft.
and on memory handling improvements.
- renamed module_event_timeout to module_event_noreply.
- memory reporting code; reports on memory usage after handling
a network packet (not on cache replies).
19 July 2007: Wouter
- shuffle NS selection when getting nameserver target addresses.
- fixup of deadlock warnings, yield cpu in checklock code so that
freebsd scheduler selects correct process to run.
- added identity and version config options and replies.
- store cname messages complete answers.
18 July 2007: Wouter
- do not query addresses, 127.0.0.1, and ::1 by default.
17 July 2007: Wouter
- forward zone options in config file.
- forward per zone in iterator. takes precedence over stubs.
- fixup commithooks.
- removed forward-to and forward-to-port features, subsumed by
new forward zones.
- fix parser to handle absent server: clause.
- change untrusted rrset test to account for scrubber that is now
applied during the test (which removes the poison, by the way).
- feature, addresses can be specified with @portnumber, like nsd.conf.
- test config files changed over to new forwarder syntax.
27 June 2007: Wouter
- delete of mesh does a postorder traverse of the tree.
- found and fixed a memory leak. For TTL=0 messages, that would
not be cached, instead the msg-replyinfo structure was leaked.
- changed server selection so it will filter out hosts that are
unresponsive. This is defined as a host with the maximum rto value.
This means that unbound tried the host for retries up to 120 secs.
The rto value will time out after host-ttl seconds from the cache.
This keeps such unresolvable queries from taking up resources.
- utility for keeping histogram.
26 June 2007: Wouter
- mesh is called by worker, and iterator uses it.
This removes the hierarchical code.
QueryTargets state and Finished state are merged for iterator.
- forwarder mode no longer sets AA bit on first reply.
- rcode in walk_supers is not needed.
25 June 2007: Wouter
- more mesh work.
- error encode routine for ease.
22 June 2007: Wouter
- removed unused _node iterator value from rbtree_t. Takes up space.
- iterator can handle querytargets state without a delegation point
set, so that a priming(stub) subquery error can be handled.
- iterator stores if it is priming or not.
- log_query_info() neater logging.
- changed iterator so that it does not alter module_qstate.qinfo
but keeps a chase query info. Also query_flags are not altered,
the iterator uses chase_flags.
- fixup crash in case no ports for the family exist.
21 June 2007: Wouter
- Fixup secondary buffer in case of error callback.
- cleanup slumber list of runnable states.
- module_subreq_depth fails to work in slumber list.
- fixup query release for cached results to sub targets.
- neater error for tcp connection failure, shows addr in verbose.
- rbtree_init so that it can be used with preallocated memory.
20 June 2007: Wouter
- new -C option to enable coredumps after forking away.
- doc update.
- fixup CNAME generation by scrubber, and memory allocation of it.
- fixup deletion of serviced queries when all callbacks delete too.
- set num target queries to 0 when you move them to slumber list.
- typo in check caused subquery errors to be ignored, fixed.
- make lint happy about rlim_t.
- freeup of modules after freeup of module-states.
- duplicate replies work, this uses secondary udp buffer in outnet.
19 June 2007: Wouter
- nicer layout in stats.c, review 0.3 change.
- spelling improvement, review 0.3 change.
- uncapped timeout for server selection, so that very fast or slow
servers will stand out from the rest.
- target-fetch-policy: "3 2 1 0 0" config setting.
- fixup queries answered without RD bit (for root prime results).
- refuse AXFR and IXFR requests.
- fixup RD flag in error reply from iterator. fixup RA flag from
worker error reply.
- fixup encoding of very short edns buffer sizes, now sets TC bit.
- config options harden-short-bufsize and harden-large-queries.
18 June 2007: Wouter
- same, move subqueries to slumber list when first has resolved.
- fixup last fix for duplicate callbacks.
- another offbyone in targetcounter. Also in Java prototype by the way.
15 June 2007: Wouter
- if a query asks to be notified of the same serviced query result
multiple times, this will succeed. Only one callback will happen;
multiple outbound-list entries result (but the double cleanup of it
will not matter).
- when iterator moves on due to CNAME or referral, it will remove
the subqueries (for other targets). These are put on the slumber
list.
- state module wait subq is OK with no new subqs, an old one may have
stopped, with an error, and it is still waiting for other ones.
- if a query loops, halt entire query (easy way to clean up properly).
14 June 2007: Wouter
- num query targets was > 0 , not >= 0 compared, so that fetch
policy of 0 did nothing.
13 June 2007: Wouter
- debug option: configure --enable-static-exe for compile where
ldns and libevent are linked statically. Default is off.
- make install and make uninstall. Works with static-exe and without.
installation of unbound binary and manual pages.
- alignment problem fix on solaris 64.
- fixup address in case of TCP error.
12 June 2007: Wouter
- num target queries was set to 0 at a bad time. Default it to 0 and
increase as target queries are done.
- synthesize CNAME and DNAME responses from the cache.
- Updated doxygen config for doxygen 1.5.
- aclocal newer version.
- doxygen 1.5 fixes for comments (for the strict check on docs).
11 June 2007: Wouter
- replies on TCP queries have the address field set in replyinfo,
for serviced queries, because the initiator does not know that
a TCP fallback has occured.
- omit DNSSEC types from nonDO replies, except if qtype is ANY or
if qtype directly queries for the type (and then only show that
'unknown type' in the answer section).
- fixed message parsing where rrsigs on their own would be put
in the signature list over the rrsig type.
7 June 2007: Wouter
- fixup error in double linked list insertion for subqueries and
for outbound list of serviced queries for iterator module.
- nicer printout of outgoing port selection.
- fixup cname target readout.
- nicer debug output.
- fixup rrset counts when prepending CNAMEs to the answer.
- fixup rrset TTL for prepended CNAMEs.
- process better check for looping modules, and which submodule to
run next.
- subreq insertion code fixup for slumber list.
- VERB_DETAIL, verbosity: 2 level gives short but readable output.
VERB_ALGO, verbosity: 3 gives extensive output.
- fixup RA bit in cached replies.
- fixup CNAME responses from the cache no longer partial response.
- error in network send handled without leakage.
- enable ip6 from config, and try ip6 addresses if available,
if ip6 is not connected, skips to next server.
5 June 2007: Wouter
- iterator state finished.
- subrequests without parent store in cache and stop.
- worker slumber list for ongoing promiscuous queries.
- subrequest error handling.
- priming failure returns SERVFAIL.
- priming gives LAME result, returns SERVFAIL.
- debug routine to print dns_msg as handled by iterator.
- memleak in config file stubs fixup.
- more small bugs, in scrubber, query compare no ID for lookup,
in dname validation for NS targets.
- sets entry.key for new special allocs.
- lognametypeclass can display unknown types and classes.
4 June 2007: Wouter
- random selection of equally preferred nameserver targets.
- reply info copy routine. Reuses existing code.
- cache lameness in response handling.
- do not touch qstate after worker_process_query because it may have
been deleted by that routine.
- Prime response state.
- Process target response state.
- some memcmp changed to dname_compare for case preservation.
1 June 2007: Wouter
- normalize incoming messages. Like unbound-java, with CNAME chain
checked, DNAME checked, CNAME's synthesized, glue checked.
- sanitize incoming messages.
- split msgreply encode functions into own file msgencode.c.
- msg_parse to queryinfo/replyinfo conversion more versatile.
- process_response, classify response, delegpt_from_message.
31 May 2007: Wouter
- querytargets state.
- dname_subdomain_c() routine.
- server selection, based on RTT. ip6 is filtered out if not available,
and lameness is checked too.
- delegation point copy routine.
30 May 2007: Wouter
- removed FLAG_CD from message and rrset caches. This was useful for
an agnostic forwarder, but not for a sophisticated (trust value per
rrset enabled) cache.
- iterator response typing.
- iterator cname handle.
- iterator prime start.
- subquery work.
- processInitRequest and processInitRequest2.
- cache synthesizes referral messages, with DS and NSEC.
- processInitRequest3.
- if a request creates multiple subrequests these are all activated.
29 May 2007: Wouter
- routines to lock and unlock array of rrsets moved to cache/rrset.
- lookup message from msg cache (and copy to region).
- fixed cast error in dns msg lookup.
- message with duplicate rrset does not increase its TTLs twice.
- 'qnamesize' changed to 'qname_len' for similar naming scheme.
25 May 2007: Wouter
- Acknowledge use of unbound-java code in iterator. Nicer readme.
- services/cache/dns.c DNS Cache. Hybrid cache uses msgcache and
rrset cache from module environment.
- packed rrset key has type and class as easily accessible struct
members. They are still kept in network format for fast msg encode.
- dns cache find_delegation routine.
- iterator main functions setup.
- dns cache lookup setup.
24 May 2007: Wouter
- small changes to prepare for subqueries.
- iterator forwarder feature separated out.
- iterator hints stub code, config file stub code, so that first
testing can proceed locally.
- replay tests now have config option to enable forwarding mode.
23 May 2007: Wouter
- outside network does precise timers for roundtrip estimates for rtt
and for setting timeout for UDP. Pending_udp takes milliseconds.
- cleaner iterator sockaddr conversion of forwarder address.
- iterator/iter_utils and iter_delegpt setup.
- root hints.
22 May 2007: Wouter
- outbound query list for modules and support to callback with the
outbound entry to the module.
- testbound support for new serviced queries.
- test for retry to TCP cannot use testbound any longer.
- testns test for EDNS fallback, test for TCP fallback already exists.
- fixes for no-locking compile.
- mini_event timer precision and fix for change in timeouts during
timeout callback. Fix for fwd_three tests, performed nonexit query.
21 May 2007: Wouter
- small comment on hash table locking.
- outside network serviced queries, contain edns and tcp fallback,
and udp retries and rtt timing.
16 May 2007: Wouter
- lruhash_touch() would cause locking order problems. Fixup in
lock-verify in case locking cycle is found.
- services/cache/rrset.c for rrset cache code.
- special rrset_cache LRU updating function that uses the rrset id.
- no dependencies calculation when make clean is called.
- config settings for infra cache.
- daemon code slightly cleaner, only creates caches once.
15 May 2007: Wouter
- host cache code.
- unit test for host cache.
14 May 2007: Wouter
- Port to OS/X and Dec Alpha. Printf format and alignment fixes.
- extensive lock debug report on join timeout.
- proper RTT calculation, in utility code.
- setup of services/cache/infra, host cache.
11 May 2007: Wouter
- iterator/iterator.c module.
- fixup to pass reply_info in testcode and in netevent.
10 May 2007: Wouter
- created release-0.3 svn tag.
- util/module.h
- fixed compression - no longer compresses root name.
9 May 2007: Wouter
- outside network cleans up waiting tcp queries on exit.
- fallback to TCP.
- testbound replay with retry in TCP mode.
- tpkg test for retry in TCP mode, against ldns-testns server.
- daemon checks max number of open files and complains if not enough.
- test where data expires in the cache.
- compiletests: fixed empty body ifstatements in alloc.c, in case
locks are disabled.
8 May 2007: Wouter
- outgoing network keeps list of available tcp buffers for outgoing
tcp queries.
- outgoing-num-tcp config option.
- outgoing network keeps waiting list of queries waiting for buffer.
- netevent supports outgoing tcp commpoints, nonblocking connects.
7 May 2007: Wouter
- EDNS read from query, used to make reply smaller.
- advertised edns value constants.
- EDNS BADVERS response, if asked for too high edns version.
- EDNS extended error responses once the EDNS record from the query
has successfully been parsed.
4 May 2007: Wouter
- msgreply sizefunc is more accurate.
- config settings for rrset cache size and slabs.
- hashtable insert takes argument so that a thread can use its own
alloc cache to store released keys.
- alloc cache special_release() locks if necessary.
- rrset trustworthiness type added.
- thread keeps a scratchpad region for handling messages.
- writev used in netevent to write tcp length and data after another.
This saves a roundtrip on tcp replies.
- test for one rrset updated in the cache.
- test for one rrset which is not updated, as it is not deemed
trustworthy enough.
- test for TTL refreshed in rrset.
3 May 2007: Wouter
- fill refs. Use new parse and encode to answer queries.
- stores rrsets in cache.
- uses new msgreply format in cache.
2 May 2007: Wouter
- dname unit tests in own file and spread out neatly in functions.
- more dname unit tests.
- message encoding creates truncated TC flagged messages if they do
not fit, and will leave out (whole)rrsets from additional if needed.
1 May 2007: Wouter
- decompress query section, extremely lenient acceptance.
But only for answers from other servers, not for plain queries.
- compression and decompression test cases.
- some stats added.
- example.conf interface: line is changed from 127.0.0.1 which leads
to problems if used (restricting communication to the localhost),
to a documentation and test address.
27 April 2007: Wouter
- removed iov usage, it is not good for dns message encoding.
- owner name compression more optimal.
- rrsig owner name compression.
- rdata domain name compression.
26 April 2007: Wouter
- floating point exception fix in lock-verify.
- lint uses make dependency
- fixup lint in dname owner domain name compression code.
- define for offset range that can be compressed to.
25 April 2007: Wouter
- prettier code; parse_rrset->type kept in host byte order.
- datatype used for hashvalue of converted rrsig structure.
- unit test compares edns section data too.
24 April 2007: Wouter
- ttl per RR, for RRSIG rrsets and others.
- dname_print debug function.
- if type is not known, size calc will skip DNAME decompression.
- RRSIG parsing and storing and putting in messages.
- dnssec enabled unit tests (from nlnetlabs.nl and se queries).
- EDNS extraction routine.
20 April 2007: Wouter
- code comes through all of the unit tests now.
- disabled warning about spurious extra data.
- documented the RRSIG parse plan in msgparse.h.
- rrsig reading and outputting.
19 April 2007: Wouter
- fix unit test to actually to tests.
- fix write iov helper, and fakevent code.
- extra builtin testcase (small packet).
- ttl converted to network format in packets.
- flags converted correctly
- rdatalen off by 2 error fixup.
- uses less iov space for header.
18 April 2007: Wouter
- review of msgparse code.
- smaller test cases.
17 April 2007: Wouter
- copy and decompress dnames.
- store calculated hash value too.
- routine to create message out of stored information.
- util/data/msgparse.c for message parsing code.
- unit test, and first fixes because of test.
* forgot rrset_count addition.
* did & of ptr on stack for memory position calculation.
* dname_pkt_copy forgot to read next label length.
- test from file and fixes
* double frees fixed in error conditions.
* types with less than full rdata allowed by parser.
Some dynamic update packets seem to use it.
16 April 2007: Wouter
- following a small change in LDNS, parsing code calculates the
memory size to allocate for rrs.
- code to handle ID creation.
13 April 2007: Wouter
- parse routines. Code that parses rrsets, rrs.
12 April 2007: Wouter
- dname compare routine that preserves case, with unit tests.
11 April 2007: Wouter
- parse work - dname packet parse, msgparse, querysection parse,
start of sectionparse.
10 April 2007: Wouter
- Improved alignment of reply_info packet, nice for 32 and 64 bit.
- Put RRset counts in reply_info, because the number of RRs can change
due to RRset updates.
- import of region-allocator code from nsd.
- set alloc special type to ub_packed_rrset_key.
Uses lruhash entry overflow chain next pointer in alloc cache.
- doxygen documentation for region-allocator.
- setup for parse scratch data.
5 April 2007: Wouter
- discussed packed rrset with Jelte.
4 April 2007: Wouter
- moved to version 0.3.
- added util/data/dname.c
- layout of memory for rrsets.
3 April 2007: Wouter
- detect sign of msghdr.msg_iovlen so that the cast to that type
in netevent (which is there to please lint) can be correct.
The type on several OSes ranges from int, int32, uint32, size_t.
Detects unsigned or signed using math trick.
- constants for DNS flags.
- compilation without locks fixup.
- removed include of unportable header from lookup3.c.
- more portable use of struct msghdr.
- casts for printf warning portability.
- tweaks to tests to port them to the testbed.
- 0.2 tag created.
2 April 2007: Wouter
- check sizes of udp received messages, not too short.
- review changes. Some memmoves can be memcpys: 4byte aligned.
set id correctly on cached answers.
- review changes msgreply.c, memleak on error condition. AA flag
clear on cached reply. Lowercase queries on hashing.
unit test on lowercasing. Test AA bit not set on cached reply.
Note that no TTLs are managed.
29 March 2007: Wouter
- writev or sendmsg used when answering from cache.
This avoids a copy of the data.
- do not do useless byteswap on query id. Store reply flags in uint16
for easier access (and no repeated byteswapping).
- reviewed code.
- configure detects and config.h includes sys/uio.h for writev decl.
28 March 2007: Wouter
- new config option: num-queries-per-thread.
- added tpkg test for answering three queries at the same time
using one thread (from the query service list).
27 March 2007: Wouter
- added test for cache and not cached answers, in testbound replays.
- testbound can give config file and commandline options from the
replay file to unbound.
- created test that checks if items drop out of the cache.
- added word 'partitioned hash table' to documentation on slab hash.
A slab hash is a partitioned hash table.
- worker can handle multiple queries at a time.
26 March 2007: Wouter
- config settings for slab hash message cache.
- test for cached answer.
- Fixup deleting fake answer from testbound list.
23 March 2007: Wouter
- review of yesterday's commits.
- covered up memory leak of the entry locks.
- answers from the cache correctly. Copies flags correctly.
- sanity check for incoming query replies.
- slabbed hash table. Much nicer contention, need dual cpu to see.
22 March 2007: Wouter
- AIX configure check.
- lock-verify can handle references to locks that are created
in files it has not yet read in.
- threaded hash table test.
- unit test runs lock-verify afterwards and checks result.
- need writelock to update data on hash_insert.
- message cache code, msgreply code.
21 March 2007: Wouter
- unit test of hash table, fixup locking problem in table_grow().
- fixup accounting of sizes for removing items from hashtable.
- unit test for hash table, single threaded test of integrity.
- lock-verify reports errors nicely. More quiet in operation.
16 March 2007: Wouter
- lock-verifier, checks consistent order of locking.
14 March 2007: Wouter
- hash table insert (and subroutines) and lookup implemented.
- hash table remove.
- unit tests for hash internal bin, lru functions.
13 March 2007: Wouter
- lock_unprotect in checklocks.
- util/storage/lruhash.h for LRU hash table structure.
12 March 2007: Wouter
- configure.ac moved to 0.2.
- query_info and replymsg util/data structure.
9 March 2007: Wouter
- added rwlock writelock checking.
So it will keep track of the writelock, and readlocks are enforced
to not change protected memory areas.
- log_hex function to dump hex strings to the logfile.
- checklocks zeroes its destroyed lock after checking memory areas.
- unit test for alloc.
- identifier for union in checklocks to please older compilers.
- created 0.1 tag.
8 March 2007: Wouter
- Reviewed checklock code.
7 March 2007: Wouter
- created a wrapper around thread calls that performs some basic
checking for data race and deadlock, and basic performance
contention measurement.
6 March 2007: Wouter
- Testbed works with threading (different machines, different options).
- alloc work, does the special type.
2 March 2007: Wouter
- do not compile fork funcs unless needed. Otherwise will give
type errors as their typedefs have not been enabled.
- log shows thread numbers much more nicely (and portably).
- even on systems with nonthreadsafe libevent signal handling,
unbound will exit if given a signal.
Reloads will not work, and exit is not graceful.
- start of alloc framework layout.
1 March 2007: Wouter
- Signals, libevent and threads work well, with libevent patch and
changes to code (close after event_del).
- set ipc pipes nonblocking.
27 February 2007: Wouter
- ub_thread_join portable definition.
- forking is used if no threading is available.
Tested, it works, since pipes work across processes as well.
Thread_join is replaced with waitpid.
- During reloads the daemon will temporarily handle signals,
so that they do not result in problems.
- Also randomize the outgoing port range for tests.
- If query list is full, will stop selecting listening ports for read.
This makes all threads service incoming requests, instead of one.
No memory is leaking during reloads, service of queries, etc.
- test that uses ldns-testns -f to test threading. Have to answer
three queries at the same time.
- with verbose=0 operates quietly.
26 February 2007: Wouter
- ub_random code used to select ID and port.
- log code prints thread id.
- unbound can thread itself, with reload(HUP) and quit working
correctly.
- don't open pipes for #0, doesn't need it.
- listens to SIGTERM, SIGQUIT, SIGINT (all quit) and SIGHUP (reload).
23 February 2007: Wouter
- Can do reloads on sigHUP. Everything is stopped, and freed,
except the listening ports. Then the config file is reread.
And everything is started again (and listening ports if needed).
- Ports for queries are shared.
- config file added interface:, chroot: and username:.
- config file: directory, logfile, pidfile. And they work too.
- will daemonize by default now. Use -d to stay in the foreground.
- got BSD random[256 state] code, made it threadsafe. util/random.
22 February 2007: Wouter
- Have a config file. Removed commandline options, moved to config.
- tests use config file.
21 February 2007: Wouter
- put -c option in man page.
- minievent fd array capped by FD_SETSIZE.
20 February 2007: Wouter
- Added locks code and pthread spinlock detection.
- can use no locks, or solaris native thread library.
- added yacc and lex configure, and config file parsing code.
also makedist.sh, and manpage.
- put include errno.h in config.h
19 February 2007: Wouter
- Created 0.0 svn tag.
- added acx_pthread.m4 autoconf check for pthreads from
the autoconf archive. It is GPL-with-autoconf-exception Licensed.
You can specify --with-pthreads, or --without-pthreads to configure.
16 February 2007: Wouter
- Updated testbed script, works better by using make on remote end.
- removed check decls, we can compile without them.
- makefile supports LIBOBJ replacements.
- docs checks ignore compat code.
- added util/mini-event.c and .h, a select based alternative used with
./configure --with-libevent=no
It is limited to 1024 file descriptors, and has less features.
- will not create ip6 sockets if ip6 not on the machine.
15 February 2007: Wouter
- port to FreeBSD 4.11 Dec Alpha. Also works on Solaris 10 sparc64,
Solaris 9, FreeBSD 6, Linux i386 and OSX powerpc.
- malloc rndstate, so that it is aligned for access.
- fixed rbtree cleanup with postorder traverse.
- fixed pending messages are deleted when handled.
- You can control verbosity; default is not verbose, every -v
adds more verbosity.
14 February 2007: Wouter
- Included configure.ac changes from ldns.
- detect (some) headers before the standards check.
- do not use isblank to test c99, since its not available on solaris9.
- review of testcode.
* entries in a RANGE are no longer reversed.
* print name of file with replay entry parse errors.
- port to OSX: cast to int for some prints of sizet.
- Makefile copies ldnstestpkts.c before doing dependencies on it.
13 February 2007: Wouter
- work on fake events, first fwd replay works.
- events can do timeouts and errors on queries to servers.
- test package that runs replay scenarios.
12 February 2007: Wouter
- work on fake events.
9 February 2007: Wouter
- replay file reading.
- fake event setup, it creates fake structures, and teardowns,
added signal callbacks to reply to be able to fake those,
and main structure of event replay routines.
8 February 2007: Wouter
- added tcp test.
- replay storage.
- testcode/fake_event work.
7 February 2007: Wouter
- return answer with the same ID as query was sent with.
- created udp forwarder test. I've done some effort to make it perform
quickly. After servers are created, no big sleep statements but
it checks the logfiles to see if servers have come up. Takes 0.14s.
- set addrlen value when calling recvfrom.
- comparison of addrs more portable.
- LIBEVENT option for testbed to set libevent directory.
- work on tcp input.
6 February 2007: Wouter
- reviewed code and improved in places.
5 February 2007: Wouter
- Picked up stdc99 and other define tests from ldns. Improved
POSIX define test to include getaddrinfo.
- defined constants for netevent callback error code.
- unit test for strisip6.
2 February 2007: Wouter
- Created udp4 and udp6 port arrays to provide service for both
address families.
- uses IPV6_USE_MIN_MTU for udp6 ,IPV6_V6ONLY to make ip6 sockets.
- listens on both ip4 and ip6 ports to provide correct return address.
- worker fwder address filled correctly.
- fixup timer code.
- forwards udp queries and sends answer.
1 February 2007: Wouter
- outside network more UDP work.
- moved * closer to type.
- comm_timer object and events.
31 January 2007: Wouter
- Added makedist.sh script to make release tarball.
- Removed listen callback layer, did not add anything.
- Added UDP recv to netevent, worker callback for udp.
- netevent communication reply storage structure.
- minimal query header sanity checking for worker.
- copied over rbtree implementation from NSD (BSD licensed too).
- outgoing network query service work.
30 January 2007: Wouter
- links in example/ldns-testpkts.c and .h for premade packet support.
- added callback argument to listen_dnsport and daemon/worker.
29 January 2007: Wouter
- unbound.8 a short manpage.
26 January 2007: Wouter
- fixed memleak.
- make lint works on BSD and Linux (openssl defines).
- make tags works.
- testbound program start.
25 January 2007: Wouter
- fixed lint so it may work on BSD.
- put license into header of every file.
- created verbosity flag.
- fixed libevent configure flag.
- detects event_base_free() in new libevent 1.2 version.
- getopt in daemon. fatal_exit() and verbose() logging funcs.
- created log_assert, that throws assertions to the logfile.
- listen_dnsport service. Binds ports.
24 January 2007: Wouter
- cleaned up configure.ac.
23 January 2007: Wouter
- added libevent to configure to link with.
- util/netevent setup work.
- configure searches for libevent.
- search for libs at end of configure (when other headers and types
have been found).
- doxygen works with ATTR_UNUSED().
- util/netevent implementation.
22 January 2007: Wouter
- Designed header file for network communication.
16 January 2007: Wouter
- added readme.svn and readme.tests.
4 January 2007: Wouter
- Testbed script (run on multiple platforms the test set).
Works on Sunos9, Sunos10, FreeBSD 6.1, Fedora core 5.
- added unit test tpkg.
3 January 2007: Wouter
- committed first set of files into subversion repository.
svn co svn+ssh://unbound.net/svn/unbound
You need a ssh login. There is no https access yet.
- Added LICENSE, the BSD license.
- Added doc/README with compile help.
- main program stub and quiet makefile.
- minimal logging service (to stderr).
- added postcommit hook that serves emails.
- added first test 00-lint. postcommit also checks if build succeeds.
- 01-doc: doxygen doc target added for html docs. And stringent test
on documented files, functions and parameters.
15 December 2006: Wouter
- Created Makefile.in and configure.ac.
diff --git a/contrib/unbound/doc/README b/contrib/unbound/doc/README
index 8bc8765d464f..592a9f4ae8d2 100644
--- a/contrib/unbound/doc/README
+++ b/contrib/unbound/doc/README
@@ -1,151 +1,151 @@
-README for Unbound 1.18.0
+README for Unbound 1.19.0
Copyright 2007 NLnet Labs
http://unbound.net
This software is under BSD license, see LICENSE for details.
The DNS64 module has BSD license in dns64/dns64.c.
The DNSTAP code has BSD license in dnstap/dnstap.c.
* Download the latest release version of this software from
http://unbound.net
or get a beta version from the svn repository at
http://unbound.net/svn/
* Uses the following libraries;
* libevent http://www.monkey.org/~provos/libevent/ (BSD license)
(optional) can use builtin alternative instead.
* libexpat (for the unbound-anchor helper program) (MIT license)
* Make and install: ./configure; make; make install
* --with-libevent=/path/to/libevent
Can be set to either the system install or the build directory.
--with-libevent=no (default) gives a builtin alternative
implementation. libevent is useful when having many (thousands)
of outgoing ports. This improves randomization and spoof
resistance. For the default of 16 ports the builtin alternative
works well and is a little faster.
* --with-libexpat=/path/to/libexpat
Can be set to the install directory of libexpat.
* --without-pthreads
This disables pthreads. Without this option the pthreads library
is detected automatically. Use this option to disable threading
altogether, or, on Solaris, also use --with(out)-solaris-threads.
* --enable-checking
This enables assertions in the code that guard against a variety of
programming errors, among which buffer overflows. The program exits
with an error if an assertion fails (but the buffer did not overflow).
* --enable-static-exe
This enables a debug option to statically link against the
libevent library.
* --enable-lock-checks
This enables a debug option to check lock and unlock calls. It needs
a recent pthreads library to work.
* --enable-alloc-checks
This enables a debug option to check malloc (calloc, realloc, free).
The server periodically checks if the amount of memory used fits with
the amount of memory it thinks it should be using, and reports
memory usage in detail.
* --with-conf-file=filename
Set default location of config file,
the default is /usr/local/etc/unbound/unbound.conf.
* --with-pidfile=filename
Set default location of pidfile,
the default is /usr/local/etc/unbound/unbound.pid.
* --with-run-dir=path
Set default working directory,
the default is /usr/local/etc/unbound.
* --with-chroot-dir=path
Set default chroot directory,
the default is /usr/local/etc/unbound.
* --with-rootkey-file=path
Set the default root.key path. This file is read and written.
the default is /usr/local/etc/unbound/root.key
* --with-rootcert-file=path
Set the default root update certificate path. A builtin certificate
is used if this file is empty or does not exist.
the default is /usr/local/etc/unbound/icannbundle.pem
* --with-username=user
Set default user name to change to,
the default is the "unbound" user.
* --with-pyunbound
Create libunbound wrapper usable from python.
Needs python-devel and swig development tools.
* --with-pythonmodule
Compile the python module that processes responses in the server.
* --disable-sha2
Disable support for RSASHA256 and RSASHA512 crypto.
* --disable-gost
Disable support for GOST crypto, RFC 5933.
* --enable-subnet
Enable EDNS client subnet processing.
* 'make test' runs a series of self checks.
Known issues
------------
o If there are no replies for a forward or stub zone, for a reverse zone,
you may need to add a local-zone: name transparent or nodefault to the
server: section of the config file to unblock the reverse zone.
Only happens for (sub)zones that are blocked by default; e.g. 10.in-addr.arpa
o If libevent is older (before 1.3c), unbound will exit instead of reload
on sighup. On a restart 'did not exit gracefully last time' warning is
printed. Perform ./configure --with-libevent=no or update libevent, rerun
configure and recompile unbound to make sighup work correctly.
It is strongly suggested to use a recent version of libevent.
o If you are not receiving the correct source IP address on replies (e.g.
you are running a multihomed, anycast server), the interface-automatic
option can be enabled to set socket options to achieve the correct
source IP address on UDP replies. Listing all IP addresses explicitly in
the config file is an alternative. The interface-automatic option uses
non portable socket options, Linux and FreeBSD should work fine.
o The warning 'openssl has no entropy, seeding with time', with chroot
enabled, may be solved with a symbolic link to /dev/urandom from <chrootdir>.
o On Solaris 5.10 some libtool packages from repositories do not work with
gcc, showing errors gcc: unrecognized option `-KPIC'
To solve this do ./configure libtool=./libtool [your options...].
On Solaris you may pass CFLAGS="-xO4 -xtarget=generic" if you use sun-cc.
o If unbound-control (or munin graphs) do not work, this can often be because
the unbound-control-setup script creates the keys with restricted
permissions, and the files need to be made readable or ownered by both the
unbound daemon and unbound-control.
o Crosscompile seems to hang. You tried to install unbound under wine.
wine regedit and remove all the unbound entries from the registry or
delete .wine/drive_c.
Acknowledgements
----------------
o Unbound was written in portable C by Wouter Wijngaards (NLnet Labs).
o Thanks to David Blacka and Matt Larson (Verisign) for the unbound-java
prototype. Design and code from that prototype has been used to create
this program. Such as the iterator state machine and the cache design.
o Other code origins are from the NSD (NLnet Labs) and LDNS (NLnet Labs)
projects. Such as buffer, region-allocator and red-black tree code.
o See Credits file for contributors.
Your Support
------------
NLnet Labs offers all of its software products as open source, most are
published under a BSD license. You can download them, not only from the
NLnet Labs website but also through the various OS distributions for
which NSD, ldns, and Unbound are packaged. We therefore have little idea
who uses our software in production environments and have no direct ties
with 'our customers'.
Therefore, we ask you to contact us at users@NLnetLabs.nl and tell us
whether you use one of our products in your production environment,
what that environment looks like, and maybe even share some praise.
We would like to refer to the fact that your organization is using our
products. We will only do that if you explicitly allow us. In all other
cases we will keep the information you share with us to ourselves.
In addition to the moral support you can also support us
financially. NLnet Labs is a recognized not-for-profit charity foundation
that is chartered to develop open-source software and open-standards
for the Internet. If you use our software to satisfaction please express
that by giving us a donation. For small donations PayPal can be used. For
larger and regular donations please contact us at users@NLnetLabs.nl. Also
see http://www.nlnetlabs.nl/labs/contributors/.
* mailto:unbound-bugs@nlnetlabs.nl
diff --git a/contrib/unbound/doc/example.conf.in b/contrib/unbound/doc/example.conf.in
index 849e6d28446f..fe0dde69fa19 100644
--- a/contrib/unbound/doc/example.conf.in
+++ b/contrib/unbound/doc/example.conf.in
@@ -1,1306 +1,1315 @@
#
# Example configuration file.
#
-# See unbound.conf(5) man page, version 1.18.0.
+# See unbound.conf(5) man page, version 1.19.0.
#
# this is a comment.
# Use this anywhere in the file to include other text into this file.
#include: "otherfile.conf"
# Use this anywhere in the file to include other text, that explicitly starts a
# clause, into this file. Text after this directive needs to start a clause.
#include-toplevel: "otherfile.conf"
# The server clause sets the main parameters.
server:
# whitespace is not necessary, but looks cleaner.
# verbosity number, 0 is least verbose. 1 is default.
# verbosity: 1
# print statistics to the log (for every thread) every N seconds.
# Set to "" or 0 to disable. Default is disabled.
# statistics-interval: 0
# enable shm for stats, default no. if you enable also enable
# statistics-interval, every time it also writes stats to the
# shared memory segment keyed with shm-key.
# shm-enable: no
# shm for stats uses this key, and key+1 for the shared mem segment.
# shm-key: 11777
# enable cumulative statistics, without clearing them after printing.
# statistics-cumulative: no
# enable extended statistics (query types, answer codes, status)
# 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
# specify the interfaces to answer queries from by ip-address.
# The default is to listen to localhost (127.0.0.1 and ::1).
# specify 0.0.0.0 and ::0 to bind to all available interfaces.
# specify every interface[@port] on a new 'interface:' labelled line.
# The listen interfaces are not changed on reload, only on restart.
# interface: 192.0.2.153
# interface: 192.0.2.154
# interface: 192.0.2.154@5003
# interface: 2001:DB8::5
# interface: eth0@5003
# enable this feature to copy the source address of queries to reply.
# Socket options are not supported on all platforms. experimental.
# interface-automatic: no
# instead of the default port, open additional ports separated by
# spaces when interface-automatic is enabled, by listing them here.
# interface-automatic-ports: ""
# port to answer queries from
# port: 53
# specify the interfaces to send outgoing queries to authoritative
# server from by ip-address. If none, the default (all) interface
# is used. Specify every interface on a 'outgoing-interface:' line.
# outgoing-interface: 192.0.2.153
# outgoing-interface: 2001:DB8::5
# outgoing-interface: 2001:DB8::6
# Specify a netblock to use remainder 64 bits as random bits for
# upstream queries. Uses freebind option (Linux).
# outgoing-interface: 2001:DB8::/64
# Also (Linux:) ip -6 addr add 2001:db8::/64 dev lo
# And: ip -6 route add local 2001:db8::/64 dev lo
# And set prefer-ip6: yes to use the ip6 randomness from a netblock.
# Set this to yes to prefer ipv6 upstream servers over ipv4.
# prefer-ip6: no
# Prefer ipv4 upstream servers, even if ipv6 is available.
# prefer-ip4: no
# number of ports to allocate per thread, determines the size of the
# port range that can be open simultaneously. About double the
# num-queries-per-thread, or, use as many as the OS will allow you.
# outgoing-range: 4096
# permit Unbound to use this port number or port range for
# making outgoing queries, using an outgoing interface.
# outgoing-port-permit: 32768
# deny Unbound the use this of port number or port range for
# making outgoing queries, using an outgoing interface.
# Use this to make sure Unbound does not grab a UDP port that some
# other server on this computer needs. The default is to avoid
# IANA-assigned port numbers.
# If multiple outgoing-port-permit and outgoing-port-avoid options
# are present, they are processed in order.
# outgoing-port-avoid: "3200-3208"
# number of outgoing simultaneous tcp buffers to hold per thread.
# outgoing-num-tcp: 10
# number of incoming simultaneous tcp buffers to hold per thread.
# incoming-num-tcp: 10
# buffer size for UDP port 53 incoming (SO_RCVBUF socket option).
# 0 is system default. Use 4m to catch query spikes for busy servers.
# so-rcvbuf: 0
# buffer size for UDP port 53 outgoing (SO_SNDBUF socket option).
# 0 is system default. Use 4m to handle spikes on very busy servers.
# so-sndbuf: 0
# use SO_REUSEPORT to distribute queries over threads.
# at extreme load it could be better to turn it off to distribute even.
# so-reuseport: yes
# use IP_TRANSPARENT so the interface: addresses can be non-local
# and you can config non-existing IPs that are going to work later on
# (uses IP_BINDANY on FreeBSD).
# ip-transparent: no
# use IP_FREEBIND so the interface: addresses can be non-local
# and you can bind to nonexisting IPs and interfaces that are down.
# Linux only. On Linux you also have ip-transparent that is similar.
# ip-freebind: no
# the value of the Differentiated Services Codepoint (DSCP)
# in the differentiated services field (DS) of the outgoing
# IP packets
# ip-dscp: 0
# EDNS reassembly buffer to advertise to UDP peers (the actual buffer
# is set with msg-buffer-size).
# edns-buffer-size: 1232
# Maximum UDP response size (not applied to TCP response).
# 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
# buffer size for handling DNS data. No messages larger than this
# size can be sent or received, by UDP or TCP. In bytes.
# msg-buffer-size: 65552
# the amount of memory to use for the message cache.
# plain value in bytes or you can append k, m or G. default is "4Mb".
# msg-cache-size: 4m
# the number of slabs to use for the message cache.
# the number of slabs must be a power of 2.
# more slabs reduce lock contention, but fragment memory usage.
# msg-cache-slabs: 4
# the number of queries that a thread gets to service.
# num-queries-per-thread: 1024
# if very busy, 50% queries run to completion, 50% get timeout in msec
# jostle-timeout: 200
# msec to wait before close of port on timeout UDP. 0 disables.
# delay-close: 0
# perform connect for UDP sockets to mitigate ICMP side channel.
# udp-connect: yes
# The number of retries, per upstream nameserver in a delegation, when
# 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
# the amount of memory to use for the RRset cache.
# plain value in bytes or you can append k, m or G. default is "4Mb".
# rrset-cache-size: 4m
# the number of slabs to use for the RRset cache.
# the number of slabs must be a power of 2.
# more slabs reduce lock contention, but fragment memory usage.
# rrset-cache-slabs: 4
# the time to live (TTL) value lower bound, in seconds. Default 0.
# If more than an hour could easily give trouble due to stale data.
# cache-min-ttl: 0
# the time to live (TTL) value cap for RRsets and messages in the
# cache. Items are not cached for longer. In seconds.
# cache-max-ttl: 86400
# the time to live (TTL) value cap for negative responses in the cache
# cache-max-negative-ttl: 3600
# the time to live (TTL) value for cached roundtrip times, lameness and
# EDNS version information for hosts. In seconds.
# infra-host-ttl: 900
# minimum wait time for responses, increase if uplink is long. In msec.
# infra-cache-min-rtt: 50
# maximum wait time for responses. In msec.
# infra-cache-max-rtt: 120000
# enable to make server probe down hosts more frequently.
# infra-keep-probing: no
# the number of slabs to use for the Infrastructure cache.
# the number of slabs must be a power of 2.
# more slabs reduce lock contention, but fragment memory usage.
# infra-cache-slabs: 4
# the maximum number of hosts that are cached (roundtrip, EDNS, lame).
# infra-cache-numhosts: 10000
# define a number of tags here, use with local-zone, access-control,
# interface-*.
# repeat the define-tag statement to add additional tags.
# define-tag: "tag1 tag2 tag3"
# Enable IPv4, "yes" or "no".
# do-ip4: yes
# 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
# Enable TCP, "yes" or "no".
# do-tcp: yes
# upstream connections use TCP only (and no UDP), "yes" or "no"
# useful for tunneling scenarios, default no.
# tcp-upstream: no
# upstream connections also use UDP (even if do-udp is no).
# useful if if you want UDP upstream, but don't provide UDP downstream.
# udp-upstream-without-downstream: no
# Maximum segment size (MSS) of TCP socket on which the server
# responds to queries. Default is 0, system default MSS.
# tcp-mss: 0
# Maximum segment size (MSS) of TCP socket for outgoing queries.
# Default is 0, system default MSS.
# outgoing-tcp-mss: 0
# Idle TCP timeout, connection closed in milliseconds
# tcp-idle-timeout: 30000
# Enable EDNS TCP keepalive option.
# edns-tcp-keepalive: no
# 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
# Detach from the terminal, run in background, "yes" or "no".
# Set the value to "no" when Unbound runs as systemd service.
# do-daemonize: yes
# control which clients are allowed to make (recursive) queries
# to this server. Specify classless netblocks with /size and action.
# By default everything is refused, except for localhost.
# Choose deny (drop message), refuse (polite error reply),
# allow (recursive ok), allow_setrd (recursive ok, rd bit is forced on),
# allow_snoop (recursive and nonrecursive ok)
# deny_non_local (drop queries unless can be answered from local-data)
# refuse_non_local (like deny_non_local but polite error reply).
# access-control: 127.0.0.0/8 allow
# access-control: ::1 allow
# access-control: ::ffff:127.0.0.1 allow
# tag access-control with list of tags (in "" with spaces between)
# Clients using this access control element use localzones that
# are tagged with one of these tags.
# access-control-tag: 192.0.2.0/24 "tag2 tag3"
# set action for particular tag for given access control element.
# if you have multiple tag values, the tag used to lookup the action
# is the first tag match between access-control-tag and local-zone-tag
# where "first" comes from the order of the define-tag values.
# access-control-tag-action: 192.0.2.0/24 tag3 refuse
# set redirect data for particular tag for access control element
# access-control-tag-data: 192.0.2.0/24 tag2 "A 127.0.0.1"
# Set view for access control element
# access-control-view: 192.0.2.0/24 viewname
# Similar to 'access-control:' but for interfaces.
# Control which listening interfaces are allowed to accept (recursive)
# queries for this server.
# The specified interfaces should be the same as the ones specified in
# 'interface:' followed by the action.
# The actions are the same as 'access-control:' above.
# By default all the interfaces configured are refused.
# Note: any 'access-control*:' setting overrides all 'interface-*:'
# settings for targeted clients.
# interface-action: 192.0.2.153 allow
# interface-action: 192.0.2.154 allow
# interface-action: 192.0.2.154@5003 allow
# interface-action: 2001:DB8::5 allow
# interface-action: eth0@5003 allow
# Similar to 'access-control-tag:' but for interfaces.
# Tag interfaces with a list of tags (in "" with spaces between).
# Interfaces using these tags use localzones that are tagged with one
# of these tags.
# The specified interfaces should be the same as the ones specified in
# 'interface:' followed by the list of tags.
# Note: any 'access-control*:' setting overrides all 'interface-*:'
# settings for targeted clients.
# interface-tag: eth0@5003 "tag2 tag3"
# Similar to 'access-control-tag-action:' but for interfaces.
# Set action for particular tag for a given interface element.
# If you have multiple tag values, the tag used to lookup the action
# is the first tag match between interface-tag and local-zone-tag
# where "first" comes from the order of the define-tag values.
# The specified interfaces should be the same as the ones specified in
# 'interface:' followed by the tag and action.
# Note: any 'access-control*:' setting overrides all 'interface-*:'
# settings for targeted clients.
# interface-tag-action: eth0@5003 tag3 refuse
# Similar to 'access-control-tag-data:' but for interfaces.
# Set redirect data for a particular tag for an interface element.
# The specified interfaces should be the same as the ones specified in
# 'interface:' followed by the tag and the redirect data.
# Note: any 'access-control*:' setting overrides all 'interface-*:'
# settings for targeted clients.
# interface-tag-data: eth0@5003 tag2 "A 127.0.0.1"
# Similar to 'access-control-view:' but for interfaces.
# Set view for an interface element.
# The specified interfaces should be the same as the ones specified in
# 'interface:' followed by the view name.
# Note: any 'access-control*:' setting overrides all 'interface-*:'
# settings for targeted clients.
# interface-view: eth0@5003 viewname
# if given, a chroot(2) is done to the given directory.
# i.e. you can chroot to the working directory, for example,
# for extra security, but make sure all files are in that directory.
#
# If chroot is enabled, you should pass the configfile (from the
# commandline) as a full path from the original root. After the
# chroot has been performed the now defunct portion of the config
# file path is removed to be able to reread the config after a reload.
#
# All other file paths (working dir, logfile, roothints, and
# key files) can be specified in several ways:
# o as an absolute path relative to the new root.
# o as a relative path to the working directory.
# o as an absolute path relative to the original root.
# In the last case the path is adjusted to remove the unused portion.
#
# The pid file can be absolute and outside of the chroot, it is
# written just prior to performing the chroot and dropping permissions.
#
# Additionally, Unbound may need to access /dev/urandom (for entropy).
# How to do this is specific to your OS.
#
# If you give "" no chroot is performed. The path must not end in a /.
# chroot: "@UNBOUND_CHROOT_DIR@"
# if given, user privileges are dropped (after binding port),
# and the given username is assumed. Default is user "unbound".
# If you give "" no privileges are dropped.
# username: "@UNBOUND_USERNAME@"
# the working directory. The relative files in this config are
# relative to this directory. If you give "" the working directory
# is not changed.
# If you give a server: directory: dir before include: file statements
# then those includes can be relative to the working directory.
# directory: "@UNBOUND_RUN_DIR@"
# the log file, "" means log to stderr.
# Use of this option sets use-syslog to "no".
# logfile: ""
# Log to syslog(3) if yes. The log facility LOG_DAEMON is used to
# log to. If yes, it overrides the logfile.
# use-syslog: yes
# Log identity to report. if empty, defaults to the name of argv[0]
# (usually "unbound").
# log-identity: ""
# print UTC timestamp in ascii to logfile, default is epoch in seconds.
# log-time-ascii: no
# print one line with time, IP, name, type, class for every query.
# log-queries: no
# print one line per reply, with time, IP, name, type, class, rcode,
# timetoresolve, fromcache and responsesize.
# log-replies: no
# log with tag 'query' and 'reply' instead of 'info' for
# filtering log-queries and log-replies from the log.
# log-tag-queryreply: no
# log the local-zone actions, like local-zone type inform is enabled
# also for the other local zone types.
# log-local-actions: no
# print log lines that say why queries return SERVFAIL to clients.
# log-servfail: no
# the pid file. Can be an absolute path outside of chroot/work dir.
# pidfile: "@UNBOUND_PIDFILE@"
# file to read root hints from.
# get one from https://www.internic.net/domain/named.cache
# root-hints: ""
# enable to not answer id.server and hostname.bind queries.
# hide-identity: no
# enable to not answer version.server and version.bind queries.
# hide-version: no
# enable to not answer trustanchor.unbound queries.
# hide-trustanchor: no
# enable to not set the User-Agent HTTP header.
# hide-http-user-agent: no
# the identity to report. Leave "" or default to return hostname.
# identity: ""
# the version to report. Leave "" or default to return package version.
# version: ""
# NSID identity (hex string, or "ascii_somestring"). default disabled.
# nsid: "aabbccdd"
# User-Agent HTTP header to use. Leave "" or default to use package name
# and version.
# http-user-agent: ""
# the target fetch policy.
# series of integers describing the policy per dependency depth.
# The number of values in the list determines the maximum dependency
# depth the recursor will pursue before giving up. Each integer means:
# -1 : fetch all targets opportunistically,
# 0: fetch on demand,
# positive value: fetch that many targets opportunistically.
# Enclose the list of numbers between quotes ("").
# target-fetch-policy: "3 2 1 0 0"
# Harden against very small EDNS buffer sizes.
# harden-short-bufsize: yes
# Harden against unseemly large queries.
# harden-large-queries: no
# Harden against out of zone rrsets, to avoid spoofing attempts.
# harden-glue: yes
# Harden against receiving dnssec-stripped data. If you turn it
# off, failing to validate dnskey data for a trustanchor will
# trigger insecure mode for that zone (like without a trustanchor).
# Default on, which insists on dnssec data for trust-anchored zones.
# harden-dnssec-stripped: yes
# Harden against queries that fall under dnssec-signed nxdomain names.
# harden-below-nxdomain: yes
# Harden the referral path by performing additional queries for
# infrastructure data. Validates the replies (if possible).
# Default off, because the lookups burden the server. Experimental
# implementation of draft-wijngaards-dnsext-resolver-side-mitigation.
# harden-referral-path: no
# Harden against algorithm downgrade when multiple algorithms are
# advertised in the DS record. If no, allows the weakest algorithm
# 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.
# qname-minimisation: yes
# QNAME minimisation in strict mode. Do not fall-back to sending full
# QNAME to potentially broken nameservers. A lot of domains will not be
# resolvable when this option in enabled.
# This option only has effect when qname-minimisation is enabled.
# qname-minimisation-strict: no
# Aggressive NSEC uses the DNSSEC NSEC chain to synthesize NXDOMAIN
# and other denials, using information from previous NXDOMAINs answers.
# aggressive-nsec: yes
# Use 0x20-encoded random bits in the query to foil spoof attempts.
# This feature is an experimental implementation of draft dns-0x20.
# use-caps-for-id: no
# Domains (and domains in them) without support for dns-0x20 and
# the fallback fails because they keep sending different answers.
# caps-exempt: "licdn.com"
# caps-exempt: "senderbase.org"
# Enforce privacy of these addresses. Strips them away from answers.
# It may cause DNSSEC validation to additionally mark it as bogus.
# Protects against 'DNS Rebinding' (uses browser as network proxy).
# Only 'private-domain' and 'local-data' names are allowed to have
# these private addresses. No default.
# private-address: 10.0.0.0/8
# private-address: 172.16.0.0/12
# private-address: 192.168.0.0/16
# private-address: 169.254.0.0/16
# private-address: fd00::/8
# private-address: fe80::/10
# private-address: ::ffff:0:0/96
# Allow the domain (and its subdomains) to contain private addresses.
# local-data statements are allowed to contain private addresses too.
# private-domain: "example.com"
# If nonzero, unwanted replies are not only reported in statistics,
# but also a running total is kept per thread. If it reaches the
# threshold, a warning is printed and a defensive action is taken,
# the cache is cleared to flush potential poison out of it.
# A suggested value is 10000000, the default is 0 (turned off).
# unwanted-reply-threshold: 0
# Do not query the following addresses. No DNS queries are sent there.
# List one address per entry. List classless netblocks with /size,
# do-not-query-address: 127.0.0.1/8
# do-not-query-address: ::1
# if yes, the above default do-not-query-address entries are present.
# if no, localhost can be queried (for testing and debugging).
# do-not-query-localhost: yes
# if yes, perform prefetching of almost expired message cache entries.
# prefetch: no
# if yes, perform key lookups adjacent to normal lookups.
# prefetch-key: no
# deny queries of type ANY with an empty response.
# deny-any: no
# if yes, Unbound rotates RRSet order in response.
# rrset-roundrobin: yes
# if yes, Unbound doesn't insert authority/additional sections
# into response messages when those sections are not required.
# minimal-responses: yes
# true to disable DNSSEC lameness check in iterator.
# disable-dnssec-lame-check: no
# module configuration of the server. A string with identifiers
# separated by spaces. Syntax: "[dns64] [validator] iterator"
# most modules have to be listed at the beginning of the line,
# except cachedb(just before iterator), and python (at the beginning,
# or, just before the iterator).
# module-config: "validator iterator"
# File with trusted keys, kept uptodate using RFC5011 probes,
# initial file like trust-anchor-file, then it stores metadata.
# Use several entries, one per domain name, to track multiple zones.
#
# If you want to perform DNSSEC validation, run unbound-anchor before
# you start Unbound (i.e. in the system boot scripts).
# And then enable the auto-trust-anchor-file config item.
# Please note usage of unbound-anchor root anchor is at your own risk
# and under the terms of our LICENSE (see that file in the source).
# auto-trust-anchor-file: "@UNBOUND_ROOTKEY_FILE@"
# trust anchor signaling sends a RFC8145 key tag query after priming.
# trust-anchor-signaling: yes
# Root key trust anchor sentinel (draft-ietf-dnsop-kskroll-sentinel)
# root-key-sentinel: yes
# File with trusted keys for validation. Specify more than one file
# with several entries, one file per entry.
# Zone file format, with DS and DNSKEY entries.
# Note this gets out of date, use auto-trust-anchor-file please.
# trust-anchor-file: ""
# Trusted key for validation. DS or DNSKEY. specify the RR on a
# single line, surrounded by "". TTL is ignored. class is IN default.
# Note this gets out of date, use auto-trust-anchor-file please.
# (These examples are from August 2007 and may not be valid anymore).
# trust-anchor: "nlnetlabs.nl. DNSKEY 257 3 5 AQPzzTWMz8qSWIQlfRnPckx2BiVmkVN6LPupO3mbz7FhLSnm26n6iG9N Lby97Ji453aWZY3M5/xJBSOS2vWtco2t8C0+xeO1bc/d6ZTy32DHchpW 6rDH1vp86Ll+ha0tmwyy9QP7y2bVw5zSbFCrefk8qCUBgfHm9bHzMG1U BYtEIQ=="
# trust-anchor: "jelte.nlnetlabs.nl. DS 42860 5 1 14D739EB566D2B1A5E216A0BA4D17FA9B038BE4A"
# File with trusted keys for validation. Specify more than one file
# with several entries, one file per entry. Like trust-anchor-file
# but has a different file format. Format is BIND-9 style format,
# the trusted-keys { name flag proto algo "key"; }; clauses are read.
# you need external update procedures to track changes in keys.
# trusted-keys-file: ""
# Ignore chain of trust. Domain is treated as insecure.
# domain-insecure: "example.com"
# Override the date for validation with a specific fixed date.
# Do not set this unless you are debugging signature inception
# and expiration. "" or "0" turns the feature off. -1 ignores date.
# val-override-date: ""
# The time to live for bogus data, rrsets and messages. This avoids
# some of the revalidation, until the time interval expires. in secs.
# val-bogus-ttl: 60
# The signature inception and expiration dates are allowed to be off
# by 10% of the signature lifetime (expir-incep) from our local clock.
# This leeway is capped with a minimum and a maximum. In seconds.
# val-sig-skew-min: 3600
# val-sig-skew-max: 86400
# The maximum number the validator should restart validation with
# another authority in case of failed validation.
# val-max-restart: 5
# Should additional section of secure message also be kept clean of
# unsecure data. Useful to shield the users of this validator from
# potential bogus data in the additional section. All unsigned data
# in the additional section is removed from secure messages.
# val-clean-additional: yes
# Turn permissive mode on to permit bogus messages. Thus, messages
# for which security checks failed will be returned to clients,
# instead of SERVFAIL. It still performs the security checks, which
# result in interesting log files and possibly the AD bit in
# replies if the message is found secure. The default is off.
# val-permissive-mode: no
# Ignore the CD flag in incoming queries and refuse them bogus data.
# Enable it if the only clients of Unbound are legacy servers (w2008)
# that set CD but cannot validate themselves.
# ignore-cd-flag: no
+ # Disable the DO flag in outgoing requests. It is helpful for upstream
+ # devices that cannot handle DNSSEC information. But do not enable it
+ # otherwise, because it would stop DNSSEC validation.
+ # disable-edns-do: no
+
# Serve expired responses from cache, with serve-expired-reply-ttl in
# the response, and then attempt to fetch the data afresh.
# serve-expired: no
#
# Limit serving of expired responses to configured seconds after
# expiration. 0 disables the limit.
# serve-expired-ttl: 0
#
# Set the TTL of expired records to the serve-expired-ttl value after a
# failed attempt to retrieve the record from upstream. This makes sure
# that the expired records will be served as long as there are queries
# for it.
# serve-expired-ttl-reset: no
#
# TTL value to use when replying with expired data.
# serve-expired-reply-ttl: 30
#
# Time in milliseconds before replying to the client with expired data.
# This essentially enables the serve-stale behavior as specified in
# RFC 8767 that first tries to resolve before
# immediately responding with expired data. 0 disables this behavior.
# A recommended value is 1800.
# serve-expired-client-timeout: 0
# Return the original TTL as received from the upstream name server rather
# than the decrementing TTL as stored in the cache. Enabling this feature
# does not impact cache expiry, it only changes the TTL Unbound embeds in
# responses to queries. Note that enabling this feature implicitly disables
# enforcement of the configured minimum and maximum TTL.
# serve-original-ttl: no
# Have the validator log failed validations for your diagnosis.
# 0: off. 1: A line per failed user query. 2: With reason and bad IP.
# val-log-level: 0
# It is possible to configure NSEC3 maximum iteration counts per
# keysize. Keep this table very short, as linear search is done.
# A message with an NSEC3 with larger count is marked insecure.
# List in ascending order the keysize and count values.
# val-nsec3-keysize-iterations: "1024 150 2048 150 4096 150"
# if enabled, ZONEMD verification failures do not block the zone.
# zonemd-permissive-mode: no
# instruct the auto-trust-anchor-file probing to add anchors after ttl.
# add-holddown: 2592000 # 30 days
# instruct the auto-trust-anchor-file probing to del anchors after ttl.
# del-holddown: 2592000 # 30 days
# auto-trust-anchor-file probing removes missing anchors after ttl.
# If the value 0 is given, missing anchors are not removed.
# keep-missing: 31622400 # 366 days
# debug option that allows very small holddown times for key rollover,
# otherwise the RFC mandates probe intervals must be at least 1 hour.
# permit-small-holddown: no
# the amount of memory to use for the key cache.
# plain value in bytes or you can append k, m or G. default is "4Mb".
# key-cache-size: 4m
# the number of slabs to use for the key cache.
# the number of slabs must be a power of 2.
# more slabs reduce lock contention, but fragment memory usage.
# key-cache-slabs: 4
# the amount of memory to use for the negative cache.
# plain value in bytes or you can append k, m or G. default is "1Mb".
# neg-cache-size: 1m
# By default, for a number of zones a small default 'nothing here'
# reply is built-in. Query traffic is thus blocked. If you
# wish to serve such zone you can unblock them by uncommenting one
# of the nodefault statements below.
# You may also have to use domain-insecure: zone to make DNSSEC work,
# unless you have your own trust anchors for this zone.
# local-zone: "localhost." nodefault
# local-zone: "127.in-addr.arpa." nodefault
# local-zone: "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa." nodefault
# local-zone: "home.arpa." nodefault
# local-zone: "onion." nodefault
# local-zone: "test." nodefault
# local-zone: "invalid." nodefault
# local-zone: "10.in-addr.arpa." nodefault
# local-zone: "16.172.in-addr.arpa." nodefault
# local-zone: "17.172.in-addr.arpa." nodefault
# local-zone: "18.172.in-addr.arpa." nodefault
# local-zone: "19.172.in-addr.arpa." nodefault
# local-zone: "20.172.in-addr.arpa." nodefault
# local-zone: "21.172.in-addr.arpa." nodefault
# local-zone: "22.172.in-addr.arpa." nodefault
# local-zone: "23.172.in-addr.arpa." nodefault
# local-zone: "24.172.in-addr.arpa." nodefault
# local-zone: "25.172.in-addr.arpa." nodefault
# local-zone: "26.172.in-addr.arpa." nodefault
# local-zone: "27.172.in-addr.arpa." nodefault
# local-zone: "28.172.in-addr.arpa." nodefault
# local-zone: "29.172.in-addr.arpa." nodefault
# local-zone: "30.172.in-addr.arpa." nodefault
# local-zone: "31.172.in-addr.arpa." nodefault
# local-zone: "168.192.in-addr.arpa." nodefault
# local-zone: "0.in-addr.arpa." nodefault
# local-zone: "254.169.in-addr.arpa." nodefault
# local-zone: "2.0.192.in-addr.arpa." nodefault
# local-zone: "100.51.198.in-addr.arpa." nodefault
# local-zone: "113.0.203.in-addr.arpa." nodefault
# local-zone: "255.255.255.255.in-addr.arpa." nodefault
# local-zone: "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa." nodefault
# local-zone: "d.f.ip6.arpa." nodefault
# local-zone: "8.e.f.ip6.arpa." nodefault
# local-zone: "9.e.f.ip6.arpa." nodefault
# local-zone: "a.e.f.ip6.arpa." nodefault
# local-zone: "b.e.f.ip6.arpa." nodefault
# local-zone: "8.b.d.0.1.0.0.2.ip6.arpa." nodefault
# And for 64.100.in-addr.arpa. to 127.100.in-addr.arpa.
# Add example.com into ipset
# local-zone: "example.com" ipset
# If Unbound is running service for the local host then it is useful
# to perform lan-wide lookups to the upstream, and unblock the
# long list of local-zones above. If this Unbound is a dns server
# for a network of computers, disabled is better and stops information
# leakage of local lan information.
# unblock-lan-zones: no
# The insecure-lan-zones option disables validation for
# these zones, as if they were all listed as domain-insecure.
# insecure-lan-zones: no
# a number of locally served zones can be configured.
# local-zone: <zone> <type>
# local-data: "<resource record string>"
# o deny serves local data (if any), else, drops queries.
# o refuse serves local data (if any), else, replies with error.
# o static serves local data, else, nxdomain or nodata answer.
# o transparent gives local data, but resolves normally for other names
# o redirect serves the zone data for any subdomain in the zone.
# o nodefault can be used to normally resolve AS112 zones.
# o typetransparent resolves normally for other types and other names
# o inform acts like transparent, but logs client IP address
# o inform_deny drops queries and logs client IP address
# o inform_redirect redirects queries and logs client IP address
# 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.
#
# defaults are localhost address, reverse for 127.0.0.1 and ::1
# and nxdomain for AS112 zones. If you configure one of these zones
# the default content is omitted, or you can omit it with 'nodefault'.
#
# If you configure local-data without specifying local-zone, by
# default a transparent local-zone is created for the data.
#
# You can add locally served data with
# local-zone: "local." static
# local-data: "mycomputer.local. IN A 192.0.2.51"
# local-data: 'mytext.local TXT "content of text record"'
#
# You can override certain queries with
# local-data: "adserver.example.com A 127.0.0.1"
#
# You can redirect a domain to a fixed address with
# (this makes example.com, www.example.com, etc, all go to 192.0.2.3)
# local-zone: "example.com" redirect
# local-data: "example.com A 192.0.2.3"
#
# Shorthand to make PTR records, "IPv4 name" or "IPv6 name".
# You can also add PTR records using local-data directly, but then
# you need to do the reverse notation yourself.
# local-data-ptr: "192.0.2.3 www.example.com"
# tag a localzone with a list of tag names (in "" with spaces between)
# local-zone-tag: "example.com" "tag2 tag3"
# add a netblock specific override to a localzone, with zone type
# local-zone-override: "example.com" 192.0.2.0/24 refuse
# service clients over TLS (on the TCP sockets) with plain DNS inside
# the TLS stream, and over HTTPS using HTTP/2 as specified in RFC8484.
# Give the certificate to use and private key.
# default is "" (disabled). requires restart to take effect.
# tls-service-key: "path/to/privatekeyfile.key"
# tls-service-pem: "path/to/publiccertfile.pem"
# tls-port: 853
# https-port: 443
# cipher setting for TLSv1.2
# tls-ciphers: "DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256"
# cipher setting for TLSv1.3
# tls-ciphersuites: "TLS_AES_128_GCM_SHA256:TLS_AES_128_CCM_8_SHA256:TLS_AES_128_CCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256"
# Pad responses to padded queries received over TLS
# pad-responses: yes
# Padded responses will be padded to the closest multiple of this size.
# pad-responses-block-size: 468
# Use the SNI extension for TLS connections. Default is yes.
# Changing the value requires a reload.
# tls-use-sni: yes
# Add the secret file for TLS Session Ticket.
# Secret file must be 80 bytes of random data.
# First key use to encrypt and decrypt TLS session tickets.
# Other keys use to decrypt only.
# requires restart to take effect.
# tls-session-ticket-keys: "path/to/secret_file1"
# tls-session-ticket-keys: "path/to/secret_file2"
# request upstream over TLS (with plain DNS inside the TLS stream).
# Default is no. Can be turned on and off with unbound-control.
# tls-upstream: no
# Certificates used to authenticate connections made upstream.
# tls-cert-bundle: ""
# Add system certs to the cert bundle, from the Windows Cert Store
# tls-win-cert: no
# and on other systems, the default openssl certificates
# tls-system-cert: no
# Pad queries over TLS upstreams
# pad-queries: yes
# Padded queries will be padded to the closest multiple of this size.
# pad-queries-block-size: 128
# Also serve tls on these port numbers (eg. 443, ...), by listing
# tls-additional-port: portno for each of the port numbers.
# HTTP endpoint to provide DNS-over-HTTPS service on.
# http-endpoint: "/dns-query"
# HTTP/2 SETTINGS_MAX_CONCURRENT_STREAMS value to use.
# http-max-streams: 100
# Maximum number of bytes used for all HTTP/2 query buffers.
# http-query-buffer-size: 4m
# Maximum number of bytes used for all HTTP/2 response buffers.
# http-response-buffer-size: 4m
# Set TCP_NODELAY socket option on sockets used for DNS-over-HTTPS
# service.
# http-nodelay: yes
# Disable TLS for DNS-over-HTTP downstream service.
# http-notls-downstream: no
# The interfaces that use these listed port numbers will support and
# expect PROXYv2. For UDP and TCP/TLS interfaces.
# proxy-protocol-port: portno for each of the port numbers.
# DNS64 prefix. Must be specified when DNS64 is use.
# Enable dns64 in module-config. Used to synthesize IPv6 from IPv4.
# dns64-prefix: 64:ff9b::0/96
# DNS64 ignore AAAA records for these domains and use A instead.
# dns64-ignore-aaaa: "example.com"
# ratelimit for uncached, new queries, this limits recursion effort.
# ratelimiting is experimental, and may help against randomqueryflood.
# if 0(default) it is disabled, otherwise state qps allowed per zone.
# ratelimit: 0
# ratelimits are tracked in a cache, size in bytes of cache (or k,m).
# ratelimit-size: 4m
# ratelimit cache slabs, reduces lock contention if equal to cpucount.
# ratelimit-slabs: 4
# 0 blocks when ratelimited, otherwise let 1/xth traffic through
# ratelimit-factor: 10
# Aggressive rate limit when the limit is reached and until demand has
# decreased in a 2 second rate window.
# ratelimit-backoff: no
# override the ratelimit for a specific domain name.
# give this setting multiple times to have multiple overrides.
# ratelimit-for-domain: example.com 1000
# override the ratelimits for all domains below a domain name
# can give this multiple times, the name closest to the zone is used.
# ratelimit-below-domain: com 1000
# global query ratelimit for all ip addresses.
# feature is experimental.
# if 0(default) it is disabled, otherwise states qps allowed per ip address
# ip-ratelimit: 0
# ip ratelimits are tracked in a cache, size in bytes of cache (or k,m).
# ip-ratelimit-size: 4m
# ip ratelimit cache slabs, reduces lock contention if equal to cpucount.
# ip-ratelimit-slabs: 4
# 0 blocks when ip is ratelimited, otherwise let 1/xth traffic through
# ip-ratelimit-factor: 10
# Aggressive rate limit when the limit is reached and until demand has
# decreased in a 2 second rate window.
# ip-ratelimit-backoff: no
# Limit the number of connections simultaneous from a netblock
# tcp-connection-limit: 192.0.2.0/24 12
# select from the fastest servers this many times out of 1000. 0 means
# the fast server select is disabled. prefetches are not sped up.
# fast-server-permil: 0
# the number of servers that will be used in the fast server selection.
# fast-server-num: 3
# Enable to attach Extended DNS Error codes (RFC8914) to responses.
# ede: no
# Enable to attach an Extended DNS Error (RFC8914) Code 3 - Stale
# Answer as EDNS0 option to expired responses.
# Note that the ede option above needs to be enabled for this to work.
# ede-serve-expired: no
# Specific options for ipsecmod. Unbound needs to be configured with
# --enable-ipsecmod for these to take effect.
#
# Enable or disable ipsecmod (it still needs to be defined in
# module-config above). Can be used when ipsecmod needs to be
# enabled/disabled via remote-control(below).
# ipsecmod-enabled: yes
#
# Path to executable external hook. It must be defined when ipsecmod is
# listed in module-config (above).
# ipsecmod-hook: "./my_executable"
#
# When enabled Unbound will reply with SERVFAIL if the return value of
# the ipsecmod-hook is not 0.
# ipsecmod-strict: no
#
# Maximum time to live (TTL) for cached A/AAAA records with IPSECKEY.
# ipsecmod-max-ttl: 3600
#
# Reply with A/AAAA even if the relevant IPSECKEY is bogus. Mainly used for
# testing.
# ipsecmod-ignore-bogus: no
#
# Domains for which ipsecmod will be triggered. If not defined (default)
# all domains are treated as being allowed.
# ipsecmod-allow: "example.com"
# ipsecmod-allow: "nlnetlabs.nl"
# Timeout for REUSE entries in milliseconds.
# tcp-reuse-timeout: 60000
# Max number of queries on a reuse connection.
# max-reuse-tcp-queries: 200
# Timeout in milliseconds for TCP queries to auth servers.
# tcp-auth-query-timeout: 3000
# Python config section. To enable:
# o use --with-pythonmodule to configure before compiling.
# o list python in the module-config string (above) to enable.
# It can be at the start, it gets validated results, or just before
# the iterator and process before DNSSEC validation.
# o and give a python-script to run.
python:
# Script file to load
# python-script: "@UNBOUND_SHARE_DIR@/ubmodule-tst.py"
# Dynamic library config section. To enable:
# o use --with-dynlibmodule to configure before compiling.
# o list dynlib in the module-config string (above) to enable.
# It can be placed anywhere, the dynlib module is only a very thin wrapper
# to load modules dynamically.
# o and give a dynlib-file to run. If more than one dynlib entry is listed in
# the module-config then you need one dynlib-file per instance.
dynlib:
# Script file to load
# dynlib-file: "@UNBOUND_SHARE_DIR@/dynlib.so"
# Remote control config section.
remote-control:
# Enable remote control with unbound-control(8) here.
# set up the keys and certificates with unbound-control-setup.
# control-enable: no
# what interfaces are listened to for remote control.
# give 0.0.0.0 and ::0 to listen to all interfaces.
# set to an absolute path to use a unix local name pipe, certificates
# are not used for that, so key and cert files need not be present.
# control-interface: 127.0.0.1
# control-interface: ::1
# port number for remote control operations.
# control-port: 8953
# for localhost, you can disable use of TLS by setting this to "no"
# For local sockets this option is ignored, and TLS is not used.
# control-use-cert: "yes"
# Unbound server key file.
# server-key-file: "@UNBOUND_RUN_DIR@/unbound_server.key"
# Unbound server certificate file.
# server-cert-file: "@UNBOUND_RUN_DIR@/unbound_server.pem"
# unbound-control key file.
# control-key-file: "@UNBOUND_RUN_DIR@/unbound_control.key"
# unbound-control certificate file.
# control-cert-file: "@UNBOUND_RUN_DIR@/unbound_control.pem"
# Stub zones.
# Create entries like below, to make all queries for 'example.com' and
# 'example.org' go to the given list of nameservers. list zero or more
# nameservers by hostname or by ipaddress. If you set stub-prime to yes,
# the list is treated as priming hints (default is no).
# With stub-first yes, it attempts without the stub if it fails.
# Consider adding domain-insecure: name and local-zone: name nodefault
# to the server: section if the stub is a locally served zone.
# stub-zone:
# name: "example.com"
# stub-addr: 192.0.2.68
# stub-prime: no
# stub-first: no
# stub-tcp-upstream: no
# stub-tls-upstream: no
# stub-no-cache: no
# stub-zone:
# name: "example.org"
# stub-host: ns.example.com.
# Forward zones
# Create entries like below, to make all queries for 'example.com' and
# 'example.org' go to the given list of servers. These servers have to handle
# recursion to other nameservers. List zero or more nameservers by hostname
# or by ipaddress. Use an entry with name "." to forward all queries.
# If you enable forward-first, it attempts without the forward if it fails.
# forward-zone:
# name: "example.com"
# forward-addr: 192.0.2.68
# forward-addr: 192.0.2.73@5355 # forward to port 5355.
# forward-first: no
# forward-tcp-upstream: no
# forward-tls-upstream: no
# forward-no-cache: no
# forward-zone:
# name: "example.org"
# forward-host: fwd.example.com
# Authority zones
# The data for these zones is kept locally, from a file or downloaded.
# The data can be served to downstream clients, or used instead of the
# upstream (which saves a lookup to the upstream). The first example
# has a copy of the root for local usage. The second serves example.org
# authoritatively. zonefile: reads from file (and writes to it if you also
# download it), primary: fetches with AXFR and IXFR, or url to zonefile.
# With allow-notify: you can give additional (apart from primaries and urls)
# sources of notifies.
# auth-zone:
# name: "."
# primary: 199.9.14.201 # b.root-servers.net
# primary: 192.33.4.12 # c.root-servers.net
# primary: 199.7.91.13 # d.root-servers.net
# primary: 192.5.5.241 # f.root-servers.net
# primary: 192.112.36.4 # g.root-servers.net
# primary: 193.0.14.129 # k.root-servers.net
# primary: 192.0.47.132 # xfr.cjr.dns.icann.org
# primary: 192.0.32.132 # xfr.lax.dns.icann.org
# primary: 2001:500:200::b # b.root-servers.net
# primary: 2001:500:2::c # c.root-servers.net
# primary: 2001:500:2d::d # d.root-servers.net
# primary: 2001:500:2f::f # f.root-servers.net
# primary: 2001:500:12::d0d # g.root-servers.net
# primary: 2001:7fd::1 # k.root-servers.net
# primary: 2620:0:2830:202::132 # xfr.cjr.dns.icann.org
# primary: 2620:0:2d0:202::132 # xfr.lax.dns.icann.org
# fallback-enabled: yes
# for-downstream: no
# for-upstream: yes
# auth-zone:
# name: "example.org"
# for-downstream: yes
# for-upstream: yes
# zonemd-check: no
# zonemd-reject-absence: no
# zonefile: "example.org.zone"
# Views
# Create named views. Name must be unique. Map views to requests using
# the access-control-view option. Views can contain zero or more local-zone
# and local-data options. Options from matching views will override global
# options. Global options will be used if no matching view is found.
# With view-first yes, it will try to answer using the global local-zone and
# local-data elements if there is no view specific match.
# view:
# name: "viewname"
# local-zone: "example.com" redirect
# local-data: "example.com A 192.0.2.3"
# local-data-ptr: "192.0.2.3 www.example.com"
# view-first: no
# view:
# name: "anotherview"
# local-zone: "example.com" refuse
# DNSCrypt
# To enable, use --enable-dnscrypt to configure before compiling.
# Caveats:
# 1. the keys/certs cannot be produced by Unbound. You can use dnscrypt-wrapper
# for this: https://github.com/cofyc/dnscrypt-wrapper/blob/master/README.md#usage
# 2. dnscrypt channel attaches to an interface. you MUST set interfaces to
# listen on `dnscrypt-port` with the follo0wing snippet:
# server:
# interface: 0.0.0.0@443
# interface: ::0@443
#
# Finally, `dnscrypt` config has its own section.
# dnscrypt:
# dnscrypt-enable: yes
# dnscrypt-port: 443
# dnscrypt-provider: 2.dnscrypt-cert.example.com.
# dnscrypt-secret-key: /path/unbound-conf/keys1/1.key
# dnscrypt-secret-key: /path/unbound-conf/keys2/1.key
# dnscrypt-provider-cert: /path/unbound-conf/keys1/1.cert
# dnscrypt-provider-cert: /path/unbound-conf/keys2/1.cert
# CacheDB
# External backend DB as auxiliary cache.
# To enable, use --enable-cachedb to configure before compiling.
# Specify the backend name
# (default is "testframe", which has no use other than for debugging and
# testing) and backend-specific options. The 'cachedb' module must be
# included in module-config, just before the iterator module.
# cachedb:
# backend: "testframe"
# # secret seed string to calculate hashed keys
# secret-seed: "default"
+# # if the backend should be read from, but not written to.
+# cachedb-no-store: no
#
# # For "redis" backend:
# # (to enable, use --with-libhiredis to configure before compiling)
# # redis server's IP address or host name
# 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
# redis-expire-records: no
+# # redis logical database to use, 0 is the default database.
+# redis-logical-db: 0
# IPSet
# Add specify domain into set via ipset.
# To enable:
# o use --enable-ipset to configure before compiling;
# o Unbound then needs to run as root user.
# ipset:
# # set name for ip v4 addresses
# name-v4: "list-v4"
# # set name for ip v6 addresses
# name-v6: "list-v6"
#
# Dnstap logging support, if compiled in by using --enable-dnstap to configure.
# To enable, set the dnstap-enable to yes and also some of
# dnstap-log-..-messages to yes. And select an upstream log destination, by
# socket path, TCP or TLS destination.
# dnstap:
# dnstap-enable: no
# # if set to yes frame streams will be used in bidirectional mode
# dnstap-bidirectional: yes
# dnstap-socket-path: "@DNSTAP_SOCKET_PATH@"
# # if "" use the unix socket in dnstap-socket-path, otherwise,
# # set it to "IPaddress[@port]" of the destination.
# dnstap-ip: ""
# # if set to yes if you want to use TLS to dnstap-ip, no for TCP.
# dnstap-tls: yes
# # name for authenticating the upstream server. or "" disabled.
# dnstap-tls-server-name: ""
# # if "", it uses the cert bundle from the main Unbound config.
# dnstap-tls-cert-bundle: ""
# # key file for client authentication, or "" disabled.
# dnstap-tls-client-key-file: ""
# # cert file for client authentication, or "" disabled.
# dnstap-tls-client-cert-file: ""
# dnstap-send-identity: no
# dnstap-send-version: no
# # if "" it uses the hostname.
# dnstap-identity: ""
# # if "" it uses the package version.
# dnstap-version: ""
# dnstap-log-resolver-query-messages: no
# dnstap-log-resolver-response-messages: no
# dnstap-log-client-query-messages: no
# dnstap-log-client-response-messages: no
# dnstap-log-forwarder-query-messages: no
# dnstap-log-forwarder-response-messages: no
# Response Policy Zones
# RPZ policies. Applied in order of configuration. QNAME, Response IP
# Address, nsdname, nsip and clientip triggers are supported. Supported
# actions are: NXDOMAIN, NODATA, PASSTHRU, DROP, Local Data, tcp-only
# and drop. Policies can be loaded from a file, or using zone
# transfer, or using HTTP. The respip module needs to be added
# to the module-config, e.g.: module-config: "respip validator iterator".
# rpz:
# name: "rpz.example.com"
# zonefile: "rpz.example.com"
# primary: 192.0.2.0
# allow-notify: 192.0.2.0/32
# url: http://www.example.com/rpz.example.org.zone
# rpz-action-override: cname
# rpz-cname-override: www.example.org
# rpz-log: yes
# rpz-log-name: "example policy"
# rpz-signal-nxdomain-ra: no
# for-downstream: no
# tags: "example"
diff --git a/contrib/unbound/doc/libunbound.3.in b/contrib/unbound/doc/libunbound.3.in
index 429ac93407fd..fa090d58186f 100644
--- a/contrib/unbound/doc/libunbound.3.in
+++ b/contrib/unbound/doc/libunbound.3.in
@@ -1,434 +1,434 @@
-.TH "libunbound" "3" "Aug 30, 2023" "NLnet Labs" "unbound 1.18.0"
+.TH "libunbound" "3" "Nov 8, 2023" "NLnet Labs" "unbound 1.19.0"
.\"
.\" libunbound.3 -- unbound library functions manual
.\"
.\" Copyright (c) 2007, NLnet Labs. All rights reserved.
.\"
.\" See LICENSE for the license.
.\"
.\"
.SH "NAME"
.B libunbound,
.B unbound.h,
.B ub_ctx,
.B ub_result,
.B ub_callback_type,
.B ub_ctx_create,
.B ub_ctx_delete,
.B ub_ctx_set_option,
.B ub_ctx_get_option,
.B ub_ctx_config,
.B ub_ctx_set_fwd,
.B ub_ctx_set_stub,
.B ub_ctx_set_tls,
.B ub_ctx_resolvconf,
.B ub_ctx_hosts,
.B ub_ctx_add_ta,
.B ub_ctx_add_ta_autr,
.B ub_ctx_add_ta_file,
.B ub_ctx_trustedkeys,
.B ub_ctx_debugout,
.B ub_ctx_debuglevel,
.B ub_ctx_async,
.B ub_poll,
.B ub_wait,
.B ub_fd,
.B ub_process,
.B ub_resolve,
.B ub_resolve_async,
.B ub_cancel,
.B ub_resolve_free,
.B ub_strerror,
.B ub_ctx_print_local_zones,
.B ub_ctx_zone_add,
.B ub_ctx_zone_remove,
.B ub_ctx_data_add,
.B ub_ctx_data_remove
-\- Unbound DNS validating resolver 1.18.0 functions.
+\- Unbound DNS validating resolver 1.19.0 functions.
.SH "SYNOPSIS"
.B #include <unbound.h>
.LP
\fIstruct ub_ctx *\fR
\fBub_ctx_create\fR(\fIvoid\fR);
.LP
\fIvoid\fR
\fBub_ctx_delete\fR(\fIstruct ub_ctx*\fR ctx);
.LP
\fIint\fR
\fBub_ctx_set_option\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR opt, \fIchar*\fR val);
.LP
\fIint\fR
\fBub_ctx_get_option\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR opt, \fIchar**\fR val);
.LP
\fIint\fR
\fBub_ctx_config\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR fname);
.LP
\fIint\fR
\fBub_ctx_set_fwd\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR addr);
.LP
\fIint\fR
\fBub_ctx_set_stub\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR zone,
\fIchar*\fR addr,
.br
\fIint\fR isprime);
.LP
\fIint\fR
\fBub_ctx_set_tls\fR(\fIstruct ub_ctx*\fR ctx, \fIint\fR tls);
.LP
\fIint\fR
\fBub_ctx_resolvconf\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR fname);
.LP
\fIint\fR
\fBub_ctx_hosts\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR fname);
.LP
\fIint\fR
\fBub_ctx_add_ta\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR ta);
.LP
\fIint\fR
\fBub_ctx_add_ta_autr\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR fname);
.LP
\fIint\fR
\fBub_ctx_add_ta_file\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR fname);
.LP
\fIint\fR
\fBub_ctx_trustedkeys\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR fname);
.LP
\fIint\fR
\fBub_ctx_debugout\fR(\fIstruct ub_ctx*\fR ctx, \fIFILE*\fR out);
.LP
\fIint\fR
\fBub_ctx_debuglevel\fR(\fIstruct ub_ctx*\fR ctx, \fIint\fR d);
.LP
\fIint\fR
\fBub_ctx_async\fR(\fIstruct ub_ctx*\fR ctx, \fIint\fR dothread);
.LP
\fIint\fR
\fBub_poll\fR(\fIstruct ub_ctx*\fR ctx);
.LP
\fIint\fR
\fBub_wait\fR(\fIstruct ub_ctx*\fR ctx);
.LP
\fIint\fR
\fBub_fd\fR(\fIstruct ub_ctx*\fR ctx);
.LP
\fIint\fR
\fBub_process\fR(\fIstruct ub_ctx*\fR ctx);
.LP
\fIint\fR
\fBub_resolve\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR name,
.br
\fIint\fR rrtype, \fIint\fR rrclass, \fIstruct ub_result**\fR result);
.LP
\fIint\fR
\fBub_resolve_async\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR name,
.br
\fIint\fR rrtype, \fIint\fR rrclass, \fIvoid*\fR mydata,
.br
\fIub_callback_type\fR callback, \fIint*\fR async_id);
.LP
\fIint\fR
\fBub_cancel\fR(\fIstruct ub_ctx*\fR ctx, \fIint\fR async_id);
.LP
\fIvoid\fR
\fBub_resolve_free\fR(\fIstruct ub_result*\fR result);
.LP
\fIconst char *\fR
\fBub_strerror\fR(\fIint\fR err);
.LP
\fIint\fR
\fBub_ctx_print_local_zones\fR(\fIstruct ub_ctx*\fR ctx);
.LP
\fIint\fR
\fBub_ctx_zone_add\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR zone_name, \fIchar*\fR zone_type);
.LP
\fIint\fR
\fBub_ctx_zone_remove\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR zone_name);
.LP
\fIint\fR
\fBub_ctx_data_add\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR data);
.LP
\fIint\fR
\fBub_ctx_data_remove\fR(\fIstruct ub_ctx*\fR ctx, \fIchar*\fR data);
.SH "DESCRIPTION"
.B Unbound
is an implementation of a DNS resolver, that does caching and
DNSSEC validation. This is the library API, for using the \-lunbound library.
The server daemon is described in \fIunbound\fR(8).
The library works independent from a running unbound server, and
can be used to convert hostnames to ip addresses, and back,
and obtain other information from the DNS. The library performs public\-key
validation of results with DNSSEC.
.P
The library uses a variable of type \fIstruct ub_ctx\fR to keep context
between calls. The user must maintain it, creating it with
.B ub_ctx_create
and deleting it with
.B ub_ctx_delete\fR.
It can be created and deleted at any time. Creating it anew removes any
previous configuration (such as trusted keys) and clears any cached results.
.P
The functions are thread\-safe, and a context can be used in a threaded (as
well as in a non\-threaded) environment. Also resolution (and validation)
can be performed blocking and non\-blocking (also called asynchronous).
The async method returns from the call immediately, so that processing
can go on, while the results become available later.
.P
The functions are discussed in turn below.
.SH "FUNCTIONS"
.TP
.B ub_ctx_create
Create a new context, initialised with defaults.
The information from /etc/resolv.conf and /etc/hosts is not utilised
by default. Use
.B ub_ctx_resolvconf
and
.B ub_ctx_hosts
to read them.
Before you call this, use the openssl functions CRYPTO_set_id_callback and
CRYPTO_set_locking_callback to set up asynchronous operation if you use
lib openssl (the application calls these functions once for initialisation).
Openssl 1.0.0 or later uses the CRYPTO_THREADID_set_callback function.
.TP
.B ub_ctx_delete
Delete validation context and free associated resources.
Outstanding async queries are killed and callbacks are not called for them.
.TP
.B ub_ctx_set_option
A power\-user interface that lets you specify one of the options from the
config file format, see \fIunbound.conf\fR(5). Not all options are
relevant. For some specific options, such as adding trust anchors, special
routines exist. Pass the option name with the trailing ':'.
.TP
.B ub_ctx_get_option
A power\-user interface that gets an option value. Some options cannot be
gotten, and others return a newline separated list. Pass the option name
without trailing ':'. The returned value must be free(2)d by the caller.
.TP
.B ub_ctx_config
A power\-user interface that lets you specify an unbound config file, see
\fIunbound.conf\fR(5), which is read for configuration. Not all options are
relevant. For some specific options, such as adding trust anchors, special
routines exist. This function is thread\-safe only if a single instance of
ub_ctx* exists in the application. If several instances exist the
application has to ensure that ub_ctx_config is not called in parallel by
the different instances.
.TP
.B ub_ctx_set_fwd
Set machine to forward DNS queries to, the caching resolver to use.
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.
At this time it is only possible to set configuration before the
first resolve is done.
.TP
.B ub_ctx_set_stub
Set a stub zone, authoritative dns servers to use for a particular zone.
IP4 or IP6 address. If the address is NULL the stub entry is removed.
Set isprime true if you configure root hints with it. Otherwise similar to
the stub zone item from unbound's config file. Can be called several times,
for different zones, or to add multiple addresses for a particular zone.
At this time it is only possible to set configuration before the
first resolve is done.
.TP
.B ub_ctx_set_tls
Enable DNS over TLS (DoT) for machines set with
.B ub_ctx_set_fwd.
At this time it is only possible to set configuration before the
first resolve is done.
.TP
.B ub_ctx_resolvconf
By default the root servers are queried and full resolver mode is used, but
you can use this call to read the list of nameservers to use from the
filename given.
Usually "/etc/resolv.conf". Uses those nameservers as caching proxies.
If they do not support DNSSEC, validation may fail.
Only nameservers are picked up, the searchdomain, ndots and other
settings from \fIresolv.conf\fR(5) are ignored.
If fname NULL is passed, "/etc/resolv.conf" is used (if on Windows,
the system\-wide configured nameserver is picked instead).
At this time it is only possible to set configuration before the
first resolve is done.
.TP
.B ub_ctx_hosts
Read list of hosts from the filename given.
Usually "/etc/hosts". When queried for, these addresses are not marked
DNSSEC secure. If fname NULL is passed, "/etc/hosts" is used
(if on Windows, etc/hosts from WINDIR is picked instead).
At this time it is only possible to set configuration before the
first resolve is done.
.TP
.B
ub_ctx_add_ta
Add a trust anchor to the given context.
At this time it is only possible to add trusted keys before the
first resolve is done.
The format is a string, similar to the zone\-file format,
[domainname] [type] [rdata contents]. Both DS and DNSKEY records are accepted.
.TP
.B ub_ctx_add_ta_autr
Add filename with automatically tracked trust anchor to the given context.
Pass name of a file with the managed trust anchor. You can create this
file with \fIunbound\-anchor\fR(8) for the root anchor. You can also
create it with an initial file with one line with a DNSKEY or DS record.
If the file is writable, it is updated when the trust anchor changes.
At this time it is only possible to add trusted keys before the
first resolve is done.
.TP
.B ub_ctx_add_ta_file
Add trust anchors to the given context.
Pass name of a file with DS and DNSKEY records in zone file format.
At this time it is only possible to add trusted keys before the
first resolve is done.
.TP
.B ub_ctx_trustedkeys
Add trust anchors to the given context.
Pass the name of a bind\-style config file with trusted\-keys{}.
At this time it is only possible to add trusted keys before the
first resolve is done.
.TP
.B ub_ctx_debugout
Set debug and error log output to the given stream. Pass NULL to disable
output. Default is stderr. File\-names or using syslog can be enabled
using config options, this routine is for using your own stream.
.TP
.B ub_ctx_debuglevel
Set debug verbosity for the context. Output is directed to stderr.
Higher debug level gives more output.
.TP
.B ub_ctx_async
Set a context behaviour for asynchronous action.
if set to true, enables threading and a call to
.B ub_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
.B ub_resolve_async
calls have been made have no effect (delete and re\-create the context
to change).
.TP
.B ub_poll
Poll a context to see if it has any new results.
Do not poll in a loop, instead extract the fd below to poll for readiness,
and then check, or wait using the wait routine.
Returns 0 if nothing to read, or nonzero if a result is available.
If nonzero, call
.B ub_process
to do callbacks.
.TP
.B ub_wait
Wait for a context to finish with results. Calls
.B ub_process
after the wait for you. After the wait, there are no more outstanding
asynchronous queries.
.TP
.B ub_fd
Get file descriptor. Wait for it to become readable, at this point
answers are returned from the asynchronous validating resolver.
Then call the \fBub_process\fR to continue processing.
.TP
.B ub_process
Call this routine to continue processing results from the validating
resolver (when the fd becomes readable).
Will perform necessary callbacks.
.TP
.B ub_resolve
Perform resolution and validation of the target name.
The name is a domain name in a zero terminated text string.
The rrtype and rrclass are DNS type and class codes.
The result structure is newly allocated with the resulting data.
.TP
.B ub_resolve_async
Perform asynchronous resolution and validation of the target name.
Arguments mean the same as for \fBub_resolve\fR except no
data is returned immediately, instead a callback is called later.
The callback receives a copy of the mydata pointer, that you can use to pass
information to the callback. The callback type is a function pointer to
a function declared as
.IP
void my_callback_function(void* my_arg, int err,
.br
struct ub_result* result);
.IP
The async_id is returned so you can (at your option) decide to track it
and cancel the request if needed. If you pass a NULL pointer the async_id
is not returned.
.TP
.B ub_cancel
Cancel an async query in progress. This may return an error if the query
does not exist, or the query is already being delivered, in that case you
may still get a callback for the query.
.TP
.B ub_resolve_free
Free struct ub_result contents after use.
.TP
.B ub_strerror
Convert error value from one of the unbound library functions
to a human readable string.
.TP
.B ub_ctx_print_local_zones
Debug printout the local authority information to debug output.
.TP
.B ub_ctx_zone_add
Add new zone to local authority info, like local\-zone \fIunbound.conf\fR(5)
statement.
.TP
.B ub_ctx_zone_remove
Delete zone from local authority info.
.TP
.B ub_ctx_data_add
Add resource record data to local authority info, like local\-data
\fIunbound.conf\fR(5) statement.
.TP
.B ub_ctx_data_remove
Delete local authority data from the name given.
.SH "RESULT DATA STRUCTURE"
The result of the DNS resolution and validation is returned as
\fIstruct ub_result\fR. The result structure contains the following entries.
.P
.nf
struct ub_result {
char* qname; /* text string, original question */
int qtype; /* type code asked for */
int qclass; /* class code asked for */
char** data; /* array of rdata items, NULL terminated*/
int* len; /* array with lengths of rdata items */
char* canonname; /* canonical name of result */
int rcode; /* additional error code in case of no data */
void* answer_packet; /* full network format answer packet */
int answer_len; /* length of packet in octets */
int havedata; /* true if there is data */
int nxdomain; /* true if nodata because name does not exist */
int secure; /* true if result is secure */
int bogus; /* true if a security failure happened */
char* why_bogus; /* string with error if bogus */
int was_ratelimited; /* true if the query was ratelimited (SERVFAIL) by unbound */
int ttl; /* number of seconds the result is valid */
};
.fi
.P
If both secure and bogus are false, security was not enabled for the
domain of the query. Else, they are not both true, one of them is true.
.SH "RETURN VALUES"
Many routines return an error code. The value 0 (zero) denotes no error
happened. Other values can be passed to
.B ub_strerror
to obtain a readable error string.
.B ub_strerror
returns a zero terminated string.
.B ub_ctx_create
returns NULL on an error (a malloc failure).
.B ub_poll
returns true if some information may be available, false otherwise.
.B ub_fd
returns a file descriptor or \-1 on error.
.B ub_ctx_config
and
.B ub_ctx_resolvconf
attempt to leave errno informative on a function return with file read failure.
.SH "SEE ALSO"
\fIunbound.conf\fR(5),
\fIunbound\fR(8).
.SH "AUTHORS"
.B Unbound
developers are mentioned in the CREDITS file in the distribution.
diff --git a/contrib/unbound/doc/unbound-anchor.8.in b/contrib/unbound/doc/unbound-anchor.8.in
index 4e862fc89c79..a108db9faa72 100644
--- a/contrib/unbound/doc/unbound-anchor.8.in
+++ b/contrib/unbound/doc/unbound-anchor.8.in
@@ -1,189 +1,189 @@
-.TH "unbound-anchor" "8" "Aug 30, 2023" "NLnet Labs" "unbound 1.18.0"
+.TH "unbound-anchor" "8" "Nov 8, 2023" "NLnet Labs" "unbound 1.19.0"
.\"
.\" unbound-anchor.8 -- unbound anchor maintenance utility manual
.\"
.\" Copyright (c) 2008, NLnet Labs. All rights reserved.
.\"
.\" See LICENSE for the license.
.\"
.\"
.SH "NAME"
.B unbound\-anchor
\- Unbound anchor utility.
.SH "SYNOPSIS"
.B unbound\-anchor
.RB [ opts ]
.SH "DESCRIPTION"
.B Unbound\-anchor
performs setup or update of the root trust anchor for DNSSEC validation.
The program fetches the trust anchor with the method from RFC7958 when
regular RFC5011 update fails to bring it up to date.
It can be run (as root) from the commandline, or run as part of startup
scripts. Before you start the \fIunbound\fR(8) DNS server.
.P
Suggested usage:
.P
.nf
# in the init scripts.
# provide or update the root anchor (if necessary)
unbound-anchor \-a "@UNBOUND_ROOTKEY_FILE@"
# Please note usage of this root anchor is at your own risk
# and under the terms of our LICENSE (see source).
#
# start validating resolver
# the unbound.conf contains:
# auto-trust-anchor-file: "@UNBOUND_ROOTKEY_FILE@"
unbound \-c unbound.conf
.fi
.P
This tool provides builtin default contents for the root anchor and root
update certificate files.
.P
It tests if the root anchor file works, and if not, and an update is possible,
attempts to update the root anchor using the root update certificate.
It performs a https fetch of root-anchors.xml and checks the results (RFC7958),
if all checks are successful, it updates the root anchor file. Otherwise
the root anchor file is unchanged. It performs RFC5011 tracking if the
DNSSEC information available via the DNS makes that possible.
.P
It does not perform an update if the certificate is expired, if the network
is down or other errors occur.
.P
The available options are:
.TP
.B \-a \fIfile
The root anchor key file, that is read in and written out.
Default is @UNBOUND_ROOTKEY_FILE@.
If the file does not exist, or is empty, a builtin root key is written to it.
.TP
.B \-c \fIfile
The root update certificate file, that is read in.
Default is @UNBOUND_ROOTCERT_FILE@.
If the file does not exist, or is empty, a builtin certificate is used.
.TP
.B \-l
List the builtin root key and builtin root update certificate on stdout.
.TP
.B \-u \fIname
The server name, it connects to https://name. Specify without https:// prefix.
The default is "data.iana.org". It connects to the port specified with \-P.
You can pass an IPv4 address or IPv6 address (no brackets) if you want.
.TP
.B \-S
Do not use SNI for the HTTPS connection. Default is to use SNI.
.TP
.B \-b \fIaddress
The source address to bind to for domain resolution and contacting the server
on https. May be either an IPv4 address or IPv6 address (no brackets).
.TP
.B \-x \fIpath
The pathname to the root\-anchors.xml file on the server. (forms URL with \-u).
The default is /root\-anchors/root\-anchors.xml.
.TP
.B \-s \fIpath
The pathname to the root\-anchors.p7s file on the server. (forms URL with \-u).
The default is /root\-anchors/root\-anchors.p7s. This file has to be a PKCS7
signature over the xml file, using the pem file (\-c) as trust anchor.
.TP
.B \-n \fIname
The emailAddress for the Subject of the signer's certificate from the p7s
signature file. Only signatures from this name are allowed. default is
dnssec@iana.org. If you pass "" then the emailAddress is not checked.
.TP
.B \-4
Use IPv4 for domain resolution and contacting the server on https. Default is
to use IPv4 and IPv6 where appropriate.
.TP
.B \-6
Use IPv6 for domain resolution and contacting the server on https. Default is
to use IPv4 and IPv6 where appropriate.
.TP
.B \-f \fIresolv.conf
Use the given resolv.conf file. Not enabled by default, but you could try to
pass /etc/resolv.conf on some systems. It contains the IP addresses of the
recursive nameservers to use. However, since this tool could be used to
bootstrap that very recursive nameserver, it would not be useful (since
that server is not up yet, since we are bootstrapping it). It could be
useful in a situation where you know an upstream cache is deployed (and
running) and in captive portal situations.
.TP
.B \-r \fIroot.hints
Use the given root.hints file (same syntax as the BIND and Unbound root hints
file) to bootstrap domain resolution. By default a list of builtin root
hints is used. Unbound\-anchor goes to the network itself for these roots,
to resolve the server (\-u option) and to check the root DNSKEY records.
It does so, because the tool when used for bootstrapping the recursive
resolver, cannot use that recursive resolver itself because it is bootstrapping
that server.
.TP
.B \-R
Allow fallback from \-f resolv.conf file to direct root servers query.
It allows you to prefer local resolvers, but fallback automatically
to direct root query if they do not respond or do not support DNSSEC.
.TP
.B \-v
More verbose. Once prints informational messages, multiple times may enable
large debug amounts (such as full certificates or byte\-dumps of downloaded
files). By default it prints almost nothing. It also prints nothing on
errors by default; in that case the original root anchor file is simply
left undisturbed, so that a recursive server can start right after it.
.TP
.B \-C \fIunbound.conf
Debug option to read unbound.conf into the resolver process used.
.TP
.B \-P \fIport
Set the port number to use for the https connection. The default is 443.
.TP
.B \-F
Debug option to force update of the root anchor through downloading the xml
file and verifying it with the certificate. By default it first tries to
update by contacting the DNS, which uses much less bandwidth, is much
faster (200 msec not 2 sec), and is nicer to the deployed infrastructure.
With this option, it still attempts to do so (and may verbosely tell you),
but then ignores the result and goes on to use the xml fallback method.
.TP
.B \-h
Show the version and commandline option help.
.SH "EXIT CODE"
This tool exits with value 1 if the root anchor was updated using the
certificate or if the builtin root-anchor was used. It exits with code
0 if no update was necessary, if the update was possible with RFC5011
tracking, or if an error occurred.
.P
You can check the exit value in this manner:
.nf
unbound-anchor \-a "root.key" || logger "Please check root.key"
.fi
Or something more suitable for your operational environment.
.SH "TRUST"
The root keys and update certificate included in this tool
are provided for convenience and under the terms of our
license (see the LICENSE file in the source distribution or
https://github.com/NLnetLabs/unbound/blob/master/LICENSE) and might be stale or
not suitable to your purpose.
.P
By running "unbound\-anchor \-l" the keys and certificate that are
configured in the code are printed for your convenience.
.P
The build\-in configuration can be overridden by providing a root\-cert
file and a rootkey file.
.SH "FILES"
.TP
.I @UNBOUND_ROOTKEY_FILE@
The root anchor file, updated with 5011 tracking, and read and written to.
The file is created if it does not exist.
.TP
.I @UNBOUND_ROOTCERT_FILE@
The trusted self\-signed certificate that is used to verify the downloaded
DNSSEC root trust anchor. You can update it by fetching it from
https://data.iana.org/root\-anchors/icannbundle.pem (and validate it).
If the file does not exist or is empty, a builtin version is used.
.TP
.I https://data.iana.org/root\-anchors/root\-anchors.xml
Source for the root key information.
.TP
.I https://data.iana.org/root\-anchors/root\-anchors.p7s
Signature on the root key information.
.SH "SEE ALSO"
\fIunbound.conf\fR(5),
\fIunbound\fR(8).
diff --git a/contrib/unbound/doc/unbound-checkconf.8.in b/contrib/unbound/doc/unbound-checkconf.8.in
index 6a2c2cc94eea..b80c723cd3f0 100644
--- a/contrib/unbound/doc/unbound-checkconf.8.in
+++ b/contrib/unbound/doc/unbound-checkconf.8.in
@@ -1,52 +1,52 @@
-.TH "unbound-checkconf" "8" "Aug 30, 2023" "NLnet Labs" "unbound 1.18.0"
+.TH "unbound-checkconf" "8" "Nov 8, 2023" "NLnet Labs" "unbound 1.19.0"
.\"
.\" unbound-checkconf.8 -- unbound configuration checker manual
.\"
.\" Copyright (c) 2007, NLnet Labs. All rights reserved.
.\"
.\" See LICENSE for the license.
.\"
.\"
.SH "NAME"
unbound\-checkconf
\- Check Unbound configuration file for errors.
.SH "SYNOPSIS"
.B unbound\-checkconf
.RB [ \-h ]
.RB [ \-f ]
.RB [ \-o
.IR option ]
.RI [ cfgfile ]
.SH "DESCRIPTION"
.B Unbound\-checkconf
checks the configuration file for the
\fIunbound\fR(8)
DNS resolver for syntax and other errors.
The config file syntax is described in
\fIunbound.conf\fR(5).
.P
The available options are:
.TP
.B \-h
Show the version and commandline option help.
.TP
.B \-f
Print full pathname, with chroot applied to it. Use with the \-o option.
.TP
.B \-o\fI option
If given, after checking the config file the value of this option is
printed to stdout. For "" (disabled) options an empty line is printed.
.TP
.I cfgfile
The config file to read with settings for Unbound. It is checked.
If omitted, the config file at the default location is checked.
.SH "EXIT CODE"
The unbound\-checkconf program exits with status code 1 on error,
0 for a correct config file.
.SH "FILES"
.TP
.I @ub_conf_file@
Unbound configuration file.
.SH "SEE ALSO"
\fIunbound.conf\fR(5),
\fIunbound\fR(8).
diff --git a/contrib/unbound/doc/unbound-control.8.in b/contrib/unbound/doc/unbound-control.8.in
index db4eb72308a6..44e73c93dfd5 100644
--- a/contrib/unbound/doc/unbound-control.8.in
+++ b/contrib/unbound/doc/unbound-control.8.in
@@ -1,763 +1,763 @@
-.TH "unbound-control" "8" "Aug 30, 2023" "NLnet Labs" "unbound 1.18.0"
+.TH "unbound-control" "8" "Nov 8, 2023" "NLnet Labs" "unbound 1.19.0"
.\"
.\" unbound-control.8 -- unbound remote control manual
.\"
.\" Copyright (c) 2008, NLnet Labs. All rights reserved.
.\"
.\" See LICENSE for the license.
.\"
.\"
.SH "NAME"
.B unbound\-control,
.B unbound\-control\-setup
\- Unbound remote server control utility.
.SH "SYNOPSIS"
.B unbound\-control
.RB [ \-hq ]
.RB [ \-c
.IR cfgfile ]
.RB [ \-s
.IR server ]
.IR command
.SH "DESCRIPTION"
.B Unbound\-control
performs remote administration on the \fIunbound\fR(8) DNS server.
It reads the configuration file, contacts the Unbound server over SSL
sends the command and displays the result.
.P
The available options are:
.TP
.B \-h
Show the version and commandline option help.
.TP
.B \-c \fIcfgfile
The config file to read with settings. If not given the default
config file @ub_conf_file@ is used.
.TP
.B \-s \fIserver[@port]
IPv4 or IPv6 address of the server to contact. If not given, the
address is read from the config file.
.TP
.B \-q
quiet, if the option is given it does not print anything if it works ok.
.SH "COMMANDS"
There are several commands that the server understands.
.TP
.B start
Start the server. Simply execs \fIunbound\fR(8). The Unbound executable
is searched for in the \fBPATH\fR set in the environment. It is started
with the config file specified using \fI\-c\fR or the default config file.
.TP
.B stop
Stop the server. The server daemon exits.
.TP
.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
a reload (taken from config file again), or the next verbosity control command.
.TP
.B log_reopen
Reopen the logfile, close and open it. Useful for logrotation to make the
daemon release the file it is logging to. If you are using syslog it will
attempt to close and open the syslog (which may not work if chrooted).
.TP
.B stats
Print statistics. Resets the internal counters to zero, this can be
controlled using the \fBstatistics\-cumulative\fR config statement.
Statistics are printed with one [name]: [value] per line.
.TP
.B stats_noreset
Peek at statistics. Prints them like the \fBstats\fR command does, but does not
reset the internal counters to zero.
.TP
.B status
Display server status. Exit code 3 if not running (the connection to the
port is refused), 1 on error, 0 if running.
.TP
.B local_zone \fIname\fR \fItype
Add new local zone with name and type. Like \fBlocal\-zone\fR config statement.
If the zone already exists, the type is changed to the given argument.
.TP
.B local_zone_remove \fIname
Remove the local zone with the given name. Removes all local data inside
it. If the zone does not exist, the command succeeds.
.TP
.B local_data \fIRR data...
Add new local data, the given resource record. Like \fBlocal\-data\fR
config statement, except for when no covering zone exists. In that case
this remote control command creates a transparent zone with the same
name as this record.
.TP
.B local_data_remove \fIname
Remove all RR data from local name. If the name already has no items,
nothing happens. Often results in NXDOMAIN for the name (in a static zone),
but if the name has become an empty nonterminal (there is still data in
domain names below the removed name), NOERROR nodata answers are the
result for that name.
.TP
.B local_zones
Add local zones read from stdin of unbound\-control. Input is read per line,
with name space type on a line. For bulk additions.
.TP
.B local_zones_remove
Remove local zones read from stdin of unbound\-control. Input is one name per
line. For bulk removals.
.TP
.B local_datas
Add local data RRs read from stdin of unbound\-control. Input is one RR per
line. For bulk additions.
.TP
.B local_datas_remove
Remove local data RRs read from stdin of unbound\-control. Input is one name per
line. For bulk removals.
.TP
.B dump_cache
The contents of the cache is printed in a text format to stdout. You can
redirect it to a file to store the cache in a file.
.TP
.B load_cache
The contents of the cache is loaded from stdin. Uses the same format as
dump_cache uses. Loading the cache with old, or wrong data can result
in old or wrong data returned to clients. Loading data into the cache
in this way is supported in order to aid with debugging.
.TP
.B lookup \fIname
Print to stdout the name servers that would be used to look up the
name specified.
.TP
.B flush \fIname
Remove the name from the cache. Removes the types
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
.B flush_zone\fR.
.TP
.B flush_type \fIname\fR \fItype
Remove the name, type information from the cache.
.TP
.B flush_zone \fIname
Remove all information at or below the name from the cache.
The rrsets and key entries are removed so that new lookups will be performed.
This needs to walk and inspect the entire cache, and is a slow operation.
The entries are set to expired in the implementation of this command (so,
with serve\-expired enabled, it'll serve that information but schedule a
prefetch for new information).
.TP
.B flush_bogus
Remove all bogus data from the cache.
.TP
.B flush_negative
Remove all negative data from the cache. This is nxdomain answers,
nodata answers and servfail answers. Also removes bad key entries
(which could be due to failed lookups) from the dnssec key cache, and
iterator last-resort lookup failures from the rrset cache.
.TP
.B flush_stats
Reset statistics to zero.
.TP
.B flush_requestlist
Drop the queries that are worked on. Stops working on the queries that the
server is working on now. The cache is unaffected. No reply is sent for
those queries, probably making those users request again later.
Useful to make the server restart working on queries with new settings,
such as a higher verbosity level.
.TP
.B dump_requestlist
Show what is worked on. Prints all queries that the server is currently
working on. Prints the time that users have been waiting. For internal
requests, no time is printed. And then prints out the module status.
This prints the queries from the first thread, and not queries that are
being serviced from other threads.
.TP
.B flush_infra \fIall|IP
If all then entire infra cache is emptied. If a specific IP address, the
entry for that address is removed from the cache. It contains EDNS, ping
and lameness data.
.TP
.B dump_infra
Show the contents of the infra cache.
.TP
.B set_option \fIopt: val
Set the option to the given value without a reload. The cache is
therefore not flushed. The option must end with a ':' and whitespace
must be between the option and the value. Some values may not have an
effect if set this way, the new values are not written to the config file,
not all options are supported. This is different from the set_option call
in libunbound, where all values work because Unbound has not been initialized.
.IP
The values that work are: statistics\-interval, statistics\-cumulative,
do\-not\-query\-localhost, harden\-short\-bufsize, harden\-large\-queries,
harden\-glue, harden\-dnssec\-stripped, harden\-below\-nxdomain,
harden\-referral\-path, prefetch, prefetch\-key, log\-queries,
hide\-identity, hide\-version, identity, version, val\-log\-level,
val\-log\-squelch, ignore\-cd\-flag, add\-holddown, del\-holddown,
keep\-missing, tcp\-upstream, ssl\-upstream, max\-udp\-size, ratelimit,
ip\-ratelimit, cache\-max\-ttl, cache\-min\-ttl, cache\-max\-negative\-ttl.
.TP
.B get_option \fIopt
Get the value of the option. Give the option name without a trailing ':'.
The value is printed. If the value is "", nothing is printed
and the connection closes. On error 'error ...' is printed (it gives
a syntax error on unknown option). For some options a list of values,
one on each line, is printed. The options are shown from the config file
as modified with set_option. For some options an override may have been
taken that does not show up with this command, not results from e.g. the
verbosity and forward control commands. Not all options work, see list_stubs,
list_forwards, list_local_zones and list_local_data for those.
.TP
.B list_stubs
List the stub zones in use. These are printed one by one to the output.
This includes the root hints in use.
.TP
.B list_forwards
List the forward zones in use. These are printed zone by zone to the output.
.TP
.B list_insecure
List the zones with domain\-insecure.
.TP
.B list_local_zones
List the local zones in use. These are printed one per line with zone type.
.TP
.B list_local_data
List the local data RRs in use. The resource records are printed.
.TP
.B insecure_add \fIzone
Add a \fBdomain\-insecure\fR for the given zone, like the statement in unbound.conf.
Adds to the running Unbound without affecting the cache contents (which may
still be bogus, use \fBflush_zone\fR to remove it), does not affect the config file.
.TP
.B insecure_remove \fIzone
Removes domain\-insecure for the given zone.
.TP
.B forward_add \fR[\fI+i\fR] \fIzone addr ...
Add a new forward zone to running Unbound. With +i option also adds a
\fIdomain\-insecure\fR for the zone (so it can resolve insecurely if you have
a DNSSEC root trust anchor configured for other names).
The addr can be IP4, IP6 or nameserver names, like \fIforward-zone\fR config
in unbound.conf.
.TP
.B forward_remove \fR[\fI+i\fR] \fIzone
Remove a forward zone from running Unbound. The +i also removes a
\fIdomain\-insecure\fR for the zone.
.TP
.B stub_add \fR[\fI+ip\fR] \fIzone addr ...
Add a new stub zone to running Unbound. With +i option also adds a
\fIdomain\-insecure\fR for the zone. With +p the stub zone is set to prime,
without it it is set to notprime. The addr can be IP4, IP6 or nameserver
names, like the \fIstub-zone\fR config in unbound.conf.
.TP
.B stub_remove \fR[\fI+i\fR] \fIzone
Remove a stub zone from running Unbound. The +i also removes a
\fIdomain\-insecure\fR for the zone.
.TP
.B forward \fR[\fIoff\fR | \fIaddr ...\fR ]
Setup forwarding mode. Configures if the server should ask other upstream
nameservers, should go to the internet root nameservers itself, or show
the current config. You could pass the nameservers after a DHCP update.
.IP
Without arguments the current list of addresses used to forward all queries
to is printed. On startup this is from the forward\-zone "." configuration.
Afterwards it shows the status. It prints off when no forwarding is used.
.IP
If \fIoff\fR is passed, forwarding is disabled and the root nameservers
are used. This can be used to avoid to avoid buggy or non\-DNSSEC supporting
nameservers returned from DHCP. But may not work in hotels or hotspots.
.IP
If one or more IPv4 or IPv6 addresses are given, those are then used to forward
queries to. The addresses must be separated with spaces. With '@port' the
port number can be set explicitly (default port is 53 (DNS)).
.IP
By default the forwarder information from the config file for the root "." is
used. The config file is not changed, so after a reload these changes are
gone. Other forward zones from the config file are not affected by this command.
.TP
.B ratelimit_list \fR[\fI+a\fR]
List the domains that are ratelimited. Printed one per line with current
estimated qps and qps limit from config. With +a it prints all domains, not
just the ratelimited domains, with their estimated qps. The ratelimited
domains return an error for uncached (new) queries, but cached queries work
as normal.
.TP
.B ip_ratelimit_list \fR[\fI+a\fR]
List the ip addresses that are ratelimited. Printed one per line with current
estimated qps and qps limit from config. With +a it prints all ips, not
just the ratelimited ips, with their estimated qps. The ratelimited
ips are dropped before checking the cache.
.TP
.B list_auth_zones
List the auth zones that are configured. Printed one per line with a status,
indicating if the zone is expired and current serial number. Configured RPZ
zones are included.
.TP
.B auth_zone_reload \fIzone\fR
Reload the auth zone (or RPZ zone) from zonefile. The zonefile is read in
overwriting the current contents of the zone in memory. This changes the auth
zone contents itself, not the cache contents. Such cache contents exists if
you set Unbound to validate with for-upstream yes and that can be cleared with
\fBflush_zone\fR \fIzone\fR.
.TP
.B auth_zone_transfer \fIzone\fR
Transfer the auth zone (or RPZ zone) from master. The auth zone probe sequence
is started, where the masters are probed to see if they have an updated zone
(with the SOA serial check). And then the zone is transferred for a newer zone
version.
.TP
.B rpz_enable \fIzone\fR
Enable the RPZ zone if it had previously been disabled.
.TP
.B rpz_disable \fIzone\fR
Disable the RPZ zone.
.TP
.B view_list_local_zones \fIview\fR
\fIlist_local_zones\fR for given view.
.TP
.B view_local_zone \fIview\fR \fIname\fR \fItype
\fIlocal_zone\fR for given view.
.TP
.B view_local_zone_remove \fIview\fR \fIname
\fIlocal_zone_remove\fR for given view.
.TP
.B view_list_local_data \fIview\fR
\fIlist_local_data\fR for given view.
.TP
.B view_local_data \fIview\fR \fIRR data...
\fIlocal_data\fR for given view.
.TP
.B view_local_data_remove \fIview\fR \fIname
\fIlocal_data_remove\fR for given view.
.TP
.B view_local_datas_remove \fIview\fR
Remove a list of \fIlocal_data\fR for given view from stdin. Like local_datas_remove.
.TP
.B view_local_datas \fIview\fR
Add a list of \fIlocal_data\fR for given view from stdin. Like local_datas.
.SH "EXIT CODE"
The unbound\-control program exits with status code 1 on error, 0 on success.
.SH "SET UP"
The setup requires a self\-signed certificate and private keys for both
the server and client. The script \fIunbound\-control\-setup\fR generates
these in the default run directory, or with \-d in another directory.
If you change the access control permissions on the key files you can decide
who can use unbound\-control, by default owner and group but not all users.
Run the script under the same username as you have configured in unbound.conf
or as root, so that the daemon is permitted to read the files, for example with:
.nf
sudo \-u unbound unbound\-control\-setup
.fi
If you have not configured
a username in unbound.conf, the keys need read permission for the user
credentials under which the daemon is started.
The script preserves private keys present in the directory.
After running the script as root, turn on \fBcontrol\-enable\fR in
\fIunbound.conf\fR.
.SH "STATISTIC COUNTERS"
The \fIstats\fR command shows a number of statistic counters.
.TP
.I threadX.num.queries
number of queries received by thread
.TP
.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.cachemiss
number of queries that needed recursive processing
.TP
.I threadX.num.dnscrypt.crypted
number of queries that were encrypted and successfully decapsulated by dnscrypt.
.TP
.I threadX.num.dnscrypt.cert
number of queries that were requesting dnscrypt certificates.
.TP
.I threadX.num.dnscrypt.cleartext
number of queries received on dnscrypt port that were cleartext and not a
request for certificates.
.TP
.I threadX.num.dnscrypt.malformed
number of request that were neither cleartext, not valid dnscrypt messages.
.TP
.I threadX.num.prefetch
number of cache prefetches performed. This number is included in
cachehits, as the original query had the unprefetched answer from cache,
and resulted in recursive processing, taking a slot in the requestlist.
Not part of the recursivereplies (or the histogram thereof) or cachemiss,
as a cache response was sent.
.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 threadX.requestlist.avg
The average number of requests in the internal recursive processing request list on insert of a new incoming recursive processing query.
.TP
.I threadX.requestlist.max
Maximum size attained by the internal recursive processing request list.
.TP
.I threadX.requestlist.overwritten
Number of requests in the request list that were overwritten by newer entries. This happens if there is a flood of queries that recursive processing and the server has a hard time.
.TP
.I threadX.requestlist.exceeded
Queries that were dropped because the request list was full. This happens if a flood of queries need recursive processing, and the server can not keep up.
.TP
.I threadX.requestlist.current.all
Current size of the request list, includes internally generated queries (such
as priming queries and glue lookups).
.TP
.I threadX.requestlist.current.user
Current size of the request list, only the requests from client queries.
.TP
.I threadX.recursion.time.avg
Average time it took to answer queries that needed recursive processing. Note that queries that were answered from the cache are not in this average.
.TP
.I threadX.recursion.time.median
The median of the time it took to answer queries that needed recursive
processing. The median means that 50% of the user queries were answered in
less than this time. Because of big outliers (usually queries to non
responsive servers), the average can be bigger than the median. This median
has been calculated by interpolation from a histogram.
.TP
.I threadX.tcpusage
The currently held tcp buffers for incoming connections. A spot value on
the time of the request. This helps you spot if the incoming\-num\-tcp
buffers are full.
.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.cachemiss
summed over threads.
.TP
.I total.num.dnscrypt.crypted
summed over threads.
.TP
.I total.num.dnscrypt.cert
summed over threads.
.TP
.I total.num.dnscrypt.cleartext
summed over threads.
.TP
.I total.num.dnscrypt.malformed
summed over threads.
.TP
.I total.num.prefetch
summed over threads.
.TP
.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
.I total.requestlist.avg
averaged over threads.
.TP
.I total.requestlist.max
the maximum of the thread requestlist.max values.
.TP
.I total.requestlist.overwritten
summed over threads.
.TP
.I total.requestlist.exceeded
summed over threads.
.TP
.I total.requestlist.current.all
summed over threads.
.TP
.I total.recursion.time.median
averaged over threads.
.TP
.I total.tcpusage
summed over threads.
.TP
.I time.now
current time in seconds since 1970.
.TP
.I time.up
uptime since server boot in seconds.
.TP
.I time.elapsed
time since last statistics printout, in seconds.
.SH EXTENDED STATISTICS
.TP
.I mem.cache.rrset
Memory in bytes in use by the RRset cache.
.TP
.I mem.cache.message
Memory in bytes in use by the message cache.
.TP
.I mem.cache.dnscrypt_shared_secret
Memory in bytes in use by the dnscrypt shared secrets cache.
.TP
.I mem.cache.dnscrypt_nonce
Memory in bytes in use by the dnscrypt nonce cache.
.TP
.I mem.mod.iterator
Memory in bytes in use by the iterator module.
.TP
.I mem.mod.validator
Memory in bytes in use by the validator module. Includes the key cache and
negative cache.
.TP
.I mem.streamwait
Memory in bytes in used by the TCP and TLS stream wait buffers. These are
answers waiting to be written back to the clients.
.TP
.I mem.http.query_buffer
Memory in bytes used by the HTTP/2 query buffers. Containing (partial) DNS
queries waiting for request stream completion.
.TP
.I mem.http.response_buffer
Memory in bytes used by the HTTP/2 response buffers. Containing DNS responses
waiting to be written back to the clients.
.TP
.I histogram.<sec>.<usec>.to.<sec>.<usec>
Shows a histogram, summed over all threads. Every element counts the
recursive queries whose reply time fit between the lower and upper bound.
Times larger or equal to the lowerbound, and smaller than the upper bound.
There are 40 buckets, with bucket sizes doubling.
.TP
.I num.query.type.A
The total number of queries over all threads with query type A.
Printed for the other query types as well, but only for the types for which
queries were received, thus =0 entries are omitted for brevity.
.TP
.I num.query.type.other
Number of queries with query types 256\-65535.
.TP
.I num.query.class.IN
The total number of queries over all threads with query class IN (internet).
Also printed for other classes (such as CH (CHAOS) sometimes used for
debugging), or NONE, ANY, used by dynamic update.
num.query.class.other is printed for classes 256\-65535.
.TP
.I num.query.opcode.QUERY
The total number of queries over all threads with query opcode QUERY.
Also printed for other opcodes, UPDATE, ...
.TP
.I num.query.tcp
Number of queries that were made using TCP towards the Unbound server.
.TP
.I num.query.tcpout
Number of queries that the Unbound server made using TCP outgoing towards
other servers.
.TP
.I num.query.udpout
Number of queries that the Unbound server made using UDP outgoing towards
other servers.
.TP
.I num.query.tls
Number of queries that were made using TLS towards the Unbound server.
These are also counted in num.query.tcp, because TLS uses TCP.
.TP
.I num.query.tls.resume
Number of TLS session resumptions, these are queries over TLS towards
the Unbound server where the client negotiated a TLS session resumption key.
.TP
.I num.query.https
Number of queries that were made using HTTPS towards the Unbound server.
These are also counted in num.query.tcp and num.query.tls, because HTTPS
uses TLS and TCP.
.TP
.I num.query.ipv6
Number of queries that were made using IPv6 towards the Unbound server.
.TP
.I num.query.flags.RD
The number of queries that had the RD flag set in the header.
Also printed for flags QR, AA, TC, RA, Z, AD, CD.
Note that queries with flags QR, AA or TC may have been rejected
because of that.
.TP
.I num.query.edns.present
number of queries that had an EDNS OPT record present.
.TP
.I num.query.edns.DO
number of queries that had an EDNS OPT record with the DO (DNSSEC OK) bit set.
These queries are also included in the num.query.edns.present number.
.TP
.I num.query.ratelimited
The number of queries that are turned away from being send to nameserver due to
ratelimiting.
.TP
.I num.query.dnscrypt.shared_secret.cachemiss
The number of dnscrypt queries that did not find a shared secret in the cache.
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
hence are considered a query replay.
.TP
.I num.answer.rcode.NXDOMAIN
The number of answers to queries, from cache or from recursion, that had the
return code NXDOMAIN. Also printed for the other return codes.
.TP
.I num.answer.rcode.nodata
The number of answers to queries that had the pseudo return code nodata.
This means the actual return code was NOERROR, but additionally, no data was
carried in the answer (making what is called a NOERROR/NODATA answer).
These queries are also included in the num.answer.rcode.NOERROR number.
Common for AAAA lookups when an A record exists, and no AAAA.
.TP
.I num.answer.secure
Number of answers that were secure. The answer validated correctly.
The AD bit might have been set in some of these answers, where the client
signalled (with DO or AD bit in the query) that they were ready to accept
the AD bit in the answer.
.TP
.I num.answer.bogus
Number of answers that were bogus. These answers resulted in SERVFAIL
to the client because the answer failed validation.
.TP
.I num.rrset.bogus
The number of rrsets marked bogus by the validator. Increased for every
RRset inspection that fails.
.TP
.I unwanted.queries
Number of queries that were refused or dropped because they failed the
access control settings.
.TP
.I unwanted.replies
Replies that were unwanted or unsolicited. Could have been random traffic,
delayed duplicates, very late answers, or could be spoofing attempts.
Some low level of late answers and delayed duplicates are to be expected
with the UDP protocol. Very high values could indicate a threat (spoofing).
.TP
.I msg.cache.count
The number of items (DNS replies) in the message cache.
.TP
.I rrset.cache.count
The number of RRsets in the rrset cache. This includes rrsets used by
the messages in the message cache, but also delegation information.
.TP
.I infra.cache.count
The number of items in the infra cache. These are IP addresses with their
timing and protocol support information.
.TP
.I key.cache.count
The number of items in the key cache. These are DNSSEC keys, one item
per delegation point, and their validation status.
.TP
.I 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
are CPU intensive and this cache allows Unbound to avoid recomputing the
shared secret when multiple dnscrypt queries are sent from the same client.
.TP
.I dnscrypt_nonce.cache.count
The number of items in the client nonce cache. This cache is used to prevent
dnscrypt queries replay. The client nonce must be unique for each client public
key/server secret key pair. This cache should be able to host QPS * `replay
window` interval keys to prevent replay of a query during `replay window`
seconds.
.TP
.I num.query.authzone.up
The number of queries answered from auth\-zone data, upstream queries.
These queries would otherwise have been sent (with fallback enabled) to
the internet, but are now answered from the auth zone.
.TP
.I num.query.authzone.down
The number of queries for downstream answered from auth\-zone data.
These queries are from downstream clients, and have had an answer from
the data in the auth zone.
.TP
.I num.query.aggressive.NOERROR
The number of queries answered using cached NSEC records with NODATA RCODE.
These queries would otherwise have been sent to the internet, but are now
answered using cached data.
.TP
.I num.query.aggressive.NXDOMAIN
The number of queries answered using cached NSEC records with NXDOMAIN RCODE.
These queries would otherwise have been sent to the internet, but are now
answered using cached data.
.TP
.I num.query.subnet
Number of queries that got an answer that contained EDNS client subnet data.
.TP
.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.
.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.
Possible actions are: nxdomain, nodata, passthru, drop, tcp\-only, local\-data,
disabled, and cname\-override.
.SH "FILES"
.TP
.I @ub_conf_file@
Unbound configuration file.
.TP
.I @UNBOUND_RUN_DIR@
directory with private keys (unbound_server.key and unbound_control.key) and
self\-signed certificates (unbound_server.pem and unbound_control.pem).
.SH "SEE ALSO"
\fIunbound.conf\fR(5),
\fIunbound\fR(8).
diff --git a/contrib/unbound/doc/unbound-host.1.in b/contrib/unbound/doc/unbound-host.1.in
index e4fe718ab071..36f22ee9b6d1 100644
--- a/contrib/unbound/doc/unbound-host.1.in
+++ b/contrib/unbound/doc/unbound-host.1.in
@@ -1,118 +1,118 @@
-.TH "unbound\-host" "1" "Aug 30, 2023" "NLnet Labs" "unbound 1.18.0"
+.TH "unbound\-host" "1" "Nov 8, 2023" "NLnet Labs" "unbound 1.19.0"
.\"
.\" unbound-host.1 -- unbound DNS lookup utility
.\"
.\" Copyright (c) 2007, NLnet Labs. All rights reserved.
.\"
.\" See LICENSE for the license.
.\"
.\"
.SH "NAME"
.B unbound\-host
\- unbound DNS lookup utility
.SH "SYNOPSIS"
.B unbound\-host
.RB [ \-C
.IR configfile ]
.RB [ \-vdhr46D ]
.RB [ \-c
.IR class ]
.RB [ \-t
.IR type ]
.RB [ \-y
.IR key ]
.RB [ \-f
.IR keyfile ]
.RB [ \-F
.IR namedkeyfile ]
.I hostname
.SH "DESCRIPTION"
.B Unbound\-host
uses the Unbound validating resolver to query for the hostname and display
results. With the \fB\-v\fR option it displays validation
status: secure, insecure, bogus (security failure).
.P
By default it reads no configuration file whatsoever. It attempts to reach
the internet root servers. With \fB\-C\fR an Unbound config file and with
\fB\-r\fR resolv.conf can be read.
.P
The available options are:
.TP
.I hostname
This name is resolved (looked up in the DNS).
If a IPv4 or IPv6 address is given, a reverse lookup is performed.
.TP
.B \-h
Show the version and commandline option help.
.TP
.B \-v
Enable verbose output and it shows validation results, on every line.
Secure means that the NXDOMAIN (no such domain name), nodata (no such data)
or positive data response validated correctly with one of the keys.
Insecure means that that domain name has no security set up for it.
Bogus (security failure) means that the response failed one or more checks,
it is likely wrong, outdated, tampered with, or broken.
.TP
.B \-d
Enable debug output to stderr. One \-d shows what the resolver and validator
are doing and may tell you what is going on. More times, \-d \-d, gives a
lot of output, with every packet sent and received.
.TP
.B \-c \fIclass
Specify the class to lookup for, the default is IN the internet class.
.TP
.B \-t \fItype
Specify the type of data to lookup. The default looks for IPv4, IPv6 and
mail handler data, or domain name pointers for reverse queries.
.TP
.B \-y \fIkey
Specify a public key to use as trust anchor. This is the base for a chain
of trust that is built up from the trust anchor to the response, in order
to validate the response message. Can be given as a DS or DNSKEY record.
For example \-y "example.com DS 31560 5 1 1CFED84787E6E19CCF9372C1187325972FE546CD".
.TP
.B \-D
Enables DNSSEC validation. Reads the root anchor from the default configured
root anchor at the default location, \fI@UNBOUND_ROOTKEY_FILE@\fR.
.TP
.B \-f \fIkeyfile
Reads keys from a file. Every line has a DS or DNSKEY record, in the format
as for \-y. The zone file format, the same as dig and drill produce.
.TP
.B \-F \fInamedkeyfile
Reads keys from a BIND\-style named.conf file. Only the trusted\-key {}; entries
are read.
.TP
.B \-C \fIconfigfile
Uses the specified unbound.conf to prime
.IR libunbound (3).
Pass it as first argument if you want to override some options from the
config file with further arguments on the commandline.
.TP
.B \-r
Read /etc/resolv.conf, and use the forward DNS servers from there (those could
have been set by DHCP). More info in
.IR resolv.conf (5).
Breaks validation if those servers do not support DNSSEC.
.TP
.B \-4
Use solely the IPv4 network for sending packets.
.TP
.B \-6
Use solely the IPv6 network for sending packets.
.SH "EXAMPLES"
Some examples of use. The keys shown below are fakes, thus a security failure
is encountered.
.P
$ unbound\-host www.example.com
.P
$ unbound\-host \-v \-y "example.com DS 31560 5 1 1CFED84787E6E19CCF9372C1187325972FE546CD" www.example.com
.P
$ unbound\-host \-v \-y "example.com DS 31560 5 1 1CFED84787E6E19CCF9372C1187325972FE546CD" 192.0.2.153
.SH "EXIT CODE"
The unbound\-host program exits with status code 1 on error,
0 on no error. The data may not be available on exit code 0, exit code 1
means the lookup encountered a fatal error.
.SH "SEE ALSO"
\fIunbound.conf\fR(5),
\fIunbound\fR(8).
diff --git a/contrib/unbound/doc/unbound.8.in b/contrib/unbound/doc/unbound.8.in
index 7b955a92e542..3d56b7bfa190 100644
--- a/contrib/unbound/doc/unbound.8.in
+++ b/contrib/unbound/doc/unbound.8.in
@@ -1,88 +1,88 @@
-.TH "unbound" "8" "Aug 30, 2023" "NLnet Labs" "unbound 1.18.0"
+.TH "unbound" "8" "Nov 8, 2023" "NLnet Labs" "unbound 1.19.0"
.\"
.\" unbound.8 -- unbound manual
.\"
.\" Copyright (c) 2007, NLnet Labs. All rights reserved.
.\"
.\" See LICENSE for the license.
.\"
.\"
.SH "NAME"
.B unbound
-\- Unbound DNS validating resolver 1.18.0.
+\- Unbound DNS validating resolver 1.19.0.
.SH "SYNOPSIS"
.B unbound
.RB [ \-h ]
.RB [ \-d ]
.RB [ \-p ]
.RB [ \-v ]
.RB [ \-c
.IR cfgfile ]
.SH "DESCRIPTION"
.B Unbound
is a caching DNS resolver.
.P
It uses a built in list of authoritative nameservers for the root zone (.),
the so called root hints.
On receiving a DNS query it will ask the root nameservers for
an answer and will in almost all cases receive a delegation to a top level
domain (TLD) authoritative nameserver.
It will then ask that nameserver for an answer.
It will recursively continue until an answer is found or no answer is
available (NXDOMAIN).
For performance and efficiency reasons that answer is cached for a
certain time (the answer's time\-to\-live or TTL).
A second query for the same name will then be answered from the cache.
Unbound can also do DNSSEC validation.
.P
To use a locally running
.B Unbound
for resolving put
.sp
.RS 6n
nameserver 127.0.0.1
.RE
.sp
into
.IR resolv.conf (5).
.P
If authoritative DNS is needed as well using
.IR nsd (8),
careful setup is required because authoritative nameservers and
resolvers are using the same port number (53).
.P
The available options are:
.TP
.B \-h
Show the version number and commandline option help, and exit.
.TP
.B \-c\fI cfgfile
Set the config file with settings for Unbound to read instead of reading the
file at the default location, @ub_conf_file@. The syntax is
described in \fIunbound.conf\fR(5).
.TP
.B \-d
Debug flag: do not fork into the background, but stay attached to
the console. This flag will also delay writing to the log file until
the thread\-spawn time, so that most config and setup errors appear on
stderr. If given twice or more, logging does not switch to the log file
or to syslog, but the log messages are printed to stderr all the time.
.TP
.B \-p
Don't use a pidfile. This argument should only be used by supervision
systems which can ensure that only one instance of Unbound will run
concurrently.
.TP
.B \-v
Increase verbosity. If given multiple times, more information is logged.
This is added to the verbosity (if any) from the config file.
.TP
.B \-V
Show the version number and build options, and exit.
.SH "SEE ALSO"
\fIunbound.conf\fR(5),
\fIunbound\-checkconf\fR(8),
\fInsd\fR(8).
.SH "AUTHORS"
.B Unbound
developers are mentioned in the CREDITS file in the distribution.
diff --git a/contrib/unbound/doc/unbound.conf.5.in b/contrib/unbound/doc/unbound.conf.5.in
index 1c785ea5fa6a..ac8fa7953f3c 100644
--- a/contrib/unbound/doc/unbound.conf.5.in
+++ b/contrib/unbound/doc/unbound.conf.5.in
@@ -1,2973 +1,3003 @@
-.TH "unbound.conf" "5" "Aug 30, 2023" "NLnet Labs" "unbound 1.18.0"
+.TH "unbound.conf" "5" "Nov 8, 2023" "NLnet Labs" "unbound 1.19.0"
.\"
.\" unbound.conf.5 -- unbound.conf manual
.\"
.\" Copyright (c) 2007, NLnet Labs. All rights reserved.
.\"
.\" See LICENSE for the license.
.\"
.\"
.SH "NAME"
.B unbound.conf
\- Unbound configuration file.
.SH "SYNOPSIS"
.B unbound.conf
.SH "DESCRIPTION"
.B unbound.conf
is used to configure
\fIunbound\fR(8).
The file format has attributes and values. Some attributes have attributes
inside them.
The notation is: attribute: value.
.P
Comments start with # and last to the end of line. Empty lines are
ignored as is whitespace at the beginning of a line.
.P
The utility
\fIunbound\-checkconf\fR(8)
can be used to check unbound.conf prior to usage.
.SH "EXAMPLE"
An example config file is shown below. Copy this to /etc/unbound/unbound.conf
and start the server with:
.P
.nf
$ unbound \-c /etc/unbound/unbound.conf
.fi
.P
Most settings are the defaults. Stop the server with:
.P
.nf
$ kill `cat /etc/unbound/unbound.pid`
.fi
.P
Below is a minimal config file. The source distribution contains an extensive
example.conf file with all the options.
.P
.nf
# unbound.conf(5) config file for unbound(8).
server:
directory: "/etc/unbound"
username: unbound
# make sure unbound can access entropy from inside the chroot.
# e.g. on linux the use these commands (on BSD, devfs(8) is used):
# mount \-\-bind \-n /dev/urandom /etc/unbound/dev/urandom
# and mount \-\-bind \-n /dev/log /etc/unbound/dev/log
chroot: "/etc/unbound"
# logfile: "/etc/unbound/unbound.log" #uncomment to use logfile.
pidfile: "/etc/unbound/unbound.pid"
# verbosity: 1 # uncomment and increase to get more logging.
# listen on all interfaces, answer queries from the local subnet.
interface: 0.0.0.0
interface: ::0
access\-control: 10.0.0.0/8 allow
access\-control: 2001:DB8::/64 allow
.fi
.SH "FILE FORMAT"
There must be whitespace between keywords. Attribute keywords end with a
colon ':'. An attribute is followed by a value, or its containing attributes
in which case it is referred to as a clause. Clauses can be repeated throughout
the file (or included files) to group attributes under the same clause.
.P
Files can be included using the
.B include:
directive. It can appear anywhere, it accepts a single file name as argument.
Processing continues as if the text from the included file was copied into
the config file at that point. If also using chroot, using full path names
for the included files works, relative pathnames for the included names work
if the directory where the daemon is started equals its chroot/working
directory or is specified before the include statement with directory: dir.
Wildcards can be used to include multiple files, see \fIglob\fR(7).
.P
For a more structural include option, the
.B include\-toplevel:
directive can be used. This closes whatever clause is currently active (if any)
and forces the use of clauses in the included files and right after this
directive.
.SS "Server Options"
These options are part of the
.B server:
clause.
.TP
.B verbosity: \fI<number>
The verbosity number, level 0 means no verbosity, only errors. Level 1
gives operational information. Level 2 gives detailed operational
information including short information per query. Level 3 gives query level
information, output per query. Level 4 gives algorithm level information.
Level 5 logs client identification for cache misses. Default is level 1.
The verbosity can also be increased from the commandline, see \fIunbound\fR(8).
.TP
.B statistics\-interval: \fI<seconds>
The number of seconds between printing statistics to the log for every thread.
Disable with value 0 or "". Default is disabled. The histogram statistics
are only printed if replies were sent during the statistics interval,
requestlist statistics are printed for every interval (but can be 0).
This is because the median calculation requires data to be present.
.TP
.B statistics\-cumulative: \fI<yes or no>
If enabled, statistics are cumulative since starting Unbound, without clearing
the statistics counters after logging the statistics. Default is no.
.TP
.B extended\-statistics: \fI<yes or no>
If enabled, extended statistics are printed from \fIunbound\-control\fR(8).
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 port: \fI<port number>
The port number, default 53, on which the server responds to queries.
.TP
.B interface: \fI<ip address or interface name [@port]>
Interface to use to connect to the network. This interface is listened to
for queries from clients, and answers to clients are given from it.
Can be given multiple times to work on several interfaces. If none are
given the default is to listen to localhost. If an interface name is used
instead of an ip address, the list of ip addresses on that interface are used.
The interfaces are not changed on a reload (kill \-HUP) but only on restart.
A port number can be specified with @port (without spaces between
interface and port number), if not specified the default port (from
\fBport\fR) is used.
.TP
.B ip\-address: \fI<ip address or interface name [@port]>
Same as interface: (for ease of compatibility with nsd.conf).
.TP
.B interface\-automatic: \fI<yes or no>
Listen on all addresses on all (current and future) interfaces, detect the
source interface on UDP queries and copy them to replies. This is a lot like
ip\-transparent, but this option services all interfaces whilst with
ip\-transparent you can select which (future) interfaces Unbound provides
service on. This feature is experimental, and needs support in your OS for
particular socket options. Default value is no.
.TP
.B interface\-automatic\-ports: \fI<string>
List the port numbers that interface-automatic listens on. If empty, the
default port is listened on. The port numbers are separated by spaces in the
string. Default is "".
.IP
This can be used to have interface automatic to deal with the interface,
and listen on the normal port number, by including it in the list, and
also https or dns over tls port numbers by putting them in the list as well.
.TP
.B outgoing\-interface: \fI<ip address or ip6 netblock>
Interface to use to connect to the network. This interface is used to send
queries to authoritative servers and receive their replies. Can be given
multiple times to work on several interfaces. If none are given the
default (all) is used. You can specify the same interfaces in
.B interface:
and
.B outgoing\-interface:
lines, the interfaces are then used for both purposes. Outgoing queries are
sent via a random outgoing interface to counter spoofing.
.IP
If an IPv6 netblock is specified instead of an individual IPv6 address,
outgoing UDP queries will use a randomised source address taken from the
netblock to counter spoofing. Requires the IPv6 netblock to be routed to the
host running Unbound, and requires OS support for unprivileged non-local binds
(currently only supported on Linux). Several netblocks may be specified with
multiple
.B outgoing\-interface:
options, but do not specify both an individual IPv6 address and an IPv6
netblock, or the randomisation will be compromised. Consider combining with
.B prefer\-ip6: yes
to increase the likelihood of IPv6 nameservers being selected for queries.
On Linux you need these two commands to be able to use the freebind socket
option to receive traffic for the ip6 netblock:
ip \-6 addr add mynetblock/64 dev lo &&
ip \-6 route add local mynetblock/64 dev lo
.TP
.B outgoing\-range: \fI<number>
Number of ports to open. This number of file descriptors can be opened per
thread. Must be at least 1. Default depends on compile options. Larger
numbers need extra resources from the operating system. For performance a
very large value is best, use libevent to make this possible.
.TP
.B outgoing\-port\-permit: \fI<port number or range>
Permit Unbound to open this port or range of ports for use to send queries.
A larger number of permitted outgoing ports increases resilience against
spoofing attempts. Make sure these ports are not needed by other daemons.
By default only ports above 1024 that have not been assigned by IANA are used.
Give a port number or a range of the form "low\-high", without spaces.
.IP
The \fBoutgoing\-port\-permit\fR and \fBoutgoing\-port\-avoid\fR statements
are processed in the line order of the config file, adding the permitted ports
and subtracting the avoided ports from the set of allowed ports. The
processing starts with the non IANA allocated ports above 1024 in the set
of allowed ports.
.TP
.B outgoing\-port\-avoid: \fI<port number or range>
Do not permit Unbound to open this port or range of ports for use to send
queries. Use this to make sure Unbound does not grab a port that another
daemon needs. The port is avoided on all outgoing interfaces, both IP4 and IP6.
By default only ports above 1024 that have not been assigned by IANA are used.
Give a port number or a range of the form "low\-high", without spaces.
.TP
.B outgoing\-num\-tcp: \fI<number>
Number of outgoing TCP buffers to allocate per thread. Default is 10. If
set to 0, or if do\-tcp is "no", no TCP queries to authoritative servers
are done. For larger installations increasing this value is a good idea.
.TP
.B incoming\-num\-tcp: \fI<number>
Number of incoming TCP buffers to allocate per thread. Default is
10. If set to 0, or if do\-tcp is "no", no TCP queries from clients are
accepted. For larger installations increasing this value is a good idea.
.TP
.B edns\-buffer\-size: \fI<number>
Number of bytes size to advertise as the EDNS reassembly buffer size.
This is the value put into datagrams over UDP towards peers. The actual
buffer size is determined by msg\-buffer\-size (both for TCP and UDP). Do
not set higher than that value. Default is 1232 which is the DNS Flag Day 2020
recommendation. Setting to 512 bypasses even the most stringent path MTU
problems, but is seen as extreme, since the amount of TCP fallback generated is
excessive (probably also for this resolver, consider tuning the outgoing tcp
number).
.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 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
4 megabytes. A plain number is in bytes, append 'k', 'm' or 'g' for kilobytes,
megabytes or gigabytes (1024*1024 bytes in a megabyte). As TCP and TLS streams
queue up multiple results, the amount of memory used for these buffers does
not exceed this number, otherwise the responses are dropped. This manages
the total memory usage of the server (under heavy use), the number of requests
that can be queued up per connection is also limited, with further requests
waiting in TCP buffers.
.TP
.B msg\-buffer\-size: \fI<number>
Number of bytes size of the message buffers. Default is 65552 bytes, enough
for 64 Kb packets, the maximum DNS message size. No message larger than this
can be sent or received. Can be reduced to use less memory, but some requests
for DNS data, such as for huge resource records, will result in a SERVFAIL
reply to the client.
.TP
.B msg\-cache\-size: \fI<number>
Number of bytes size of the message cache. Default is 4 megabytes.
A plain number is in bytes, append 'k', 'm' or 'g' for kilobytes, megabytes
or gigabytes (1024*1024 bytes in a megabyte).
.TP
.B msg\-cache\-slabs: \fI<number>
Number of slabs in the message cache. Slabs reduce lock contention by threads.
Must be set to a power of 2. Setting (close) to the number of cpus is a
reasonable guess.
.TP
.B num\-queries\-per\-thread: \fI<number>
The number of queries that every thread will service simultaneously.
If more queries arrive that need servicing, and no queries can be jostled out
(see \fIjostle\-timeout\fR), then the queries are dropped. This forces
the client to resend after a timeout; allowing the server time to work on
the existing queries. Default depends on compile options, 512 or 1024.
.TP
.B jostle\-timeout: \fI<msec>
Timeout used when the server is very busy. Set to a value that usually
results in one roundtrip to the authority servers. If too many queries
arrive, then 50% of the queries are allowed to run to completion, and
the other 50% are replaced with the new incoming query if they have already
spent more than their allowed time. This protects against denial of
service by slow queries or high query rates. Default 200 milliseconds.
The effect is that the qps for long-lasting queries is about
(numqueriesperthread / 2) / (average time for such long queries) qps.
The qps for short queries can be about (numqueriesperthread / 2)
/ (jostletimeout in whole seconds) qps per thread, about (1024/2)*5 = 2560
qps by default.
.TP
.B delay\-close: \fI<msec>
Extra delay for timeouted UDP ports before they are closed, in msec.
Default is 0, and that disables it. This prevents very delayed answer
packets from the upstream (recursive) servers from bouncing against
closed ports and setting off all sort of close-port counters, with
eg. 1500 msec. When timeouts happen you need extra sockets, it checks
the ID and remote IP of packets, and unwanted packets are added to the
unwanted packet counter.
.TP
.B udp\-connect: \fI<yes or no>
Perform connect for UDP sockets that mitigates ICMP side channel leakage.
Default is yes.
.TP
.B unknown\-server\-time\-limit: \fI<msec>
The wait time in msec for waiting for an unknown server to reply.
Increase this if you are behind a slow satellite link, to eg. 1128.
That would then avoid re\-querying every initial query because it times out.
Default is 376 msec.
.TP
.B so\-rcvbuf: \fI<number>
If not 0, then set the SO_RCVBUF socket option to get more buffer
space on UDP port 53 incoming queries. So that short spikes on busy
servers do not drop packets (see counter in netstat \-su). Default is
0 (use system value). Otherwise, the number of bytes to ask for, try
"4m" on a busy server. The OS caps it at a maximum, on linux Unbound
needs root permission to bypass the limit, or the admin can use sysctl
net.core.rmem_max. On BSD change kern.ipc.maxsockbuf in /etc/sysctl.conf.
On OpenBSD change header and recompile kernel. On Solaris ndd \-set
/dev/udp udp_max_buf 8388608.
.TP
.B so\-sndbuf: \fI<number>
If not 0, then set the SO_SNDBUF socket option to get more buffer space on
UDP port 53 outgoing queries. This for very busy servers handles spikes
in answer traffic, otherwise 'send: resource temporarily unavailable'
can get logged, the buffer overrun is also visible by netstat \-su.
Default is 0 (use system value). Specify the number of bytes to ask
for, try "4m" on a very busy server. The OS caps it at a maximum, on
linux Unbound needs root permission to bypass the limit, or the admin
can use sysctl net.core.wmem_max. On BSD, Solaris changes are similar
to so\-rcvbuf.
.TP
.B so\-reuseport: \fI<yes or no>
If yes, then open dedicated listening sockets for incoming queries for each
thread and try to set the SO_REUSEPORT socket option on each socket. May
distribute incoming queries to threads more evenly. Default is yes.
On Linux it is supported in kernels >= 3.9. On other systems, FreeBSD, OSX
it may also work. You can enable it (on any platform and kernel),
it then attempts to open the port and passes the option if it was available
at compile time, if that works it is used, if it fails, it continues
silently (unless verbosity 3) without the option.
At extreme load it could be better to turn it off to distribute the queries
evenly, reported for Linux systems (4.4.x).
.TP
.B ip\-transparent: \fI<yes or no>
If yes, then use IP_TRANSPARENT socket option on sockets where Unbound
is listening for incoming traffic. Default no. Allows you to bind to
non\-local interfaces. For example for non\-existent IP addresses that
are going to exist later on, with host failover configuration. This is
a lot like interface\-automatic, but that one services all interfaces
and with this option you can select which (future) interfaces Unbound
provides service on. This option needs Unbound to be started with root
permissions on some systems. The option uses IP_BINDANY on FreeBSD systems
and SO_BINDANY on OpenBSD systems.
.TP
.B ip\-freebind: \fI<yes or no>
If yes, then use IP_FREEBIND socket option on sockets where Unbound
is listening to incoming traffic. Default no. Allows you to bind to
IP addresses that are nonlocal or do not exist, like when the network
interface or IP address is down. Exists only on Linux, where the similar
ip\-transparent option is also available.
.TP
.B ip-dscp: \fI<number>
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.
.TP
.B rrset\-cache\-size: \fI<number>
Number of bytes size of the RRset cache. Default is 4 megabytes.
A plain number is in bytes, append 'k', 'm' or 'g' for kilobytes, megabytes
or gigabytes (1024*1024 bytes in a megabyte).
.TP
.B rrset\-cache\-slabs: \fI<number>
Number of slabs in the RRset cache. Slabs reduce lock contention by threads.
Must be set to a power of 2.
.TP
.B cache\-max\-ttl: \fI<seconds>
Time to live maximum for RRsets and messages in the cache. Default is
86400 seconds (1 day). When the TTL expires, the cache item has expired.
Can be set lower to force the resolver to query for data often, and not
trust (very large) TTL values. Downstream clients also see the lower TTL.
.TP
.B cache\-min\-ttl: \fI<seconds>
Time to live minimum for RRsets and messages in the cache. Default is 0.
If the minimum kicks in, the data is cached for longer than the domain
owner intended, and thus less queries are made to look up the data.
Zero makes sure the data in the cache is as the domain owner intended,
higher values, especially more than an hour or so, can lead to trouble as
the data in the cache does not match up with the actual data any more.
.TP
.B cache\-max\-negative\-ttl: \fI<seconds>
Time to live maximum for negative responses, these have a SOA in the
authority section that is limited in time. Default is 3600.
This applies to nxdomain and nodata answers.
.TP
.B infra\-host\-ttl: \fI<seconds>
Time to live for entries in the host cache. The host cache contains
roundtrip timing, lameness and EDNS support information. Default is 900.
.TP
.B infra\-cache\-slabs: \fI<number>
Number of slabs in the infrastructure cache. Slabs reduce lock contention
by threads. Must be set to a power of 2.
.TP
.B infra\-cache\-numhosts: \fI<number>
Number of hosts for which information is cached. Default is 10000.
.TP
.B infra\-cache\-min\-rtt: \fI<msec>
Lower limit for dynamic retransmit timeout calculation in infrastructure
cache. Default is 50 milliseconds. Increase this value if using forwarders
needing more time to do recursive name resolution.
.TP
.B infra\-cache\-max\-rtt: \fI<msec>
Upper limit for dynamic retransmit timeout calculation in infrastructure
cache. Default is 2 minutes.
.TP
.B infra\-keep\-probing: \fI<yes or no>
If enabled the server keeps probing hosts that are down, in the one probe
at a time regime. Default is no. Hosts that are down, eg. they did
not respond during the one probe at a time period, are marked as down and
it may take \fBinfra\-host\-ttl\fR time to get probed again.
.TP
.B define\-tag: \fI<"list of tags">
Define the tags that can be used with local\-zone and access\-control.
Enclose the list between quotes ("") and put spaces between tags.
.TP
.B do\-ip4: \fI<yes or no>
Enable or disable whether ip4 queries are answered or issued. Default is yes.
.TP
.B do\-ip6: \fI<yes or no>
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
the DNS traffic, which may have ip4 and ip6 addresses in it.
.TP
.B prefer\-ip4: \fI<yes or no>
If enabled, prefer IPv4 transport for sending DNS queries to internet
nameservers. Default is no. Useful if the IPv6 netblock the server has,
the entire /64 of that is not owned by one operator and the reputation of
the netblock /64 is an issue, using IPv4 then uses the IPv4 filters that
the upstream servers have.
.TP
.B prefer\-ip6: \fI<yes or no>
If enabled, prefer IPv6 transport for sending DNS queries to internet
nameservers. Default is no.
.TP
.B do\-udp: \fI<yes or no>
Enable or disable whether UDP queries are answered or issued. Default is yes.
.TP
.B do\-tcp: \fI<yes or no>
Enable or disable whether TCP queries are answered or issued. Default is yes.
.TP
.B tcp\-mss: \fI<number>
Maximum segment size (MSS) of TCP socket on which the server responds
to queries. Value lower than common MSS on Ethernet
(1220 for example) will address path MTU problem.
Note that not all platform supports socket option to set MSS (TCP_MAXSEG).
Default is system default MSS determined by interface MTU and
negotiation between server and client.
.TP
.B outgoing\-tcp\-mss: \fI<number>
Maximum segment size (MSS) of TCP socket for outgoing queries
(from Unbound to other servers). Value lower than
common MSS on Ethernet (1220 for example) will address path MTU problem.
Note that not all platform supports socket option to set MSS (TCP_MAXSEG).
Default is system default MSS determined by interface MTU and
negotiation between Unbound and other servers.
.TP
.B tcp-idle-timeout: \fI<msec>\fR
The period Unbound will wait for a query on a TCP connection.
If this timeout expires Unbound closes the connection.
This option defaults to 30000 milliseconds.
When the number of free incoming TCP buffers falls below 50% of the
total number configured, the option value used is progressively
reduced, first to 1% of the configured value, then to 0.2% of the
configured value if the number of free buffers falls below 35% of the
total number configured, and finally to 0 if the number of free buffers
falls below 20% of the total number configured. A minimum timeout of
200 milliseconds is observed regardless of the option value used.
.TP
.B tcp-reuse-timeout: \fI<msec>\fR
The period Unbound will keep TCP persistent connections open to
authority servers. This option defaults to 60000 milliseconds.
.TP
.B max-reuse-tcp-queries: \fI<number>\fR
The maximum number of queries that can be sent on a persistent TCP
connection.
This option defaults to 200 queries.
.TP
.B tcp-auth-query-timeout: \fI<number>\fR
Timeout in milliseconds for TCP queries to auth servers.
This option defaults to 3000 milliseconds.
.TP
.B edns-tcp-keepalive: \fI<yes or no>\fR
Enable or disable EDNS TCP Keepalive. Default is no.
.TP
.B edns-tcp-keepalive-timeout: \fI<msec>\fR
The period Unbound will wait for a query on a TCP connection when
EDNS TCP Keepalive is active. If this timeout expires Unbound closes
the connection. If the client supports the EDNS TCP Keepalive option,
Unbound sends the timeout value to the client to encourage it to
close the connection before the server times out.
This option defaults to 120000 milliseconds.
When the number of free incoming TCP buffers falls below 50% of
the total number configured, the advertised timeout is progressively
reduced to 1% of the configured value, then to 0.2% of the configured
value if the number of free buffers falls below 35% of the total number
configured, and finally to 0 if the number of free buffers falls below
20% of the total number configured.
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
TCP transport only for selected forward or stub zones using forward-tcp-upstream
or stub-tcp-upstream respectively.
.TP
.B udp\-upstream\-without\-downstream: \fI<yes or no>
Enable udp upstream even if do-udp is no. Default is no, and this does not
change anything. Useful for TLS service providers, that want no udp downstream
but use udp to fetch data upstream.
.TP
.B tls\-upstream: \fI<yes or no>
Enabled or disable whether the upstream queries use TLS only for transport.
Default is no. Useful in tunneling scenarios. The TLS contains plain DNS in
TCP wireformat. The other server must support this (see
\fBtls\-service\-key\fR).
If you enable this, also configure a tls\-cert\-bundle or use tls\-win\-cert or
tls\-system\-cert to load CA certs, otherwise the connections cannot be
authenticated. This option enables TLS for all of them, but if you do not set
this you can configure TLS specifically for some forward zones with
forward\-tls\-upstream. And also with stub\-tls\-upstream.
.TP
.B ssl\-upstream: \fI<yes or no>
Alternate syntax for \fBtls\-upstream\fR. If both are present in the config
file the last is used.
.TP
.B tls\-service\-key: \fI<file>
If enabled, the server provides DNS-over-TLS or DNS-over-HTTPS service on the
TCP ports marked implicitly or explicitly for these services with tls\-port or
https\-port. The file must contain the private key for the TLS session, the
public certificate is in the tls\-service\-pem file and it must also be
specified if tls\-service\-key is specified. The default is "", turned off.
Enabling or disabling this service requires a restart (a reload is not enough),
because the key is read while root permissions are held and before chroot (if any).
The ports enabled implicitly or explicitly via \fBtls\-port:\fR and
\fBhttps\-port:\fR do not provide normal DNS TCP service. Unbound needs to be
compiled with libnghttp2 in order to provide DNS-over-HTTPS.
.TP
.B ssl\-service\-key: \fI<file>
Alternate syntax for \fBtls\-service\-key\fR.
.TP
.B tls\-service\-pem: \fI<file>
The public key certificate pem file for the tls service. Default is "",
turned off.
.TP
.B ssl\-service\-pem: \fI<file>
Alternate syntax for \fBtls\-service\-pem\fR.
.TP
.B tls\-port: \fI<number>
The port number on which to provide TCP TLS service, default 853, only
interfaces configured with that port number as @number get the TLS service.
.TP
.B ssl\-port: \fI<number>
Alternate syntax for \fBtls\-port\fR.
.TP
.B tls\-cert\-bundle: \fI<file>
If null or "", no file is used. Set it to the certificate bundle file,
for example "/etc/pki/tls/certs/ca\-bundle.crt". These certificates are used
for authenticating connections made to outside peers. For example auth\-zone
urls, and also DNS over TLS connections. It is read at start up before
permission drop and chroot.
.TP
.B ssl\-cert\-bundle: \fI<file>
Alternate syntax for \fBtls\-cert\-bundle\fR.
.TP
.B tls\-win\-cert: \fI<yes or no>
Add the system certificates to the cert bundle certificates for authentication.
If no cert bundle, it uses only these certificates. Default is no.
On windows this option uses the certificates from the cert store. Use
the tls\-cert\-bundle option on other systems. On other systems, this option
enables the system certificates.
.TP
.B tls\-system\-cert: \fI<yes or no>
This the same setting as the tls\-win\-cert setting, under a different name.
Because it is not windows specific.
.TP
.B tls\-additional\-port: \fI<portnr>
List portnumbers as tls\-additional\-port, and when interfaces are defined,
eg. with the @port suffix, as this port number, they provide dns over TLS
service. Can list multiple, each on a new statement.
.TP
.B tls-session-ticket-keys: \fI<file>
If not "", lists files with 80 bytes of random contents that are used to
perform TLS session resumption for clients using the Unbound server.
These files contain the secret key for the TLS session tickets.
First key use to encrypt and decrypt TLS session tickets.
Other keys use to decrypt only. With this you can roll over to new keys,
by generating a new first file and allowing decrypt of the old file by
listing it after the first file for some time, after the wait clients are not
using the old key any more and the old key can be removed.
One way to create the file is dd if=/dev/random bs=1 count=80 of=ticket.dat
The first 16 bytes should be different from the old one if you create a second key, that is the name used to identify the key. Then there is 32 bytes random
data for an AES key and then 32 bytes random data for the HMAC key.
.TP
.B tls\-ciphers: \fI<string with cipher list>
Set the list of ciphers to allow when serving TLS. Use "" for defaults,
and that is the default.
.TP
.B tls\-ciphersuites: \fI<string with ciphersuites list>
Set the list of ciphersuites to allow when serving TLS. This is for newer
TLS 1.3 connections. Use "" for defaults, and that is the default.
.TP
.B pad\-responses: \fI<yes or no>
If enabled, TLS serviced queries that contained an EDNS Padding option will
cause responses padded to the closest multiple of the size specified in
\fBpad\-responses\-block\-size\fR.
Default is yes.
.TP
.B pad\-responses\-block\-size: \fI<number>
The block size with which to pad responses serviced over TLS. Only responses
to padded queries will be padded.
Default is 468.
.TP
.B pad\-queries: \fI<yes or no>
If enabled, all queries sent over TLS upstreams will be padded to the closest
multiple of the size specified in \fBpad\-queries\-block\-size\fR.
Default is yes.
.TP
.B pad\-queries\-block\-size: \fI<number>
The block size with which to pad queries sent over TLS upstreams.
Default is 128.
.TP
.B tls\-use\-sni: \fI<yes or no>
Enable or disable sending the SNI extension on TLS connections.
Default is yes.
Changing the value requires a reload.
.TP
.B https\-port: \fI<number>
The port number on which to provide DNS-over-HTTPS service, default 443, only
interfaces configured with that port number as @number get the HTTPS service.
.TP
.B http\-endpoint: \fI<endpoint string>
The HTTP endpoint to provide DNS-over-HTTPS service on. Default "/dns-query".
.TP
.B http\-max\-streams: \fI<number of streams>
Number used in the SETTINGS_MAX_CONCURRENT_STREAMS parameter in the HTTP/2
SETTINGS frame for DNS-over-HTTPS connections. Default 100.
.TP
.B http\-query\-buffer\-size: \fI<size in bytes>
Maximum number of bytes used for all HTTP/2 query buffers combined. These
buffers contain (partial) DNS queries waiting for request stream completion.
An RST_STREAM frame will be send to streams exceeding this limit. Default is 4
megabytes. A plain number is in bytes, append 'k', 'm' or 'g' for kilobytes,
megabytes or gigabytes (1024*1024 bytes in a megabyte).
.TP
.B http\-response\-buffer\-size: \fI<size in bytes>
Maximum number of bytes used for all HTTP/2 response buffers combined. These
buffers contain DNS responses waiting to be written back to the clients.
An RST_STREAM frame will be send to streams exceeding this limit. Default is 4
megabytes. A plain number is in bytes, append 'k', 'm' or 'g' for kilobytes,
megabytes or gigabytes (1024*1024 bytes in a megabyte).
.TP
.B http\-nodelay: \fI<yes or no>
Set TCP_NODELAY socket option on sockets used to provide DNS-over-HTTPS service.
Ignored if the option is not available. Default is yes.
.TP
.B http\-notls\-downstream: \fI<yes or no>
Disable use of TLS for the downstream DNS-over-HTTP connections. Useful for
local back end servers. Default is no.
.TP
.B proxy\-protocol\-port: \fI<portnr>
List port numbers as proxy\-protocol\-port, and when interfaces are defined,
eg. with the @port suffix, as this port number, they support and expect PROXYv2.
In this case the proxy address will only be used for the network communication
and initial ACL (check if the proxy itself is denied/refused by configuration).
The proxied address (if any) will then be used as the true client address and
will be used where applicable for logging, ACL, DNSTAP, RPZ and IP ratelimiting.
PROXYv2 is supported for UDP and TCP/TLS listening interfaces.
There is no support for PROXYv2 on a DoH or DNSCrypt listening interface.
Can list multiple, each on a new statement.
.TP
.B use\-systemd: \fI<yes or no>
Enable or disable systemd socket activation.
Default is no.
.TP
.B do\-daemonize: \fI<yes or no>
Enable or disable whether the Unbound server forks into the background as
a daemon. Set the value to \fIno\fR when Unbound runs as systemd service.
Default is yes.
.TP
.B tcp\-connection\-limit: \fI<IP netblock> <limit>
Allow up to \fIlimit\fR simultaneous TCP connections from the given netblock.
When at the limit, further connections are accepted but closed immediately.
This option is experimental at this time.
.TP
.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, \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 \fIdeny\fR action stops queries from hosts from that netblock.
.IP
The \fIrefuse\fR action stops queries too, but sends a DNS rcode REFUSED
error message back.
.IP
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
The \fIallow\fR action does allow nonrecursive queries to access the
local\-data that is configured. The reason is that this does not involve
the Unbound server recursive lookup algorithm, and static data is served
in the reply. This supports normal operations where nonrecursive queries
are made for the authoritative data. For nonrecursive queries any replies
from the dynamic cache are refused.
.IP
The \fIallow_setrd\fR action ignores the recursion desired (RD) bit and
treats all requests as if the recursion desired bit is set. Note that this
behavior violates RFC 1034 which states that a name server should never perform
recursive service unless asked via the RD bit since this interferes with
trouble shooting of name servers and their databases. This prohibited behavior
may be useful if another DNS server must forward requests for specific
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 \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
dropping may result in (possibly excessive) retried queries.
.IP
The deny_non_local and refuse_non_local settings are for hosts that are
only allowed to query for the authoritative local\-data, they are not
allowed full recursion but only the static data. With deny_non_local,
messages that are disallowed are dropped, with refuse_non_local they
receive error code REFUSED.
.TP
.B access\-control\-tag: \fI<IP netblock> <"list of tags">
Assign tags to access-control elements. Clients using this access control
element use localzones that are tagged with one of these tags. Tags must be
defined in \fIdefine\-tags\fR. Enclose list of tags in quotes ("") and put
spaces between tags. If access\-control\-tag is configured for a netblock that
does not have an access\-control, an access\-control element with action
\fIallow\fR is configured for this netblock.
.TP
.B access\-control\-tag\-action: \fI<IP netblock> <tag> <action>
Set action for particular tag for given access control element. If you have
multiple tag values, the tag used to lookup the action is the first tag match
between access\-control\-tag and local\-zone\-tag where "first" comes from the
order of the define-tag values.
.TP
.B access\-control\-tag\-data: \fI<IP netblock> <tag> <"resource record string">
Set redirect data for particular tag for given access control element.
.TP
.B access\-control\-view: \fI<IP netblock> <view name>
Set view for given access control element.
.TP
.B interface\-action: \fI<ip address or interface name [@port]> <action>
Similar to \fBaccess\-control:\fR but for interfaces.
.IP
The action is the same as the ones defined under \fBaccess\-control:\fR.
Interfaces are \fIrefuse\fRd by default.
By default only localhost (the IP netblock, not the loopback interface) is
\fIallow\fRed through the default \fBaccess\-control:\fR behavior.
.IP
Note that the interface needs to be already specified with \fBinterface:\fR
and that any \fBaccess-control*:\fR setting overrides all \fBinterface-*:\fR
settings for targeted clients.
.TP
.B interface\-tag: \fI<ip address or interface name [@port]> <"list of tags">
Similar to \fBaccess\-control-tag:\fR but for interfaces.
.IP
Note that the interface needs to be already specified with \fBinterface:\fR
and that any \fBaccess-control*:\fR setting overrides all \fBinterface-*:\fR
settings for targeted clients.
.TP
.B interface\-tag\-action: \fI<ip address or interface name [@port]> <tag> <action>
Similar to \fBaccess\-control-tag-action:\fR but for interfaces.
.IP
Note that the interface needs to be already specified with \fBinterface:\fR
and that any \fBaccess-control*:\fR setting overrides all \fBinterface-*:\fR
settings for targeted clients.
.TP
.B interface\-tag\-data: \fI<ip address or interface name [@port]> <tag> <"resource record string">
Similar to \fBaccess\-control-tag-data:\fR but for interfaces.
.IP
Note that the interface needs to be already specified with \fBinterface:\fR
and that any \fBaccess-control*:\fR setting overrides all \fBinterface-*:\fR
settings for targeted clients.
.TP
.B interface\-view: \fI<ip address or interface name [@port]> <view name>
Similar to \fBaccess\-control-view:\fR but for interfaces.
.IP
Note that the interface needs to be already specified with \fBinterface:\fR
and that any \fBaccess-control*:\fR setting overrides all \fBinterface-*:\fR
settings for targeted clients.
.TP
.B chroot: \fI<directory>
If chroot is enabled, you should pass the configfile (from the
commandline) as a full path from the original root. After the
chroot has been performed the now defunct portion of the config
file path is removed to be able to reread the config after a reload.
.IP
All other file paths (working dir, logfile, roothints, and
key files) can be specified in several ways:
as an absolute path relative to the new root,
as a relative path to the working directory, or
as an absolute path relative to the original root.
In the last case the path is adjusted to remove the unused portion.
.IP
The pidfile can be either a relative path to the working directory, or
an absolute path relative to the original root. It is written just prior
to chroot and dropping permissions. This allows the pidfile to be
/var/run/unbound.pid and the chroot to be /var/unbound, for example. Note that
Unbound is not able to remove the pidfile after termination when it is located
outside of the chroot directory.
.IP
Additionally, Unbound may need to access /dev/urandom (for entropy)
from inside the chroot.
.IP
If given a chroot is done to the given directory. By default chroot is
enabled and the default is "@UNBOUND_CHROOT_DIR@". If you give "" no
chroot is performed.
.TP
.B username: \fI<name>
If given, after binding the port the user privileges are dropped. Default is
"@UNBOUND_USERNAME@". If you give username: "" no user change is performed.
.IP
If this user is not capable of binding the
port, reloads (by signal HUP) will still retain the opened ports.
If you change the port number in the config file, and that new port number
requires privileges, then a reload will fail; a restart is needed.
.TP
.B directory: \fI<directory>
Sets the working directory for the program. Default is "@UNBOUND_RUN_DIR@".
On Windows the string "%EXECUTABLE%" tries to change to the directory
that unbound.exe resides in.
If you give a server: directory: dir before include: file statements
then those includes can be relative to the working directory.
.TP
.B logfile: \fI<filename>
If "" is given, logging goes to stderr, or nowhere once daemonized.
The logfile is appended to, in the following format:
.nf
[seconds since 1970] unbound[pid:tid]: type: message.
.fi
If this option is given, the use\-syslog is option is set to "no".
The logfile is reopened (for append) when the config file is reread, on
SIGHUP.
.TP
.B use\-syslog: \fI<yes or no>
Sets Unbound to send log messages to the syslogd, using
\fIsyslog\fR(3).
The log facility LOG_DAEMON is used, with identity "unbound".
The logfile setting is overridden when use\-syslog is turned on.
The default is to log to syslog.
.TP
.B log\-identity: \fI<string>
If "" is given (default), then the name of the executable, usually "unbound"
is used to report to the log. Enter a string to override it
with that, which is useful on systems that run more than one instance of
Unbound, with different configurations, so that the logs can be easily
distinguished against.
.TP
.B log\-time\-ascii: \fI<yes or no>
Sets logfile lines to use a timestamp in UTC ascii. Default is no, which
prints the seconds since 1970 in brackets. No effect if using syslog, in
that case syslog formats the timestamp printed into the log files.
.TP
.B log\-queries: \fI<yes or no>
Prints one line per query to the log, with the log timestamp and IP address,
name, type and class. Default is no. Note that it takes time to print these
lines which makes the server (significantly) slower. Odd (nonprintable)
characters in names are printed as '?'.
.TP
.B log\-replies: \fI<yes or no>
Prints one line per reply to the log, with the log timestamp and IP address,
name, type, class, return code, time to resolve, from cache and response size.
Default is no. Note that it takes time to print these
lines which makes the server (significantly) slower. Odd (nonprintable)
characters in names are printed as '?'.
.TP
.B log\-tag\-queryreply: \fI<yes or no>
Prints the word 'query' and 'reply' with log\-queries and log\-replies.
This makes filtering logs easier. The default is off (for backwards
compatibility).
.TP
.B log\-local\-actions: \fI<yes or no>
Print log lines to inform about local zone actions. These lines are like the
local\-zone type inform prints out, but they are also printed for the other
types of local zones.
.TP
.B log\-servfail: \fI<yes or no>
Print log lines that say why queries return SERVFAIL to clients.
This is separate from the verbosity debug logs, much smaller, and printed
at the error level, not the info level of debug info from verbosity.
.TP
.B pidfile: \fI<filename>
The process id is written to the file. Default is "@UNBOUND_PIDFILE@".
So,
.nf
kill \-HUP `cat @UNBOUND_PIDFILE@`
.fi
triggers a reload,
.nf
kill \-TERM `cat @UNBOUND_PIDFILE@`
.fi
gracefully terminates.
.TP
.B root\-hints: \fI<filename>
Read the root hints from this file. Default is nothing, using builtin hints
for the IN class. The file has the format of zone files, with root
nameserver names and addresses only. The default may become outdated,
when servers change, therefore it is good practice to use a root\-hints file.
.TP
.B hide\-identity: \fI<yes or no>
If enabled id.server and hostname.bind queries are refused.
.TP
.B identity: \fI<string>
Set the identity to report. If set to "", the default, then the hostname
of the server is returned.
.TP
.B hide\-version: \fI<yes or no>
If enabled version.server and version.bind queries are refused.
.TP
.B version: \fI<string>
Set the version to report. If set to "", the default, then the package
version is returned.
.TP
.B hide\-http\-user\-agent: \fI<yes or no>
If enabled the HTTP header User-Agent is not set. Use with caution as some
webserver configurations may reject HTTP requests lacking this header.
If needed, it is better to explicitly set the
.B http\-user\-agent
below.
.TP
.B http\-user\-agent: \fI<string>
Set the HTTP User-Agent header for outgoing HTTP requests. If set to "",
the default, then the package name and version are used.
.TP
.B nsid:\fR <string>
Add the specified nsid to the EDNS section of the answer when queried
with an NSID EDNS enabled packet. As a sequence of hex characters or
with ascii_ prefix and then an ascii string.
.TP
.B hide\-trustanchor: \fI<yes or no>
If enabled trustanchor.unbound queries are refused.
.TP
.B target\-fetch\-policy: \fI<"list of numbers">
Set the target fetch policy used by Unbound to determine if it should fetch
nameserver target addresses opportunistically. The policy is described per
dependency depth.
.IP
The number of values determines the maximum dependency depth
that Unbound will pursue in answering a query.
A value of \-1 means to fetch all targets opportunistically for that dependency
depth. A value of 0 means to fetch on demand only. A positive value fetches
that many targets opportunistically.
.IP
Enclose the list between quotes ("") and put spaces between numbers.
The default is "3 2 1 0 0". Setting all zeroes, "0 0 0 0 0" gives behaviour
closer to that of BIND 9, while setting "\-1 \-1 \-1 \-1 \-1" gives behaviour
rumoured to be closer to that of BIND 8.
.TP
.B harden\-short\-bufsize: \fI<yes or no>
Very small EDNS buffer sizes from queries are ignored. Default is on, as
described in the standard.
.TP
.B harden\-large\-queries: \fI<yes or no>
Very large queries are ignored. Default is off, since it is legal protocol
wise to send these, and could be necessary for operation if TSIG or EDNS
payload is very large.
.TP
.B harden\-glue: \fI<yes or no>
Will trust glue only if it is within the servers authority. Default is yes.
.TP
.B harden\-dnssec\-stripped: \fI<yes or no>
Require DNSSEC data for trust\-anchored zones, if such data is absent,
the zone becomes bogus. If turned off, and no DNSSEC data is received
(or the DNSKEY data fails to validate), then the zone is made insecure,
this behaves like there is no trust anchor. You could turn this off if
you are sometimes behind an intrusive firewall (of some sort) that
removes DNSSEC data from packets, or a zone changes from signed to
unsigned to badly signed often. If turned off you run the risk of a
downgrade attack that disables security for a zone. Default is yes.
.TP
.B harden\-below\-nxdomain: \fI<yes or no>
From RFC 8020 (with title "NXDOMAIN: There Really Is Nothing Underneath"),
returns nxdomain to queries for a name
below another name that is already known to be nxdomain. DNSSEC mandates
noerror for empty nonterminals, hence this is possible. Very old software
might return nxdomain for empty nonterminals (that usually happen for reverse
IP address lookups), and thus may be incompatible with this. To try to avoid
this only DNSSEC-secure nxdomains are used, because the old software does not
have DNSSEC. Default is yes.
The nxdomain must be secure, this means nsec3 with optout is insufficient.
.TP
.B harden\-referral\-path: \fI<yes or no>
Harden the referral path by performing additional queries for
infrastructure data. Validates the replies if trust anchors are configured
and the zones are signed. This enforces DNSSEC validation on nameserver
NS sets and the nameserver addresses that are encountered on the referral
path to the answer.
Default no, because it burdens the authority servers, and it is
not RFC standard, and could lead to performance problems because of the
extra query load that is generated. Experimental option.
If you enable it consider adding more numbers after the target\-fetch\-policy
to increase the max depth that is checked to.
.TP
.B harden\-algo\-downgrade: \fI<yes or no>
Harden against algorithm downgrade when multiple algorithms are
advertised in the DS record. If no, allows the weakest algorithm to
validate the zone. Default is no. Zone signers must produce zones
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
authority servers and checks if the reply still has the correct casing.
Disabled by default.
This feature is an experimental implementation of draft dns\-0x20.
.TP
.B caps\-exempt: \fI<domain>
Exempt the domain so that it does not receive caps\-for\-id perturbed
queries. For domains that do not support 0x20 and also fail with fallback
because they keep sending different answers, like some load balancers.
Can be given multiple times, for different domains.
.TP
.B caps\-whitelist: \fI<yes or no>
Alternate syntax for \fBcaps\-exempt\fR.
.TP
.B qname\-minimisation: \fI<yes or no>
Send minimum amount of information to upstream servers to enhance privacy.
Only send minimum required labels of the QNAME and set QTYPE to A when
possible. Best effort approach; full QNAME and original QTYPE will be sent when
upstream replies with a RCODE other than NOERROR, except when receiving
NXDOMAIN from a DNSSEC signed zone. Default is yes.
.TP
.B qname\-minimisation\-strict: \fI<yes or no>
QNAME minimisation in strict mode. Do not fall-back to sending full QNAME to
potentially broken nameservers. A lot of domains will not be resolvable when
this option in enabled. Only use if you know what you are doing.
This option only has effect when qname-minimisation is enabled. Default is no.
.TP
.B aggressive\-nsec: \fI<yes or no>
Aggressive NSEC uses the DNSSEC NSEC chain to synthesize NXDOMAIN
and other denials, using information from previous NXDOMAINs answers.
Default is yes. It helps to reduce the query rate towards targets that get
a very high nonexistent name lookup rate.
.TP
.B private\-address: \fI<IP address or subnet>
Give IPv4 of IPv6 addresses or classless subnets. These are addresses
on your private network, and are not allowed to be returned for
public internet names. Any occurrence of such addresses are removed
from DNS answers. Additionally, the DNSSEC validator may mark the
answers bogus. This protects against so\-called DNS Rebinding, where
a user browser is turned into a network proxy, allowing remote access
through the browser to other parts of your private network. Some names
can be allowed to contain your private addresses, by default all the
\fBlocal\-data\fR that you configured is allowed to, and you can specify
additional names using \fBprivate\-domain\fR. No private addresses are
enabled by default. We consider to enable this for the RFC1918 private
IP address space by default in later releases. That would enable private
addresses for 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 169.254.0.0/16
fd00::/8 and fe80::/10, since the RFC standards say these addresses
should not be visible on the public internet. Turning on 127.0.0.0/8
would hinder many spamblocklists as they use that. Adding ::ffff:0:0/96
stops IPv4-mapped IPv6 addresses from bypassing the filter.
.TP
.B private\-domain: \fI<domain name>
Allow this domain, and all its subdomains to contain private addresses.
Give multiple times to allow multiple domain names to contain private
addresses. Default is none.
.TP
.B unwanted\-reply\-threshold: \fI<number>
If set, a total number of unwanted replies is kept track of in every thread.
When it reaches the threshold, a defensive action is taken and a warning
is printed to the log. The defensive action is to clear the rrset and
message caches, hopefully flushing away any poison. A value of 10 million
is suggested. Default is 0 (turned off).
.TP
.B do\-not\-query\-address: \fI<IP address>
Do not query the given IP address. Can be IP4 or IP6. Append /num to
indicate a classless delegation netblock, for example like
10.2.3.4/24 or 2001::11/64.
.TP
.B do\-not\-query\-localhost: \fI<yes or no>
If yes, localhost is added to the do\-not\-query\-address entries, both
IP6 ::1 and IP4 127.0.0.1/8. If no, then localhost can be used to send
queries to. Default is yes.
.TP
.B prefetch: \fI<yes or no>
If yes, message cache elements are prefetched before they expire to
keep the cache up to date. Default is no. Turning it on gives about
10 percent more traffic and load on the machine, but popular items do
not expire from the cache.
.TP
.B prefetch\-key: \fI<yes or no>
If yes, fetch the DNSKEYs earlier in the validation process, when a DS
record is encountered. This lowers the latency of requests. It does use
a little more CPU. Also if the cache is set to 0, it is no use. Default is no.
.TP
.B deny\-any: \fI<yes or no>
If yes, deny queries of type ANY with an empty response. Default is no.
If disabled, Unbound responds with a short list of resource records if some
can be found in the cache and makes the upstream type ANY query if there
are none.
.TP
.B rrset\-roundrobin: \fI<yes or no>
If yes, Unbound rotates RRSet order in response (the random number is taken
from the query ID, for speed and thread safety). Default is yes.
.TP
.B minimal-responses: \fI<yes or no>
If yes, Unbound does not insert authority/additional sections into response
messages when those sections are not required. This reduces response
size significantly, and may avoid TCP fallback for some responses.
This may cause a slight speedup. The default is yes, even though the DNS
protocol RFCs mandate these sections, and the additional content could
be of use and save roundtrips for clients. Because they are not used,
and the saved roundtrips are easier saved with prefetch, whilst this is
faster.
.TP
.B disable-dnssec-lame-check: \fI<yes or no>
If true, disables the DNSSEC lameness check in the iterator. This check
sees if RRSIGs are present in the answer, when dnssec is expected,
and retries another authority if RRSIGs are unexpectedly missing.
The validator will insist in RRSIGs for DNSSEC signed domains regardless
of this setting, if a trust anchor is loaded.
.TP
.B module\-config: \fI<"module names">
Module configuration, a list of module names separated by spaces, surround
the string with quotes (""). The modules can be \fIrespip\fR,
\fIvalidator\fR, or \fIiterator\fR (and possibly more, see below).
Setting this to just "\fIiterator\fR" will result in a non\-validating
server.
Setting this to "\fIvalidator iterator\fR" will turn on DNSSEC validation.
The ordering of the modules is significant, the order decides the
order of processing.
You must also set \fItrust\-anchors\fR for validation to be useful.
Adding \fIrespip\fR to the front will cause RPZ processing to be done on
all queries.
The default is "\fIvalidator iterator\fR".
.IP
When the server is built with
EDNS client subnet support the default is "\fIsubnetcache validator
iterator\fR".
Most modules that need to be listed here have to be listed at the beginning
of the line. The subnetcachedb module has to be listed just before
the iterator.
The python module can be listed in different places, it then processes the
output of the module it is just before. The dynlib module can be listed pretty
much anywhere, it is only a very thin wrapper that allows dynamic libraries to
run in its place.
.TP
.B trust\-anchor\-file: \fI<filename>
File with trusted keys for validation. Both DS and DNSKEY entries can appear
in the file. The format of the file is the standard DNS Zone file format.
Default is "", or no trust anchor file.
.TP
.B auto\-trust\-anchor\-file: \fI<filename>
File with trust anchor for one zone, which is tracked with RFC5011 probes.
The probes are run several times per month, thus the machine must be online
frequently. The initial file can be one with contents as described in
\fBtrust\-anchor\-file\fR. The file is written to when the anchor is updated,
so the Unbound user must have write permission. Write permission to the file,
but also to the directory it is in (to create a temporary file, which is
necessary to deal with filesystem full events), it must also be inside the
chroot (if that is used).
.TP
.B trust\-anchor: \fI<"Resource Record">
A DS or DNSKEY RR for a key to use for validation. Multiple entries can be
given to specify multiple trusted keys, in addition to the trust\-anchor\-files.
The resource record is entered in the same format as 'dig' or 'drill' prints
them, the same format as in the zone file. Has to be on a single line, with
"" around it. A TTL can be specified for ease of cut and paste, but is ignored.
A class can be specified, but class IN is default.
.TP
.B trusted\-keys\-file: \fI<filename>
File with trusted keys for validation. Specify more than one file
with several entries, one file per entry. Like \fBtrust\-anchor\-file\fR
but has a different file format. Format is BIND\-9 style format,
the trusted\-keys { name flag proto algo "key"; }; clauses are read.
It is possible to use wildcards with this statement, the wildcard is
expanded on start and on reload.
.TP
.B trust\-anchor\-signaling: \fI<yes or no>
Send RFC8145 key tag query after trust anchor priming. Default is yes.
.TP
.B root\-key\-sentinel: \fI<yes or no>
Root key trust anchor sentinel. Default is yes.
.TP
.B domain\-insecure: \fI<domain name>
Sets domain name to be insecure, DNSSEC chain of trust is ignored towards
the domain name. So a trust anchor above the domain name can not make the
domain secure with a DS record, such a DS record is then ignored.
Can be given multiple times
to specify multiple domains that are treated as if unsigned. If you set
trust anchors for the domain they override this setting (and the domain
is secured).
.IP
This can be useful if you want to make sure a trust anchor for external
lookups does not affect an (unsigned) internal domain. A DS record
externally can create validation failures for that internal domain.
.TP
.B val\-override\-date: \fI<rrsig\-style date spec>
Default is "" or "0", which disables this debugging feature. If enabled by
giving a RRSIG style date, that date is used for verifying RRSIG inception
and expiration dates, instead of the current date. Do not set this unless
you are debugging signature inception and expiration. The value \-1 ignores
the date altogether, useful for some special applications.
.TP
.B val\-sig\-skew\-min: \fI<seconds>
Minimum number of seconds of clock skew to apply to validated signatures.
A value of 10% of the signature lifetime (expiration \- inception) is
used, capped by this setting. Default is 3600 (1 hour) which allows for
daylight savings differences. Lower this value for more strict checking
of short lived signatures.
.TP
.B val\-sig\-skew\-max: \fI<seconds>
Maximum number of seconds of clock skew to apply to validated signatures.
A value of 10% of the signature lifetime (expiration \- inception)
is used, capped by this setting. Default is 86400 (24 hours) which
allows for timezone setting problems in stable domains. Setting both
min and max very low disables the clock skew allowances. Setting both
min and max very high makes the validator check the signature timestamps
less strictly.
.TP
.B val\-max\-restart: \fI<number>
The maximum number the validator should restart validation with
another authority in case of failed validation. Default is 5.
.TP
.B val\-bogus\-ttl: \fI<number>
The time to live for bogus data. This is data that has failed validation;
due to invalid signatures or other checks. The TTL from that data cannot be
trusted, and this value is used instead. The value is in seconds, default 60.
The time interval prevents repeated revalidation of bogus data.
.TP
.B val\-clean\-additional: \fI<yes or no>
Instruct the validator to remove data from the additional section of secure
messages that are not signed properly. Messages that are insecure, bogus,
indeterminate or unchecked are not affected. Default is yes. Use this setting
to protect the users that rely on this validator for authentication from
potentially bad data in the additional section.
.TP
.B val\-log\-level: \fI<number>
Have the validator print validation failures to the log. Regardless of
the verbosity setting. Default is 0, off. At 1, for every user query
that fails a line is printed to the logs. This way you can monitor what
happens with validation. Use a diagnosis tool, such as dig or drill,
to find out why validation is failing for these queries. At 2, not only
the query that failed is printed but also the reason why Unbound thought
it was wrong and which server sent the faulty data.
.TP
.B val\-permissive\-mode: \fI<yes or no>
Instruct the validator to mark bogus messages as indeterminate. The security
checks are performed, but if the result is bogus (failed security), the
reply is not withheld from the client with SERVFAIL as usual. The client
receives the bogus data. For messages that are found to be secure the AD bit
is set in replies. Also logging is performed as for full validation.
The default value is "no".
.TP
.B ignore\-cd\-flag: \fI<yes or no>
Instruct Unbound to ignore the CD flag from clients and refuse to
return bogus answers to them. Thus, the CD (Checking Disabled) flag
does not disable checking any more. This is useful if legacy (w2008)
servers that set the CD flag but cannot validate DNSSEC themselves are
the clients, and then Unbound provides them with DNSSEC protection.
The default value is "no".
.TP
+.B disable\-edns\-do: \fI<yes or no>
+Disable the EDNS DO flag in upstream requests.
+It breaks DNSSEC validation for Unbound's clients.
+This results in the upstream name servers to not include DNSSEC records in
+their replies and could be helpful for devices that cannot handle DNSSEC
+information.
+When the option is enabled, clients that set the DO flag receive no EDNS
+record in the response to indicate the lack of support to them.
+If this option is enabled but Unbound is already configured for DNSSEC
+validation (i.e., the validator module is enabled; default) this option is
+implicitly turned off with a warning as to not break DNSSEC validation in
+Unbound.
+Default is no.
+.TP
.B serve\-expired: \fI<yes or no>
If enabled, Unbound attempts to serve old responses from cache with a
TTL of \fBserve\-expired\-reply\-ttl\fR in the response without waiting for the
actual resolution to finish. The actual resolution answer ends up in the cache
later on. Default is "no".
.TP
.B serve\-expired\-ttl: \fI<seconds>
Limit serving of expired responses to configured seconds after expiration. 0
disables the limit. This option only applies when \fBserve\-expired\fR is
enabled. A suggested value per RFC 8767 is between
86400 (1 day) and 259200 (3 days). The default is 0.
.TP
.B serve\-expired\-ttl\-reset: \fI<yes or no>
Set the TTL of expired records to the \fBserve\-expired\-ttl\fR value after a
failed attempt to retrieve the record from upstream. This makes sure that the
expired records will be served as long as there are queries for it. Default is
"no".
.TP
.B serve\-expired\-reply\-ttl: \fI<seconds>
TTL value to use when replying with expired data. If
\fBserve\-expired\-client\-timeout\fR is also used then it is RECOMMENDED to
use 30 as the value (RFC 8767). The default is 30.
.TP
.B serve\-expired\-client\-timeout: \fI<msec>
Time in milliseconds before replying to the client with expired data. This
essentially enables the serve-stale behavior as specified in
RFC 8767 that first tries to resolve before immediately
responding with expired data. A recommended value per
RFC 8767 is 1800. Setting this to 0 will disable this
behavior. Default is 0.
.TP
.B serve\-original\-ttl: \fI<yes or no>
If enabled, Unbound will always return the original TTL as received from
the upstream name server rather than the decrementing TTL as
stored in the cache. This feature may be useful if Unbound serves as a
front-end to a hidden authoritative name server. Enabling this feature does
not impact cache expiry, it only changes the TTL Unbound embeds in responses to
queries. Note that enabling this feature implicitly disables enforcement of
the configured minimum and maximum TTL, as it is assumed users who enable this
feature do not want Unbound to change the TTL obtained from an upstream server.
Thus, the values set using \fBcache\-min\-ttl\fR and \fBcache\-max\-ttl\fR are
ignored.
Default is "no".
.TP
.B val\-nsec3\-keysize\-iterations: \fI<"list of values">
List of keysize and iteration count values, separated by spaces, surrounded
by quotes. Default is "1024 150 2048 150 4096 150". This determines the
maximum allowed NSEC3 iteration count before a message is simply marked
insecure instead of performing the many hashing iterations. The list must
be in ascending order and have at least one entry. If you set it to
"1024 65535" there is no restriction to NSEC3 iteration values.
This table must be kept short; a very long list could cause slower operation.
.TP
.B zonemd\-permissive\-mode: \fI<yes or no>
If enabled the ZONEMD verification failures are only logged and do not cause
the zone to be blocked and only return servfail. Useful for testing out
if it works, or if the operator only wants to be notified of a problem without
disrupting service. Default is no.
.TP
.B add\-holddown: \fI<seconds>
Instruct the \fBauto\-trust\-anchor\-file\fR probe mechanism for RFC5011
autotrust updates to add new trust anchors only after they have been
visible for this time. Default is 30 days as per the RFC.
.TP
.B del\-holddown: \fI<seconds>
Instruct the \fBauto\-trust\-anchor\-file\fR probe mechanism for RFC5011
autotrust updates to remove revoked trust anchors after they have been
kept in the revoked list for this long. Default is 30 days as per
the RFC.
.TP
.B keep\-missing: \fI<seconds>
Instruct the \fBauto\-trust\-anchor\-file\fR probe mechanism for RFC5011
autotrust updates to remove missing trust anchors after they have been
unseen for this long. This cleans up the state file if the target zone
does not perform trust anchor revocation, so this makes the auto probe
mechanism work with zones that perform regular (non\-5011) rollovers.
The default is 366 days. The value 0 does not remove missing anchors,
as per the RFC.
.TP
.B permit\-small\-holddown: \fI<yes or no>
Debug option that allows the autotrust 5011 rollover timers to assume
very small values. Default is no.
.TP
.B key\-cache\-size: \fI<number>
Number of bytes size of the key cache. Default is 4 megabytes.
A plain number is in bytes, append 'k', 'm' or 'g' for kilobytes, megabytes
or gigabytes (1024*1024 bytes in a megabyte).
.TP
.B key\-cache\-slabs: \fI<number>
Number of slabs in the key cache. Slabs reduce lock contention by threads.
Must be set to a power of 2. Setting (close) to the number of cpus is a
reasonable guess.
.TP
.B neg\-cache\-size: \fI<number>
Number of bytes size of the aggressive negative cache. Default is 1 megabyte.
A plain number is in bytes, append 'k', 'm' or 'g' for kilobytes, megabytes
or gigabytes (1024*1024 bytes in a megabyte).
.TP
.B unblock\-lan\-zones: \fI<yes or no>
Default is disabled. If enabled, then for private address space,
the reverse lookups are no longer filtered. This allows Unbound when
running as dns service on a host where it provides service for that host,
to put out all of the queries for the 'lan' upstream. When enabled,
only localhost, 127.0.0.1 reverse and ::1 reverse zones are configured
with default local zones. Disable the option when Unbound is running
as a (DHCP-) DNS network resolver for a group of machines, where such
lookups should be filtered (RFC compliance), this also stops potential
data leakage about the local network to the upstream DNS servers.
.TP
.B insecure\-lan\-zones: \fI<yes or no>
Default is disabled. If enabled, then reverse lookups in private
address space are not validated. This is usually required whenever
\fIunblock\-lan\-zones\fR is used.
.TP
.B local\-zone: \fI<zone> <type>
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, 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
it as detailed in the stub zone section below. A stub\-zone can be used to
have unbound send queries to another server, an authoritative server, to
fetch the information. With a forward\-zone, unbound sends queries to a server
that is a recursive server to fetch the information. With an auth\-zone a
zone can be loaded from file and used, it can be used like a local\-zone
for users downstream, or the auth\-zone information can be used to fetch
information from when resolving like it is an upstream server. The
forward\-zone and auth\-zone options are described in their sections below.
If you want to perform filtering of the information that the users can fetch,
the local\-zone and local\-data statements allow for this, but also the
rpz functionality can be used, described in the RPZ section.
.TP 10
\h'5'\fIdeny\fR
Do not send an answer, drop the query.
If there is a match from local data, the query is answered.
.TP 10
\h'5'\fIrefuse\fR
Send an error message reply, with rcode REFUSED.
If there is a match from local data, the query is answered.
.TP 10
\h'5'\fIstatic\fR
If there is a match from local data, the query is answered.
Otherwise, the query is answered with nodata or nxdomain.
For a negative answer a SOA is included in the answer if present
as local\-data for the zone apex domain.
.TP 10
\h'5'\fItransparent\fR
If there is a match from local data, the query is answered.
Otherwise if the query has a different name, the query is resolved normally.
If the query is for a name given in localdata but no such type of data is
given in localdata, then a noerror nodata answer is returned.
If no local\-zone is given local\-data causes a transparent zone
to be created by default.
.TP 10
\h'5'\fItypetransparent\fR
If there is a match from local data, the query is answered. If the query
is for a different name, or for the same name but for a different type,
the query is resolved normally. So, similar to transparent but types
that are not listed in local data are resolved normally, so if an A record
is in the local data that does not cause a nodata reply for AAAA queries.
.TP 10
\h'5'\fIredirect\fR
The query is answered from the local data for the zone name.
There may be no local data beneath the zone name.
This answers queries for the zone, and all subdomains of the zone
with the local data for the zone.
It can be used to redirect a domain to return a different address record
to the end user, with
local\-zone: "example.com." redirect and
local\-data: "example.com. A 127.0.0.1"
queries for www.example.com and www.foo.example.com are redirected, so
that users with web browsers cannot access sites with suffix example.com.
.TP 10
\h'5'\fIinform\fR
The query is answered normally, same as transparent. The client IP
address (@portnumber) is printed to the logfile. The log message is:
timestamp, unbound-pid, info: zonename inform IP@port queryname type
class. This option can be used for normal resolution, but machines
looking up infected names are logged, eg. to run antivirus on them.
.TP 10
\h'5'\fIinform_deny\fR
The query is dropped, like 'deny', and logged, like 'inform'. Ie. find
infected machines without answering the queries.
.TP 10
\h'5'\fIinform_redirect\fR
The query is redirected, like 'redirect', and logged, like 'inform'.
Ie. answer queries with fixed data and also log the machines that ask.
.TP 10
\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
\h'5'\fIalways_nxdomain\fR
Like static, but ignores local data and returns nxdomain for the query.
.TP 10
\h'5'\fIalways_nodata\fR
Like static, but ignores local data and returns nodata for the query.
.TP 10
\h'5'\fIalways_deny\fR
Like deny, but ignores local data and drops the query.
.TP 10
\h'5'\fIalways_null\fR
Always returns 0.0.0.0 or ::0 for every name in the zone. Like redirect
with zero data for A and AAAA. Ignores local data in the zone. Used for
some block lists.
.TP 10
\h'5'\fInoview\fR
Breaks out of that view and moves towards the global local zones for answer
to the query. If the view first is no, it'll resolve normally. If view first
is enabled, it'll break perform that step and check the global answers.
For when the view has view specific overrides but some zone has to be
answered from global local zone contents.
.TP 10
\h'5'\fInodefault\fR
Used to turn off default contents for AS112 zones. The other types
also turn off default contents for the zone. The 'nodefault' option
has no other effect than turning off default contents for the
given zone. Use \fInodefault\fR if you use exactly that zone, if you want to
use a subzone, use \fItransparent\fR.
.P
The default zones are localhost, reverse 127.0.0.1 and ::1, the home.arpa,
the onion, test, invalid and the AS112 zones. The AS112 zones are reverse
DNS zones for private use and reserved IP addresses for which the servers
on the internet cannot provide correct answers. They are configured by
default to give nxdomain (no reverse information) answers. The defaults
can be turned off by specifying your own local\-zone of that name, or
using the 'nodefault' type. Below is a list of the default zone contents.
.TP 10
\h'5'\fIlocalhost\fR
The IP4 and IP6 localhost information is given. NS and SOA records are provided
for completeness and to satisfy some DNS update tools. Default content:
.nf
local\-zone: "localhost." redirect
local\-data: "localhost. 10800 IN NS localhost."
local\-data: "localhost. 10800 IN
SOA localhost. nobody.invalid. 1 3600 1200 604800 10800"
local\-data: "localhost. 10800 IN A 127.0.0.1"
local\-data: "localhost. 10800 IN AAAA ::1"
.fi
.TP 10
\h'5'\fIreverse IPv4 loopback\fR
Default content:
.nf
local\-zone: "127.in\-addr.arpa." static
local\-data: "127.in\-addr.arpa. 10800 IN NS localhost."
local\-data: "127.in\-addr.arpa. 10800 IN
SOA localhost. nobody.invalid. 1 3600 1200 604800 10800"
local\-data: "1.0.0.127.in\-addr.arpa. 10800 IN
PTR localhost."
.fi
.TP 10
\h'5'\fIreverse IPv6 loopback\fR
Default content:
.nf
local\-zone: "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.
0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa." static
local\-data: "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.
0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. 10800 IN
NS localhost."
local\-data: "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.
0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. 10800 IN
SOA localhost. nobody.invalid. 1 3600 1200 604800 10800"
local\-data: "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.
0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. 10800 IN
PTR localhost."
.fi
.TP 10
\h'5'\fIhome.arpa (RFC 8375)\fR
Default content:
.nf
local\-zone: "home.arpa." static
local\-data: "home.arpa. 10800 IN NS localhost."
local\-data: "home.arpa. 10800 IN
SOA localhost. nobody.invalid. 1 3600 1200 604800 10800"
.fi
.TP 10
\h'5'\fIonion (RFC 7686)\fR
Default content:
.nf
local\-zone: "onion." static
local\-data: "onion. 10800 IN NS localhost."
local\-data: "onion. 10800 IN
SOA localhost. nobody.invalid. 1 3600 1200 604800 10800"
.fi
.TP 10
\h'5'\fItest (RFC 6761)\fR
Default content:
.nf
local\-zone: "test." static
local\-data: "test. 10800 IN NS localhost."
local\-data: "test. 10800 IN
SOA localhost. nobody.invalid. 1 3600 1200 604800 10800"
.fi
.TP 10
\h'5'\fIinvalid (RFC 6761)\fR
Default content:
.nf
local\-zone: "invalid." static
local\-data: "invalid. 10800 IN NS localhost."
local\-data: "invalid. 10800 IN
SOA localhost. nobody.invalid. 1 3600 1200 604800 10800"
.fi
.TP 10
\h'5'\fIreverse RFC1918 local use zones\fR
Reverse data for zones 10.in\-addr.arpa, 16.172.in\-addr.arpa to
31.172.in\-addr.arpa, 168.192.in\-addr.arpa.
The \fBlocal\-zone:\fR is set static and as \fBlocal\-data:\fR SOA and NS
records are provided.
.TP 10
\h'5'\fIreverse RFC3330 IP4 this, link\-local, testnet and broadcast\fR
Reverse data for zones 0.in\-addr.arpa, 254.169.in\-addr.arpa,
2.0.192.in\-addr.arpa (TEST NET 1), 100.51.198.in\-addr.arpa (TEST NET 2),
113.0.203.in\-addr.arpa (TEST NET 3), 255.255.255.255.in\-addr.arpa.
And from 64.100.in\-addr.arpa to 127.100.in\-addr.arpa (Shared Address Space).
.TP 10
\h'5'\fIreverse RFC4291 IP6 unspecified\fR
Reverse data for zone
.nf
0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.
0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.
.fi
.TP 10
\h'5'\fIreverse RFC4193 IPv6 Locally Assigned Local Addresses\fR
Reverse data for zone D.F.ip6.arpa.
.TP 10
\h'5'\fIreverse RFC4291 IPv6 Link Local Addresses\fR
Reverse data for zones 8.E.F.ip6.arpa to B.E.F.ip6.arpa.
.TP 10
\h'5'\fIreverse IPv6 Example Prefix\fR
Reverse data for zone 8.B.D.0.1.0.0.2.ip6.arpa. This zone is used for
tutorials and examples. You can remove the block on this zone with:
.nf
local\-zone: 8.B.D.0.1.0.0.2.ip6.arpa. nodefault
.fi
You can also selectively unblock a part of the zone by making that part
transparent with a local\-zone statement.
This also works with the other default zones.
.\" End of local-zone listing.
.TP 5
.B local\-data: \fI"<resource record string>"
Configure local data, which is served in reply to queries for it.
The query has to match exactly unless you configure the local\-zone as
redirect. If not matched exactly, the local\-zone type determines
further processing. If local\-data is configured that is not a subdomain of
a local\-zone, a transparent local\-zone is configured.
For record types such as TXT, use single quotes, as in
local\-data: 'example. TXT "text"'.
.IP
If you need more complicated authoritative data, with referrals, wildcards,
CNAME/DNAME support, or DNSSEC authoritative service, setup a stub\-zone for
it as detailed in the stub zone section below.
.TP 5
.B local\-data\-ptr: \fI"IPaddr name"
Configure local data shorthand for a PTR record with the reversed IPv4 or
IPv6 address and the host name. For example "192.0.2.4 www.example.com".
TTL can be inserted like this: "2001:DB8::4 7200 www.example.com"
.TP 5
.B local\-zone\-tag: \fI<zone> <"list of tags">
Assign tags to localzones. Tagged localzones will only be applied when the
used access-control element has a matching tag. Tags must be defined in
\fIdefine\-tags\fR. Enclose list of tags in quotes ("") and put spaces between
tags. When there are multiple tags it checks if the intersection of the
list of tags for the query and local\-zone\-tag is non-empty.
.TP 5
.B local\-zone\-override: \fI<zone> <IP netblock> <type>
Override the localzone type for queries from addresses matching netblock.
Use this localzone type, regardless the type configured for the local-zone
(both tagged and untagged) and regardless the type configured using
access\-control\-tag\-action.
.TP 5
.B response\-ip: \fI<IP-netblock> <action>
This requires use of the "respip" module.
.IP
If the IP address in an AAAA or A RR in the answer section of a
response matches the specified IP netblock, the specified action will
apply.
\fI<action>\fR has generally the same semantics as that for
\fIaccess-control-tag-action\fR, but there are some exceptions.
.IP
Actions for \fIresponse-ip\fR are different from those for
\fIlocal-zone\fR in that in case of the former there is no point of
such conditions as "the query matches it but there is no local data".
Because of this difference, the semantics of \fIresponse-ip\fR actions
are modified or simplified as follows: The \fIstatic, refuse,
transparent, typetransparent,\fR and \fInodefault\fR actions are
invalid for \fIresponse-ip\fR.
Using any of these will cause the configuration to be rejected as
faulty. The \fIdeny\fR action is non-conditional, i.e. it always
results in dropping the corresponding query.
The resolution result before applying the deny action is still cached
and can be used for other queries.
.TP 5
.B response-ip-data: \fI<IP-netblock> <"resource record string">
This requires use of the "respip" module.
.IP
This specifies the action data for \fIresponse-ip\fR with action being
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
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.
Also, CNAME and other types of records must not coexist for the same
IP-netblock, following the normal rules for CNAME records.
The textual domain name for the CNAME does not have to be explicitly
terminated with a dot ("."); the root name is assumed to be the origin
for the name.
.TP 5
.B response-ip-tag: \fI<IP-netblock> <"list of tags">
This requires use of the "respip" module.
.IP
Assign tags to response IP-netblocks. If the IP address in an AAAA or
A RR in the answer section of a response matches the specified
IP-netblock, the specified tags are assigned to the IP address.
Then, if an \fIaccess-control-tag\fR is defined for the client and it
includes one of the tags for the response IP, the corresponding
\fIaccess-control-tag-action\fR will apply.
Tag matching rule is the same as that for \fIaccess-control-tag\fR and
\fIlocal-zones\fR.
Unlike \fIlocal-zone-tag\fR, \fIresponse-ip-tag\fR can be defined for
an IP-netblock even if no \fIresponse-ip\fR is defined for that
netblock.
If multiple \fIresponse-ip-tag\fR options are specified for the same
IP-netblock in different statements, all but the first will be
ignored.
However, this will not be flagged as a configuration error, but the
result is probably not what was intended.
.IP
Actions specified in an
\fIaccess-control-tag-action\fR that has a matching tag with
\fIresponse-ip-tag\fR can be those that are "invalid" for
\fIresponse-ip\fR listed above, since \fIaccess-control-tag-action\fRs
can be shared with local zones.
For these actions, if they behave differently depending on whether
local data exists or not in case of local zones, the behavior for
\fIresponse-ip-data\fR will generally result in NOERROR/NODATA instead
of NXDOMAIN, since the \fIresponse-ip\fR data are inherently type
specific, and non-existence of data does not indicate anything about
the existence or non-existence of the qname itself.
For example, if the matching tag action is \fIstatic\fR but there is
no data for the corresponding \fIresponse-ip\fR configuration, then
the result will be NOERROR/NODATA.
The only case where NXDOMAIN is returned is when an
\fIalways_nxdomain\fR action applies.
.TP 5
.B ratelimit: \fI<number or 0>
Enable ratelimiting of queries sent to nameserver for performing recursion.
If 0, the default, it is disabled. This option is experimental at this time.
The ratelimit is in queries per second that are allowed. More queries are
turned away with an error (servfail). This stops recursive floods, eg. random
query names, but not spoofed reflection floods. Cached responses are not
ratelimited by this setting. The zone of the query is determined by examining
the nameservers for it, the zone name is used to keep track of the rate.
For example, 1000 may be a suitable value to stop the server from being
overloaded with random names, and keeps Unbound from sending traffic to the
nameservers for those zones. Configured forwarders are excluded from
ratelimiting.
.TP 5
.B ratelimit\-size: \fI<memory size>
Give the size of the data structure in which the current ongoing rates are
kept track in. Default 4m. In bytes or use m(mega), k(kilo), g(giga).
The ratelimit structure is small, so this data structure likely does
not need to be large.
.TP 5
.B ratelimit\-slabs: \fI<number>
Give power of 2 number of slabs, this is used to reduce lock contention
in the ratelimit tracking data structure. Close to the number of cpus is
a fairly good setting.
.TP 5
.B ratelimit\-factor: \fI<number>
Set the amount of queries to rate limit when the limit is exceeded.
If set to 0, all queries are dropped for domains where the limit is
exceeded. If set to another value, 1 in that number is allowed through
to complete. Default is 10, allowing 1/10 traffic to flow normally.
This can make ordinary queries complete (if repeatedly queried for),
and enter the cache, whilst also mitigating the traffic flow by the
factor given.
.TP 5
.B ratelimit\-backoff: \fI<yes or no>
If enabled, the ratelimit is treated as a hard failure instead of the default
maximum allowed constant rate. When the limit is reached, traffic is
ratelimited and demand continues to be kept track of for a 2 second rate
window. No traffic is allowed, except for ratelimit\-factor, until demand
decreases below the configured ratelimit for a 2 second rate window. Useful to
set ratelimit to a suspicious rate to aggressively limit unusually high
traffic. Default is off.
.TP 5
.B ratelimit\-for\-domain: \fI<domain> <number qps or 0>
Override the global ratelimit for an exact match domain name with the listed
number. You can give this for any number of names. For example, for
a top\-level\-domain you may want to have a higher limit than other names.
A value of 0 will disable ratelimiting for that domain.
.TP 5
.B ratelimit\-below\-domain: \fI<domain> <number qps or 0>
Override the global ratelimit for a domain name that ends in this name.
You can give this multiple times, it then describes different settings
in different parts of the namespace. The closest matching suffix is used
to determine the qps limit. The rate for the exact matching domain name
is not changed, use ratelimit\-for\-domain to set that, you might want
to use different settings for a top\-level\-domain and subdomains.
A value of 0 will disable ratelimiting for domain names that end in this name.
.TP 5
.B ip\-ratelimit: \fI<number or 0>
Enable global ratelimiting of queries accepted per IP address.
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
kept track in. Default 4m. In bytes or use m(mega), k(kilo), g(giga).
The ip ratelimit structure is small, so this data structure likely does
not need to be large.
.TP 5
.B ip\-ratelimit\-slabs: \fI<number>
Give power of 2 number of slabs, this is used to reduce lock contention
in the ip ratelimit tracking data structure. Close to the number of cpus is
a fairly good setting.
.TP 5
.B ip\-ratelimit\-factor: \fI<number>
Set the amount of queries to rate limit when the limit is exceeded.
If set to 0, all queries are dropped for addresses where the limit is
exceeded. If set to another value, 1 in that number is allowed through
to complete. Default is 10, allowing 1/10 traffic to flow normally.
This can make ordinary queries complete (if repeatedly queried for),
and enter the cache, whilst also mitigating the traffic flow by the
factor given.
.TP 5
.B ip\-ratelimit\-backoff: \fI<yes or no>
If enabled, the ratelimit is treated as a hard failure instead of the default
maximum allowed constant rate. When the limit is reached, traffic is
ratelimited and demand continues to be kept track of for a 2 second rate
window. No traffic is allowed, except for ip\-ratelimit\-factor, until demand
decreases below the configured ratelimit for a 2 second rate window. Useful to
set ip\-ratelimit to a suspicious rate to aggressively limit unusually high
traffic. Default is off.
.TP 5
.B outbound\-msg\-retry: \fI<number>
The number of retries, per upstream nameserver in a delegation, that Unbound
will attempt in case a throwaway response is received.
No response (timeout) contributes to the retry counter.
If a forward/stub zone is used, this is the number of retries per nameserver in
the zone.
Default is 5.
.TP 5
.B 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
servers 90 percent of the time, and would perform normal exploration of random
servers for the remaining time. When prefetch is enabled (or serve\-expired),
such prefetches are not sped up, because there is no one waiting for it, and it
presents a good moment to perform server exploration. The
\fBfast\-server\-num\fR option can be used to specify the size of the fastest
servers set. The default for fast\-server\-permil is 0.
.TP 5
.B fast\-server\-num: \fI<number>
Set the number of servers that should be used for fast server selection. Only
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
option can be used multiple times. The most specific match will be used.
.TP 5
.B edns\-client\-string\-opcode: \fI<opcode>
EDNS0 option code for the \fIedns\-client\-string\fR option, from 0 to 65535.
A value from the `Reserved for Local/Experimental` range (65001-65534) should
be used. Default is 65001.
.TP 5
.B ede: \fI<yes or no>
If enabled, Unbound will respond with Extended DNS Error codes (RFC8914).
These EDEs attach informative error messages to a response for various
errors. Default is "no".
When the \fBval-log-level\fR option is also set to \fB2\fR, responses with
Extended DNS Errors concerning DNSSEC failures that are not served from cache,
will also contain a descriptive text message about the reason for the failure.
.TP 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
the EDE code without setting the global \fBede\fR option to "yes" as well.
Default is "no".
.SS "Remote Control Options"
In the
.B remote\-control:
clause are the declarations for the remote control facility. If this is
enabled, the \fIunbound\-control\fR(8) utility can be used to send
commands to the running Unbound server. The server uses these clauses
to setup TLSv1 security for the connection. The
\fIunbound\-control\fR(8) utility also reads the \fBremote\-control\fR
section for options. To setup the correct self\-signed certificates use the
\fIunbound\-control\-setup\fR(8) utility.
.TP 5
.B control\-enable: \fI<yes or no>
The option is used to enable remote control, default is "no".
If turned off, the server does not listen for control commands.
.TP 5
.B control\-interface: \fI<ip address or interface name or path>
Give IPv4 or IPv6 addresses or local socket path to listen on for
control commands.
If an interface name is used instead of an ip address, the list of ip addresses
on that interface are used.
By default localhost (127.0.0.1 and ::1) is listened to.
Use 0.0.0.0 and ::0 to listen to all interfaces.
If you change this and permissions have been dropped, you must restart
the server for the change to take effect.
.IP
If you set it to an absolute path, a unix domain socket is used. This socket
does not use the certificates and keys, so those files need not be present.
To restrict access, Unbound sets permissions on the file to the user and
group that is configured, the access bits are set to allow the group members
to access the control socket file. Put users that need to access the socket
in the that group. To restrict access further, create a directory to put
the control socket in and restrict access to that directory.
.TP 5
.B control\-port: \fI<port number>
The port number to listen on for IPv4 or IPv6 control interfaces,
default is 8953.
If you change this and permissions have been dropped, you must restart
the server for the change to take effect.
.TP 5
.B control\-use\-cert: \fI<yes or no>
For localhost control-interface you can disable the use of TLS by setting
this option to "no", default is "yes". For local sockets, TLS is disabled
and the value of this option is ignored.
.TP 5
.B server\-key\-file: \fI<private key file>
Path to the server private key, by default unbound_server.key.
This file is generated by the \fIunbound\-control\-setup\fR utility.
This file is used by the Unbound server, but not by \fIunbound\-control\fR.
.TP 5
.B server\-cert\-file: \fI<certificate file.pem>
Path to the server self signed certificate, by default unbound_server.pem.
This file is generated by the \fIunbound\-control\-setup\fR utility.
This file is used by the Unbound server, and also by \fIunbound\-control\fR.
.TP 5
.B control\-key\-file: \fI<private key file>
Path to the control client private key, by default unbound_control.key.
This file is generated by the \fIunbound\-control\-setup\fR utility.
This file is used by \fIunbound\-control\fR.
.TP 5
.B control\-cert\-file: \fI<certificate file.pem>
Path to the control client certificate, by default unbound_control.pem.
This certificate has to be signed with the server certificate.
This file is generated by the \fIunbound\-control\-setup\fR utility.
This file is used by \fIunbound\-control\fR.
.SS "Stub Zone Options"
.LP
There may be multiple
.B stub\-zone:
clauses. Each with a name: and zero or more hostnames or IP addresses.
For the stub zone this list of nameservers is used. Class IN is assumed.
The servers should be authority servers, not recursors; Unbound performs
the recursive processing itself for stub zones.
.P
The stub zone can be used to configure authoritative data to be used
by the resolver that cannot be accessed using the public internet servers.
This is useful for company\-local data or private zones. Setup an
authoritative server on a different host (or different port). Enter a config
entry for Unbound with
.B stub\-addr:
<ip address of host[@port]>.
The Unbound resolver can then access the data, without referring to the
public internet for it.
.P
This setup allows DNSSEC signed zones to be served by that
authoritative server, in which case a trusted key entry with the public key
can be put in config, so that Unbound can validate the data and set the AD
bit on replies for the private zone (authoritative servers do not set the
AD bit). This setup makes Unbound capable of answering queries for the
private zone, and can even set the AD bit ('authentic'), but the AA
('authoritative') bit is not set on these replies.
.P
Consider adding \fBserver:\fR statements for \fBdomain\-insecure:\fR and
for \fBlocal\-zone:\fI name nodefault\fR for the zone if it is a locally
served zone. The insecure clause stops DNSSEC from invalidating the
zone. The local zone nodefault (or \fItransparent\fR) clause makes the
(reverse\-) zone bypass Unbound's filtering of RFC1918 zones.
.TP
.B name: \fI<domain name>
Name of the stub zone. This is the full domain name of the zone.
.TP
.B stub\-host: \fI<domain name>
Name of stub zone nameserver. Is itself resolved before it is used.
To use a nondefault port for DNS communication append '@' with the port number.
If tls is enabled, then you can append a '#' and a name, then it'll check the
tls authentication certificates with that name. If you combine the '@'
and '#', the '@' comes first. If only '#' is used the default port is the
configured tls\-port.
.TP
.B stub\-addr: \fI<IP address>
IP address of stub zone nameserver. Can be IP 4 or IP 6.
To use a nondefault port for DNS communication append '@' with the port number.
If tls is enabled, then you can append a '#' and a name, then it'll check the
tls authentication certificates with that name. If you combine the '@'
and '#', the '@' comes first. If only '#' is used the default port is the
configured tls\-port.
.TP
.B stub\-prime: \fI<yes or no>
This option is by default no. If enabled it performs NS set priming,
which is similar to root hints, where it starts using the list of nameservers
currently published by the zone. Thus, if the hint list is slightly outdated,
the resolver picks up a correct list online.
.TP
.B stub\-first: \fI<yes or no>
If enabled, a query is attempted without the stub clause if it fails.
The data could not be retrieved and would have caused SERVFAIL because
the servers are unreachable, instead it is tried without this clause.
The default is no.
.TP
.B stub\-tls\-upstream: \fI<yes or no>
Enabled or disable whether the queries to this stub use TLS for transport.
Default is no.
.TP
.B stub\-ssl\-upstream: \fI<yes or no>
Alternate syntax for \fBstub\-tls\-upstream\fR.
.TP
.B stub\-tcp\-upstream: \fI<yes or no>
If it is set to "yes" then upstream queries use TCP only for transport regardless of global flag tcp-upstream.
Default is no.
.TP
.B stub\-no\-cache: \fI<yes or no>
Default is no. If enabled, data inside the stub is not cached. This is
useful when you want immediate changes to be visible.
.SS "Forward Zone Options"
.LP
There may be multiple
.B forward\-zone:
clauses. Each with a \fBname:\fR and zero or more hostnames or IP
addresses. For the forward zone this list of nameservers is used to
forward the queries to. The servers listed as \fBforward\-host:\fR and
\fBforward\-addr:\fR have to handle further recursion for the query. Thus,
those servers are not authority servers, but are (just like Unbound is)
recursive servers too; Unbound does not perform recursion itself for the
forward zone, it lets the remote server do it. Class IN is assumed.
CNAMEs are chased by Unbound itself, asking the remote server for every
name in the indirection chain, to protect the local cache from illegal
indirect referenced items.
A forward\-zone entry with name "." and a forward\-addr target will
forward all queries to that other server (unless it can answer from
the cache).
.TP
.B name: \fI<domain name>
Name of the forward zone. This is the full domain name of the zone.
.TP
.B forward\-host: \fI<domain name>
Name of server to forward to. Is itself resolved before it is used.
To use a nondefault port for DNS communication append '@' with the port number.
If tls is enabled, then you can append a '#' and a name, then it'll check the
tls authentication certificates with that name. If you combine the '@'
and '#', the '@' comes first. If only '#' is used the default port is the
configured tls\-port.
.TP
.B forward\-addr: \fI<IP address>
IP address of server to forward to. Can be IP 4 or IP 6.
To use a nondefault port for DNS communication append '@' with the port number.
If tls is enabled, then you can append a '#' and a name, then it'll check the
tls authentication certificates with that name. If you combine the '@'
and '#', the '@' comes first. If only '#' is used the default port is the
configured tls\-port.
.IP
At high verbosity it logs the TLS certificate, with TLS enabled.
If you leave out the '#' and auth name from the forward\-addr, any
name is accepted. The cert must also match a CA from the tls\-cert\-bundle.
.TP
.B forward\-first: \fI<yes or no>
If a forwarded query is met with a SERVFAIL error, and this option is
enabled, Unbound will fall back to normal recursive resolution for this
query as if no query forwarding had been specified. The default is "no".
.TP
.B forward\-tls\-upstream: \fI<yes or no>
Enabled or disable whether the queries to this forwarder use TLS for transport.
Default is no.
If you enable this, also configure a tls\-cert\-bundle or use tls\-win\-cert to
load CA certs, otherwise the connections cannot be authenticated.
.TP
.B forward\-ssl\-upstream: \fI<yes or no>
Alternate syntax for \fBforward\-tls\-upstream\fR.
.TP
.B forward\-tcp\-upstream: \fI<yes or no>
If it is set to "yes" then upstream queries use TCP only for transport regardless of global flag tcp-upstream.
Default is no.
.TP
.B forward\-no\-cache: \fI<yes or no>
Default is no. If enabled, data inside the forward is not cached. This is
useful when you want immediate changes to be visible.
.SS "Authority Zone Options"
.LP
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 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
uses the SOA timer values and performs SOA UDP queries to detect zone changes.
.LP
If the update fetch fails, the timers in the SOA record are used to time
another fetch attempt. Until the SOA expiry timer is reached. Then the
zone is expired. When a zone is expired, queries are SERVFAIL, and
any new serial number is accepted from the primary (even if older), and if
fallback is enabled, the fallback activates to fetch from the upstream instead
of the SERVFAIL.
.TP
.B name: \fI<zone name>
Name of the authority zone.
.TP
.B primary: \fI<IP address or host name>
Where to download a copy of the zone from, with AXFR and IXFR. Multiple
primaries can be specified. They are all tried if one fails.
To use a nondefault port for DNS communication append '@' with the port number.
You can append a '#' and a name, then AXFR over TLS can be used and the tls authentication certificates will be checked with that name. If you combine
the '@' and '#', the '@' comes first.
If you point it at another Unbound instance, it would not work because
that does not support AXFR/IXFR for the zone, but if you used \fBurl:\fR to download
the zonefile as a text file from a webserver that would work.
If you specify the hostname, you cannot use the domain from the zonefile,
because it may not have that when retrieving that data, instead use a plain
IP address to avoid a circular dependency on retrieving that IP address.
.TP
.B master: \fI<IP address or host name>
Alternate syntax for \fBprimary\fR.
.TP
.B url: \fI<url to zonefile>
Where to download a zonefile for the zone. With http or https. An example
for the url is "http://www.example.com/example.org.zone". Multiple url
statements can be given, they are tried in turn. If only urls are given
the SOA refresh timer is used to wait for making new downloads. If also
primaries are listed, the primaries are first probed with UDP SOA queries to
see if the SOA serial number has changed, reducing the number of downloads.
If none of the urls work, the primaries are tried with IXFR and AXFR.
For https, the \fBtls\-cert\-bundle\fR and the hostname from the url are used
to authenticate the connection.
If you specify a hostname in the URL, you cannot use the domain from the
zonefile, because it may not have that when retrieving that data, instead
use a plain IP address to avoid a circular dependency on retrieving that IP
address. Avoid dependencies on name lookups by using a notation like
"http://192.0.2.1/unbound-primaries/example.com.zone", with an explicit IP address.
.TP
.B allow\-notify: \fI<IP address or host name or netblockIP/prefix>
With allow\-notify you can specify additional sources of notifies.
When notified, the server attempts to first probe and then zone transfer.
If the notify is from a primary, it first attempts that primary. Otherwise
other primaries are attempted. If there are no primaries, but only urls, the
file is downloaded when notified. The primaries from primary: and url:
statements are allowed notify by default.
.TP
.B fallback\-enabled: \fI<yes or no>
Default no. If enabled, Unbound falls back to querying the internet as
a resolver for this zone when lookups fail. For example for DNSSEC
validation failures.
.TP
.B for\-downstream: \fI<yes or no>
Default yes. If enabled, Unbound serves authority responses to
downstream clients for this zone. This option makes Unbound behave, for
the queries with names in this zone, like one of the authority servers for
that zone. Turn it off if you want Unbound to provide recursion for the
zone but have a local copy of zone data. If for\-downstream is no and
for\-upstream is yes, then Unbound will DNSSEC validate the contents of the
zone before serving the zone contents to clients and store validation
results in the cache.
.TP
.B for\-upstream: \fI<yes or no>
Default yes. If enabled, Unbound fetches data from this data collection
for answering recursion queries. Instead of sending queries over the internet
to the authority servers for this zone, it'll fetch the data directly from
the zone data. Turn it on when you want Unbound to provide recursion for
downstream clients, and use the zone data as a local copy to speed up lookups.
.TP
.B zonemd\-check: \fI<yes or no>
Enable this option to check ZONEMD records in the zone. Default is disabled.
The ZONEMD record is a checksum over the zone data. This includes glue in
the zone and data from the zone file, and excludes comments from the zone file.
When there is a DNSSEC chain of trust, DNSSEC signatures are checked too.
.TP
.B zonemd\-reject\-absence: \fI<yes or no>
Enable this option to reject the absence of the ZONEMD record. Without it,
when zonemd is not there it is not checked. It is useful to enable for a
nonDNSSEC signed zone where the operator wants to require the verification
of a ZONEMD, hence a missing ZONEMD is a failure. The action upon
failure is controlled by the \fBzonemd\-permissive\-mode\fR option, for
log only or also block the zone. The default is no.
.IP
Without the option absence of a ZONEMD is only a failure when the zone is
DNSSEC signed, and we have a trust anchor, and the DNSSEC verification of
the absence of the ZONEMD fails. With the option enabled, the absence of
a ZONEMD is always a failure, also for nonDNSSEC signed zones.
.TP
.B zonefile: \fI<filename>
The filename where the zone is stored. If not given then no zonefile is used.
If the file does not exist or is empty, Unbound will attempt to fetch zone
data (eg. from the primary servers).
.SS "View Options"
.LP
There may be multiple
.B view:
clauses. Each with a \fBname:\fR and zero or more \fBlocal\-zone\fR and
\fBlocal\-data\fR elements. Views can also contain view\-first,
response\-ip, response\-ip\-data and local\-data\-ptr elements.
View can be mapped to requests by specifying the
view name in an \fBaccess\-control\-view\fR element. Options from matching
views will override global options. Global options will be used if no matching
view is found, or when the matching view does not have the option specified.
.TP
.B name: \fI<view name>
Name of the view. Must be unique. This name is used in access\-control\-view
elements.
.TP
.B local\-zone: \fI<zone> <type>
View specific local\-zone elements. Has the same types and behaviour as the
global local\-zone elements. When there is at least one local\-zone specified
and view\-first is no, the default local-zones will be added to this view.
Defaults can be disabled using the nodefault type. When view\-first is yes or
when a view does not have a local\-zone, the global local\-zone will be used
including it's default zones.
.TP
.B local\-data: \fI"<resource record string>"
View specific local\-data elements. Has the same behaviour as the global
local\-data elements.
.TP
.B local\-data\-ptr: \fI"IPaddr name"
View specific local\-data\-ptr elements. Has the same behaviour as the global
local\-data\-ptr elements.
.TP
.B view\-first: \fI<yes or no>
If enabled, it attempts to use the global local\-zone and local\-data if there
is no match in the view specific options.
The default is no.
.SS "Python Module Options"
.LP
The
.B python:
clause gives the settings for the \fIpython\fR(1) script module. This module
acts like the iterator and validator modules do, on queries and answers.
To enable the script module it has to be compiled into the daemon,
and the word "python" has to be put in the \fBmodule\-config:\fR option
(usually first, or between the validator and iterator). Multiple instances of
the python module are supported by adding the word "python" more than once.
.LP
If the \fBchroot:\fR option is enabled, you should make sure Python's
library directory structure is bind mounted in the new root environment, see
\fImount\fR(8). Also the \fBpython\-script:\fR path should be specified as an
absolute path relative to the new root, or as a relative path to the working
directory.
.TP
.B python\-script: \fI<python file>\fR
The script file to load. Repeat this option for every python module instance
added to the \fBmodule\-config:\fR option.
.SS "Dynamic Library Module Options"
.LP
The
.B dynlib:
clause gives the settings for the \fIdynlib\fR module. This module is only
a very small wrapper that allows dynamic modules to be loaded on runtime
instead of being compiled into the application. To enable the dynlib module it
has to be compiled into the daemon, and the word "dynlib" has to be put in the
\fBmodule\-config:\fR option. Multiple instances of dynamic libraries are
supported by adding the word "dynlib" more than once.
.LP
The \fBdynlib\-file:\fR path should be specified as an absolute path relative
to the new path set by \fBchroot:\fR option, or as a relative path to the
working directory.
.TP
.B dynlib\-file: \fI<dynlib file>\fR
The dynamic library file to load. Repeat this option for every dynlib module
instance added to the \fBmodule\-config:\fR option.
.SS "DNS64 Module Options"
.LP
The dns64 module must be configured in the \fBmodule\-config:\fR "dns64
validator iterator" directive and be compiled into the daemon to be
enabled. These settings go in the \fBserver:\fR section.
.TP
.B dns64\-prefix: \fI<IPv6 prefix>\fR
This sets the DNS64 prefix to use to synthesize AAAA records with.
It must be /96 or shorter. The default prefix is 64:ff9b::/96.
.TP
.B dns64\-synthall: \fI<yes or no>\fR
Debug option, default no. If enabled, synthesize all AAAA records
despite the presence of actual AAAA records.
.TP
.B dns64\-ignore\-aaaa: \fI<name>\fR
List domain for which the AAAA records are ignored and the A record is
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
.B dnscrypt:
clause gives the settings of the dnscrypt channel. While those options are
available, they are only meaningful if Unbound was compiled with
\fB\-\-enable\-dnscrypt\fR.
Currently certificate and secret/public keys cannot be generated by Unbound.
You can use dnscrypt-wrapper to generate those: https://github.com/cofyc/\
dnscrypt-wrapper/blob/master/README.md#usage
.TP
.B dnscrypt\-enable: \fI<yes or no>\fR
Whether or not the \fBdnscrypt\fR config should be enabled. You may define
configuration but not activate it.
The default is no.
.TP
.B dnscrypt\-port: \fI<port number>
On which port should \fBdnscrypt\fR should be activated. Note that you should
have a matching \fBinterface\fR option defined in the \fBserver\fR section for
this port.
.TP
.B dnscrypt\-provider: \fI<provider name>\fR
The provider name to use to distribute certificates. This is of the form:
\fB2.dnscrypt-cert.example.com.\fR. The name \fIMUST\fR end with a dot.
.TP
.B dnscrypt\-secret\-key: \fI<path to secret key file>\fR
Path to the time limited secret key file. This option may be specified multiple
times.
.TP
.B dnscrypt\-provider\-cert: \fI<path to cert file>\fR
Path to the certificate related to the \fBdnscrypt\-secret\-key\fRs.
This option may be specified multiple times.
.TP
.B dnscrypt\-provider\-cert\-rotated: \fI<path to cert file>\fR
Path to a certificate that we should be able to serve existing connection from
but do not want to advertise over \fBdnscrypt\-provider\fR's TXT record certs
distribution.
A typical use case is when rotating certificates, existing clients may still use
the client magic from the old cert in their queries until they fetch and update
the new cert. Likewise, it would allow one to prime the new cert/key without
distributing the new cert yet, this can be useful when using a network of
servers using anycast and on which the configuration may not get updated at the
exact same time. By priming the cert, the servers can handle both old and new
certs traffic while distributing only one.
This option may be specified multiple times.
.TP
.B dnscrypt\-shared\-secret\-cache\-size: \fI<memory size>
Give the size of the data structure in which the shared secret keys are kept
in. Default 4m. In bytes or use m(mega), k(kilo), g(giga).
The shared secret cache is used when a same client is making multiple queries
using the same public key. It saves a substantial amount of CPU.
.TP
.B dnscrypt\-shared\-secret\-cache\-slabs: \fI<number>
Give power of 2 number of slabs, this is used to reduce lock contention
in the dnscrypt shared secrets cache. Close to the number of cpus is
a fairly good setting.
.TP
.B dnscrypt\-nonce\-cache\-size: \fI<memory size>
Give the size of the data structure in which the client nonces are kept in.
Default 4m. In bytes or use m(mega), k(kilo), g(giga).
The nonce cache is used to prevent dnscrypt message replaying. Client nonce
should be unique for any pair of client pk/server sk.
.TP
.B dnscrypt\-nonce\-cache\-slabs: \fI<number>
Give power of 2 number of slabs, this is used to reduce lock contention
in the dnscrypt nonce cache. Close to the number of cpus is
a fairly good setting.
.SS "EDNS Client Subnet Module Options"
.LP
The ECS module must be configured in the \fBmodule\-config:\fR "subnetcache
validator iterator" directive and be compiled into the daemon to be
enabled. These settings go in the \fBserver:\fR section.
.LP
If the destination address is allowed in the configuration Unbound will add the
EDNS0 option to the query containing the relevant part of the client's address.
When an answer contains the ECS option the response and the option are placed in
a specialized cache. If the authority indicated no support, the response is
stored in the regular cache.
.LP
Additionally, when a client includes the option in its queries, Unbound will
forward the option when sending the query to addresses that are explicitly
allowed in the configuration using \fBsend\-client\-subnet\fR. The option will
always be forwarded, regardless the allowed addresses, if
\fBclient\-subnet\-always\-forward\fR is set to yes. In this case the lookup in
the regular cache is skipped.
.LP
The maximum size of the ECS cache is controlled by 'msg-cache-size' in 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
classless delegation netblock, for example like 10.2.3.4/24 or 2001::11/64. Can
be given multiple times. Authorities not listed will not receive edns-subnet
information, unless domain in query is specified in \fBclient\-subnet\-zone\fR.
.TP
.B client\-subnet\-zone: \fI<domain>\fR
Send client source address in queries for this domain and its subdomains. Can be
given multiple times. Zones not listed will not receive edns-subnet information,
unless hosted by authority specified in \fBsend\-client\-subnet\fR.
.TP
.B client\-subnet\-always\-forward: \fI<yes or no>\fR
Specify whether the ECS address check (configured using
\fBsend\-client\-subnet\fR) is applied for all queries, even if the triggering
query contains an ECS record, or only for queries for which the ECS record is
generated using the querier address (and therefore did not contain ECS data in
the client query). If enabled, the address check is skipped when the client
query contains an ECS record. And the lookup in the regular cache is skipped.
Default is no.
.TP
.B max\-client\-subnet\-ipv6: \fI<number>\fR
Specifies the maximum prefix length of the client source address we are willing
to expose to third parties for IPv6. Defaults to 56.
.TP
.B max\-client\-subnet\-ipv4: \fI<number>\fR
Specifies the maximum prefix length of the client source address we are willing
to expose to third parties for IPv4. Defaults to 24.
.TP
.B min\-client\-subnet\-ipv6: \fI<number>\fR
Specifies the minimum prefix length of the IPv6 source mask we are willing to
accept in queries. Shorter source masks result in REFUSED answers. Source mask
of 0 is always accepted. Default is 0.
.TP
.B min\-client\-subnet\-ipv4: \fI<number>\fR
Specifies the minimum prefix length of the IPv4 source mask we are willing to
accept in queries. Shorter source masks result in REFUSED answers. Source mask
of 0 is always accepted. Default is 0.
.TP
.B max\-ecs\-tree\-size\-ipv4: \fI<number>\fR
Specifies the maximum number of subnets ECS answers kept in the ECS radix tree.
This number applies for each qname/qclass/qtype tuple. Defaults to 100.
.TP
.B max\-ecs\-tree\-size\-ipv6: \fI<number>\fR
Specifies the maximum number of subnets ECS answers kept in the ECS radix tree.
This number applies for each qname/qclass/qtype tuple. Defaults to 100.
.SS "Opportunistic IPsec Support Module Options"
.LP
The IPsec module must be configured in the \fBmodule\-config:\fR "ipsecmod
validator iterator" directive and be compiled into Unbound by using
\fB\-\-enable\-ipsecmod\fR to be enabled.
These settings go in the \fBserver:\fR section.
.LP
When Unbound receives an A/AAAA query that is not in the cache and finds a
valid answer, it will withhold returning the answer and instead will generate
an IPSECKEY subquery for the same domain name. If an answer was found, Unbound
will call an external hook passing the following arguments:
.TP 10
\h'5'\fIQNAME\fR
Domain name of the A/AAAA and IPSECKEY query. In string format.
.TP 10
\h'5'\fIIPSECKEY TTL\fR
TTL of the IPSECKEY RRset.
.TP 10
\h'5'\fIA/AAAA\fR
String of space separated IP addresses present in the A/AAAA RRset. The IP
addresses are in string format.
.TP 10
\h'5'\fIIPSECKEY\fR
String of space separated IPSECKEY RDATA present in the IPSECKEY RRset. The
IPSECKEY RDATA are in DNS presentation format.
.LP
The A/AAAA answer is then cached and returned to the client. If the external
hook was called the TTL changes to ensure it doesn't surpass
\fBipsecmod-max-ttl\fR.
.LP
The same procedure is also followed when \fBprefetch:\fR is used, but the
A/AAAA answer is given to the client before the hook is called.
\fBipsecmod-max-ttl\fR ensures that the A/AAAA answer given from cache is still
relevant for opportunistic IPsec.
.TP
.B ipsecmod-enabled: \fI<yes or no>\fR
Specifies whether the IPsec module is enabled or not. The IPsec module still
needs to be defined in the \fBmodule\-config:\fR directive. This option
facilitates turning on/off the module without restarting/reloading Unbound.
Defaults to yes.
.TP
.B ipsecmod\-hook: \fI<filename>\fR
Specifies the external hook that Unbound will call with \fIsystem\fR(3). The
file can be specified as an absolute/relative path. The file needs the proper
permissions to be able to be executed by the same user that runs Unbound. It
must be present when the IPsec module is defined in the \fBmodule\-config:\fR
directive.
.TP
.B ipsecmod-strict: \fI<yes or no>\fR
If enabled Unbound requires the external hook to return a success value of 0.
Failing to do so Unbound will reply with SERVFAIL. The A/AAAA answer will also
not be cached. Defaults to no.
.TP
.B ipsecmod\-max-ttl: \fI<seconds>\fR
Time to live maximum for A/AAAA cached records after calling the external hook.
Defaults to 3600.
.TP
.B ipsecmod-ignore-bogus: \fI<yes or no>\fR
Specifies the behaviour of Unbound when the IPSECKEY answer is bogus. If set
to yes, the hook will be called and the A/AAAA answer will be returned to the
client. If set to no, the hook will not be called and the answer to the
A/AAAA query will be SERVFAIL. Mainly used for testing. Defaults to no.
.TP
.B ipsecmod\-allow: \fI<domain>\fR
Allow the ipsecmod functionality for the domain so that the module logic will be
executed. Can be given multiple times, for different domains. If the option is
not specified, all domains are treated as being allowed (default).
.TP
.B ipsecmod\-whitelist: \fI<yes or no>
Alternate syntax for \fBipsecmod\-allow\fR.
.SS "Cache DB Module Options"
.LP
The Cache DB module must be configured in the \fBmodule\-config:\fR
"validator cachedb iterator" directive and be compiled into the daemon
with \fB\-\-enable\-cachedb\fR.
If this module is enabled and configured, the specified backend database
works as a second level cache:
When Unbound cannot find an answer to a query in its built-in in-memory
cache, it consults the specified backend.
If it finds a valid answer in the backend, Unbound uses it to respond
to the query without performing iterative DNS resolution.
If Unbound cannot even find an answer in the backend, it resolves the
query as usual, and stores the answer in the backend.
.P
This module interacts with the \fBserve\-expired\-*\fR options and will reply
with expired data if Unbound is configured for that. Currently the use
of \fBserve\-expired\-client\-timeout:\fR and
\fBserve\-expired\-reply\-ttl:\fR is not consistent for data originating from
the external cache as these will result in a reply with 0 TTL without trying to
update the data first, ignoring the configured values.
.P
If Unbound was built with
\fB\-\-with\-libhiredis\fR
on a system that has installed the hiredis C client library of Redis,
then the "redis" backend can be used.
This backend communicates with the specified Redis server over a TCP
connection to store and retrieve cache data.
It can be used as a persistent and/or shared cache backend.
It should be noted that Unbound never removes data stored in the Redis server,
even if some data have expired in terms of DNS TTL or the Redis server has
cached too much data;
if necessary the Redis server must be configured to limit the cache size,
preferably with some kind of least-recently-used eviction policy.
Additionally, the \fBredis\-expire\-records\fR option can be used in order to
set the relative DNS TTL of the message as timeout to the Redis records; keep
in mind that some additional memory is used per key and that the expire
information is stored as absolute Unix timestamps in Redis (computer time must
be stable).
This backend uses synchronous communication with the Redis server
based on the assumption that the communication is stable and sufficiently
fast.
The thread waiting for a response from the Redis server cannot handle
other DNS queries.
Although the backend has the ability to reconnect to the server when
the connection is closed unexpectedly and there is a configurable timeout
in case the server is overly slow or hangs up, these cases are assumed
to be very rare.
If connection close or timeout happens too often, Unbound will be
effectively unusable with this backend.
It's the administrator's responsibility to make the assumption hold.
.P
The
.B cachedb:
clause gives custom settings of the cache DB module.
.TP
.B backend: \fI<backend name>\fR
Specify the backend database name.
The default database is the in-memory backend named "testframe", which,
as the name suggests, is not of any practical use.
Depending on the build-time configuration, "redis" backend may also be
used as described above.
.TP
.B secret-seed: \fI<"secret string">\fR
Specify a seed to calculate a hash value from query information.
This value will be used as the key of the corresponding answer for the
backend database and can be customized if the hash should not be predictable
operationally.
If the backend database is shared by multiple Unbound instances,
all instances must use the same secret seed.
This option defaults to "default".
+.TP
+.B cachedb-no-store: \fI<yes or no>\fR
+If the backend should be read from, but not written to. This makes this
+instance not store dns messages in the backend. But if data is available it
+is retrieved. The default is no.
.P
The following
.B cachedb
options are specific to the redis backend.
.TP
.B redis-server-host: \fI<server address or name>\fR
The IP (either v6 or v4) address or domain name of the Redis server.
In general an IP address should be specified as otherwise Unbound will have to
resolve the name of the server every time it establishes a connection
to the server.
This option defaults to "127.0.0.1".
.TP
.B redis-server-port: \fI<port number>\fR
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
if the Redis server does not have the requested data, and will try to
re-establish a new connection later.
This option defaults to 100 milliseconds.
.TP
.B redis-expire-records: \fI<yes or no>
If Redis record expiration is enabled. If yes, Unbound sets timeout for Redis
records so that Redis can evict keys that have expired automatically. If
Unbound is configured with \fBserve-expired\fR and \fBserve-expired-ttl\fR is 0,
this option is internally reverted to "no". Redis SETEX support is required
for this option (Redis >= 2.0.0).
This option defaults to no.
+.TP
+.B redis-logical-db: \fI<logical database index>
+The logical database in Redis to use.
+These are databases in the same Redis instance sharing the same configuration
+and persisted in the same RDB/AOF file.
+If unsure about using this option, Redis documentation
+(https://redis.io/commands/select/) suggests not to use a single Redis instance
+for multiple unrelated applications.
+The default database in Redis is 0 while other logical databases need to be
+explicitly SELECT'ed upon connecting.
+This option defaults to 0.
.SS DNSTAP Logging Options
DNSTAP support, when compiled in by using \fB\-\-enable\-dnstap\fR, is enabled
in the \fBdnstap:\fR section.
This starts an extra thread (when compiled with threading) that writes
the log information to the destination. If Unbound is compiled without
threading it does not spawn a thread, but connects per-process to the
destination.
.TP
.B dnstap-enable: \fI<yes or no>
If dnstap is enabled. Default no. If yes, it connects to the dnstap server
and if any of the dnstap-log-..-messages options is enabled it sends logs
for those messages to the server.
.TP
.B dnstap-bidirectional: \fI<yes or no>
Use frame streams in bidirectional mode to transfer DNSTAP messages. Default is
yes.
.TP
.B dnstap-socket-path: \fI<file name>
Sets the unix socket file name for connecting to the server that is
listening on that socket. Default is "@DNSTAP_SOCKET_PATH@".
.TP
.B dnstap-ip: \fI<IPaddress[@port]>
If "", the unix socket is used, if set with an IP address (IPv4 or IPv6)
that address is used to connect to the server.
.TP
.B dnstap-tls: \fI<yes or no>
Set this to use TLS to connect to the server specified in \fBdnstap-ip\fR.
The default is yes. If set to no, TCP is used to connect to the server.
.TP
.B dnstap-tls-server-name: \fI<name of TLS authentication>
The TLS server name to authenticate the server with. Used when \fBdnstap-tls\fR is enabled. If "" it is ignored, default "".
.TP
.B dnstap-tls-cert-bundle: \fI<file name of cert bundle>
The pem file with certs to verify the TLS server certificate. If "" the
server default cert bundle is used, or the windows cert bundle on windows.
Default is "".
.TP
.B dnstap-tls-client-key-file: \fI<file name>
The client key file for TLS client authentication. If "" client
authentication is not used. Default is "".
.TP
.B dnstap-tls-client-cert-file: \fI<file name>
The client cert file for TLS client authentication. Default is "".
.TP
.B dnstap-send-identity: \fI<yes or no>
If enabled, the server identity is included in the log messages.
Default is no.
.TP
.B dnstap-send-version: \fI<yes or no>
If enabled, the server version if included in the log messages.
Default is no.
.TP
.B dnstap-identity: \fI<string>
The identity to send with messages, if "" the hostname is used.
Default is "".
.TP
.B dnstap-version: \fI<string>
The version to send with messages, if "" the package version is used.
Default is "".
.TP
.B dnstap-log-resolver-query-messages: \fI<yes or no>
Enable to log resolver query messages. Default is no.
These are messages from Unbound to upstream servers.
.TP
.B dnstap-log-resolver-response-messages: \fI<yes or no>
Enable to log resolver response messages. Default is no.
These are replies from upstream servers to Unbound.
.TP
.B dnstap-log-client-query-messages: \fI<yes or no>
Enable to log client query messages. Default is no.
These are client queries to Unbound.
.TP
.B dnstap-log-client-response-messages: \fI<yes or no>
Enable to log client response messages. Default is no.
These are responses from Unbound to clients.
.TP
.B dnstap-log-forwarder-query-messages: \fI<yes or no>
Enable to log forwarder query messages. Default is no.
.TP
.B dnstap-log-forwarder-response-messages: \fI<yes or no>
Enable to log forwarder response messages. Default is no.
.SS Response Policy Zone Options
.LP
Response Policy Zones are configured with \fBrpz:\fR, and each one must have a
\fBname:\fR. There can be multiple ones, by listing multiple rpz clauses, each
with a different name. RPZ clauses are applied in order of configuration. The
\fBrespip\fR module needs to be added to the \fBmodule-config\fR, e.g.:
\fBmodule-config: "respip validator iterator"\fR.
.P
QNAME, Response IP Address, nsdname, nsip and clientip triggers are supported.
Supported actions are: NXDOMAIN, NODATA, PASSTHRU, DROP, Local Data, tcp\-only
and drop. RPZ QNAME triggers are applied after \fBlocal\-zones\fR and
before \fBauth\-zones\fR.
.P
The rpz zone is formatted with a SOA start record as usual. The items in
the zone are entries, that specify what to act on (the trigger) and what to
do (the action). The trigger to act on is recorded in the name, the action
to do is recorded as the resource record. The names all end in the zone
name, so you could type the trigger names without a trailing dot in the
zonefile.
.P
An example RPZ record, that answers example.com with NXDOMAIN
.nf
example.com CNAME .
.fi
.P
The triggers are encoded in the name on the left
.nf
name query name
netblock.rpz-client-ip client IP address
netblock.rpz-ip response IP address in the answer
name.rpz-nsdname nameserver name
netblock.rpz-nsip nameserver IP address
.fi
The netblock is written as <netblocklen>.<ip address in reverse>.
For IPv6 use 'zz' for '::'. Specify individual addresses with scope length
of 32 or 128. For example, 24.10.100.51.198.rpz-ip is 198.51.100.10/24 and
32.10.zz.db8.2001.rpz-ip is 2001:db8:0:0:0:0:0:10/32.
.P
The actions are specified with the record on the right
.nf
CNAME . nxdomain reply
CNAME *. nodata reply
CNAME rpz-passthru. do nothing, allow to continue
CNAME rpz-drop. the query is dropped
CNAME rpz-tcp-only. answer over TCP
A 192.0.2.1 answer with this IP address
.fi
Other records like AAAA, TXT and other CNAMEs (not rpz-..) can also be used to
answer queries with that content.
.P
The RPZ zones can be configured in the config file with these settings in the \fBrpz:\fR block.
.TP
.B name: \fI<zone name>
Name of the authority zone.
.TP
.B primary: \fI<IP address or host name>
Where to download a copy of the zone from, with AXFR and IXFR. Multiple
primaries can be specified. They are all tried if one fails.
To use a nondefault port for DNS communication append '@' with the port number.
You can append a '#' and a name, then AXFR over TLS can be used and the tls authentication certificates will be checked with that name. If you combine
the '@' and '#', the '@' comes first.
If you point it at another Unbound instance, it would not work because
that does not support AXFR/IXFR for the zone, but if you used \fBurl:\fR to download
the zonefile as a text file from a webserver that would work.
If you specify the hostname, you cannot use the domain from the zonefile,
because it may not have that when retrieving that data, instead use a plain
IP address to avoid a circular dependency on retrieving that IP address.
.TP
.B master: \fI<IP address or host name>
Alternate syntax for \fBprimary\fR.
.TP
.B url: \fI<url to zonefile>
Where to download a zonefile for the zone. With http or https. An example
for the url is "http://www.example.com/example.org.zone". Multiple url
statements can be given, they are tried in turn. If only urls are given
the SOA refresh timer is used to wait for making new downloads. If also
primaries are listed, the primaries are first probed with UDP SOA queries to
see if the SOA serial number has changed, reducing the number of downloads.
If none of the urls work, the primaries are tried with IXFR and AXFR.
For https, the \fBtls\-cert\-bundle\fR and the hostname from the url are used
to authenticate the connection.
.TP
.B allow\-notify: \fI<IP address or host name or netblockIP/prefix>
With allow\-notify you can specify additional sources of notifies.
When notified, the server attempts to first probe and then zone transfer.
If the notify is from a primary, it first attempts that primary. Otherwise
other primaries are attempted. If there are no primaries, but only urls, the
file is downloaded when notified. The primaries from primary: and url:
statements are allowed notify by default.
.TP
.B zonefile: \fI<filename>
The filename where the zone is stored. If not given then no zonefile is used.
If the file does not exist or is empty, Unbound will attempt to fetch zone
data (eg. from the primary servers).
.TP
.B rpz\-action\-override: \fI<action>
Always use this RPZ action for matching triggers from this zone. Possible action
are: nxdomain, nodata, passthru, drop, disabled and cname.
.TP
.B rpz\-cname\-override: \fI<domain>
The CNAME target domain to use if the cname action is configured for
\fBrpz\-action\-override\fR.
.TP
.B rpz\-log: \fI<yes or no>
Log all applied RPZ actions for this RPZ zone. Default is no.
.TP
.B rpz\-log\-name: \fI<name>
Specify a string to be part of the log line, for easy referencing.
.TP
.B rpz\-signal\-nxdomain\-ra: \fI<yes or no>
Signal when a query is blocked by the RPZ with NXDOMAIN with an unset RA flag.
This allows certain clients, like dnsmasq, to infer that the domain is
externally blocked. Default is no.
.TP
.B for\-downstream: \fI<yes or no>
If enabled the zone is authoritatively answered for and queries for the RPZ
zone information are answered to downstream clients. This is useful for
monitoring scripts, that can then access the SOA information to check if
the rpz information is up to date. Default is no.
.TP
.B tags: \fI<list of tags>
Limit the policies from this RPZ clause to clients with a matching tag. Tags
need to be defined in \fBdefine\-tag\fR and can be assigned to client addresses
using \fBaccess\-control\-tag\fR. Enclose list of tags in quotes ("") and put
spaces between tags. If no tags are specified the policies from this clause will
be applied for all clients.
.SH "MEMORY CONTROL EXAMPLE"
In the example config settings below memory usage is reduced. Some service
levels are lower, notable very large data and a high TCP load are no longer
supported. Very large data and high TCP loads are exceptional for the DNS.
DNSSEC validation is enabled, just add trust anchors.
If you do not have to worry about programs using more than 3 Mb of memory,
the below example is not for you. Use the defaults to receive full service,
which on BSD\-32bit tops out at 30\-40 Mb after heavy usage.
.P
.nf
# example settings that reduce memory usage
server:
num\-threads: 1
outgoing\-num\-tcp: 1 # this limits TCP service, uses less buffers.
incoming\-num\-tcp: 1
outgoing\-range: 60 # uses less memory, but less performance.
msg\-buffer\-size: 8192 # note this limits service, 'no huge stuff'.
msg\-cache\-size: 100k
msg\-cache\-slabs: 1
rrset\-cache\-size: 100k
rrset\-cache\-slabs: 1
infra\-cache\-numhosts: 200
infra\-cache\-slabs: 1
key\-cache\-size: 100k
key\-cache\-slabs: 1
neg\-cache\-size: 10k
num\-queries\-per\-thread: 30
target\-fetch\-policy: "2 1 0 0 0 0"
harden\-large\-queries: "yes"
harden\-short\-bufsize: "yes"
.fi
.SH "FILES"
.TP
.I @UNBOUND_RUN_DIR@
default Unbound working directory.
.TP
.I @UNBOUND_CHROOT_DIR@
default
\fIchroot\fR(2)
location.
.TP
.I @ub_conf_file@
Unbound configuration file.
.TP
.I @UNBOUND_PIDFILE@
default Unbound pidfile with process ID of the running daemon.
.TP
.I unbound.log
Unbound log file. default is to log to
\fIsyslog\fR(3).
.SH "SEE ALSO"
\fIunbound\fR(8),
\fIunbound\-checkconf\fR(8).
.SH "AUTHORS"
.B Unbound
was written by NLnet Labs. Please see CREDITS file
in the distribution for further details.
diff --git a/contrib/unbound/dynlibmod/dynlibmod.c b/contrib/unbound/dynlibmod/dynlibmod.c
index ffac7ff306a5..1e040a30e743 100644
--- a/contrib/unbound/dynlibmod/dynlibmod.c
+++ b/contrib/unbound/dynlibmod/dynlibmod.c
@@ -1,306 +1,307 @@
/**
* \file
* This file contains the dynamic library module for Unbound.
* This loads a dynamic library (.dll, .so) and calls that for the
* module actions.
*/
#include "config.h"
#include "dynlibmod/dynlibmod.h"
#include "util/module.h"
#include "util/config_file.h"
#if HAVE_WINDOWS_H
#include <windows.h>
#define __DYNMOD HMODULE
#define __DYNSYM FARPROC
#define __LOADSYM GetProcAddress
static void log_dlerror() {
DWORD dwLastError = GetLastError();
LPSTR MessageBuffer;
DWORD dwBufferLength;
DWORD dwFormatFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_FROM_SYSTEM ;
if((dwBufferLength = FormatMessageA(
dwFormatFlags,
NULL, // module to get message from (NULL == system)
dwLastError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language
(LPSTR) &MessageBuffer,
0,
NULL
)))
{
log_err("dynlibmod: %s (%ld)", MessageBuffer, dwLastError);
LocalFree(MessageBuffer);
}
}
static HMODULE open_library(const char* fname) {
return LoadLibrary(fname);
}
static void close_library(const char* fname, __DYNMOD handle) {
(void)fname;
(void)handle;
}
#else
#include <dlfcn.h>
#define __DYNMOD void*
#define __DYNSYM void*
#define __LOADSYM dlsym
static void log_dlerror() {
log_err("dynlibmod: %s", dlerror());
}
static void* open_library(const char* fname) {
return dlopen(fname, RTLD_LAZY | RTLD_GLOBAL);
}
static void close_library(const char* fname, __DYNMOD handle) {
if(!handle) return;
if(dlclose(handle) != 0) {
log_err("dlclose %s: %s", fname, strerror(errno));
}
}
#endif
/** module counter for multiple dynlib modules */
static int dynlib_mod_count = 0;
/** dynlib module init */
int dynlibmod_init(struct module_env* env, int id) {
int dynlib_mod_idx = dynlib_mod_count++;
struct config_strlist* cfg_item = env->cfg->dynlib_file;
struct dynlibmod_env* de = (struct dynlibmod_env*)calloc(1, sizeof(struct dynlibmod_env));
__DYNMOD dynamic_library;
+ int i;
if (!de)
{
log_err("dynlibmod[%d]: malloc failure", dynlib_mod_idx);
return 0;
}
env->modinfo[id] = (void*) de;
de->fname = NULL;
- for(int i = dynlib_mod_idx;
+ for(i = dynlib_mod_idx;
i != 0 && cfg_item != NULL;
i--, cfg_item = cfg_item->next) {}
if (cfg_item == NULL || cfg_item->str == NULL || cfg_item->str[0] == 0) {
log_err("dynlibmod[%d]: no dynamic library given.", dynlib_mod_idx);
return 0;
} else {
de->fname = cfg_item->str;
}
verbose(VERB_ALGO, "dynlibmod[%d]: Trying to load library %s", dynlib_mod_idx, de->fname);
dynamic_library = open_library(de->fname);
de->dynamic_library = (void*)dynamic_library;
if (dynamic_library == NULL) {
log_dlerror();
log_err("dynlibmod[%d]: unable to load dynamic library \"%s\".", dynlib_mod_idx, de->fname);
return 0;
} else {
__DYNSYM initializer;
__DYNSYM deinitializer;
__DYNSYM operate;
__DYNSYM inform;
__DYNSYM clear;
__DYNSYM get_mem;
initializer = __LOADSYM(dynamic_library,"init");
if (initializer == NULL) {
log_dlerror();
log_err("dynlibmod[%d]: unable to load init procedure from dynamic library \"%s\".", dynlib_mod_idx, de->fname);
return 0;
} else {
de->func_init = (func_init_t)(void*)initializer;
}
deinitializer = __LOADSYM(dynamic_library,"deinit");
if (deinitializer == NULL) {
log_dlerror();
log_err("dynlibmod[%d]: unable to load deinit procedure from dynamic library \"%s\".", dynlib_mod_idx, de->fname);
return 0;
} else {
de->func_deinit = (func_deinit_t)(void*)deinitializer;
}
operate = __LOADSYM(dynamic_library,"operate");
if (operate == NULL) {
log_dlerror();
log_err("dynlibmod[%d]: unable to load operate procedure from dynamic library \"%s\".", dynlib_mod_idx, de->fname);
return 0;
} else {
de->func_operate = (func_operate_t)(void*)operate;
}
inform = __LOADSYM(dynamic_library,"inform_super");
if (inform == NULL) {
log_dlerror();
log_err("dynlibmod[%d]: unable to load inform_super procedure from dynamic library \"%s\".", dynlib_mod_idx, de->fname);
return 0;
} else {
de->func_inform = (func_inform_t)(void*)inform;
}
clear = __LOADSYM(dynamic_library,"clear");
if (clear == NULL) {
log_dlerror();
log_err("dynlibmod[%d]: unable to load clear procedure from dynamic library \"%s\".", dynlib_mod_idx, de->fname);
return 0;
} else {
de->func_clear = (func_clear_t)(void*)clear;
}
get_mem = __LOADSYM(dynamic_library,"get_mem");
if (get_mem == NULL) {
log_dlerror();
log_err("dynlibmod[%d]: unable to load get_mem procedure from dynamic library \"%s\".", dynlib_mod_idx, de->fname);
return 0;
} else {
de->func_get_mem = (func_get_mem_t)(void*)get_mem;
}
}
de->inplace_cb_delete_wrapped = &inplace_cb_delete_wrapped;
de->inplace_cb_register_wrapped = &inplace_cb_register_wrapped;
return de->func_init(env, id);
}
/** dynlib module deinit */
void dynlibmod_deinit(struct module_env* env, int id) {
struct dynlibmod_env* de = env->modinfo[id];
if(de == NULL)
return;
de->func_deinit(env, id);
close_library(de->fname, (__DYNMOD)de->dynamic_library);
dynlib_mod_count--;
de->fname = NULL;
free(de);
}
/** dynlib module operate on a query */
void dynlibmod_operate(struct module_qstate* qstate, enum module_ev event,
int id, struct outbound_entry* outbound) {
struct dynlibmod_env* de = qstate->env->modinfo[id];
de->func_operate(qstate, event, id, outbound);
}
/** dynlib module */
void dynlibmod_inform_super(struct module_qstate* qstate, int id,
struct module_qstate* super) {
struct dynlibmod_env* de = qstate->env->modinfo[id];
de->func_inform(qstate, id, super);
}
/** dynlib module cleanup query state */
void dynlibmod_clear(struct module_qstate* qstate, int id) {
struct dynlibmod_env* de = qstate->env->modinfo[id];
de->func_clear(qstate, id);
}
/** dynlib module alloc size routine */
size_t dynlibmod_get_mem(struct module_env* env, int id) {
struct dynlibmod_env* de = (struct dynlibmod_env*)env->modinfo[id];
size_t size;
verbose(VERB_ALGO, "dynlibmod: get_mem, id: %d, de:%p", id, de);
if(!de)
return 0;
size = de->func_get_mem(env, id);
return size + sizeof(*de);
}
int dynlib_inplace_cb_reply_generic(struct query_info* qinfo,
struct module_qstate* qstate, struct reply_info* rep, int rcode,
struct edns_data* edns, struct edns_option** opt_list_out,
struct comm_reply* repinfo, struct regional* region,
struct timeval* start_time, int id, void* callback) {
struct cb_pair* cb_pair = (struct cb_pair*) callback;
return ((inplace_cb_reply_func_type*) cb_pair->cb)(qinfo, qstate, rep, rcode, edns, opt_list_out, repinfo, region, start_time, id, cb_pair->cb_arg);
}
int dynlib_inplace_cb_query_generic(struct query_info* qinfo, uint16_t flags,
struct module_qstate* qstate, struct sockaddr_storage* addr,
socklen_t addrlen, uint8_t* zone, size_t zonelen, struct regional* region,
int id, void* callback) {
struct cb_pair* cb_pair = (struct cb_pair*) callback;
return ((inplace_cb_query_func_type*) cb_pair->cb)(qinfo, flags, qstate, addr, addrlen, zone, zonelen, region, id, cb_pair->cb_arg);
}
int dynlib_inplace_cb_edns_back_parsed(struct module_qstate* qstate,
int id, void* cb_args) {
struct cb_pair* cb_pair = (struct cb_pair*) cb_args;
return ((inplace_cb_edns_back_parsed_func_type*) cb_pair->cb)(qstate, id, cb_pair->cb_arg);
}
int dynlib_inplace_cb_query_response(struct module_qstate* qstate,
struct dns_msg* response, int id, void* cb_args) {
struct cb_pair* cb_pair = (struct cb_pair*) cb_args;
return ((inplace_cb_query_response_func_type*) cb_pair->cb)(qstate, response, id, cb_pair->cb_arg);
}
int
inplace_cb_register_wrapped(void* cb, enum inplace_cb_list_type type, void* cbarg,
struct module_env* env, int id) {
struct cb_pair* cb_pair = malloc(sizeof(struct cb_pair));
if(cb_pair == NULL) {
log_err("dynlibmod[%d]: malloc failure", id);
return 0;
}
cb_pair->cb = cb;
cb_pair->cb_arg = cbarg;
if(type >= inplace_cb_reply && type <= inplace_cb_reply_servfail) {
return inplace_cb_register(&dynlib_inplace_cb_reply_generic, type, (void*) cb_pair, env, id);
} else if(type == inplace_cb_query) {
return inplace_cb_register(&dynlib_inplace_cb_query_generic, type, (void*) cb_pair, env, id);
} else if(type == inplace_cb_query_response) {
return inplace_cb_register(&dynlib_inplace_cb_query_response, type, (void*) cb_pair, env, id);
} else if(type == inplace_cb_edns_back_parsed) {
return inplace_cb_register(&dynlib_inplace_cb_edns_back_parsed, type, (void*) cb_pair, env, id);
} else {
free(cb_pair);
return 0;
}
}
void
inplace_cb_delete_wrapped(struct module_env* env, enum inplace_cb_list_type type,
int id) {
struct inplace_cb* temp = env->inplace_cb_lists[type];
struct inplace_cb* prev = NULL;
while(temp) {
if(temp->id == id) {
if(!prev) {
env->inplace_cb_lists[type] = temp->next;
free(temp->cb_arg);
free(temp);
temp = env->inplace_cb_lists[type];
}
else {
prev->next = temp->next;
free(temp->cb_arg);
free(temp);
temp = prev->next;
}
}
else {
prev = temp;
temp = temp->next;
}
}
}
/**
* The module function block
*/
static struct module_func_block dynlibmod_block = {
"dynlib",
&dynlibmod_init, &dynlibmod_deinit, &dynlibmod_operate, &dynlibmod_inform_super,
&dynlibmod_clear, &dynlibmod_get_mem
};
struct module_func_block* dynlibmod_get_funcblock(void)
{
return &dynlibmod_block;
}
diff --git a/contrib/unbound/edns-subnet/subnetmod.c b/contrib/unbound/edns-subnet/subnetmod.c
index 13fd669b5d52..cefde84e5f4c 100644
--- a/contrib/unbound/edns-subnet/subnetmod.c
+++ b/contrib/unbound/edns-subnet/subnetmod.c
@@ -1,957 +1,983 @@
/*
* edns-subnet/subnetmod.c - edns subnet module. Must be called before validator
* and iterator.
*
* Copyright (c) 2013, 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
* subnet module for unbound.
*/
#include "config.h"
#ifdef CLIENT_SUBNET /* keeps splint happy */
#include "edns-subnet/subnetmod.h"
#include "edns-subnet/edns-subnet.h"
#include "edns-subnet/addrtree.h"
#include "edns-subnet/subnet-whitelist.h"
#include "services/mesh.h"
#include "services/cache/dns.h"
#include "util/module.h"
#include "util/regional.h"
#include "util/storage/slabhash.h"
#include "util/config_file.h"
#include "util/data/msgreply.h"
#include "sldns/sbuffer.h"
#include "sldns/wire2str.h"
#include "iterator/iter_utils.h"
/** externally called */
void
subnet_data_delete(void *d, void *ATTR_UNUSED(arg))
{
struct subnet_msg_cache_data *r;
r = (struct subnet_msg_cache_data*)d;
addrtree_delete(r->tree4);
addrtree_delete(r->tree6);
free(r);
}
/** externally called */
size_t
msg_cache_sizefunc(void *k, void *d)
{
struct msgreply_entry *q = (struct msgreply_entry*)k;
struct subnet_msg_cache_data *r = (struct subnet_msg_cache_data*)d;
size_t s = sizeof(struct msgreply_entry)
+ sizeof(struct subnet_msg_cache_data)
+ q->key.qname_len + lock_get_mem(&q->entry.lock);
s += addrtree_size(r->tree4);
s += addrtree_size(r->tree6);
return s;
}
/** new query for ecs module */
static int
subnet_new_qstate(struct module_qstate *qstate, int id)
{
struct subnet_qstate *sq = (struct subnet_qstate*)regional_alloc(
qstate->region, sizeof(struct subnet_qstate));
if(!sq)
return 0;
qstate->minfo[id] = sq;
memset(sq, 0, sizeof(*sq));
sq->started_no_cache_store = qstate->no_cache_store;
sq->started_no_cache_lookup = qstate->no_cache_lookup;
return 1;
}
/** Add ecs struct to edns list, after parsing it to wire format. */
void
subnet_ecs_opt_list_append(struct ecs_data* ecs, struct edns_option** list,
struct module_qstate *qstate, struct regional *region)
{
size_t sn_octs, sn_octs_remainder;
sldns_buffer* buf = qstate->env->scratch_buffer;
if(ecs->subnet_validdata) {
log_assert(ecs->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4 ||
ecs->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP6);
log_assert(ecs->subnet_addr_fam != EDNSSUBNET_ADDRFAM_IP4 ||
ecs->subnet_source_mask <= INET_SIZE*8);
log_assert(ecs->subnet_addr_fam != EDNSSUBNET_ADDRFAM_IP6 ||
ecs->subnet_source_mask <= INET6_SIZE*8);
sn_octs = ecs->subnet_source_mask / 8;
sn_octs_remainder =
(size_t)((ecs->subnet_source_mask % 8)>0?1:0);
log_assert(sn_octs + sn_octs_remainder <= INET6_SIZE);
sldns_buffer_clear(buf);
sldns_buffer_write_u16(buf, ecs->subnet_addr_fam);
sldns_buffer_write_u8(buf, ecs->subnet_source_mask);
sldns_buffer_write_u8(buf, ecs->subnet_scope_mask);
sldns_buffer_write(buf, ecs->subnet_addr, sn_octs);
if(sn_octs_remainder)
sldns_buffer_write_u8(buf, ecs->subnet_addr[sn_octs] &
~(0xFF >> (ecs->subnet_source_mask % 8)));
sldns_buffer_flip(buf);
edns_opt_list_append(list,
qstate->env->cfg->client_subnet_opcode,
sn_octs + sn_octs_remainder + 4,
sldns_buffer_begin(buf), region);
}
}
int ecs_whitelist_check(struct query_info* qinfo,
uint16_t ATTR_UNUSED(flags), struct module_qstate* qstate,
struct sockaddr_storage* addr, socklen_t addrlen,
uint8_t* ATTR_UNUSED(zone), size_t ATTR_UNUSED(zonelen),
struct regional *region, int id, void* ATTR_UNUSED(cbargs))
{
struct subnet_qstate *sq;
struct subnet_env *sn_env;
if(!(sq=(struct subnet_qstate*)qstate->minfo[id]))
return 1;
sn_env = (struct subnet_env*)qstate->env->modinfo[id];
/* Cache by default, might be disabled after parsing EDNS option
* received from nameserver. */
if(!iter_stub_fwd_no_cache(qstate, &qstate->qinfo, NULL, NULL)) {
qstate->no_cache_store = 0;
}
+ sq->subnet_sent_no_subnet = 0;
if(sq->ecs_server_out.subnet_validdata && ((sq->subnet_downstream &&
qstate->env->cfg->client_subnet_always_forward) ||
ecs_is_whitelisted(sn_env->whitelist,
addr, addrlen, qinfo->qname, qinfo->qname_len,
qinfo->qclass))) {
/* Address on whitelist or client query contains ECS option, we
* want to sent out ECS. Only add option if it is not already
* set. */
if(!edns_opt_list_find(qstate->edns_opts_back_out,
qstate->env->cfg->client_subnet_opcode)) {
+ /* if the client is not wanting an EDNS subnet option,
+ * omit it and store that we omitted it but actually
+ * are doing EDNS subnet to the server. */
+ if(sq->ecs_server_out.subnet_source_mask == 0) {
+ sq->subnet_sent_no_subnet = 1;
+ sq->subnet_sent = 0;
+ return 1;
+ }
subnet_ecs_opt_list_append(&sq->ecs_server_out,
&qstate->edns_opts_back_out, qstate, region);
}
sq->subnet_sent = 1;
}
else {
/* Outgoing ECS option is set, but we don't want to sent it to
* this address, remove option. */
if(edns_opt_list_find(qstate->edns_opts_back_out,
qstate->env->cfg->client_subnet_opcode)) {
edns_opt_list_remove(&qstate->edns_opts_back_out,
qstate->env->cfg->client_subnet_opcode);
}
sq->subnet_sent = 0;
}
return 1;
}
void
subnet_markdel(void* key)
{
struct msgreply_entry *e = (struct msgreply_entry*)key;
e->key.qtype = 0;
e->key.qclass = 0;
}
int
subnetmod_init(struct module_env *env, int id)
{
struct subnet_env *sn_env = (struct subnet_env*)calloc(1,
sizeof(struct subnet_env));
if(!sn_env) {
log_err("malloc failure");
return 0;
}
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,
msg_cache_sizefunc, query_info_compare, query_entry_delete,
subnet_data_delete, NULL);
slabhash_setmarkdel(sn_env->subnet_msg_cache, &subnet_markdel);
if(!sn_env->subnet_msg_cache) {
log_err("subnetcache: could not create cache");
free(sn_env);
env->modinfo[id] = NULL;
return 0;
}
/* whitelist for edns subnet capable servers */
sn_env->whitelist = ecs_whitelist_create();
if(!sn_env->whitelist ||
!ecs_whitelist_apply_cfg(sn_env->whitelist, env->cfg)) {
log_err("subnetcache: could not create ECS whitelist");
slabhash_delete(sn_env->subnet_msg_cache);
free(sn_env);
env->modinfo[id] = NULL;
return 0;
}
verbose(VERB_QUERY, "subnetcache: option registered (%d)",
env->cfg->client_subnet_opcode);
/* Create new mesh state for all queries. */
env->unique_mesh = 1;
if(!edns_register_option(env->cfg->client_subnet_opcode,
env->cfg->client_subnet_always_forward /* bypass cache */,
1 /* no aggregation */, env)) {
log_err("subnetcache: could not register opcode");
ecs_whitelist_delete(sn_env->whitelist);
slabhash_delete(sn_env->subnet_msg_cache);
free(sn_env);
env->modinfo[id] = NULL;
return 0;
}
inplace_cb_register((void*)ecs_whitelist_check, inplace_cb_query, NULL,
env, id);
inplace_cb_register((void*)ecs_edns_back_parsed,
inplace_cb_edns_back_parsed, NULL, env, id);
inplace_cb_register((void*)ecs_query_response,
inplace_cb_query_response, NULL, env, id);
lock_rw_init(&sn_env->biglock);
return 1;
}
void
subnetmod_deinit(struct module_env *env, int id)
{
struct subnet_env *sn_env;
if(!env || !env->modinfo[id])
return;
sn_env = (struct subnet_env*)env->modinfo[id];
lock_rw_destroy(&sn_env->biglock);
inplace_cb_delete(env, inplace_cb_edns_back_parsed, id);
inplace_cb_delete(env, inplace_cb_query, id);
inplace_cb_delete(env, inplace_cb_query_response, id);
ecs_whitelist_delete(sn_env->whitelist);
slabhash_delete(sn_env->subnet_msg_cache);
alloc_clear(&sn_env->alloc);
free(sn_env);
env->modinfo[id] = NULL;
}
/** Tells client that upstream has no/improper support */
static void
cp_edns_bad_response(struct ecs_data *target, struct ecs_data *source)
{
target->subnet_scope_mask = 0;
target->subnet_source_mask = source->subnet_source_mask;
target->subnet_addr_fam = source->subnet_addr_fam;
memcpy(target->subnet_addr, source->subnet_addr, INET6_SIZE);
target->subnet_validdata = 1;
}
static void
delfunc(void *envptr, void *elemptr) {
struct reply_info *elem = (struct reply_info *)elemptr;
struct subnet_env *env = (struct subnet_env *)envptr;
reply_info_parsedelete(elem, &env->alloc);
}
static size_t
sizefunc(void *elemptr) {
struct reply_info *elem = (struct reply_info *)elemptr;
return sizeof (struct reply_info) - sizeof (struct rrset_ref)
+ elem->rrset_count * sizeof (struct rrset_ref)
+ elem->rrset_count * sizeof (struct ub_packed_rrset_key *);
}
/**
* Select tree from cache entry based on edns data.
* If for address family not present it will create a new one.
* NULL on failure to create. */
static struct addrtree*
get_tree(struct subnet_msg_cache_data *data, struct ecs_data *edns,
struct subnet_env *env, struct config_file* cfg)
{
struct addrtree *tree;
if (edns->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4) {
if (!data->tree4)
data->tree4 = addrtree_create(
cfg->max_client_subnet_ipv4, &delfunc,
&sizefunc, env, cfg->max_ecs_tree_size_ipv4);
tree = data->tree4;
} else {
if (!data->tree6)
data->tree6 = addrtree_create(
cfg->max_client_subnet_ipv6, &delfunc,
&sizefunc, env, cfg->max_ecs_tree_size_ipv6);
tree = data->tree6;
}
return tree;
}
static void
update_cache(struct module_qstate *qstate, int id)
{
struct msgreply_entry *mrep_entry;
struct addrtree *tree;
struct reply_info *rep;
struct query_info qinf;
struct subnet_env *sne = qstate->env->modinfo[id];
struct subnet_qstate *sq = (struct subnet_qstate*)qstate->minfo[id];
struct slabhash *subnet_msg_cache = sne->subnet_msg_cache;
struct ecs_data *edns = &sq->ecs_client_in;
size_t i;
int only_match_scope_zero;
/* We already calculated hash upon lookup (lookup_and_reply) if we were
* allowed to look in the ECS cache */
hashvalue_type h = qstate->minfo[id] &&
((struct subnet_qstate*)qstate->minfo[id])->qinfo_hash_calculated?
((struct subnet_qstate*)qstate->minfo[id])->qinfo_hash :
query_info_hash(&qstate->qinfo, qstate->query_flags);
/* Step 1, general qinfo lookup */
struct lruhash_entry* lru_entry = slabhash_lookup(subnet_msg_cache, h,
&qstate->qinfo, 1);
int need_to_insert = (lru_entry == NULL);
if (!lru_entry) {
void* data = calloc(1,
sizeof(struct subnet_msg_cache_data));
if(!data) {
log_err("malloc failed");
return;
}
qinf = qstate->qinfo;
qinf.qname = memdup(qstate->qinfo.qname,
qstate->qinfo.qname_len);
if(!qinf.qname) {
free(data);
log_err("memdup failed");
return;
}
mrep_entry = query_info_entrysetup(&qinf, data, h);
free(qinf.qname); /* if qname 'consumed', it is set to NULL */
if (!mrep_entry) {
free(data);
log_err("query_info_entrysetup failed");
return;
}
lru_entry = &mrep_entry->entry;
lock_rw_wrlock(&lru_entry->lock);
}
/* lru_entry->lock is locked regardless of how we got here,
* either from the slabhash_lookup, or above in the new allocated */
/* Step 2, find the correct tree */
if (!(tree = get_tree(lru_entry->data, edns, sne, qstate->env->cfg))) {
lock_rw_unlock(&lru_entry->lock);
log_err("subnetcache: cache insertion failed");
return;
}
lock_quick_lock(&sne->alloc.lock);
rep = reply_info_copy(qstate->return_msg->rep, &sne->alloc, NULL);
lock_quick_unlock(&sne->alloc.lock);
if (!rep) {
lock_rw_unlock(&lru_entry->lock);
log_err("subnetcache: cache insertion failed");
return;
}
/* store RRsets */
for(i=0; i<rep->rrset_count; i++) {
rep->ref[i].key = rep->rrsets[i];
rep->ref[i].id = rep->rrsets[i]->id;
}
reply_info_set_ttls(rep, *qstate->env->now);
rep->flags |= (BIT_RA | BIT_QR); /* fix flags to be sensible for */
rep->flags &= ~(BIT_AA | BIT_CD);/* a reply based on the cache */
if(edns->subnet_source_mask == 0 && edns->subnet_scope_mask == 0)
only_match_scope_zero = 1;
else only_match_scope_zero = 0;
addrtree_insert(tree, (addrkey_t*)edns->subnet_addr,
edns->subnet_source_mask, sq->max_scope, rep,
rep->ttl, *qstate->env->now, only_match_scope_zero);
lock_rw_unlock(&lru_entry->lock);
if (need_to_insert) {
slabhash_insert(subnet_msg_cache, h, lru_entry, lru_entry->data,
NULL);
}
}
/** 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, int prefetch)
{
struct lruhash_entry *e;
struct module_env *env = qstate->env;
struct subnet_env *sne = (struct subnet_env*)env->modinfo[id];
hashvalue_type h = query_info_hash(&qstate->qinfo, qstate->query_flags);
struct subnet_msg_cache_data *data;
struct ecs_data *ecs = &sq->ecs_client_in;
struct addrtree *tree;
struct addrnode *node;
uint8_t scope;
memset(&sq->ecs_client_out, 0, sizeof(sq->ecs_client_out));
if (sq) {
sq->qinfo_hash = h; /* Might be useful on cache miss */
sq->qinfo_hash_calculated = 1;
}
e = slabhash_lookup(sne->subnet_msg_cache, h, &qstate->qinfo, 1);
if (!e) return 0; /* qinfo not in cache */
data = e->data;
tree = (ecs->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4)?
data->tree4 : data->tree6;
if (!tree) { /* qinfo in cache but not for this family */
lock_rw_unlock(&e->lock);
return 0;
}
node = addrtree_find(tree, (addrkey_t*)ecs->subnet_addr,
ecs->subnet_source_mask, *env->now);
if (!node) { /* plain old cache miss */
lock_rw_unlock(&e->lock);
return 0;
}
qstate->return_msg = tomsg(NULL, &qstate->qinfo,
(struct reply_info *)node->elem, qstate->region, *env->now, 0,
env->scratch);
scope = (uint8_t)node->scope;
lock_rw_unlock(&e->lock);
if (!qstate->return_msg) { /* Failed allocation or expired TTL */
return 0;
}
if (sq->subnet_downstream) { /* relay to interested client */
sq->ecs_client_out.subnet_scope_mask = scope;
sq->ecs_client_out.subnet_addr_fam = ecs->subnet_addr_fam;
sq->ecs_client_out.subnet_source_mask = ecs->subnet_source_mask;
memcpy(&sq->ecs_client_out.subnet_addr, &ecs->subnet_addr,
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;
}
/**
* Test first bits of addresses for equality. Caller is responsible
* for making sure that both a and b are at least net/8 octets long.
* @param a: first address.
* @param a: seconds address.
* @param net: Number of bits to test.
* @return: 1 if equal, 0 otherwise.
*/
static int
common_prefix(uint8_t *a, uint8_t *b, uint8_t net)
{
size_t n = (size_t)net / 8;
return !memcmp(a, b, n) && ((net % 8) == 0 || a[n] == b[n]);
}
static enum module_ext_state
eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq)
{
struct subnet_env *sne = qstate->env->modinfo[id];
struct ecs_data *c_in = &sq->ecs_client_in; /* rcvd from client */
struct ecs_data *c_out = &sq->ecs_client_out;/* will send to client */
struct ecs_data *s_in = &sq->ecs_server_in; /* rcvd from auth */
struct ecs_data *s_out = &sq->ecs_server_out;/* sent to auth */
memset(c_out, 0, sizeof(*c_out));
if (!qstate->return_msg) {
/* already an answer and its not a message, but retain
* the actual rcode, instead of module_error, so send
* module_finished */
return module_finished;
}
/* We have not asked for subnet data */
- if (!sq->subnet_sent) {
+ if (!sq->subnet_sent && !sq->subnet_sent_no_subnet) {
if (s_in->subnet_validdata)
verbose(VERB_QUERY, "subnetcache: received spurious data");
if (sq->subnet_downstream) /* Copy back to client */
cp_edns_bad_response(c_out, c_in);
return module_finished;
}
/* subnet sent but nothing came back */
- if (!s_in->subnet_validdata) {
+ if (!s_in->subnet_validdata && !sq->subnet_sent_no_subnet) {
/* The authority indicated no support for edns subnet. As a
* consequence the answer ended up in the regular cache. It
* is still useful to put it in the edns subnet cache for
* when a client explicitly asks for subnet specific answer. */
verbose(VERB_QUERY, "subnetcache: Authority indicates no support");
if(!sq->started_no_cache_store) {
lock_rw_wrlock(&sne->biglock);
update_cache(qstate, id);
lock_rw_unlock(&sne->biglock);
}
if (sq->subnet_downstream)
cp_edns_bad_response(c_out, c_in);
return module_finished;
}
+ /* Purposefully there was no sent subnet, and there is consequently
+ * no subnet in the answer. If there was, use the subnet in the answer
+ * anyway. But if there is not, treat it as a prefix 0 answer. */
+ if(sq->subnet_sent_no_subnet && !s_in->subnet_validdata) {
+ /* Fill in 0.0.0.0/0 scope 0, or ::0/0 scope 0, for caching. */
+ s_in->subnet_addr_fam = s_out->subnet_addr_fam;
+ s_in->subnet_source_mask = 0;
+ s_in->subnet_scope_mask = 0;
+ memset(s_in->subnet_addr, 0, INET6_SIZE);
+ s_in->subnet_validdata = 1;
+ }
+
/* 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 ||
!common_prefix(s_out->subnet_addr, s_in->subnet_addr,
s_out->subnet_source_mask))
{
/* we can not accept, restart query without option */
verbose(VERB_QUERY, "subnetcache: forged data");
s_out->subnet_validdata = 0;
(void)edns_opt_list_remove(&qstate->edns_opts_back_out,
qstate->env->cfg->client_subnet_opcode);
sq->subnet_sent = 0;
+ sq->subnet_sent_no_subnet = 0;
return module_restart_next;
}
lock_rw_wrlock(&sne->biglock);
if(!sq->started_no_cache_store) {
update_cache(qstate, id);
}
sne->num_msg_nocache++;
lock_rw_unlock(&sne->biglock);
if (sq->subnet_downstream) {
/* Client wants to see the answer, echo option back
* and adjust the scope. */
c_out->subnet_addr_fam = c_in->subnet_addr_fam;
c_out->subnet_source_mask = c_in->subnet_source_mask;
memcpy(&c_out->subnet_addr, &c_in->subnet_addr, INET6_SIZE);
c_out->subnet_scope_mask = sq->max_scope;
/* Limit scope returned to client to scope used for caching. */
if(c_out->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4) {
if(c_out->subnet_scope_mask >
qstate->env->cfg->max_client_subnet_ipv4) {
c_out->subnet_scope_mask =
qstate->env->cfg->max_client_subnet_ipv4;
}
}
else if(c_out->subnet_scope_mask >
qstate->env->cfg->max_client_subnet_ipv6) {
c_out->subnet_scope_mask =
qstate->env->cfg->max_client_subnet_ipv6;
}
c_out->subnet_validdata = 1;
}
return module_finished;
}
/** Parse EDNS opt data containing ECS */
static int
parse_subnet_option(struct edns_option* ecs_option, struct ecs_data* ecs)
{
memset(ecs, 0, sizeof(*ecs));
if (ecs_option->opt_len < 4)
return 0;
ecs->subnet_addr_fam = sldns_read_uint16(ecs_option->opt_data);
ecs->subnet_source_mask = ecs_option->opt_data[2];
ecs->subnet_scope_mask = ecs_option->opt_data[3];
/* remaining bytes indicate address */
/* validate input*/
/* option length matches calculated length? */
if (ecs_option->opt_len != (size_t)((ecs->subnet_source_mask+7)/8 + 4))
return 0;
if (ecs_option->opt_len - 4 > INET6_SIZE || ecs_option->opt_len == 0)
return 0;
if (ecs->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4) {
if (ecs->subnet_source_mask > 32 || ecs->subnet_scope_mask > 32)
return 0;
} else if (ecs->subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP6) {
if (ecs->subnet_source_mask > 128 ||
ecs->subnet_scope_mask > 128)
return 0;
} else
return 0;
/* valid ECS data, write to ecs_data */
if (copy_clear(ecs->subnet_addr, INET6_SIZE, ecs_option->opt_data + 4,
ecs_option->opt_len - 4, ecs->subnet_source_mask))
return 0;
ecs->subnet_validdata = 1;
return 1;
}
void
subnet_option_from_ss(struct sockaddr_storage *ss, struct ecs_data* ecs,
struct config_file* cfg)
{
void* sinaddr;
/* Construct subnet option from original query */
if(((struct sockaddr_in*)ss)->sin_family == AF_INET) {
ecs->subnet_source_mask = cfg->max_client_subnet_ipv4;
ecs->subnet_addr_fam = EDNSSUBNET_ADDRFAM_IP4;
sinaddr = &((struct sockaddr_in*)ss)->sin_addr;
if (!copy_clear( ecs->subnet_addr, INET6_SIZE,
(uint8_t *)sinaddr, INET_SIZE,
ecs->subnet_source_mask)) {
ecs->subnet_validdata = 1;
}
}
#ifdef INET6
else {
ecs->subnet_source_mask = cfg->max_client_subnet_ipv6;
ecs->subnet_addr_fam = EDNSSUBNET_ADDRFAM_IP6;
sinaddr = &((struct sockaddr_in6*)ss)->sin6_addr;
if (!copy_clear( ecs->subnet_addr, INET6_SIZE,
(uint8_t *)sinaddr, INET6_SIZE,
ecs->subnet_source_mask)) {
ecs->subnet_validdata = 1;
}
}
#else
/* We don't know how to handle ip6, just pass */
#endif /* INET6 */
}
int
ecs_query_response(struct module_qstate* qstate, struct dns_msg* response,
int id, void* ATTR_UNUSED(cbargs))
{
struct subnet_qstate *sq;
if(!response || !(sq=(struct subnet_qstate*)qstate->minfo[id]))
return 1;
if(sq->subnet_sent &&
FLAGS_GET_RCODE(response->rep->flags) == LDNS_RCODE_REFUSED) {
/* REFUSED response to ECS query, remove ECS option. */
edns_opt_list_remove(&qstate->edns_opts_back_out,
qstate->env->cfg->client_subnet_opcode);
sq->subnet_sent = 0;
+ sq->subnet_sent_no_subnet = 0;
memset(&sq->ecs_server_out, 0, sizeof(sq->ecs_server_out));
} else if (!sq->track_max_scope &&
FLAGS_GET_RCODE(response->rep->flags) == LDNS_RCODE_NOERROR &&
response->rep->an_numrrsets > 0
) {
struct ub_packed_rrset_key* s = response->rep->rrsets[0];
if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME &&
query_dname_compare(qstate->qinfo.qname,
s->rk.dname) == 0) {
/* CNAME response for QNAME. From now on keep track of
* longest received ECS prefix for all queries on this
* qstate. */
sq->track_max_scope = 1;
}
}
return 1;
}
/** verbose print edns subnet option in pretty print */
static void
subnet_log_print(const char* s, struct edns_option* ecs_opt)
{
if(verbosity >= VERB_ALGO) {
char buf[256];
char* str = buf;
size_t str_len = sizeof(buf);
if(!ecs_opt) {
verbose(VERB_ALGO, "%s (null)", s);
return;
}
(void)sldns_wire2str_edns_subnet_print(&str, &str_len,
ecs_opt->opt_data, ecs_opt->opt_len);
verbose(VERB_ALGO, "%s %s", s, buf);
}
}
int
ecs_edns_back_parsed(struct module_qstate* qstate, int id,
void* ATTR_UNUSED(cbargs))
{
struct subnet_qstate *sq;
struct edns_option* ecs_opt;
if(!(sq=(struct subnet_qstate*)qstate->minfo[id]))
return 1;
if((ecs_opt = edns_opt_list_find(
qstate->edns_opts_back_in,
qstate->env->cfg->client_subnet_opcode)) &&
parse_subnet_option(ecs_opt, &sq->ecs_server_in) &&
sq->subnet_sent && sq->ecs_server_in.subnet_validdata) {
subnet_log_print("answer has edns subnet", ecs_opt);
/* Only skip global cache store if we sent an ECS option
* and received one back. Answers from non-whitelisted
* servers will end up in global cache. Answers for
* queries with 0 source will not (unless nameserver
* does not support ECS). */
qstate->no_cache_store = 1;
if(!sq->track_max_scope || (sq->track_max_scope &&
sq->ecs_server_in.subnet_scope_mask >
sq->max_scope))
sq->max_scope = sq->ecs_server_in.subnet_scope_mask;
+ } else if(sq->subnet_sent_no_subnet) {
+ /* The answer can be stored as scope 0, not in global cache. */
+ qstate->no_cache_store = 1;
}
return 1;
}
void
subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
int id, struct outbound_entry* outbound)
{
struct subnet_env *sne = qstate->env->modinfo[id];
struct subnet_qstate *sq = (struct subnet_qstate*)qstate->minfo[id];
verbose(VERB_QUERY, "subnetcache[module %d] operate: extstate:%s "
"event:%s", id, strextstate(qstate->ext_state[id]),
strmodulevent(event));
log_query_info(VERB_QUERY, "subnetcache operate: query", &qstate->qinfo);
if((event == module_event_new || event == module_event_pass) &&
sq == NULL) {
struct edns_option* ecs_opt;
if(!subnet_new_qstate(qstate, id)) {
qstate->return_msg = NULL;
qstate->ext_state[id] = module_finished;
return;
}
sq = (struct subnet_qstate*)qstate->minfo[id];
if((ecs_opt = edns_opt_list_find(
qstate->edns_opts_front_in,
qstate->env->cfg->client_subnet_opcode))) {
if(!parse_subnet_option(ecs_opt, &sq->ecs_client_in)) {
/* Wrongly formatted ECS option. RFC mandates to
* return FORMERROR. */
qstate->return_rcode = LDNS_RCODE_FORMERR;
qstate->ext_state[id] = module_finished;
return;
}
subnet_log_print("query has edns subnet", ecs_opt);
sq->subnet_downstream = 1;
}
else if(qstate->mesh_info->reply_list) {
subnet_option_from_ss(
&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
* parse it, we don't do client subnet */
sq->ecs_server_out.subnet_validdata = 0;
verbose(VERB_ALGO, "subnetcache: pass to next module");
qstate->ext_state[id] = module_wait_module;
return;
}
/* Limit to minimum allowed source mask */
if(sq->ecs_client_in.subnet_source_mask != 0 && (
(sq->ecs_client_in.subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4 &&
sq->ecs_client_in.subnet_source_mask < qstate->env->cfg->min_client_subnet_ipv4) ||
(sq->ecs_client_in.subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP6 &&
sq->ecs_client_in.subnet_source_mask < qstate->env->cfg->min_client_subnet_ipv6))) {
qstate->return_rcode = LDNS_RCODE_REFUSED;
qstate->ext_state[id] = module_finished;
return;
}
if(!sq->started_no_cache_lookup && !qstate->blacklist) {
lock_rw_wrlock(&sne->biglock);
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");
qstate->ext_state[id] = module_finished;
subnet_ecs_opt_list_append(&sq->ecs_client_out,
&qstate->edns_opts_front_out, qstate,
qstate->region);
if(verbosity >= VERB_ALGO) {
subnet_log_print("reply has edns subnet",
edns_opt_list_find(
qstate->edns_opts_front_out,
qstate->env->cfg->
client_subnet_opcode));
}
return;
}
lock_rw_unlock(&sne->biglock);
}
sq->ecs_server_out.subnet_addr_fam =
sq->ecs_client_in.subnet_addr_fam;
sq->ecs_server_out.subnet_source_mask =
sq->ecs_client_in.subnet_source_mask;
/* Limit source prefix to configured maximum */
if(sq->ecs_server_out.subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP4
&& sq->ecs_server_out.subnet_source_mask >
qstate->env->cfg->max_client_subnet_ipv4)
sq->ecs_server_out.subnet_source_mask =
qstate->env->cfg->max_client_subnet_ipv4;
else if(sq->ecs_server_out.subnet_addr_fam == EDNSSUBNET_ADDRFAM_IP6
&& sq->ecs_server_out.subnet_source_mask >
qstate->env->cfg->max_client_subnet_ipv6)
sq->ecs_server_out.subnet_source_mask =
qstate->env->cfg->max_client_subnet_ipv6;
/* Safe to copy completely, even if the source is limited by the
* configuration. subnet_ecs_opt_list_append() will limit the address.
* */
memcpy(&sq->ecs_server_out.subnet_addr,
sq->ecs_client_in.subnet_addr, INET6_SIZE);
sq->ecs_server_out.subnet_scope_mask = 0;
sq->ecs_server_out.subnet_validdata = 1;
if(sq->ecs_server_out.subnet_source_mask != 0 &&
qstate->env->cfg->client_subnet_always_forward &&
sq->subnet_downstream)
/* ECS specific data required, do not look at the global
* cache in other modules. */
qstate->no_cache_lookup = 1;
/* pass request to next module */
verbose(VERB_ALGO,
"subnetcache: not found in cache. pass to next module");
qstate->ext_state[id] = module_wait_module;
return;
}
/* Query handed back by next module, we have a 'final' answer */
if(sq && event == module_event_moddone) {
qstate->ext_state[id] = eval_response(qstate, id, sq);
if(qstate->ext_state[id] == module_finished &&
qstate->return_msg) {
subnet_ecs_opt_list_append(&sq->ecs_client_out,
&qstate->edns_opts_front_out, qstate,
qstate->region);
if(verbosity >= VERB_ALGO) {
subnet_log_print("reply has edns subnet",
edns_opt_list_find(
qstate->edns_opts_front_out,
qstate->env->cfg->
client_subnet_opcode));
}
}
qstate->no_cache_store = sq->started_no_cache_store;
qstate->no_cache_lookup = sq->started_no_cache_lookup;
return;
}
if(sq && outbound) {
return;
}
/* We are being revisited */
if(event == module_event_pass || event == module_event_new) {
/* Just pass it on, we already did the work */
verbose(VERB_ALGO, "subnetcache: pass to next module");
qstate->ext_state[id] = module_wait_module;
return;
}
if(!sq && (event == module_event_moddone)) {
/* during priming, module done but we never started */
qstate->ext_state[id] = module_finished;
return;
}
log_err("subnetcache: bad event %s", strmodulevent(event));
qstate->ext_state[id] = module_error;
return;
}
void
subnetmod_clear(struct module_qstate *ATTR_UNUSED(qstate),
int ATTR_UNUSED(id))
{
/* qstate has no data outside region */
}
void
subnetmod_inform_super(struct module_qstate *ATTR_UNUSED(qstate),
int ATTR_UNUSED(id), struct module_qstate *ATTR_UNUSED(super))
{
/* Not used */
}
size_t
subnetmod_get_mem(struct module_env *env, int id)
{
struct subnet_env *sn_env = env->modinfo[id];
if (!sn_env) return 0;
return sizeof(*sn_env) +
slabhash_get_mem(sn_env->subnet_msg_cache) +
ecs_whitelist_get_mem(sn_env->whitelist);
}
/**
* The module function block
*/
static struct module_func_block subnetmod_block = {
"subnetcache", &subnetmod_init, &subnetmod_deinit, &subnetmod_operate,
&subnetmod_inform_super, &subnetmod_clear, &subnetmod_get_mem
};
struct module_func_block*
subnetmod_get_funcblock(void)
{
return &subnetmod_block;
}
/** Wrappers for static functions to unit test */
size_t
unittest_wrapper_subnetmod_sizefunc(void *elemptr)
{
return sizefunc(elemptr);
}
#endif /* CLIENT_SUBNET */
diff --git a/contrib/unbound/edns-subnet/subnetmod.h b/contrib/unbound/edns-subnet/subnetmod.h
index f0bcaad33e15..1ff8a23ecdba 100644
--- a/contrib/unbound/edns-subnet/subnetmod.h
+++ b/contrib/unbound/edns-subnet/subnetmod.h
@@ -1,156 +1,163 @@
/*
* edns-subnet/subnetmod.h - edns subnet module. Must be called before validator
* and iterator.
*
* Copyright (c) 2013, 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
* subnet module for unbound.
*/
#ifndef SUBNETMOD_H
#define SUBNETMOD_H
#include "util/module.h"
#include "services/outbound_list.h"
#include "util/alloc.h"
#include "util/net_help.h"
#include "util/storage/slabhash.h"
#include "util/data/dname.h"
#include "edns-subnet/addrtree.h"
#include "edns-subnet/edns-subnet.h"
/**
* Global state for the subnet module.
*/
struct subnet_env {
/** shared message cache
* key: struct query_info*
* data: struct subnet_msg_cache_data* */
struct slabhash* subnet_msg_cache;
/** access control, which upstream servers we send client address */
struct ecs_whitelist* whitelist;
/** allocation service */
struct alloc_cache alloc;
lock_rw_type biglock;
/** number of messages from cache */
size_t num_msg_cache;
/** number of messages not from cache */
size_t num_msg_nocache;
};
struct subnet_msg_cache_data {
struct addrtree* tree4;
struct addrtree* tree6;
};
struct subnet_qstate {
/** We need the hash for both cache lookup and insert */
hashvalue_type qinfo_hash;
int qinfo_hash_calculated;
/** ecs_data for client communication */
struct ecs_data ecs_client_in;
struct ecs_data ecs_client_out;
/** ecss data for server communication */
struct ecs_data ecs_server_in;
struct ecs_data ecs_server_out;
int subnet_downstream;
int subnet_sent;
+ /**
+ * If there was no subnet sent because the client used source prefix
+ * length 0 for omitting the information. Then the answer is cached
+ * like subnet was a /0 scope. Like the subnet_sent flag, but when
+ * the EDNS subnet option is omitted because the client asked.
+ */
+ int subnet_sent_no_subnet;
/** keep track of longest received scope, set after receiving CNAME for
* incoming QNAME. */
int track_max_scope;
/** longest received scope mask since track_max_scope is set. This value
* is used for caching and answereing to client. */
uint8_t max_scope;
/** has the subnet module been started with no_cache_store? */
int started_no_cache_store;
/** has the subnet module been started with no_cache_lookup? */
int started_no_cache_lookup;
};
void subnet_data_delete(void* d, void* ATTR_UNUSED(arg));
size_t msg_cache_sizefunc(void* k, void* d);
/**
* Get the module function block.
* @return: function block with function pointers to module methods.
*/
struct module_func_block* subnetmod_get_funcblock(void);
/** subnet module init */
int subnetmod_init(struct module_env* env, int id);
/** subnet module deinit */
void subnetmod_deinit(struct module_env* env, int id);
/** subnet module operate on a query */
void subnetmod_operate(struct module_qstate* qstate, enum module_ev event,
int id, struct outbound_entry* outbound);
/** subnet module */
void subnetmod_inform_super(struct module_qstate* qstate, int id,
struct module_qstate* super);
/** subnet module cleanup query state */
void subnetmod_clear(struct module_qstate* qstate, int id);
/** subnet module alloc size routine */
size_t subnetmod_get_mem(struct module_env* env, int id);
/** Wrappers for static functions to unit test */
size_t unittest_wrapper_subnetmod_sizefunc(void *elemptr);
/** Whitelist check, called just before query is sent upstream. */
int ecs_whitelist_check(struct query_info* qinfo, uint16_t flags,
struct module_qstate* qstate, struct sockaddr_storage* addr,
socklen_t addrlen, uint8_t* zone, size_t zonelen,
struct regional* region, int id, void* cbargs);
/** Check whether response from server contains ECS record, if so, skip cache
* store. Called just after parsing EDNS data from server. */
int ecs_edns_back_parsed(struct module_qstate* qstate, int id, void* cbargs);
/** Remove ECS record from back_out when query resulted in REFUSED response. */
int ecs_query_response(struct module_qstate* qstate, struct dns_msg* response,
int id, void* cbargs);
/** mark subnet msg to be deleted */
void subnet_markdel(void* key);
/** Add ecs struct to edns list, after parsing it to wire format. */
void subnet_ecs_opt_list_append(struct ecs_data* ecs, struct edns_option** list,
struct module_qstate *qstate, struct regional *region);
/** Create ecs_data from the sockaddr_storage information. */
void subnet_option_from_ss(struct sockaddr_storage *ss, struct ecs_data* ecs,
struct config_file* cfg);
#endif /* SUBNETMOD_H */
diff --git a/contrib/unbound/ipset/ipset.c b/contrib/unbound/ipset/ipset.c
index c61ebc205ee8..af55de8d6fc2 100644
--- a/contrib/unbound/ipset/ipset.c
+++ b/contrib/unbound/ipset/ipset.c
@@ -1,386 +1,386 @@
/**
* \file
* This file implements the ipset module. It can handle packets by putting
* the A and AAAA addresses that are configured in unbound.conf as type
* ipset (local-zone statements) into a firewall rule IPSet. For firewall
* blacklist and whitelist usage.
*/
#include "config.h"
#include "ipset/ipset.h"
#include "util/regional.h"
#include "util/net_help.h"
#include "util/config_file.h"
#include "services/cache/dns.h"
#include "sldns/sbuffer.h"
#include "sldns/wire2str.h"
#include "sldns/parseutil.h"
#include <libmnl/libmnl.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/ipset/ip_set.h>
#define BUFF_LEN 256
/**
* Return an error
* @param qstate: our query state
* @param id: module id
* @param rcode: error code (DNS errcode).
* @return: 0 for use by caller, to make notation easy, like:
* return error_response(..).
*/
static int error_response(struct module_qstate* qstate, int id, int rcode) {
verbose(VERB_QUERY, "return error response %s",
sldns_lookup_by_id(sldns_rcodes, rcode)?
sldns_lookup_by_id(sldns_rcodes, rcode)->name:"??");
qstate->return_rcode = rcode;
qstate->return_msg = NULL;
qstate->ext_state[id] = module_finished;
return 0;
}
static struct mnl_socket * open_mnl_socket() {
struct mnl_socket *mnl;
mnl = mnl_socket_open(NETLINK_NETFILTER);
if (!mnl) {
log_err("ipset: could not open netfilter.");
return NULL;
}
if (mnl_socket_bind(mnl, 0, MNL_SOCKET_AUTOPID) < 0) {
mnl_socket_close(mnl);
log_err("ipset: could not bind netfilter.");
return NULL;
}
return mnl;
}
static int add_to_ipset(struct mnl_socket *mnl, const char *setname, const void *ipaddr, int af) {
struct nlmsghdr *nlh;
struct nfgenmsg *nfg;
struct nlattr *nested[2];
static char buffer[BUFF_LEN];
if (strlen(setname) >= IPSET_MAXNAMELEN) {
errno = ENAMETOOLONG;
return -1;
}
if (af != AF_INET && af != AF_INET6) {
errno = EAFNOSUPPORT;
return -1;
}
nlh = mnl_nlmsg_put_header(buffer);
nlh->nlmsg_type = IPSET_CMD_ADD | (NFNL_SUBSYS_IPSET << 8);
nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK|NLM_F_EXCL;
nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
nfg->nfgen_family = af;
nfg->version = NFNETLINK_V0;
nfg->res_id = htons(0);
mnl_attr_put_u8(nlh, IPSET_ATTR_PROTOCOL, IPSET_PROTOCOL);
mnl_attr_put(nlh, IPSET_ATTR_SETNAME, strlen(setname) + 1, setname);
nested[0] = mnl_attr_nest_start(nlh, IPSET_ATTR_DATA);
nested[1] = mnl_attr_nest_start(nlh, IPSET_ATTR_IP);
mnl_attr_put(nlh, (af == AF_INET ? IPSET_ATTR_IPADDR_IPV4 : IPSET_ATTR_IPADDR_IPV6)
| NLA_F_NET_BYTEORDER, (af == AF_INET ? sizeof(struct in_addr) : sizeof(struct in6_addr)), ipaddr);
mnl_attr_nest_end(nlh, nested[1]);
mnl_attr_nest_end(nlh, nested[0]);
if (mnl_socket_sendto(mnl, nlh, nlh->nlmsg_len) < 0) {
return -1;
}
return 0;
}
static void
ipset_add_rrset_data(struct ipset_env *ie, struct mnl_socket *mnl,
struct packed_rrset_data *d, const char* setname, int af,
const char* dname)
{
int ret;
size_t j, rr_len, rd_len;
uint8_t *rr_data;
/* to d->count, not d->rrsig_count, because we do not want to add the RRSIGs, only the addresses */
for (j = 0; j < d->count; j++) {
rr_len = d->rr_len[j];
rr_data = d->rr_data[j];
rd_len = sldns_read_uint16(rr_data);
if(af == AF_INET && rd_len != INET_SIZE)
continue;
if(af == AF_INET6 && rd_len != INET6_SIZE)
continue;
if (rr_len - 2 >= rd_len) {
if(verbosity >= VERB_QUERY) {
char ip[128];
if(inet_ntop(af, rr_data+2, ip, (socklen_t)sizeof(ip)) == 0)
snprintf(ip, sizeof(ip), "(inet_ntop_error)");
verbose(VERB_QUERY, "ipset: add %s to %s for %s", ip, setname, dname);
}
ret = add_to_ipset(mnl, setname, rr_data + 2, af);
if (ret < 0) {
log_err("ipset: could not add %s into %s", dname, setname);
mnl_socket_close(mnl);
ie->mnl = NULL;
break;
}
}
}
}
static int
ipset_check_zones_for_rrset(struct module_env *env, struct ipset_env *ie,
struct mnl_socket *mnl, struct ub_packed_rrset_key *rrset,
const char *qname, const int qlen, const char *setname, int af)
{
static char dname[BUFF_LEN];
const char *ds, *qs;
int dlen, plen;
struct config_strlist *p;
struct packed_rrset_data *d;
dlen = sldns_wire2str_dname_buf(rrset->rk.dname, rrset->rk.dname_len, dname, BUFF_LEN);
if (dlen == 0) {
log_err("bad domain name");
return -1;
}
for (p = env->cfg->local_zones_ipset; p; p = p->next) {
ds = NULL;
qs = NULL;
plen = strlen(p->str);
- if (dlen >= plen) {
+ if (dlen == plen || (dlen > plen && dname[dlen - plen - 1] == '.' )) {
ds = dname + (dlen - plen);
}
- if (qlen >= plen) {
+ if (qlen == plen || (qlen > plen && qname[qlen - plen - 1] == '.' )) {
qs = qname + (qlen - plen);
}
if ((ds && strncasecmp(p->str, ds, plen) == 0)
|| (qs && strncasecmp(p->str, qs, plen) == 0)) {
d = (struct packed_rrset_data*)rrset->entry.data;
ipset_add_rrset_data(ie, mnl, d, setname,
af, dname);
break;
}
}
return 0;
}
static int ipset_update(struct module_env *env, struct dns_msg *return_msg,
struct query_info qinfo, struct ipset_env *ie)
{
struct mnl_socket *mnl;
size_t i;
const char *setname;
struct ub_packed_rrset_key *rrset;
int af;
static char qname[BUFF_LEN];
int qlen;
mnl = (struct mnl_socket *)ie->mnl;
if (!mnl) {
/* retry to create mnl socket */
mnl = open_mnl_socket();
if (!mnl) {
return -1;
}
ie->mnl = mnl;
}
qlen = sldns_wire2str_dname_buf(qinfo.qname, qinfo.qname_len,
qname, BUFF_LEN);
if(qlen == 0) {
log_err("bad domain name");
return -1;
}
for(i = 0; i < return_msg->rep->rrset_count; i++) {
setname = NULL;
rrset = return_msg->rep->rrsets[i];
if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_A &&
ie->v4_enabled == 1) {
af = AF_INET;
setname = ie->name_v4;
} else if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_AAAA &&
ie->v6_enabled == 1) {
af = AF_INET6;
setname = ie->name_v6;
}
if (setname) {
if(ipset_check_zones_for_rrset(env, ie, mnl, rrset,
qname, qlen, setname, af) == -1)
return -1;
}
}
return 0;
}
int ipset_init(struct module_env* env, int id) {
struct ipset_env *ipset_env;
ipset_env = (struct ipset_env *)calloc(1, sizeof(struct ipset_env));
if (!ipset_env) {
log_err("malloc failure");
return 0;
}
env->modinfo[id] = (void *)ipset_env;
ipset_env->mnl = NULL;
ipset_env->name_v4 = env->cfg->ipset_name_v4;
ipset_env->name_v6 = env->cfg->ipset_name_v6;
ipset_env->v4_enabled = !ipset_env->name_v4 || (strlen(ipset_env->name_v4) == 0) ? 0 : 1;
ipset_env->v6_enabled = !ipset_env->name_v6 || (strlen(ipset_env->name_v6) == 0) ? 0 : 1;
if ((ipset_env->v4_enabled < 1) && (ipset_env->v6_enabled < 1)) {
log_err("ipset: set name no configuration?");
return 0;
}
return 1;
}
void ipset_deinit(struct module_env *env, int id) {
struct mnl_socket *mnl;
struct ipset_env *ipset_env;
if (!env || !env->modinfo[id]) {
return;
}
ipset_env = (struct ipset_env *)env->modinfo[id];
mnl = (struct mnl_socket *)ipset_env->mnl;
if (mnl) {
mnl_socket_close(mnl);
ipset_env->mnl = NULL;
}
free(ipset_env);
env->modinfo[id] = NULL;
}
static int ipset_new(struct module_qstate* qstate, int id) {
struct ipset_qstate *iq = (struct ipset_qstate *)regional_alloc(
qstate->region, sizeof(struct ipset_qstate));
qstate->minfo[id] = iq;
if (!iq) {
return 0;
}
memset(iq, 0, sizeof(*iq));
/* initialise it */
/* TODO */
return 1;
}
void ipset_operate(struct module_qstate *qstate, enum module_ev event, int id,
struct outbound_entry *outbound) {
struct ipset_env *ie = (struct ipset_env *)qstate->env->modinfo[id];
struct ipset_qstate *iq = (struct ipset_qstate *)qstate->minfo[id];
verbose(VERB_QUERY, "ipset[module %d] operate: extstate:%s event:%s",
id, strextstate(qstate->ext_state[id]), strmodulevent(event));
if (iq) {
log_query_info(VERB_QUERY, "ipset operate: query", &qstate->qinfo);
}
/* perform ipset state machine */
if ((event == module_event_new || event == module_event_pass) && !iq) {
if (!ipset_new(qstate, id)) {
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
return;
}
iq = (struct ipset_qstate*)qstate->minfo[id];
}
if (iq && (event == module_event_pass || event == module_event_new)) {
qstate->ext_state[id] = module_wait_module;
return;
}
if (iq && (event == module_event_moddone)) {
if (qstate->return_msg && qstate->return_msg->rep) {
ipset_update(qstate->env, qstate->return_msg, qstate->qinfo, ie);
}
qstate->ext_state[id] = module_finished;
return;
}
if (iq && outbound) {
/* ipset does not need to process responses at this time
* ignore it.
ipset_process_response(qstate, iq, ie, id, outbound, event);
*/
return;
}
if (event == module_event_error) {
verbose(VERB_ALGO, "got called with event error, giving up");
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
return;
}
if (!iq && (event == module_event_moddone)) {
/* during priming, module done but we never started */
qstate->ext_state[id] = module_finished;
return;
}
log_err("bad event for ipset");
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
void ipset_inform_super(struct module_qstate *ATTR_UNUSED(qstate),
int ATTR_UNUSED(id), struct module_qstate *ATTR_UNUSED(super)) {
/* ipset does not use subordinate requests at this time */
verbose(VERB_ALGO, "ipset inform_super was called");
}
void ipset_clear(struct module_qstate *qstate, int id) {
struct cachedb_qstate *iq;
if (!qstate) {
return;
}
iq = (struct cachedb_qstate *)qstate->minfo[id];
if (iq) {
/* free contents of iq */
/* TODO */
}
qstate->minfo[id] = NULL;
}
size_t ipset_get_mem(struct module_env *env, int id) {
struct ipset_env *ie = (struct ipset_env *)env->modinfo[id];
if (!ie) {
return 0;
}
return sizeof(*ie);
}
/**
* The ipset function block
*/
static struct module_func_block ipset_block = {
"ipset",
&ipset_init, &ipset_deinit, &ipset_operate,
&ipset_inform_super, &ipset_clear, &ipset_get_mem
};
struct module_func_block * ipset_get_funcblock(void) {
return &ipset_block;
}
diff --git a/contrib/unbound/iterator/iter_priv.c b/contrib/unbound/iterator/iter_priv.c
index 90bea1746d9a..be4219216a4c 100644
--- a/contrib/unbound/iterator/iter_priv.c
+++ b/contrib/unbound/iterator/iter_priv.c
@@ -1,296 +1,274 @@
/*
* iterator/iter_priv.c - iterative resolver private address and domain store
*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file contains functions to assist the iterator module.
* Keep track of the private addresses and lookup fast.
*/
#include "config.h"
#include "iterator/iter_priv.h"
#include "util/regional.h"
#include "util/log.h"
#include "util/config_file.h"
#include "util/data/dname.h"
#include "util/data/msgparse.h"
#include "util/net_help.h"
#include "util/storage/dnstree.h"
#include "sldns/str2wire.h"
#include "sldns/sbuffer.h"
struct iter_priv* priv_create(void)
{
struct iter_priv* priv = (struct iter_priv*)calloc(1, sizeof(*priv));
if(!priv)
return NULL;
priv->region = regional_create();
if(!priv->region) {
priv_delete(priv);
return NULL;
}
addr_tree_init(&priv->a);
name_tree_init(&priv->n);
return priv;
}
void priv_delete(struct iter_priv* priv)
{
if(!priv) return;
regional_destroy(priv->region);
free(priv);
}
/** Read private-addr declarations from config */
static int read_addrs(struct iter_priv* priv, struct config_file* cfg)
{
/* parse addresses, report errors, insert into tree */
struct config_strlist* p;
struct addr_tree_node* n;
struct sockaddr_storage addr;
int net;
socklen_t addrlen;
for(p = cfg->private_address; p; p = p->next) {
log_assert(p->str);
if(!netblockstrtoaddr(p->str, UNBOUND_DNS_PORT, &addr,
&addrlen, &net)) {
log_err("cannot parse private-address: %s", p->str);
return 0;
}
n = (struct addr_tree_node*)regional_alloc(priv->region,
sizeof(*n));
if(!n) {
log_err("out of memory");
return 0;
}
if(!addr_tree_insert(&priv->a, n, &addr, addrlen, net)) {
verbose(VERB_QUERY, "ignoring duplicate "
"private-address: %s", p->str);
}
}
return 1;
}
/** Read private-domain declarations from config */
static int read_names(struct iter_priv* priv, struct config_file* cfg)
{
/* parse names, report errors, insert into tree */
struct config_strlist* p;
struct name_tree_node* n;
uint8_t* nm, *nmr;
size_t nm_len;
int nm_labs;
for(p = cfg->private_domain; p; p = p->next) {
log_assert(p->str);
nm = sldns_str2wire_dname(p->str, &nm_len);
if(!nm) {
log_err("cannot parse private-domain: %s", p->str);
return 0;
}
nm_labs = dname_count_size_labels(nm, &nm_len);
nmr = (uint8_t*)regional_alloc_init(priv->region, nm, nm_len);
free(nm);
if(!nmr) {
log_err("out of memory");
return 0;
}
n = (struct name_tree_node*)regional_alloc(priv->region,
sizeof(*n));
if(!n) {
log_err("out of memory");
return 0;
}
if(!name_tree_insert(&priv->n, n, nmr, nm_len, nm_labs,
LDNS_RR_CLASS_IN)) {
verbose(VERB_QUERY, "ignoring duplicate "
"private-domain: %s", p->str);
}
}
return 1;
}
int priv_apply_cfg(struct iter_priv* priv, struct config_file* cfg)
{
/* empty the current contents */
regional_free_all(priv->region);
addr_tree_init(&priv->a);
name_tree_init(&priv->n);
/* read new contents */
if(!read_addrs(priv, cfg))
return 0;
if(!read_names(priv, cfg))
return 0;
/* prepare for lookups */
addr_tree_init_parents(&priv->a);
name_tree_init_parents(&priv->n);
return 1;
}
/**
* See if an address is blocked.
* @param priv: structure for address storage.
* @param addr: address to check
* @param addrlen: length of addr.
* @return: true if the address must not be queried. false if unlisted.
*/
static int
priv_lookup_addr(struct iter_priv* priv, struct sockaddr_storage* addr,
socklen_t addrlen)
{
return addr_tree_lookup(&priv->a, addr, addrlen) != NULL;
}
/**
* See if a name is whitelisted.
* @param priv: structure for address storage.
* @param pkt: the packet (for compression ptrs).
* @param name: name to check.
* @param name_len: uncompressed length of the name to check.
* @param dclass: class to check.
* @return: true if the name is OK. false if unlisted.
*/
static int
priv_lookup_name(struct iter_priv* priv, sldns_buffer* pkt,
uint8_t* name, size_t name_len, uint16_t dclass)
{
size_t len;
uint8_t decomp[256];
int labs;
if(name_len >= sizeof(decomp))
return 0;
dname_pkt_copy(pkt, decomp, name);
labs = dname_count_size_labels(decomp, &len);
log_assert(name_len == len);
return name_tree_lookup(&priv->n, decomp, len, labs, dclass) != NULL;
}
size_t priv_get_mem(struct iter_priv* priv)
{
if(!priv) return 0;
return sizeof(*priv) + regional_get_mem(priv->region);
}
-/** remove RR from msgparse RRset, return true if rrset is entirely bad */
-static int
-remove_rr(const char* str, sldns_buffer* pkt, struct rrset_parse* rrset,
- struct rr_parse* prev, struct rr_parse** rr, struct sockaddr_storage* addr, socklen_t addrlen)
-{
- if(verbosity >= VERB_QUERY && rrset->dname_len <= LDNS_MAX_DOMAINLEN && str) {
- uint8_t buf[LDNS_MAX_DOMAINLEN+1];
- dname_pkt_copy(pkt, buf, rrset->dname);
- log_name_addr(VERB_QUERY, str, buf, addr, addrlen);
- }
- if(prev)
- prev->next = (*rr)->next;
- else rrset->rr_first = (*rr)->next;
- if(rrset->rr_last == *rr)
- rrset->rr_last = prev;
- rrset->rr_count --;
- rrset->size -= (*rr)->size;
- /* rr struct still exists, but is unlinked, so that in the for loop
- * the rr->next works fine to continue. */
- return rrset->rr_count == 0;
-}
-
int priv_rrset_bad(struct iter_priv* priv, sldns_buffer* pkt,
struct rrset_parse* rrset)
{
if(priv->a.count == 0)
return 0; /* there are no blocked addresses */
/* see if it is a private name, that is allowed to have any */
if(priv_lookup_name(priv, pkt, rrset->dname, rrset->dname_len,
ntohs(rrset->rrset_class))) {
return 0;
} else {
/* so its a public name, check the address */
socklen_t len;
struct rr_parse* rr, *prev = NULL;
if(rrset->type == LDNS_RR_TYPE_A) {
struct sockaddr_storage addr;
struct sockaddr_in sa;
len = (socklen_t)sizeof(sa);
memset(&sa, 0, len);
sa.sin_family = AF_INET;
sa.sin_port = (in_port_t)htons(UNBOUND_DNS_PORT);
for(rr = rrset->rr_first; rr; rr = rr->next) {
if(sldns_read_uint16(rr->ttl_data+4)
!= INET_SIZE) {
prev = rr;
continue;
}
memmove(&sa.sin_addr, rr->ttl_data+4+2,
INET_SIZE);
memmove(&addr, &sa, len);
if(priv_lookup_addr(priv, &addr, len)) {
- if(remove_rr("sanitize: removing public name with private address", pkt, rrset, prev, &rr, &addr, len))
+ if(msgparse_rrset_remove_rr("sanitize: removing public name with private address", pkt, rrset, prev, rr, &addr, len))
return 1;
continue;
}
prev = rr;
}
} else if(rrset->type == LDNS_RR_TYPE_AAAA) {
struct sockaddr_storage addr;
struct sockaddr_in6 sa;
len = (socklen_t)sizeof(sa);
memset(&sa, 0, len);
sa.sin6_family = AF_INET6;
sa.sin6_port = (in_port_t)htons(UNBOUND_DNS_PORT);
for(rr = rrset->rr_first; rr; rr = rr->next) {
if(sldns_read_uint16(rr->ttl_data+4)
!= INET6_SIZE) {
prev = rr;
continue;
}
memmove(&sa.sin6_addr, rr->ttl_data+4+2,
INET6_SIZE);
memmove(&addr, &sa, len);
if(priv_lookup_addr(priv, &addr, len)) {
- if(remove_rr("sanitize: removing public name with private address", pkt, rrset, prev, &rr, &addr, len))
+ if(msgparse_rrset_remove_rr("sanitize: removing public name with private address", pkt, rrset, prev, rr, &addr, len))
return 1;
continue;
}
prev = rr;
}
}
}
return 0;
}
diff --git a/contrib/unbound/iterator/iter_resptype.c b/contrib/unbound/iterator/iter_resptype.c
index e85595b843d3..38e186e79048 100644
--- a/contrib/unbound/iterator/iter_resptype.c
+++ b/contrib/unbound/iterator/iter_resptype.c
@@ -1,298 +1,309 @@
/*
* iterator/iter_resptype.c - response type information and classification.
*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file defines the response type. DNS Responses can be classified as
* one of the response types.
*/
#include "config.h"
#include "iterator/iter_resptype.h"
#include "iterator/iter_delegpt.h"
+#include "iterator/iterator.h"
#include "services/cache/dns.h"
#include "util/net_help.h"
#include "util/data/dname.h"
#include "sldns/rrdef.h"
#include "sldns/pkthdr.h"
enum response_type
response_type_from_cache(struct dns_msg* msg,
struct query_info* request)
{
/* If the message is NXDOMAIN, then it is an ANSWER. */
if(FLAGS_GET_RCODE(msg->rep->flags) == LDNS_RCODE_NXDOMAIN)
return RESPONSE_TYPE_ANSWER;
if(request->qtype == LDNS_RR_TYPE_ANY)
return RESPONSE_TYPE_ANSWER;
/* First we look at the answer section. This can tell us if this is
* CNAME or positive ANSWER. */
if(msg->rep->an_numrrsets > 0) {
/* Now look at the answer section first. 3 states:
* o our answer is there directly,
* o our answer is there after a cname,
* o or there is just a cname. */
uint8_t* mname = request->qname;
size_t mname_len = request->qname_len;
size_t i;
for(i=0; i<msg->rep->an_numrrsets; i++) {
struct ub_packed_rrset_key* s = msg->rep->rrsets[i];
/* If we have encountered an answer (before or
* after a CNAME), then we are done! Note that
* if qtype == CNAME then this will be noted as
* an ANSWER before it gets treated as a CNAME,
* as it should */
if(ntohs(s->rk.type) == request->qtype &&
ntohs(s->rk.rrset_class) == request->qclass &&
query_dname_compare(mname, s->rk.dname) == 0) {
return RESPONSE_TYPE_ANSWER;
}
/* If we have encountered a CNAME, make sure that
* it is relevant. */
if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME &&
query_dname_compare(mname, s->rk.dname) == 0) {
get_cname_target(s, &mname, &mname_len);
}
}
/* if we encountered a CNAME (or a bunch of CNAMEs), and
* still got to here, then it is a CNAME response. (i.e.,
* the CNAME chain didn't terminate in an answer rrset.) */
if(mname != request->qname) {
return RESPONSE_TYPE_CNAME;
}
}
/* At this point, since we don't need to detect REFERRAL or LAME
* messages, it can only be an ANSWER. */
return RESPONSE_TYPE_ANSWER;
}
enum response_type
response_type_from_server(int rdset,
- struct dns_msg* msg, struct query_info* request, struct delegpt* dp)
+ struct dns_msg* msg, struct query_info* request, struct delegpt* dp,
+ int* empty_nodata_found)
{
uint8_t* origzone = (uint8_t*)"\000"; /* the default */
struct ub_packed_rrset_key* s;
size_t i;
if(!msg || !request)
return RESPONSE_TYPE_THROWAWAY;
/* If the TC flag is set, the response is incomplete. Too large to
* fit even in TCP or so. Discard it, it cannot be retrieved here. */
if((msg->rep->flags & BIT_TC))
return RESPONSE_TYPE_THROWAWAY;
/* If the message is NXDOMAIN, then it answers the question. */
if(FLAGS_GET_RCODE(msg->rep->flags) == LDNS_RCODE_NXDOMAIN) {
/* make sure its not recursive when we don't want it to */
if( (msg->rep->flags&BIT_RA) &&
!(msg->rep->flags&BIT_AA) && !rdset)
return RESPONSE_TYPE_REC_LAME;
/* it could be a CNAME with NXDOMAIN rcode */
for(i=0; i<msg->rep->an_numrrsets; i++) {
s = msg->rep->rrsets[i];
if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME &&
query_dname_compare(request->qname,
s->rk.dname) == 0) {
return RESPONSE_TYPE_CNAME;
}
}
return RESPONSE_TYPE_ANSWER;
}
/* Other response codes mean (so far) to throw the response away as
* meaningless and move on to the next nameserver. */
if(FLAGS_GET_RCODE(msg->rep->flags) != LDNS_RCODE_NOERROR)
return RESPONSE_TYPE_THROWAWAY;
/* Note: TC bit has already been handled */
if(dp) {
origzone = dp->name;
}
/* First we look at the answer section. This can tell us if this is a
* CNAME or ANSWER or (provisional) ANSWER. */
if(msg->rep->an_numrrsets > 0) {
uint8_t* mname = request->qname;
size_t mname_len = request->qname_len;
/* Now look at the answer section first. 3 states: our
* answer is there directly, our answer is there after
* a cname, or there is just a cname. */
for(i=0; i<msg->rep->an_numrrsets; i++) {
s = msg->rep->rrsets[i];
/* if the answer section has NS rrset, and qtype ANY
* and the delegation is lower, and no CNAMEs followed,
* this is a referral where the NS went to AN section */
if((request->qtype == LDNS_RR_TYPE_ANY ||
request->qtype == LDNS_RR_TYPE_NS) &&
ntohs(s->rk.type) == LDNS_RR_TYPE_NS &&
ntohs(s->rk.rrset_class) == request->qclass &&
dname_strict_subdomain_c(s->rk.dname,
origzone)) {
if((msg->rep->flags&BIT_AA))
return RESPONSE_TYPE_ANSWER;
return RESPONSE_TYPE_REFERRAL;
}
/* If we have encountered an answer (before or
* after a CNAME), then we are done! Note that
* if qtype == CNAME then this will be noted as an
* ANSWER before it gets treated as a CNAME, as
* it should. */
if(ntohs(s->rk.type) == request->qtype &&
ntohs(s->rk.rrset_class) == request->qclass &&
query_dname_compare(mname, s->rk.dname) == 0) {
if((msg->rep->flags&BIT_AA))
return RESPONSE_TYPE_ANSWER;
/* If the AA bit isn't on, and we've seen
* the answer, we only provisionally say
* 'ANSWER' -- it very well could be a
* REFERRAL. */
break;
}
/* If we have encountered a CNAME, make sure that
* it is relevant. */
if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME &&
query_dname_compare(mname, s->rk.dname) == 0) {
get_cname_target(s, &mname, &mname_len);
}
}
/* not a referral, and qtype any, thus an answer */
if(request->qtype == LDNS_RR_TYPE_ANY)
return RESPONSE_TYPE_ANSWER;
/* if we encountered a CNAME (or a bunch of CNAMEs), and
* still got to here, then it is a CNAME response.
* (This is regardless of the AA bit at this point) */
if(mname != request->qname) {
return RESPONSE_TYPE_CNAME;
}
}
/* Looking at the authority section, we just look and see if
* there is a SOA record, that means a NOERROR/NODATA */
for(i = msg->rep->an_numrrsets; i < (msg->rep->an_numrrsets +
msg->rep->ns_numrrsets); i++) {
s = msg->rep->rrsets[i];
/* The normal way of detecting NOERROR/NODATA. */
if(ntohs(s->rk.type) == LDNS_RR_TYPE_SOA &&
dname_subdomain_c(request->qname, s->rk.dname)) {
/* we do our own recursion, thank you */
if( (msg->rep->flags&BIT_RA) &&
!(msg->rep->flags&BIT_AA) && !rdset)
return RESPONSE_TYPE_REC_LAME;
return RESPONSE_TYPE_ANSWER;
}
}
/* Looking at the authority section, we just look and see if
* there is a delegation NS set, turning it into a delegation.
* Otherwise, we will have to conclude ANSWER (either it is
* NOERROR/NODATA, or an non-authoritative answer). */
for(i = msg->rep->an_numrrsets; i < (msg->rep->an_numrrsets +
msg->rep->ns_numrrsets); i++) {
s = msg->rep->rrsets[i];
/* Detect REFERRAL/LAME/ANSWER based on the relationship
* of the NS set to the originating zone name. */
if(ntohs(s->rk.type) == LDNS_RR_TYPE_NS) {
/* If we are getting an NS set for the zone we
* thought we were contacting, then it is an answer.*/
if(query_dname_compare(s->rk.dname, origzone) == 0) {
/* see if mistakenly a recursive server was
* deployed and is responding nonAA */
if( (msg->rep->flags&BIT_RA) &&
!(msg->rep->flags&BIT_AA) && !rdset)
return RESPONSE_TYPE_REC_LAME;
/* Or if a lame server is deployed,
* which gives ns==zone delegation from cache
* without AA bit as well, with nodata nosoa*/
/* real answer must be +AA and SOA RFC(2308),
* so this is wrong, and we SERVFAIL it if
* this is the only possible reply, if it
* is misdeployed the THROWAWAY makes us pick
* the next server from the selection */
if(msg->rep->an_numrrsets==0 &&
!(msg->rep->flags&BIT_AA) && !rdset)
return RESPONSE_TYPE_THROWAWAY;
return RESPONSE_TYPE_ANSWER;
}
/* If we are getting a referral upwards (or to
* the same zone), then the server is 'lame'. */
if(dname_subdomain_c(origzone, s->rk.dname)) {
if(rdset) /* forward or reclame not LAME */
return RESPONSE_TYPE_THROWAWAY;
return RESPONSE_TYPE_LAME;
}
/* If the NS set is below the delegation point we
* are on, and it is non-authoritative, then it is
* a referral, otherwise it is an answer. */
if(dname_subdomain_c(s->rk.dname, origzone)) {
/* NOTE: I no longer remember in what case
* we would like this to be an answer.
* NODATA should have a SOA or nothing,
* not an NS rrset.
* True, referrals should not have the AA
* bit set, but... */
/* if((msg->rep->flags&BIT_AA))
return RESPONSE_TYPE_ANSWER; */
return RESPONSE_TYPE_REFERRAL;
}
/* Otherwise, the NS set is irrelevant. */
}
}
/* 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. */
+ /* For entirely empty messages, try again, at first, then accept
+ * it it happens more. A regular noerror/nodata response has a soa
+ * negative ttl value in the authority section. This makes it try
+ * again at another authority. And decides between storing a 5 second
+ * empty message or 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;
+ msg->rep->ar_numrrsets == 0) {
+ if(empty_nodata_found) {
+ /* detect as throwaway at first, but accept later. */
+ (*empty_nodata_found)++;
+ if(*empty_nodata_found < EMPTY_NODATA_RETRY_COUNT)
+ return RESPONSE_TYPE_THROWAWAY;
+ return RESPONSE_TYPE_ANSWER;
+ }
+ return RESPONSE_TYPE_ANSWER;
+ }
/* 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 RESPONSE_TYPE_ANSWER;
}
diff --git a/contrib/unbound/iterator/iter_resptype.h b/contrib/unbound/iterator/iter_resptype.h
index fee9ef35f83f..bfd4b664f621 100644
--- a/contrib/unbound/iterator/iter_resptype.h
+++ b/contrib/unbound/iterator/iter_resptype.h
@@ -1,127 +1,129 @@
/*
* iterator/iter_resptype.h - response type information and classification.
*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file defines the response type. DNS Responses can be classified as
* one of the response types.
*/
#ifndef ITERATOR_ITER_RESPTYPE_H
#define ITERATOR_ITER_RESPTYPE_H
struct dns_msg;
struct query_info;
struct delegpt;
/**
* The response type is used to interpret the response.
*/
enum response_type {
/**
* 'untyped' means that the type of this response hasn't been
* assigned.
*/
RESPONSE_TYPE_UNTYPED = 0,
/**
* 'answer' means that the response terminates the resolution
* process.
*/
RESPONSE_TYPE_ANSWER,
/** 'delegation' means that the response is a delegation. */
RESPONSE_TYPE_REFERRAL,
/**
* 'cname' means that the response is a cname without the final
* answer, and thus must be restarted.
*/
RESPONSE_TYPE_CNAME,
/**
* 'throwaway' means that this particular response should be
* discarded and the next nameserver should be contacted
*/
RESPONSE_TYPE_THROWAWAY,
/**
* 'lame' means that this particular response indicates that
* the nameserver knew nothing about the question.
*/
RESPONSE_TYPE_LAME,
/**
* Recursion lame means that the nameserver is some sort of
* open recursor, and not authoritative for the question.
* It may know something, but not authoritatively.
*/
RESPONSE_TYPE_REC_LAME
};
/**
* Classifies a response message from cache based on the current request.
* Note that this routine assumes that THROWAWAY or LAME responses will not
* occur. Also, it will not detect REFERRAL type messages, since those are
* (currently) automatically classified based on how they came from the
* cache (findDelegation() instead of lookup()).
*
* @param msg: the message from the cache.
* @param request: the request that generated the response.
* @return the response type (CNAME or ANSWER).
*/
enum response_type response_type_from_cache(struct dns_msg* msg,
struct query_info* request);
/**
* Classifies a response message (from the wire) based on the current
* request.
*
* NOTE: currently this routine uses the AA bit in the response to help
* distinguish between some non-standard referrals and answers. It also
* relies somewhat on the originating zone to be accurate (for lameness
* detection, mostly).
*
* @param rdset: if RD bit was sent in query sent by unbound.
* @param msg: the message from the cache.
* @param request: the request that generated the response.
* @param dp: The delegation point that was being queried
* when the response was returned.
+ * @param empty_nodata_found: flag to keep track of empty nodata detection.
* @return the response type (CNAME or ANSWER).
*/
enum response_type response_type_from_server(int rdset,
- struct dns_msg* msg, struct query_info* request, struct delegpt* dp);
+ struct dns_msg* msg, struct query_info* request, struct delegpt* dp,
+ int* empty_nodata_found);
#endif /* ITERATOR_ITER_RESPTYPE_H */
diff --git a/contrib/unbound/iterator/iter_scrub.c b/contrib/unbound/iterator/iter_scrub.c
index d1fedcd0f908..5f2e30337229 100644
--- a/contrib/unbound/iterator/iter_scrub.c
+++ b/contrib/unbound/iterator/iter_scrub.c
@@ -1,892 +1,953 @@
/*
* iterator/iter_scrub.c - scrubbing, normalization, sanitization of DNS msgs.
*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file has routine(s) for cleaning up incoming DNS messages from
* possible useless or malicious junk in it.
*/
#include "config.h"
#include "iterator/iter_scrub.h"
#include "iterator/iterator.h"
#include "iterator/iter_priv.h"
#include "services/cache/rrset.h"
#include "util/log.h"
#include "util/net_help.h"
#include "util/regional.h"
#include "util/config_file.h"
#include "util/module.h"
#include "util/data/msgparse.h"
#include "util/data/dname.h"
#include "util/data/msgreply.h"
#include "util/alloc.h"
#include "sldns/sbuffer.h"
/** RRset flag used during scrubbing. The RRset is OK. */
#define RRSET_SCRUB_OK 0x80
/** remove rrset, update loop variables */
static void
remove_rrset(const char* str, sldns_buffer* pkt, struct msg_parse* msg,
struct rrset_parse* prev, struct rrset_parse** rrset)
{
if(verbosity >= VERB_QUERY && str
&& (*rrset)->dname_len <= LDNS_MAX_DOMAINLEN) {
uint8_t buf[LDNS_MAX_DOMAINLEN+1];
dname_pkt_copy(pkt, buf, (*rrset)->dname);
log_nametypeclass(VERB_QUERY, str, buf,
(*rrset)->type, ntohs((*rrset)->rrset_class));
}
if(prev)
prev->rrset_all_next = (*rrset)->rrset_all_next;
else msg->rrset_first = (*rrset)->rrset_all_next;
if(msg->rrset_last == *rrset)
msg->rrset_last = prev;
msg->rrset_count --;
switch((*rrset)->section) {
case LDNS_SECTION_ANSWER: msg->an_rrsets--; break;
case LDNS_SECTION_AUTHORITY: msg->ns_rrsets--; break;
case LDNS_SECTION_ADDITIONAL: msg->ar_rrsets--; break;
default: log_assert(0);
}
msgparse_bucket_remove(msg, *rrset);
*rrset = (*rrset)->rrset_all_next;
}
/** return true if rr type has additional names in it */
static int
has_additional(uint16_t t)
{
switch(t) {
case LDNS_RR_TYPE_MB:
case LDNS_RR_TYPE_MD:
case LDNS_RR_TYPE_MF:
case LDNS_RR_TYPE_NS:
case LDNS_RR_TYPE_MX:
case LDNS_RR_TYPE_KX:
case LDNS_RR_TYPE_SRV:
return 1;
case LDNS_RR_TYPE_NAPTR:
/* TODO: NAPTR not supported, glue stripped off */
return 0;
}
return 0;
}
/** get additional name from rrset RR, return false if no name present */
static int
get_additional_name(struct rrset_parse* rrset, struct rr_parse* rr,
uint8_t** nm, size_t* nmlen, sldns_buffer* pkt)
{
size_t offset = 0;
size_t len, oldpos;
switch(rrset->type) {
case LDNS_RR_TYPE_MB:
case LDNS_RR_TYPE_MD:
case LDNS_RR_TYPE_MF:
case LDNS_RR_TYPE_NS:
offset = 0;
break;
case LDNS_RR_TYPE_MX:
case LDNS_RR_TYPE_KX:
offset = 2;
break;
case LDNS_RR_TYPE_SRV:
offset = 6;
break;
case LDNS_RR_TYPE_NAPTR:
/* TODO: NAPTR not supported, glue stripped off */
return 0;
default:
return 0;
}
len = sldns_read_uint16(rr->ttl_data+sizeof(uint32_t));
if(len < offset+1)
return 0; /* rdata field too small */
*nm = rr->ttl_data+sizeof(uint32_t)+sizeof(uint16_t)+offset;
oldpos = sldns_buffer_position(pkt);
sldns_buffer_set_position(pkt, (size_t)(*nm - sldns_buffer_begin(pkt)));
*nmlen = pkt_dname_len(pkt);
sldns_buffer_set_position(pkt, oldpos);
if(*nmlen == 0)
return 0;
return 1;
}
/** Place mark on rrsets in additional section they are OK */
static void
mark_additional_rrset(sldns_buffer* pkt, struct msg_parse* msg,
struct rrset_parse* rrset)
{
/* Mark A and AAAA for NS as appropriate additional section info. */
uint8_t* nm = NULL;
size_t nmlen = 0;
struct rr_parse* rr;
if(!has_additional(rrset->type))
return;
for(rr = rrset->rr_first; rr; rr = rr->next) {
if(get_additional_name(rrset, rr, &nm, &nmlen, pkt)) {
/* mark A */
hashvalue_type h = pkt_hash_rrset(pkt, nm,
LDNS_RR_TYPE_A, rrset->rrset_class, 0);
struct rrset_parse* r = msgparse_hashtable_lookup(
msg, pkt, h, 0, nm, nmlen,
LDNS_RR_TYPE_A, rrset->rrset_class);
if(r && r->section == LDNS_SECTION_ADDITIONAL) {
r->flags |= RRSET_SCRUB_OK;
}
/* mark AAAA */
h = pkt_hash_rrset(pkt, nm, LDNS_RR_TYPE_AAAA,
rrset->rrset_class, 0);
r = msgparse_hashtable_lookup(msg, pkt, h, 0, nm,
nmlen, LDNS_RR_TYPE_AAAA, rrset->rrset_class);
if(r && r->section == LDNS_SECTION_ADDITIONAL) {
r->flags |= RRSET_SCRUB_OK;
}
}
}
}
/** Get target name of a CNAME */
static int
parse_get_cname_target(struct rrset_parse* rrset, uint8_t** sname,
size_t* snamelen, sldns_buffer* pkt)
{
size_t oldpos, dlen;
if(rrset->rr_count != 1) {
struct rr_parse* sig;
verbose(VERB_ALGO, "Found CNAME rrset with "
"size > 1: %u", (unsigned)rrset->rr_count);
/* use the first CNAME! */
rrset->rr_count = 1;
rrset->size = rrset->rr_first->size;
for(sig=rrset->rrsig_first; sig; sig=sig->next)
rrset->size += sig->size;
rrset->rr_last = rrset->rr_first;
rrset->rr_first->next = NULL;
}
if(rrset->rr_first->size < sizeof(uint16_t)+1)
return 0; /* CNAME rdata too small */
*sname = rrset->rr_first->ttl_data + sizeof(uint32_t)
+ sizeof(uint16_t); /* skip ttl, rdatalen */
*snamelen = rrset->rr_first->size - sizeof(uint16_t);
if(rrset->rr_first->outside_packet) {
if(!dname_valid(*sname, *snamelen))
return 0;
return 1;
}
oldpos = sldns_buffer_position(pkt);
sldns_buffer_set_position(pkt, (size_t)(*sname - sldns_buffer_begin(pkt)));
dlen = pkt_dname_len(pkt);
sldns_buffer_set_position(pkt, oldpos);
if(dlen == 0)
return 0; /* parse fail on the rdata name */
*snamelen = dlen;
return 1;
}
/** Synthesize CNAME from DNAME, false if too long */
static int
synth_cname(uint8_t* qname, size_t qnamelen, struct rrset_parse* dname_rrset,
uint8_t* alias, size_t* aliaslen, sldns_buffer* pkt)
{
/* we already know that sname is a strict subdomain of DNAME owner */
uint8_t* dtarg = NULL;
size_t dtarglen;
if(!parse_get_cname_target(dname_rrset, &dtarg, &dtarglen, pkt))
return 0;
if(qnamelen <= dname_rrset->dname_len)
return 0;
if(qnamelen == 0)
return 0;
log_assert(qnamelen > dname_rrset->dname_len);
/* DNAME from com. to net. with qname example.com. -> example.net. */
/* so: \3com\0 to \3net\0 and qname \7example\3com\0 */
*aliaslen = qnamelen + dtarglen - dname_rrset->dname_len;
if(*aliaslen > LDNS_MAX_DOMAINLEN)
return 0; /* should have been RCODE YXDOMAIN */
/* decompress dnames into buffer, we know it fits */
dname_pkt_copy(pkt, alias, qname);
dname_pkt_copy(pkt, alias+(qnamelen-dname_rrset->dname_len), dtarg);
return 1;
}
/** synthesize a CNAME rrset */
static struct rrset_parse*
synth_cname_rrset(uint8_t** sname, size_t* snamelen, uint8_t* alias,
size_t aliaslen, struct regional* region, struct msg_parse* msg,
struct rrset_parse* rrset, struct rrset_parse* prev,
struct rrset_parse* nx, sldns_buffer* pkt)
{
struct rrset_parse* cn = (struct rrset_parse*)regional_alloc(region,
sizeof(struct rrset_parse));
if(!cn)
return NULL;
memset(cn, 0, sizeof(*cn));
cn->rr_first = (struct rr_parse*)regional_alloc(region,
sizeof(struct rr_parse));
if(!cn->rr_first)
return NULL;
cn->rr_last = cn->rr_first;
/* CNAME from sname to alias */
cn->dname = (uint8_t*)regional_alloc(region, *snamelen);
if(!cn->dname)
return NULL;
dname_pkt_copy(pkt, cn->dname, *sname);
cn->dname_len = *snamelen;
cn->type = LDNS_RR_TYPE_CNAME;
cn->section = rrset->section;
cn->rrset_class = rrset->rrset_class;
cn->rr_count = 1;
cn->size = sizeof(uint16_t) + aliaslen;
cn->hash=pkt_hash_rrset(pkt, cn->dname, cn->type, cn->rrset_class, 0);
/* allocate TTL + rdatalen + uncompressed dname */
memset(cn->rr_first, 0, sizeof(struct rr_parse));
cn->rr_first->outside_packet = 1;
cn->rr_first->ttl_data = (uint8_t*)regional_alloc(region,
sizeof(uint32_t)+sizeof(uint16_t)+aliaslen);
if(!cn->rr_first->ttl_data)
return NULL;
sldns_write_uint32(cn->rr_first->ttl_data, 0); /* TTL = 0 */
sldns_write_uint16(cn->rr_first->ttl_data+4, aliaslen);
memmove(cn->rr_first->ttl_data+6, alias, aliaslen);
cn->rr_first->size = sizeof(uint16_t)+aliaslen;
/* link it in */
cn->rrset_all_next = nx;
if(prev)
prev->rrset_all_next = cn;
else msg->rrset_first = cn;
if(nx == NULL)
msg->rrset_last = cn;
msg->rrset_count ++;
msg->an_rrsets++;
/* it is not inserted in the msg hashtable. */
*sname = cn->rr_first->ttl_data + sizeof(uint32_t)+sizeof(uint16_t);
*snamelen = aliaslen;
return cn;
}
/** check if DNAME applies to a name */
static int
pkt_strict_sub(sldns_buffer* pkt, uint8_t* sname, uint8_t* dr)
{
uint8_t buf1[LDNS_MAX_DOMAINLEN+1];
uint8_t buf2[LDNS_MAX_DOMAINLEN+1];
/* decompress names */
dname_pkt_copy(pkt, buf1, sname);
dname_pkt_copy(pkt, buf2, dr);
return dname_strict_subdomain_c(buf1, buf2);
}
/** check subdomain with decompression */
static int
pkt_sub(sldns_buffer* pkt, uint8_t* comprname, uint8_t* zone)
{
uint8_t buf[LDNS_MAX_DOMAINLEN+1];
dname_pkt_copy(pkt, buf, comprname);
return dname_subdomain_c(buf, zone);
}
/** check subdomain with decompression, compressed is parent */
static int
sub_of_pkt(sldns_buffer* pkt, uint8_t* zone, uint8_t* comprname)
{
uint8_t buf[LDNS_MAX_DOMAINLEN+1];
dname_pkt_copy(pkt, buf, comprname);
return dname_subdomain_c(zone, buf);
}
/** Check if there are SOA records in the authority section (negative) */
static int
soa_in_auth(struct msg_parse* msg)
{
struct rrset_parse* rrset;
for(rrset = msg->rrset_first; rrset; rrset = rrset->rrset_all_next)
if(rrset->type == LDNS_RR_TYPE_SOA &&
rrset->section == LDNS_SECTION_AUTHORITY)
return 1;
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
* CNAMEs from DNAMEs, if present.
*
* @param pkt: packet.
* @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 module_env* env)
{
uint8_t* sname = qinfo->qname;
size_t snamelen = qinfo->qname_len;
struct rrset_parse* rrset, *prev, *nsset=NULL;
if(FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NOERROR &&
FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NXDOMAIN)
return 1;
/* For the ANSWER section, remove all "irrelevant" records and add
* synthesized CNAMEs from DNAMEs
* This will strip out-of-order CNAMEs as well. */
/* walk through the parse packet rrset list, keep track of previous
* for insert and delete ease, and examine every RRset */
prev = NULL;
rrset = msg->rrset_first;
while(rrset && rrset->section == LDNS_SECTION_ANSWER) {
if(rrset->type == LDNS_RR_TYPE_DNAME &&
pkt_strict_sub(pkt, sname, rrset->dname)) {
/* check if next rrset is correct CNAME. else,
* synthesize a CNAME */
struct rrset_parse* nx = rrset->rrset_all_next;
uint8_t alias[LDNS_MAX_DOMAINLEN+1];
size_t aliaslen = 0;
if(rrset->rr_count != 1) {
verbose(VERB_ALGO, "Found DNAME rrset with "
"size > 1: %u",
(unsigned)rrset->rr_count);
return 0;
}
if(!synth_cname(sname, snamelen, rrset, alias,
&aliaslen, pkt)) {
verbose(VERB_ALGO, "synthesized CNAME "
"too long");
return 0;
}
if(nx && nx->type == LDNS_RR_TYPE_CNAME &&
dname_pkt_compare(pkt, sname, nx->dname) == 0) {
/* check next cname */
uint8_t* t = NULL;
size_t tlen = 0;
if(!parse_get_cname_target(nx, &t, &tlen, pkt))
return 0;
if(dname_pkt_compare(pkt, alias, t) == 0) {
/* it's OK and better capitalized */
prev = rrset;
rrset = nx;
continue;
}
/* synth ourselves */
}
/* synth a CNAME rrset */
prev = synth_cname_rrset(&sname, &snamelen, alias,
aliaslen, region, msg, rrset, rrset, nx, pkt);
if(!prev) {
log_err("out of memory synthesizing CNAME");
return 0;
}
/* FIXME: resolve the conflict between synthesized
* CNAME ttls and the cache. */
rrset = nx;
continue;
}
/* The only records in the ANSWER section not allowed to */
if(dname_pkt_compare(pkt, sname, rrset->dname) != 0) {
remove_rrset("normalize: removing irrelevant RRset:",
pkt, msg, prev, &rrset);
continue;
}
/* Follow the CNAME chain. */
if(rrset->type == LDNS_RR_TYPE_CNAME) {
struct rrset_parse* nx = rrset->rrset_all_next;
uint8_t* oldsname = sname;
/* see if the next one is a DNAME, if so, swap them */
if(nx && nx->section == LDNS_SECTION_ANSWER &&
nx->type == LDNS_RR_TYPE_DNAME &&
nx->rr_count == 1 &&
pkt_strict_sub(pkt, sname, nx->dname)) {
/* there is a DNAME after this CNAME, it
* is in the ANSWER section, and the DNAME
* applies to the name we cover */
/* check if the alias of the DNAME equals
* this CNAME */
uint8_t alias[LDNS_MAX_DOMAINLEN+1];
size_t aliaslen = 0;
uint8_t* t = NULL;
size_t tlen = 0;
if(synth_cname(sname, snamelen, nx, alias,
&aliaslen, pkt) &&
parse_get_cname_target(rrset, &t, &tlen, pkt) &&
dname_pkt_compare(pkt, alias, t) == 0) {
/* the synthesized CNAME equals the
* current CNAME. This CNAME is the
* one that the DNAME creates, and this
* CNAME is better capitalised */
verbose(VERB_ALGO, "normalize: re-order of DNAME and its CNAME");
if(prev) prev->rrset_all_next = nx;
else msg->rrset_first = nx;
if(nx->rrset_all_next == NULL)
msg->rrset_last = rrset;
rrset->rrset_all_next =
nx->rrset_all_next;
nx->rrset_all_next = rrset;
/* prev = nx; unused, enable if there
* is other rrset removal code after
* this */
}
}
/* move to next name in CNAME chain */
if(!parse_get_cname_target(rrset, &sname, &snamelen, pkt))
return 0;
prev = rrset;
rrset = rrset->rrset_all_next;
/* in CNAME ANY response, can have data after CNAME */
if(qinfo->qtype == LDNS_RR_TYPE_ANY) {
while(rrset && rrset->section ==
LDNS_SECTION_ANSWER &&
dname_pkt_compare(pkt, oldsname,
rrset->dname) == 0) {
prev = rrset;
rrset = rrset->rrset_all_next;
}
}
continue;
}
/* Otherwise, make sure that the RRset matches the qtype. */
if(qinfo->qtype != LDNS_RR_TYPE_ANY &&
qinfo->qtype != rrset->type) {
remove_rrset("normalize: removing irrelevant RRset:",
pkt, msg, prev, &rrset);
continue;
}
/* Mark the additional names from relevant rrset as OK. */
/* only for RRsets that match the query name, other ones
* will be removed by sanitize, so no additional for them */
if(dname_pkt_compare(pkt, qinfo->qname, rrset->dname) == 0)
mark_additional_rrset(pkt, msg, rrset);
prev = rrset;
rrset = rrset->rrset_all_next;
}
/* 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->type==LDNS_RR_TYPE_AAAA) {
remove_rrset("normalize: removing irrelevant "
"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 */
if(!sub_of_pkt(pkt, qinfo->qname, rrset->dname)) {
remove_rrset("normalize: removing irrelevant "
"RRset:", pkt, msg, prev, &rrset);
continue;
}
/* we don't want NS sets for NXDOMAIN answers,
* because they could contain poisonous contents,
* from. eg. fragmentation attacks, inserted after
* long RRSIGs in the packet get to the packet
* border and such */
/* also for NODATA answers */
if(FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NXDOMAIN ||
(FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NOERROR
&& soa_in_auth(msg) && msg->an_rrsets == 0)) {
remove_rrset("normalize: removing irrelevant "
"RRset:", pkt, msg, prev, &rrset);
continue;
}
if(nsset == NULL) {
nsset = rrset;
} else {
remove_rrset("normalize: removing irrelevant "
"RRset:", pkt, msg, prev, &rrset);
continue;
}
}
/* if this is type DS and we query for type DS we just got
* a referral answer for our type DS query, fix packet */
if(rrset->type==LDNS_RR_TYPE_DS &&
qinfo->qtype == LDNS_RR_TYPE_DS &&
dname_pkt_compare(pkt, qinfo->qname, rrset->dname) == 0) {
rrset->section = LDNS_SECTION_ANSWER;
msg->ancount = rrset->rr_count + rrset->rrsig_count;
msg->nscount = 0;
msg->arcount = 0;
msg->an_rrsets = 1;
msg->ns_rrsets = 0;
msg->ar_rrsets = 0;
msg->rrset_count = 1;
msg->rrset_first = rrset;
msg->rrset_last = rrset;
rrset->rrset_all_next = NULL;
return 1;
}
mark_additional_rrset(pkt, msg, rrset);
prev = rrset;
rrset = rrset->rrset_all_next;
}
/* For each record in the additional section, remove it if it is an
* address record and not in the collection of additional names
* found in ANSWER and AUTHORITY. */
/* These records have not been marked OK previously */
while(rrset && rrset->section == LDNS_SECTION_ADDITIONAL) {
if(rrset->type==LDNS_RR_TYPE_A ||
rrset->type==LDNS_RR_TYPE_AAAA)
{
if((rrset->flags & RRSET_SCRUB_OK)) {
/* remove flag to clean up flags variable */
rrset->flags &= ~RRSET_SCRUB_OK;
} else {
remove_rrset("normalize: removing irrelevant "
"RRset:", pkt, msg, prev, &rrset);
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) {
remove_rrset("normalize: removing irrelevant "
"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;
}
return 1;
}
/**
* Store potential poison in the cache (only if hardening disabled).
* The rrset is stored in the cache but removed from the message.
* So that it will be used for infrastructure purposes, but not be
* returned to the client.
* @param pkt: packet
* @param msg: message parsed
* @param env: environment with cache
* @param rrset: to store.
*/
static void
store_rrset(sldns_buffer* pkt, struct msg_parse* msg, struct module_env* env,
struct rrset_parse* rrset)
{
struct ub_packed_rrset_key* k;
struct packed_rrset_data* d;
struct rrset_ref ref;
time_t now = *env->now;
k = alloc_special_obtain(env->alloc);
if(!k)
return;
k->entry.data = NULL;
if(!parse_copy_decompress_rrset(pkt, msg, rrset, NULL, k)) {
alloc_special_release(env->alloc, k);
return;
}
d = (struct packed_rrset_data*)k->entry.data;
packed_rrset_ttl_add(d, now);
ref.key = k;
ref.id = k->id;
/*ignore ret: it was in the cache, ref updated */
(void)rrset_cache_update(env->rrset_cache, &ref, env->alloc, now);
}
/**
* Check if right hand name in NSEC is within zone
* @param pkt: the packet buffer for decompression.
* @param rrset: the NSEC rrset
* @param zonename: the zone name.
* @return true if BAD.
*/
static int sanitize_nsec_is_overreach(sldns_buffer* pkt,
struct rrset_parse* rrset, uint8_t* zonename)
{
struct rr_parse* rr;
uint8_t* rhs;
size_t len;
log_assert(rrset->type == LDNS_RR_TYPE_NSEC);
for(rr = rrset->rr_first; rr; rr = rr->next) {
size_t pos = sldns_buffer_position(pkt);
size_t rhspos;
rhs = rr->ttl_data+4+2;
len = sldns_read_uint16(rr->ttl_data+4);
rhspos = rhs-sldns_buffer_begin(pkt);
sldns_buffer_set_position(pkt, rhspos);
if(pkt_dname_len(pkt) == 0) {
/* malformed */
sldns_buffer_set_position(pkt, pos);
return 1;
}
if(sldns_buffer_position(pkt)-rhspos > len) {
/* outside of rdata boundaries */
sldns_buffer_set_position(pkt, pos);
return 1;
}
sldns_buffer_set_position(pkt, pos);
if(!pkt_sub(pkt, rhs, zonename)) {
/* overreaching */
return 1;
}
}
/* all NSEC RRs OK */
return 0;
}
+/** Remove individual RRs, if the length is wrong. Returns true if the RRset
+ * has been removed. */
+static int
+scrub_sanitize_rr_length(sldns_buffer* pkt, struct msg_parse* msg,
+ struct rrset_parse* prev, struct rrset_parse** rrset, int* added_ede,
+ struct module_qstate* qstate)
+{
+ struct rr_parse* rr, *rr_prev = NULL;
+ for(rr = (*rrset)->rr_first; rr; rr = rr->next) {
+
+ /* Sanity check for length of records
+ * An A record should be 6 bytes only
+ * (2 bytes for length and 4 for IPv4 addr)*/
+ if((*rrset)->type == LDNS_RR_TYPE_A && rr->size != 6 ) {
+ if(!*added_ede) {
+ *added_ede = 1;
+ errinf_ede(qstate, "sanitize: records of inappropriate length have been removed.",
+ LDNS_EDE_OTHER);
+ }
+ if(msgparse_rrset_remove_rr("sanitize: removing type A RR of inappropriate length:",
+ pkt, *rrset, rr_prev, rr, NULL, 0)) {
+ remove_rrset("sanitize: removing type A RRset of inappropriate length:",
+ pkt, msg, prev, rrset);
+ return 1;
+ }
+ continue;
+ }
+
+ /* Sanity check for length of records
+ * An AAAA record should be 18 bytes only
+ * (2 bytes for length and 16 for IPv6 addr)*/
+ if((*rrset)->type == LDNS_RR_TYPE_AAAA && rr->size != 18 ) {
+ if(!*added_ede) {
+ *added_ede = 1;
+ errinf_ede(qstate, "sanitize: records of inappropriate length have been removed.",
+ LDNS_EDE_OTHER);
+ }
+ if(msgparse_rrset_remove_rr("sanitize: removing type AAAA RR of inappropriate length:",
+ pkt, *rrset, rr_prev, rr, NULL, 0)) {
+ remove_rrset("sanitize: removing type AAAA RRset of inappropriate length:",
+ pkt, msg, prev, rrset);
+ return 1;
+ }
+ continue;
+ }
+ rr_prev = rr;
+ }
+ return 0;
+}
+
/**
* Given a response event, remove suspect RRsets from the response.
* "Suspect" rrsets are potentially poison. Note that this routine expects
* the response to be in a "normalized" state -- that is, all "irrelevant"
* RRsets have already been removed, CNAMEs are in order, etc.
*
* @param pkt: packet.
* @param msg: msg to normalize.
* @param qinfo: the question originally asked.
* @param zonename: name of server zone.
* @param env: module environment with config and cache.
* @param ie: iterator environment with private address data.
+ * @param qstate: for setting errinf for EDE error messages.
* @return 0 on error.
*/
static int
scrub_sanitize(sldns_buffer* pkt, struct msg_parse* msg,
struct query_info* qinfo, uint8_t* zonename, struct module_env* env,
- struct iter_env* ie)
+ struct iter_env* ie, struct module_qstate* qstate)
{
int del_addi = 0; /* if additional-holding rrsets are deleted, we
do not trust the normalized additional-A-AAAA any more */
+ int added_rrlen_ede = 0;
struct rrset_parse* rrset, *prev;
prev = NULL;
rrset = msg->rrset_first;
/* the first DNAME is allowed to stay. It needs checking before
* it can be used from the cache. After normalization, an initial
* DNAME will have a correctly synthesized CNAME after it. */
if(rrset && rrset->type == LDNS_RR_TYPE_DNAME &&
rrset->section == LDNS_SECTION_ANSWER &&
pkt_strict_sub(pkt, qinfo->qname, rrset->dname) &&
pkt_sub(pkt, rrset->dname, zonename)) {
prev = rrset; /* DNAME allowed to stay in answer section */
rrset = rrset->rrset_all_next;
}
/* remove all records from the answer section that are
* not the same domain name as the query domain name.
* The answer section should contain rrsets with the same name
* as the question. For DNAMEs a CNAME has been synthesized.
* Wildcards have the query name in answer section.
* ANY queries get query name in answer section.
* Remainders of CNAME chains are cut off and resolved by iterator. */
while(rrset && rrset->section == LDNS_SECTION_ANSWER) {
if(dname_pkt_compare(pkt, qinfo->qname, rrset->dname) != 0) {
if(has_additional(rrset->type)) del_addi = 1;
remove_rrset("sanitize: removing extraneous answer "
"RRset:", pkt, msg, prev, &rrset);
continue;
}
prev = rrset;
rrset = rrset->rrset_all_next;
}
/* At this point, we brutally remove ALL rrsets that aren't
* children of the originating zone. The idea here is that,
* as far as we know, the server that we contacted is ONLY
* authoritative for the originating zone. It, of course, MAY
* be authoritative for any other zones, and of course, MAY
* NOT be authoritative for some subdomains of the originating
* zone. */
prev = NULL;
rrset = msg->rrset_first;
while(rrset) {
+ /* Sanity check for length of records */
+ if(rrset->type == LDNS_RR_TYPE_A ||
+ rrset->type == LDNS_RR_TYPE_AAAA) {
+ if(scrub_sanitize_rr_length(pkt, msg, prev, &rrset,
+ &added_rrlen_ede, qstate))
+ continue;
+ }
+
/* remove private addresses */
if( (rrset->type == LDNS_RR_TYPE_A ||
rrset->type == LDNS_RR_TYPE_AAAA)) {
/* do not set servfail since this leads to too
* many drops of other people using rfc1918 space */
/* also do not remove entire rrset, unless all records
* in it are bad */
if(priv_rrset_bad(ie->priv, pkt, rrset)) {
remove_rrset(NULL, pkt, msg, prev, &rrset);
continue;
}
}
/* skip DNAME records -- they will always be followed by a
* synthesized CNAME, which will be relevant.
* FIXME: should this do something differently with DNAME
* rrsets NOT in Section.ANSWER? */
/* But since DNAME records are also subdomains of the zone,
* same check can be used */
if(!pkt_sub(pkt, rrset->dname, zonename)) {
if(msg->an_rrsets == 0 &&
rrset->type == LDNS_RR_TYPE_NS &&
rrset->section == LDNS_SECTION_AUTHORITY &&
FLAGS_GET_RCODE(msg->flags) ==
LDNS_RCODE_NOERROR && !soa_in_auth(msg) &&
sub_of_pkt(pkt, zonename, rrset->dname)) {
/* noerror, nodata and this NS rrset is above
* the zone. This is LAME!
* Leave in the NS for lame classification. */
/* remove everything from the additional
* (we dont want its glue that was approved
* during the normalize action) */
del_addi = 1;
} else if(!env->cfg->harden_glue && (
rrset->type == LDNS_RR_TYPE_A ||
rrset->type == LDNS_RR_TYPE_AAAA)) {
/* store in cache! Since it is relevant
* (from normalize) it will be picked up
* from the cache to be used later */
store_rrset(pkt, msg, env, rrset);
remove_rrset("sanitize: storing potential "
"poison RRset:", pkt, msg, prev, &rrset);
continue;
} else {
if(has_additional(rrset->type)) del_addi = 1;
remove_rrset("sanitize: removing potential "
"poison RRset:", pkt, msg, prev, &rrset);
continue;
}
}
if(del_addi && rrset->section == LDNS_SECTION_ADDITIONAL) {
remove_rrset("sanitize: removing potential "
"poison reference RRset:", pkt, msg, prev, &rrset);
continue;
}
/* check if right hand side of NSEC is within zone */
if(rrset->type == LDNS_RR_TYPE_NSEC &&
sanitize_nsec_is_overreach(pkt, rrset, zonename)) {
remove_rrset("sanitize: removing overreaching NSEC "
"RRset:", pkt, msg, prev, &rrset);
continue;
}
prev = rrset;
rrset = rrset->rrset_all_next;
}
return 1;
}
int
scrub_message(sldns_buffer* pkt, struct msg_parse* msg,
struct query_info* qinfo, uint8_t* zonename, struct regional* region,
- struct module_env* env, struct iter_env* ie)
+ struct module_env* env, struct module_qstate* qstate,
+ struct iter_env* ie)
{
/* basic sanity checks */
log_nametypeclass(VERB_ALGO, "scrub for", zonename, LDNS_RR_TYPE_NS,
qinfo->qclass);
if(msg->qdcount > 1)
return 0;
if( !(msg->flags&BIT_QR) )
return 0;
msg->flags &= ~(BIT_AD|BIT_Z); /* force off bit AD and Z */
/* make sure that a query is echoed back when NOERROR or NXDOMAIN */
/* this is not required for basic operation but is a forgery
* resistance (security) feature */
if((FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NOERROR ||
FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NXDOMAIN) &&
msg->qdcount == 0)
return 0;
/* if a query is echoed back, make sure it is correct. Otherwise,
* this may be not a reply to our query. */
if(msg->qdcount == 1) {
if(dname_pkt_compare(pkt, msg->qname, qinfo->qname) != 0)
return 0;
if(msg->qtype != qinfo->qtype || msg->qclass != qinfo->qclass)
return 0;
}
/* normalize the response, this cleans up the additional. */
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))
+ if(!scrub_sanitize(pkt, msg, qinfo, zonename, env, ie, qstate))
return 0;
return 1;
}
diff --git a/contrib/unbound/iterator/iter_scrub.h b/contrib/unbound/iterator/iter_scrub.h
index cbbaf73c91d2..4d6ce7166516 100644
--- a/contrib/unbound/iterator/iter_scrub.h
+++ b/contrib/unbound/iterator/iter_scrub.h
@@ -1,69 +1,72 @@
/*
* iterator/iter_scrub.h - scrubbing, normalization, sanitization of DNS msgs.
*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file has routine(s) for cleaning up incoming DNS messages from
* possible useless or malicious junk in it.
*/
#ifndef ITERATOR_ITER_SCRUB_H
#define ITERATOR_ITER_SCRUB_H
struct sldns_buffer;
struct msg_parse;
struct query_info;
struct regional;
struct module_env;
struct iter_env;
+struct module_qstate;
/**
* Cleanup the passed dns message.
* @param pkt: the packet itself, for resolving name compression pointers.
* the packet buffer is unaltered.
* @param msg: the parsed packet, this structure is cleaned up.
* @param qinfo: the query info that was sent to the server. Checked.
* @param zonename: the name of the last delegation point.
* Used to determine out of bailiwick information.
* @param regional: where to allocate (new) parts of the message.
* @param env: module environment with config settings and cache.
+ * @param qstate: for setting errinf for EDE error messages.
* @param ie: iterator module environment data.
* @return: false if the message is total waste. true if scrubbed with success.
*/
int scrub_message(struct sldns_buffer* pkt, struct msg_parse* msg,
struct query_info* qinfo, uint8_t* zonename, struct regional* regional,
- struct module_env* env, struct iter_env* ie);
+ struct module_env* env, struct module_qstate* qstate,
+ struct iter_env* ie);
#endif /* ITERATOR_ITER_SCRUB_H */
diff --git a/contrib/unbound/iterator/iterator.c b/contrib/unbound/iterator/iterator.c
index 1548dfcaee62..6ff811a27d06 100644
--- a/contrib/unbound/iterator/iterator.c
+++ b/contrib/unbound/iterator/iterator.c
@@ -1,4337 +1,4401 @@
/*
* iterator/iterator.c - iterative resolver DNS query response module
*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file contains a module that performs recursive iterative DNS query
* processing.
*/
#include "config.h"
#include "iterator/iterator.h"
#include "iterator/iter_utils.h"
#include "iterator/iter_hints.h"
#include "iterator/iter_fwd.h"
#include "iterator/iter_donotq.h"
#include "iterator/iter_delegpt.h"
#include "iterator/iter_resptype.h"
#include "iterator/iter_scrub.h"
#include "iterator/iter_priv.h"
#include "validator/val_neg.h"
#include "services/cache/dns.h"
#include "services/cache/infra.h"
#include "services/authzone.h"
#include "util/module.h"
#include "util/netevent.h"
#include "util/net_help.h"
#include "util/regional.h"
#include "util/data/dname.h"
#include "util/data/msgencode.h"
#include "util/fptr_wlist.h"
#include "util/config_file.h"
#include "util/random.h"
#include "sldns/rrdef.h"
#include "sldns/wire2str.h"
#include "sldns/str2wire.h"
#include "sldns/parseutil.h"
#include "sldns/sbuffer.h"
/* in msec */
int UNKNOWN_SERVER_NICENESS = 376;
/* in msec */
int USEFUL_SERVER_TOP_TIMEOUT = 120000;
/* Equals USEFUL_SERVER_TOP_TIMEOUT*4 */
int BLACKLIST_PENALTY = (120000*4);
static void target_count_increase_nx(struct iter_qstate* iq, int num);
int
iter_init(struct module_env* env, int id)
{
struct iter_env* iter_env = (struct iter_env*)calloc(1,
sizeof(struct iter_env));
if(!iter_env) {
log_err("malloc failure");
return 0;
}
env->modinfo[id] = (void*)iter_env;
lock_basic_init(&iter_env->queries_ratelimit_lock);
lock_protect(&iter_env->queries_ratelimit_lock,
&iter_env->num_queries_ratelimited,
sizeof(iter_env->num_queries_ratelimited));
if(!iter_apply_cfg(iter_env, env->cfg)) {
log_err("iterator: could not apply configuration settings.");
return 0;
}
return 1;
}
/** delete caps_whitelist element */
static void
caps_free(struct rbnode_type* n, void* ATTR_UNUSED(d))
{
if(n) {
free(((struct name_tree_node*)n)->name);
free(n);
}
}
void
iter_deinit(struct module_env* env, int id)
{
struct iter_env* iter_env;
if(!env || !env->modinfo[id])
return;
iter_env = (struct iter_env*)env->modinfo[id];
lock_basic_destroy(&iter_env->queries_ratelimit_lock);
free(iter_env->target_fetch_policy);
priv_delete(iter_env->priv);
donotq_delete(iter_env->donotq);
if(iter_env->caps_white) {
traverse_postorder(iter_env->caps_white, caps_free, NULL);
free(iter_env->caps_white);
}
free(iter_env);
env->modinfo[id] = NULL;
}
/** new query for iterator */
static int
iter_new(struct module_qstate* qstate, int id)
{
struct iter_qstate* iq = (struct iter_qstate*)regional_alloc(
qstate->region, sizeof(struct iter_qstate));
qstate->minfo[id] = iq;
if(!iq)
return 0;
memset(iq, 0, sizeof(*iq));
iq->state = INIT_REQUEST_STATE;
iq->final_state = FINISHED_STATE;
iq->an_prepend_list = NULL;
iq->an_prepend_last = NULL;
iq->ns_prepend_list = NULL;
iq->ns_prepend_last = NULL;
iq->dp = NULL;
iq->depth = 0;
iq->num_target_queries = 0;
iq->num_current_queries = 0;
iq->query_restart_count = 0;
iq->referral_count = 0;
iq->sent_count = 0;
iq->ratelimit_ok = 0;
iq->target_count = NULL;
iq->dp_target_count = 0;
iq->wait_priming_stub = 0;
iq->refetch_glue = 0;
iq->dnssec_expected = 0;
iq->dnssec_lame_query = 0;
iq->chase_flags = qstate->query_flags;
/* Start with the (current) qname. */
iq->qchase = qstate->qinfo;
outbound_list_init(&iq->outlist);
iq->minimise_count = 0;
iq->timeout_count = 0;
if (qstate->env->cfg->qname_minimisation)
iq->minimisation_state = INIT_MINIMISE_STATE;
else
iq->minimisation_state = DONOT_MINIMISE_STATE;
memset(&iq->qinfo_out, 0, sizeof(struct query_info));
return 1;
}
/**
* Transition to the next state. This can be used to advance a currently
* processing event. It cannot be used to reactivate a forEvent.
*
* @param iq: iterator query state
* @param nextstate The state to transition to.
* @return true. This is so this can be called as the return value for the
* actual process*State() methods. (Transitioning to the next state
* implies further processing).
*/
static int
next_state(struct iter_qstate* iq, enum iter_state nextstate)
{
/* If transitioning to a "response" state, make sure that there is a
* response */
if(iter_state_is_responsestate(nextstate)) {
if(iq->response == NULL) {
log_err("transitioning to response state sans "
"response.");
}
}
iq->state = nextstate;
return 1;
}
/**
* Transition an event to its final state. Final states always either return
* a result up the module chain, or reactivate a dependent event. Which
* final state to transition to is set in the module state for the event when
* it was created, and depends on the original purpose of the event.
*
* The response is stored in the qstate->buf buffer.
*
* @param iq: iterator query state
* @return false. This is so this method can be used as the return value for
* the processState methods. (Transitioning to the final state
*/
static int
final_state(struct iter_qstate* iq)
{
return next_state(iq, iq->final_state);
}
/**
* Callback routine to handle errors in parent query states
* @param qstate: query state that failed.
* @param id: module id.
* @param super: super state.
*/
static void
error_supers(struct module_qstate* qstate, int id, struct module_qstate* super)
{
struct iter_env* ie = (struct iter_env*)qstate->env->modinfo[id];
struct iter_qstate* super_iq = (struct iter_qstate*)super->minfo[id];
if(qstate->qinfo.qtype == LDNS_RR_TYPE_A ||
qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA) {
/* mark address as failed. */
struct delegpt_ns* dpns = NULL;
super_iq->num_target_queries--;
if(super_iq->dp)
dpns = delegpt_find_ns(super_iq->dp,
qstate->qinfo.qname, qstate->qinfo.qname_len);
if(!dpns) {
/* not interested */
/* this can happen, for eg. qname minimisation asked
* for an NXDOMAIN to be validated, and used qtype
* A for that, and the error of that, the name, is
* not listed in super_iq->dp */
verbose(VERB_ALGO, "subq error, but not interested");
log_query_info(VERB_ALGO, "superq", &super->qinfo);
return;
} else {
/* see if the failure did get (parent-lame) info */
if(!cache_fill_missing(super->env, super_iq->qchase.qclass,
super->region, super_iq->dp))
log_err("out of memory adding missing");
}
delegpt_mark_neg(dpns, qstate->qinfo.qtype);
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);
}
}
if(qstate->qinfo.qtype == LDNS_RR_TYPE_NS) {
/* prime failed to get delegation */
super_iq->dp = NULL;
}
/* evaluate targets again */
super_iq->state = QUERYTARGETS_STATE;
/* super becomes runnable, and will process this change */
}
/**
* Return an error to the client
* @param qstate: our query state
* @param id: module id
* @param rcode: error code (DNS errcode).
* @return: 0 for use by caller, to make notation easy, like:
* return error_response(..).
*/
static int
error_response(struct module_qstate* qstate, int id, int rcode)
{
verbose(VERB_QUERY, "return error response %s",
sldns_lookup_by_id(sldns_rcodes, rcode)?
sldns_lookup_by_id(sldns_rcodes, rcode)->name:"??");
qstate->return_rcode = rcode;
qstate->return_msg = NULL;
qstate->ext_state[id] = module_finished;
return 0;
}
/**
* Return an error to the client and cache the error code in the
* message cache (so per qname, qtype, qclass).
* @param qstate: our query state
* @param id: module id
* @param rcode: error code (DNS errcode).
* @return: 0 for use by caller, to make notation easy, like:
* return error_response(..).
*/
static int
error_response_cache(struct module_qstate* qstate, int id, int rcode)
{
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(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);
}
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);
}
/** check if prepend item is duplicate item */
static int
prepend_is_duplicate(struct ub_packed_rrset_key** sets, size_t to,
struct ub_packed_rrset_key* dup)
{
size_t i;
for(i=0; i<to; i++) {
if(sets[i]->rk.type == dup->rk.type &&
sets[i]->rk.rrset_class == dup->rk.rrset_class &&
sets[i]->rk.dname_len == dup->rk.dname_len &&
query_dname_compare(sets[i]->rk.dname, dup->rk.dname)
== 0)
return 1;
}
return 0;
}
/** prepend the prepend list in the answer and authority section of dns_msg */
static int
iter_prepend(struct iter_qstate* iq, struct dns_msg* msg,
struct regional* region)
{
struct iter_prep_list* p;
struct ub_packed_rrset_key** sets;
size_t num_an = 0, num_ns = 0;;
for(p = iq->an_prepend_list; p; p = p->next)
num_an++;
for(p = iq->ns_prepend_list; p; p = p->next)
num_ns++;
if(num_an + num_ns == 0)
return 1;
verbose(VERB_ALGO, "prepending %d rrsets", (int)num_an + (int)num_ns);
if(num_an > RR_COUNT_MAX || num_ns > RR_COUNT_MAX ||
msg->rep->rrset_count > RR_COUNT_MAX) return 0; /* overflow */
sets = regional_alloc(region, (num_an+num_ns+msg->rep->rrset_count) *
sizeof(struct ub_packed_rrset_key*));
if(!sets)
return 0;
/* ANSWER section */
num_an = 0;
for(p = iq->an_prepend_list; p; p = p->next) {
sets[num_an++] = p->rrset;
if(ub_packed_rrset_ttl(p->rrset) < msg->rep->ttl)
msg->rep->ttl = ub_packed_rrset_ttl(p->rrset);
}
memcpy(sets+num_an, msg->rep->rrsets, msg->rep->an_numrrsets *
sizeof(struct ub_packed_rrset_key*));
/* AUTH section */
num_ns = 0;
for(p = iq->ns_prepend_list; p; p = p->next) {
if(prepend_is_duplicate(sets+msg->rep->an_numrrsets+num_an,
num_ns, p->rrset) || prepend_is_duplicate(
msg->rep->rrsets+msg->rep->an_numrrsets,
msg->rep->ns_numrrsets, p->rrset))
continue;
sets[msg->rep->an_numrrsets + num_an + num_ns++] = p->rrset;
if(ub_packed_rrset_ttl(p->rrset) < msg->rep->ttl)
msg->rep->ttl = ub_packed_rrset_ttl(p->rrset);
}
memcpy(sets + num_an + msg->rep->an_numrrsets + num_ns,
msg->rep->rrsets + msg->rep->an_numrrsets,
(msg->rep->ns_numrrsets + msg->rep->ar_numrrsets) *
sizeof(struct ub_packed_rrset_key*));
/* NXDOMAIN rcode can stay if we prepended DNAME/CNAMEs, because
* this is what recursors should give. */
msg->rep->rrset_count += num_an + num_ns;
msg->rep->an_numrrsets += num_an;
msg->rep->ns_numrrsets += num_ns;
msg->rep->rrsets = sets;
return 1;
}
/**
* Find rrset in ANSWER prepend list.
* to avoid duplicate DNAMEs when a DNAME is traversed twice.
* @param iq: iterator query state.
* @param rrset: rrset to add.
* @return false if not found
*/
static int
iter_find_rrset_in_prepend_answer(struct iter_qstate* iq,
struct ub_packed_rrset_key* rrset)
{
struct iter_prep_list* p = iq->an_prepend_list;
while(p) {
if(ub_rrset_compare(p->rrset, rrset) == 0 &&
rrsetdata_equal((struct packed_rrset_data*)p->rrset
->entry.data, (struct packed_rrset_data*)rrset
->entry.data))
return 1;
p = p->next;
}
return 0;
}
/**
* Add rrset to ANSWER prepend list
* @param qstate: query state.
* @param iq: iterator query state.
* @param rrset: rrset to add.
* @return false on failure (malloc).
*/
static int
iter_add_prepend_answer(struct module_qstate* qstate, struct iter_qstate* iq,
struct ub_packed_rrset_key* rrset)
{
struct iter_prep_list* p = (struct iter_prep_list*)regional_alloc(
qstate->region, sizeof(struct iter_prep_list));
if(!p)
return 0;
p->rrset = rrset;
p->next = NULL;
/* add at end */
if(iq->an_prepend_last)
iq->an_prepend_last->next = p;
else iq->an_prepend_list = p;
iq->an_prepend_last = p;
return 1;
}
/**
* Add rrset to AUTHORITY prepend list
* @param qstate: query state.
* @param iq: iterator query state.
* @param rrset: rrset to add.
* @return false on failure (malloc).
*/
static int
iter_add_prepend_auth(struct module_qstate* qstate, struct iter_qstate* iq,
struct ub_packed_rrset_key* rrset)
{
struct iter_prep_list* p = (struct iter_prep_list*)regional_alloc(
qstate->region, sizeof(struct iter_prep_list));
if(!p)
return 0;
p->rrset = rrset;
p->next = NULL;
/* add at end */
if(iq->ns_prepend_last)
iq->ns_prepend_last->next = p;
else iq->ns_prepend_list = p;
iq->ns_prepend_last = p;
return 1;
}
/**
* Given a CNAME response (defined as a response containing a CNAME or DNAME
* that does not answer the request), process the response, modifying the
* state as necessary. This follows the CNAME/DNAME chain and returns the
* final query name.
*
* sets the new query name, after following the CNAME/DNAME chain.
* @param qstate: query state.
* @param iq: iterator query state.
* @param msg: the response.
* @param mname: returned target new query name.
* @param mname_len: length of mname.
* @return false on (malloc) error.
*/
static int
handle_cname_response(struct module_qstate* qstate, struct iter_qstate* iq,
struct dns_msg* msg, uint8_t** mname, size_t* mname_len)
{
size_t i;
/* Start with the (current) qname. */
*mname = iq->qchase.qname;
*mname_len = iq->qchase.qname_len;
/* Iterate over the ANSWER rrsets in order, looking for CNAMEs and
* DNAMES. */
for(i=0; i<msg->rep->an_numrrsets; i++) {
struct ub_packed_rrset_key* r = msg->rep->rrsets[i];
/* If there is a (relevant) DNAME, add it to the list.
* We always expect there to be CNAME that was generated
* by this DNAME following, so we don't process the DNAME
* directly. */
if(ntohs(r->rk.type) == LDNS_RR_TYPE_DNAME &&
dname_strict_subdomain_c(*mname, r->rk.dname) &&
!iter_find_rrset_in_prepend_answer(iq, r)) {
if(!iter_add_prepend_answer(qstate, iq, r))
return 0;
continue;
}
if(ntohs(r->rk.type) == LDNS_RR_TYPE_CNAME &&
query_dname_compare(*mname, r->rk.dname) == 0 &&
!iter_find_rrset_in_prepend_answer(iq, r)) {
/* Add this relevant CNAME rrset to the prepend list.*/
if(!iter_add_prepend_answer(qstate, iq, r))
return 0;
get_cname_target(r, mname, mname_len);
}
/* Other rrsets in the section are ignored. */
}
/* add authority rrsets to authority prepend, for wildcarded CNAMEs */
for(i=msg->rep->an_numrrsets; i<msg->rep->an_numrrsets +
msg->rep->ns_numrrsets; i++) {
struct ub_packed_rrset_key* r = msg->rep->rrsets[i];
/* only add NSEC/NSEC3, as they may be needed for validation */
if(ntohs(r->rk.type) == LDNS_RR_TYPE_NSEC ||
ntohs(r->rk.type) == LDNS_RR_TYPE_NSEC3) {
if(!iter_add_prepend_auth(qstate, iq, r))
return 0;
}
}
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_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
print_fail_addr(iq, from, sizeof(from));
snprintf(frm, sizeof(frm), "from %s", from);
errinf(qstate, frm);
}
if(iq->scrub_failures || iq->parse_failures) {
if(iq->scrub_failures)
errinf(qstate, "upstream response failed scrub");
if(iq->parse_failures)
errinf(qstate, "could not parse upstream response");
} else if(iq->response == NULL && iq->timeout_count != 0) {
errinf(qstate, "upstream server timeout");
} else if(iq->response == NULL) {
errinf(qstate, "no server to query");
if(iq->dp) {
if(iq->dp->target_list == NULL)
errinf(qstate, "no addresses for nameservers");
else errinf(qstate, "nameserver addresses not usable");
if(iq->dp->nslist == NULL)
errinf(qstate, "have no nameserver names");
if(iq->dp->bogus)
errinf(qstate, "NS record was dnssec bogus");
}
}
if(iq->response && iq->response->rep) {
if(FLAGS_GET_RCODE(iq->response->rep->flags) != 0) {
char rcode[256], rc[32];
(void)sldns_wire2str_rcode_buf(
FLAGS_GET_RCODE(iq->response->rep->flags),
rc, sizeof(rc));
snprintf(rcode, sizeof(rcode), "got %s", rc);
errinf(qstate, rcode);
} else {
/* rcode NOERROR */
if(iq->response->rep->an_numrrsets == 0) {
errinf(qstate, "nodata answer");
}
}
}
}
/** see if last resort is possible - does config allow queries to parent */
static int
can_have_last_resort(struct module_env* env, uint8_t* nm, size_t nmlen,
uint16_t qclass, struct delegpt** retdp)
{
struct delegpt* fwddp;
struct iter_hints_stub* stub;
int labs = dname_count_labels(nm);
/* do not process a last resort (the parent side) if a stub
* or forward is configured, because we do not want to go 'above'
* the configured servers */
if(!dname_is_root(nm) && (stub = (struct iter_hints_stub*)
name_tree_find(&env->hints->tree, nm, nmlen, labs, qclass)) &&
/* has_parent side is turned off for stub_first, where we
* are allowed to go to the parent */
stub->dp->has_parent_side_NS) {
if(retdp) *retdp = stub->dp;
return 0;
}
if((fwddp = forwards_find(env->fwds, nm, qclass)) &&
/* has_parent_side is turned off for forward_first, where
* we are allowed to go to the parent */
fwddp->has_parent_side_NS) {
if(retdp) *retdp = fwddp;
return 0;
}
return 1;
}
/** see if target name is caps-for-id whitelisted */
static int
is_caps_whitelisted(struct iter_env* ie, struct iter_qstate* iq)
{
if(!ie->caps_white) return 0; /* no whitelist, or no capsforid */
return name_tree_lookup(ie->caps_white, iq->qchase.qname,
iq->qchase.qname_len, dname_count_labels(iq->qchase.qname),
iq->qchase.qclass) != NULL;
}
/**
* Create target count structure for this query. This is always explicitly
* created for the parent query.
*/
static void
target_count_create(struct iter_qstate* iq)
{
if(!iq->target_count) {
iq->target_count = (int*)calloc(TARGET_COUNT_MAX, sizeof(int));
/* if calloc fails we simply do not track this number */
if(iq->target_count) {
iq->target_count[TARGET_COUNT_REF] = 1;
iq->nxns_dp = (uint8_t**)calloc(1, sizeof(uint8_t*));
}
}
}
static void
target_count_increase(struct iter_qstate* iq, int num)
{
target_count_create(iq);
if(iq->target_count)
iq->target_count[TARGET_COUNT_QUERIES] += num;
iq->dp_target_count++;
}
static void
target_count_increase_nx(struct iter_qstate* iq, int num)
{
target_count_create(iq);
if(iq->target_count)
iq->target_count[TARGET_COUNT_NX] += num;
}
/**
* Generate a subrequest.
* Generate a local request event. Local events are tied to this module, and
* have a corresponding (first tier) event that is waiting for this event to
* resolve to continue.
*
* @param qname The query name for this request.
* @param qnamelen length of qname
* @param qtype The query type for this request.
* @param qclass The query class for this request.
* @param qstate The event that is generating this event.
* @param id: module id.
* @param iq: The iterator state that is generating this event.
* @param initial_state The initial response state (normally this
* is QUERY_RESP_STATE, unless it is known that the request won't
* need iterative processing
* @param finalstate The final state for the response to this request.
* @param subq_ret: if newly allocated, the subquerystate, or NULL if it does
* not need initialisation.
* @param v: if true, validation is done on the subquery.
* @param detached: true if this qstate should not attach to the subquery
* @return false on error (malloc).
*/
static int
generate_sub_request(uint8_t* qname, size_t qnamelen, uint16_t qtype,
uint16_t qclass, struct module_qstate* qstate, int id,
struct iter_qstate* iq, enum iter_state initial_state,
enum iter_state finalstate, struct module_qstate** subq_ret, int v,
int detached)
{
struct module_qstate* subq = NULL;
struct iter_qstate* subiq = NULL;
uint16_t qflags = 0; /* OPCODE QUERY, no flags */
struct query_info qinf;
int prime = (finalstate == PRIME_RESP_STATE)?1:0;
int valrec = 0;
qinf.qname = qname;
qinf.qname_len = qnamelen;
qinf.qtype = qtype;
qinf.qclass = qclass;
qinf.local_alias = NULL;
/* RD should be set only when sending the query back through the INIT
* state. */
if(initial_state == INIT_REQUEST_STATE)
qflags |= BIT_RD;
/* We set the CD flag so we can send this through the "head" of
* the resolution chain, which might have a validator. We are
* uninterested in validating things not on the direct resolution
* path. */
if(!v) {
qflags |= BIT_CD;
valrec = 1;
}
if(detached) {
struct mesh_state* sub = NULL;
fptr_ok(fptr_whitelist_modenv_add_sub(
qstate->env->add_sub));
if(!(*qstate->env->add_sub)(qstate, &qinf,
qflags, prime, valrec, &subq, &sub)){
return 0;
}
}
else {
/* attach subquery, lookup existing or make a new one */
fptr_ok(fptr_whitelist_modenv_attach_sub(
qstate->env->attach_sub));
if(!(*qstate->env->attach_sub)(qstate, &qinf, qflags, prime,
valrec, &subq)) {
return 0;
}
}
*subq_ret = subq;
if(subq) {
/* initialise the new subquery */
subq->curmod = id;
subq->ext_state[id] = module_state_initial;
subq->minfo[id] = regional_alloc(subq->region,
sizeof(struct iter_qstate));
if(!subq->minfo[id]) {
log_err("init subq: out of memory");
fptr_ok(fptr_whitelist_modenv_kill_sub(
qstate->env->kill_sub));
(*qstate->env->kill_sub)(subq);
return 0;
}
subiq = (struct iter_qstate*)subq->minfo[id];
memset(subiq, 0, sizeof(*subiq));
subiq->num_target_queries = 0;
target_count_create(iq);
subiq->target_count = iq->target_count;
if(iq->target_count) {
iq->target_count[TARGET_COUNT_REF] ++; /* extra reference */
subiq->nxns_dp = iq->nxns_dp;
}
subiq->dp_target_count = 0;
subiq->num_current_queries = 0;
subiq->depth = iq->depth+1;
outbound_list_init(&subiq->outlist);
subiq->state = initial_state;
subiq->final_state = finalstate;
subiq->qchase = subq->qinfo;
subiq->chase_flags = subq->query_flags;
subiq->refetch_glue = 0;
if(qstate->env->cfg->qname_minimisation)
subiq->minimisation_state = INIT_MINIMISE_STATE;
else
subiq->minimisation_state = DONOT_MINIMISE_STATE;
memset(&subiq->qinfo_out, 0, sizeof(struct query_info));
}
return 1;
}
/**
* Generate and send a root priming request.
* @param qstate: the qtstate that triggered the need to prime.
* @param iq: iterator query state.
* @param id: module id.
* @param qclass: the class to prime.
* @return 0 on failure
*/
static int
prime_root(struct module_qstate* qstate, struct iter_qstate* iq, int id,
uint16_t qclass)
{
struct delegpt* dp;
struct module_qstate* subq;
verbose(VERB_DETAIL, "priming . %s NS",
sldns_lookup_by_id(sldns_rr_classes, (int)qclass)?
sldns_lookup_by_id(sldns_rr_classes, (int)qclass)->name:"??");
dp = hints_lookup_root(qstate->env->hints, qclass);
if(!dp) {
verbose(VERB_ALGO, "Cannot prime due to lack of hints");
return 0;
}
/* Priming requests start at the QUERYTARGETS state, skipping
* the normal INIT state logic (which would cause an infloop). */
if(!generate_sub_request((uint8_t*)"\000", 1, LDNS_RR_TYPE_NS,
qclass, qstate, id, iq, QUERYTARGETS_STATE, PRIME_RESP_STATE,
&subq, 0, 0)) {
verbose(VERB_ALGO, "could not prime root");
return 0;
}
if(subq) {
struct iter_qstate* subiq =
(struct iter_qstate*)subq->minfo[id];
/* Set the initial delegation point to the hint.
* copy dp, it is now part of the root prime query.
* dp was part of in the fixed hints structure. */
subiq->dp = delegpt_copy(dp, subq->region);
if(!subiq->dp) {
log_err("out of memory priming root, copydp");
fptr_ok(fptr_whitelist_modenv_kill_sub(
qstate->env->kill_sub));
(*qstate->env->kill_sub)(subq);
return 0;
}
/* there should not be any target queries. */
subiq->num_target_queries = 0;
subiq->dnssec_expected = iter_indicates_dnssec(
qstate->env, subiq->dp, NULL, subq->qinfo.qclass);
}
/* this module stops, our submodule starts, and does the query. */
qstate->ext_state[id] = module_wait_subquery;
return 1;
}
/**
* Generate and process a stub priming request. This method tests for the
* need to prime a stub zone, so it is safe to call for every request.
*
* @param qstate: the qtstate that triggered the need to prime.
* @param iq: iterator query state.
* @param id: module id.
* @param qname: request name.
* @param qclass: request class.
* @return true if a priming subrequest was made, false if not. The will only
* issue a priming request if it detects an unprimed stub.
* Uses value of 2 to signal during stub-prime in root-prime situation
* that a noprime-stub is available and resolution can continue.
*/
static int
prime_stub(struct module_qstate* qstate, struct iter_qstate* iq, int id,
uint8_t* qname, uint16_t qclass)
{
/* Lookup the stub hint. This will return null if the stub doesn't
* need to be re-primed. */
struct iter_hints_stub* stub;
struct delegpt* stub_dp;
struct module_qstate* subq;
if(!qname) return 0;
stub = hints_lookup_stub(qstate->env->hints, qname, qclass, iq->dp);
/* The stub (if there is one) does not need priming. */
if(!stub)
return 0;
stub_dp = stub->dp;
/* if we have an auth_zone dp, and stub is equal, don't prime stub
* yet, unless we want to fallback and avoid the auth_zone */
if(!iq->auth_zone_avoid && iq->dp && iq->dp->auth_dp &&
query_dname_compare(iq->dp->name, stub_dp->name) == 0)
return 0;
/* is it a noprime stub (always use) */
if(stub->noprime) {
int r = 0;
if(iq->dp == NULL) r = 2;
/* copy the dp out of the fixed hints structure, so that
* it can be changed when servicing this query */
iq->dp = delegpt_copy(stub_dp, qstate->region);
if(!iq->dp) {
log_err("out of memory priming stub");
errinf(qstate, "malloc failure, priming stub");
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
return 1; /* return 1 to make module stop, with error */
}
log_nametypeclass(VERB_DETAIL, "use stub", stub_dp->name,
LDNS_RR_TYPE_NS, qclass);
return r;
}
/* Otherwise, we need to (re)prime the stub. */
log_nametypeclass(VERB_DETAIL, "priming stub", stub_dp->name,
LDNS_RR_TYPE_NS, qclass);
/* Stub priming events start at the QUERYTARGETS state to avoid the
* redundant INIT state processing. */
if(!generate_sub_request(stub_dp->name, stub_dp->namelen,
LDNS_RR_TYPE_NS, qclass, qstate, id, iq,
QUERYTARGETS_STATE, PRIME_RESP_STATE, &subq, 0, 0)) {
verbose(VERB_ALGO, "could not prime stub");
errinf(qstate, "could not generate lookup for stub prime");
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
return 1; /* return 1 to make module stop, with error */
}
if(subq) {
struct iter_qstate* subiq =
(struct iter_qstate*)subq->minfo[id];
/* Set the initial delegation point to the hint. */
/* make copy to avoid use of stub dp by different qs/threads */
subiq->dp = delegpt_copy(stub_dp, subq->region);
if(!subiq->dp) {
log_err("out of memory priming stub, copydp");
fptr_ok(fptr_whitelist_modenv_kill_sub(
qstate->env->kill_sub));
(*qstate->env->kill_sub)(subq);
errinf(qstate, "malloc failure, in stub prime");
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
return 1; /* return 1 to make module stop, with error */
}
/* there should not be any target queries -- although there
* wouldn't be anyway, since stub hints never have
* missing targets. */
subiq->num_target_queries = 0;
subiq->wait_priming_stub = 1;
subiq->dnssec_expected = iter_indicates_dnssec(
qstate->env, subiq->dp, NULL, subq->qinfo.qclass);
}
/* this module stops, our submodule starts, and does the query. */
qstate->ext_state[id] = module_wait_subquery;
return 1;
}
/**
* Generate a delegation point for an auth zone (unless cached dp is better)
* false on alloc failure.
*/
static int
auth_zone_delegpt(struct module_qstate* qstate, struct iter_qstate* iq,
uint8_t* delname, size_t delnamelen)
{
struct auth_zone* z;
if(iq->auth_zone_avoid)
return 1;
if(!delname) {
delname = iq->qchase.qname;
delnamelen = iq->qchase.qname_len;
}
lock_rw_rdlock(&qstate->env->auth_zones->lock);
z = auth_zones_find_zone(qstate->env->auth_zones, delname, delnamelen,
qstate->qinfo.qclass);
if(!z) {
lock_rw_unlock(&qstate->env->auth_zones->lock);
return 1;
}
lock_rw_rdlock(&z->lock);
lock_rw_unlock(&qstate->env->auth_zones->lock);
if(z->for_upstream) {
if(iq->dp && query_dname_compare(z->name, iq->dp->name) == 0
&& iq->dp->auth_dp && qstate->blacklist &&
z->fallback_enabled) {
/* cache is blacklisted and fallback, and we
* already have an auth_zone dp */
if(verbosity>=VERB_ALGO) {
char buf[255+1];
dname_str(z->name, buf);
verbose(VERB_ALGO, "auth_zone %s "
"fallback because cache blacklisted",
buf);
}
lock_rw_unlock(&z->lock);
iq->dp = NULL;
return 1;
}
if(iq->dp==NULL || dname_subdomain_c(z->name, iq->dp->name)) {
struct delegpt* dp;
if(qstate->blacklist && z->fallback_enabled) {
/* cache is blacklisted because of a DNSSEC
* validation failure, and the zone allows
* fallback to the internet, query there. */
if(verbosity>=VERB_ALGO) {
char buf[255+1];
dname_str(z->name, buf);
verbose(VERB_ALGO, "auth_zone %s "
"fallback because cache blacklisted",
buf);
}
lock_rw_unlock(&z->lock);
return 1;
}
dp = (struct delegpt*)regional_alloc_zero(
qstate->region, sizeof(*dp));
if(!dp) {
log_err("alloc failure");
if(z->fallback_enabled) {
lock_rw_unlock(&z->lock);
return 1; /* just fallback */
}
lock_rw_unlock(&z->lock);
errinf(qstate, "malloc failure");
return 0;
}
dp->name = regional_alloc_init(qstate->region,
z->name, z->namelen);
if(!dp->name) {
log_err("alloc failure");
if(z->fallback_enabled) {
lock_rw_unlock(&z->lock);
return 1; /* just fallback */
}
lock_rw_unlock(&z->lock);
errinf(qstate, "malloc failure");
return 0;
}
dp->namelen = z->namelen;
dp->namelabs = z->namelabs;
dp->auth_dp = 1;
iq->dp = dp;
}
}
lock_rw_unlock(&z->lock);
return 1;
}
/**
* Generate A and AAAA checks for glue that is in-zone for the referral
* we just got to obtain authoritative information on the addresses.
*
* @param qstate: the qtstate that triggered the need to prime.
* @param iq: iterator query state.
* @param id: module id.
*/
static void
generate_a_aaaa_check(struct module_qstate* qstate, struct iter_qstate* iq,
int id)
{
struct iter_env* ie = (struct iter_env*)qstate->env->modinfo[id];
struct module_qstate* subq;
size_t i;
struct reply_info* rep = iq->response->rep;
struct ub_packed_rrset_key* s;
log_assert(iq->dp);
if(iq->depth == ie->max_dependency_depth)
return;
/* walk through additional, and check if in-zone,
* only relevant A, AAAA are left after scrub anyway */
for(i=rep->an_numrrsets+rep->ns_numrrsets; i<rep->rrset_count; i++) {
s = rep->rrsets[i];
/* check *ALL* addresses that are transmitted in additional*/
/* is it an address ? */
if( !(ntohs(s->rk.type)==LDNS_RR_TYPE_A ||
ntohs(s->rk.type)==LDNS_RR_TYPE_AAAA)) {
continue;
}
/* is this query the same as the A/AAAA check for it */
if(qstate->qinfo.qtype == ntohs(s->rk.type) &&
qstate->qinfo.qclass == ntohs(s->rk.rrset_class) &&
query_dname_compare(qstate->qinfo.qname,
s->rk.dname)==0 &&
(qstate->query_flags&BIT_RD) &&
!(qstate->query_flags&BIT_CD))
continue;
/* generate subrequest for it */
log_nametypeclass(VERB_ALGO, "schedule addr fetch",
s->rk.dname, ntohs(s->rk.type),
ntohs(s->rk.rrset_class));
if(!generate_sub_request(s->rk.dname, s->rk.dname_len,
ntohs(s->rk.type), ntohs(s->rk.rrset_class),
qstate, id, iq,
INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1, 0)) {
verbose(VERB_ALGO, "could not generate addr check");
return;
}
/* ignore subq - not need for more init */
}
}
/**
* Generate a NS check request to obtain authoritative information
* on an NS rrset.
*
* @param qstate: the qstate that triggered the need to prime.
* @param iq: iterator query state.
* @param id: module id.
*/
static void
generate_ns_check(struct module_qstate* qstate, struct iter_qstate* iq, int id)
{
struct iter_env* ie = (struct iter_env*)qstate->env->modinfo[id];
struct module_qstate* subq;
log_assert(iq->dp);
if(iq->depth == ie->max_dependency_depth)
return;
if(!can_have_last_resort(qstate->env, iq->dp->name, iq->dp->namelen,
iq->qchase.qclass, NULL))
return;
/* is this query the same as the nscheck? */
if(qstate->qinfo.qtype == LDNS_RR_TYPE_NS &&
query_dname_compare(iq->dp->name, qstate->qinfo.qname)==0 &&
(qstate->query_flags&BIT_RD) && !(qstate->query_flags&BIT_CD)){
/* spawn off A, AAAA queries for in-zone glue to check */
generate_a_aaaa_check(qstate, iq, id);
return;
}
/* no need to get the NS record for DS, it is above the zonecut */
if(qstate->qinfo.qtype == LDNS_RR_TYPE_DS)
return;
log_nametypeclass(VERB_ALGO, "schedule ns fetch",
iq->dp->name, LDNS_RR_TYPE_NS, iq->qchase.qclass);
if(!generate_sub_request(iq->dp->name, iq->dp->namelen,
LDNS_RR_TYPE_NS, iq->qchase.qclass, qstate, id, iq,
INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1, 0)) {
verbose(VERB_ALGO, "could not generate ns check");
return;
}
if(subq) {
struct iter_qstate* subiq =
(struct iter_qstate*)subq->minfo[id];
/* make copy to avoid use of stub dp by different qs/threads */
/* refetch glue to start higher up the tree */
subiq->refetch_glue = 1;
subiq->dp = delegpt_copy(iq->dp, subq->region);
if(!subiq->dp) {
log_err("out of memory generating ns check, copydp");
fptr_ok(fptr_whitelist_modenv_kill_sub(
qstate->env->kill_sub));
(*qstate->env->kill_sub)(subq);
return;
}
}
}
/**
* Generate a DNSKEY prefetch query to get the DNSKEY for the DS record we
* just got in a referral (where we have dnssec_expected, thus have trust
* anchors above it). Note that right after calling this routine the
* iterator detached subqueries (because of following the referral), and thus
* the DNSKEY query becomes detached, its return stored in the cache for
* later lookup by the validator. This cache lookup by the validator avoids
* the roundtrip incurred by the DNSKEY query. The DNSKEY query is now
* performed at about the same time the original query is sent to the domain,
* thus the two answers are likely to be returned at about the same time,
* saving a roundtrip from the validated lookup.
*
* @param qstate: the qtstate that triggered the need to prime.
* @param iq: iterator query state.
* @param id: module id.
*/
static void
generate_dnskey_prefetch(struct module_qstate* qstate,
struct iter_qstate* iq, int id)
{
struct module_qstate* subq;
log_assert(iq->dp);
/* is this query the same as the prefetch? */
if(qstate->qinfo.qtype == LDNS_RR_TYPE_DNSKEY &&
query_dname_compare(iq->dp->name, qstate->qinfo.qname)==0 &&
(qstate->query_flags&BIT_RD) && !(qstate->query_flags&BIT_CD)){
return;
}
/* we do not generate this prefetch when the query list is full,
* the query is fetched, if needed, when the validator wants it.
* At that time the validator waits for it, after spawning it.
* This means there is one state that uses cpu and a socket, the
* spawned while this one waits, and not several at the same time,
* if we had created the lookup here. And this helps to keep
* the total load down, but the query still succeeds to resolve. */
if(mesh_jostle_exceeded(qstate->env->mesh))
return;
/* if the DNSKEY is in the cache this lookup will stop quickly */
log_nametypeclass(VERB_ALGO, "schedule dnskey prefetch",
iq->dp->name, LDNS_RR_TYPE_DNSKEY, iq->qchase.qclass);
if(!generate_sub_request(iq->dp->name, iq->dp->namelen,
LDNS_RR_TYPE_DNSKEY, iq->qchase.qclass, qstate, id, iq,
INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0, 0)) {
/* we'll be slower, but it'll work */
verbose(VERB_ALGO, "could not generate dnskey prefetch");
return;
}
if(subq) {
struct iter_qstate* subiq =
(struct iter_qstate*)subq->minfo[id];
/* this qstate has the right delegation for the dnskey lookup*/
/* make copy to avoid use of stub dp by different qs/threads */
subiq->dp = delegpt_copy(iq->dp, subq->region);
/* if !subiq->dp, it'll start from the cache, no problem */
}
}
/**
* See if the query needs forwarding.
*
* @param qstate: query state.
* @param iq: iterator query state.
* @return true if the request is forwarded, false if not.
* If returns true but, iq->dp is NULL then a malloc failure occurred.
*/
static int
forward_request(struct module_qstate* qstate, struct iter_qstate* iq)
{
struct delegpt* dp;
uint8_t* delname = iq->qchase.qname;
size_t delnamelen = iq->qchase.qname_len;
if(iq->refetch_glue && iq->dp) {
delname = iq->dp->name;
delnamelen = iq->dp->namelen;
}
/* strip one label off of DS query to lookup higher for it */
if( (iq->qchase.qtype == LDNS_RR_TYPE_DS || iq->refetch_glue)
&& !dname_is_root(iq->qchase.qname))
dname_remove_label(&delname, &delnamelen);
dp = forwards_lookup(qstate->env->fwds, delname, iq->qchase.qclass);
if(!dp)
return 0;
/* send recursion desired to forward addr */
iq->chase_flags |= BIT_RD;
iq->dp = delegpt_copy(dp, qstate->region);
/* iq->dp checked by caller */
verbose(VERB_ALGO, "forwarding request");
return 1;
}
/**
* Process the initial part of the request handling. This state roughly
* corresponds to resolver algorithms steps 1 (find answer in cache) and 2
* (find the best servers to ask).
*
* Note that all requests start here, and query restarts revisit this state.
*
* This state either generates: 1) a response, from cache or error, 2) a
* priming event, or 3) forwards the request to the next state (init2,
* generally).
*
* @param qstate: query state.
* @param iq: iterator query state.
* @param ie: iterator shared global environment.
* @param id: module id.
* @return true if the event needs more request processing immediately,
* false if not.
*/
static int
processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
struct iter_env* ie, int id)
{
uint8_t* delname, *dpname=NULL;
size_t delnamelen, dpnamelen=0;
struct dns_msg* msg = NULL;
log_query_info(VERB_DETAIL, "resolving", &qstate->qinfo);
/* check effort */
/* We enforce a maximum number of query restarts. This is primarily a
* cheap way to prevent CNAME loops. */
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 "
"restarts (eg. indirections)");
if(iq->qchase.qname)
errinf_dname(qstate, "stop at", iq->qchase.qname);
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
/* We enforce a maximum recursion/dependency depth -- in general,
* this is unnecessary for dependency loops (although it will
* catch those), but it provides a sensible limit to the amount
* of work required to answer a given query. */
verbose(VERB_ALGO, "request has dependency depth of %d", iq->depth);
if(iq->depth > ie->max_dependency_depth) {
verbose(VERB_QUERY, "request has exceeded the maximum "
"dependency depth with depth of %d", iq->depth);
errinf(qstate, "request has exceeded the maximum dependency "
"depth (eg. nameserver lookup recursion)");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
/* If the request is qclass=ANY, setup to generate each class */
if(qstate->qinfo.qclass == LDNS_RR_CLASS_ANY) {
iq->qchase.qclass = 0;
return next_state(iq, COLLECT_CLASS_STATE);
}
/*
* If we are restricted by a forward-zone or a stub-zone, we
* can't re-fetch glue for this delegation point.
* we won’t try to re-fetch glue if the iq->dp is null.
*/
if (iq->refetch_glue &&
iq->dp &&
!can_have_last_resort(qstate->env, iq->dp->name,
iq->dp->namelen, iq->qchase.qclass, NULL)) {
iq->refetch_glue = 0;
}
/* Resolver Algorithm Step 1 -- Look for the answer in local data. */
/* This either results in a query restart (CNAME cache response), a
* terminating response (ANSWER), or a cache miss (null). */
if (iter_stub_fwd_no_cache(qstate, &iq->qchase, &dpname, &dpnamelen)) {
/* Asked to not query cache. */
verbose(VERB_ALGO, "no-cache set, going to the network");
qstate->no_cache_lookup = 1;
qstate->no_cache_store = 1;
msg = NULL;
} else if(qstate->blacklist) {
/* if cache, or anything else, was blacklisted then
* getting older results from cache is a bad idea, no cache */
verbose(VERB_ALGO, "cache blacklisted, going to the network");
msg = NULL;
} else if(!qstate->no_cache_lookup) {
msg = dns_cache_lookup(qstate->env, iq->qchase.qname,
iq->qchase.qname_len, iq->qchase.qtype,
iq->qchase.qclass, qstate->query_flags,
qstate->region, qstate->env->scratch, 0, dpname,
dpnamelen);
if(!msg && qstate->env->neg_cache &&
iter_qname_indicates_dnssec(qstate->env, &iq->qchase)) {
/* lookup in negative cache; may result in
* NOERROR/NODATA or NXDOMAIN answers that need validation */
msg = val_neg_getmsg(qstate->env->neg_cache, &iq->qchase,
qstate->region, qstate->env->rrset_cache,
qstate->env->scratch_buffer,
*qstate->env->now, 1/*add SOA*/, NULL,
qstate->env->cfg);
}
/* item taken from cache does not match our query name, thus
* security needs to be re-examined later */
if(msg && query_dname_compare(qstate->qinfo.qname,
iq->qchase.qname) != 0)
msg->rep->security = sec_status_unchecked;
}
if(msg) {
/* handle positive cache response */
enum response_type type = response_type_from_cache(msg,
&iq->qchase);
if(verbosity >= VERB_ALGO) {
log_dns_msg("msg from cache lookup", &msg->qinfo,
msg->rep);
verbose(VERB_ALGO, "msg ttl is %d, prefetch ttl %d",
(int)msg->rep->ttl,
(int)msg->rep->prefetch_ttl);
}
if(type == RESPONSE_TYPE_CNAME) {
uint8_t* sname = 0;
size_t slen = 0;
verbose(VERB_ALGO, "returning CNAME response from "
"cache");
if(!handle_cname_response(qstate, iq, msg,
&sname, &slen)) {
errinf(qstate, "failed to prepend CNAME "
"components, malloc failure");
return error_response(qstate, id,
LDNS_RCODE_SERVFAIL);
}
iq->qchase.qname = sname;
iq->qchase.qname_len = slen;
+ if(qstate->env->auth_zones) {
+ /* apply rpz qname triggers after cname */
+ struct dns_msg* forged_response =
+ rpz_callback_from_iterator_cname(qstate, iq);
+ while(forged_response && reply_find_rrset_section_an(
+ forged_response->rep, iq->qchase.qname,
+ iq->qchase.qname_len, LDNS_RR_TYPE_CNAME,
+ iq->qchase.qclass)) {
+ /* another cname to follow */
+ if(!handle_cname_response(qstate, iq, forged_response,
+ &sname, &slen)) {
+ errinf(qstate, "malloc failure, CNAME info");
+ return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+ }
+ iq->qchase.qname = sname;
+ iq->qchase.qname_len = slen;
+ forged_response =
+ rpz_callback_from_iterator_cname(qstate, iq);
+ }
+ if(forged_response != NULL) {
+ qstate->ext_state[id] = module_finished;
+ qstate->return_rcode = LDNS_RCODE_NOERROR;
+ qstate->return_msg = forged_response;
+ iq->response = forged_response;
+ next_state(iq, FINISHED_STATE);
+ if(!iter_prepend(iq, qstate->return_msg, qstate->region)) {
+ log_err("rpz: after cached cname, prepend rrsets: out of memory");
+ return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
+ }
+ qstate->return_msg->qinfo = qstate->qinfo;
+ return 0;
+ }
+ }
/* This *is* a query restart, even if it is a cheap
* one. */
iq->dp = NULL;
iq->refetch_glue = 0;
iq->query_restart_count++;
iq->sent_count = 0;
iq->dp_target_count = 0;
sock_list_insert(&qstate->reply_origin, NULL, 0, qstate->region);
if(qstate->env->cfg->qname_minimisation)
iq->minimisation_state = INIT_MINIMISE_STATE;
return next_state(iq, INIT_REQUEST_STATE);
}
/* if from cache, NULL, else insert 'cache IP' len=0 */
if(qstate->reply_origin)
sock_list_insert(&qstate->reply_origin, NULL, 0, qstate->region);
if(FLAGS_GET_RCODE(msg->rep->flags) == LDNS_RCODE_SERVFAIL)
errinf(qstate, "SERVFAIL in cache");
/* it is an answer, response, to final state */
verbose(VERB_ALGO, "returning answer from cache.");
iq->response = msg;
return final_state(iq);
}
/* attempt to forward the request */
if(forward_request(qstate, iq))
{
if(!iq->dp) {
log_err("alloc failure for forward dp");
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.
* forwarded requests need to be immediately sent to the
* next state, QUERYTARGETS. */
return next_state(iq, QUERYTARGETS_STATE);
}
/* Resolver Algorithm Step 2 -- find the "best" servers. */
/* first, adjust for DS queries. To avoid the grandparent problem,
* we just look for the closest set of server to the parent of qname.
* When re-fetching glue we also need to ask the parent.
*/
if(iq->refetch_glue) {
if(!iq->dp) {
log_err("internal or malloc fail: no dp for refetch");
errinf(qstate, "malloc failure, for delegation info");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
delname = iq->dp->name;
delnamelen = iq->dp->namelen;
} else {
delname = iq->qchase.qname;
delnamelen = iq->qchase.qname_len;
}
if(iq->qchase.qtype == LDNS_RR_TYPE_DS || iq->refetch_glue ||
(iq->qchase.qtype == LDNS_RR_TYPE_NS && qstate->prefetch_leeway
&& can_have_last_resort(qstate->env, delname, delnamelen, iq->qchase.qclass, NULL))) {
/* remove first label from delname, root goes to hints,
* but only to fetch glue, not for qtype=DS. */
/* also when prefetching an NS record, fetch it again from
* its parent, just as if it expired, so that you do not
* get stuck on an older nameserver that gives old NSrecords */
if(dname_is_root(delname) && (iq->refetch_glue ||
(iq->qchase.qtype == LDNS_RR_TYPE_NS &&
qstate->prefetch_leeway)))
delname = NULL; /* go to root priming */
else dname_remove_label(&delname, &delnamelen);
}
/* delname is the name to lookup a delegation for. If NULL rootprime */
while(1) {
/* Lookup the delegation in the cache. If null, then the
* cache needs to be primed for the qclass. */
if(delname)
iq->dp = dns_cache_find_delegation(qstate->env, delname,
delnamelen, iq->qchase.qtype, iq->qchase.qclass,
qstate->region, &iq->deleg_msg,
*qstate->env->now+qstate->prefetch_leeway, 1,
dpname, dpnamelen);
else iq->dp = NULL;
/* If the cache has returned nothing, then we have a
* root priming situation. */
if(iq->dp == NULL) {
int r;
/* if under auth zone, no prime needed */
if(!auth_zone_delegpt(qstate, iq, delname, delnamelen))
return error_response(qstate, id,
LDNS_RCODE_SERVFAIL);
if(iq->dp) /* use auth zone dp */
return next_state(iq, INIT_REQUEST_2_STATE);
/* if there is a stub, then no root prime needed */
r = prime_stub(qstate, iq, id, delname,
iq->qchase.qclass);
if(r == 2)
break; /* got noprime-stub-zone, continue */
else if(r)
return 0; /* stub prime request made */
if(forwards_lookup_root(qstate->env->fwds,
iq->qchase.qclass)) {
/* forward zone root, no root prime needed */
/* fill in some dp - safety belt */
iq->dp = hints_lookup_root(qstate->env->hints,
iq->qchase.qclass);
if(!iq->dp) {
log_err("internal error: no hints dp");
errinf(qstate, "no hints for this class");
return error_response(qstate, id,
LDNS_RCODE_SERVFAIL);
}
iq->dp = delegpt_copy(iq->dp, qstate->region);
if(!iq->dp) {
log_err("out of memory in safety belt");
errinf(qstate, "malloc failure, in safety belt");
return error_response(qstate, id,
LDNS_RCODE_SERVFAIL);
}
return next_state(iq, INIT_REQUEST_2_STATE);
}
/* Note that the result of this will set a new
* DelegationPoint based on the result of priming. */
if(!prime_root(qstate, iq, id, iq->qchase.qclass))
return error_response(qstate, id,
LDNS_RCODE_REFUSED);
/* priming creates and sends a subordinate query, with
* this query as the parent. So further processing for
* this event will stop until reactivated by the
* results of priming. */
return 0;
}
if(!iq->ratelimit_ok && qstate->prefetch_leeway)
iq->ratelimit_ok = 1; /* allow prefetches, this keeps
otherwise valid data in the cache */
/* see if this dp not useless.
* It is useless if:
* 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.
* 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,
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) {
verbose(VERB_QUERY, "cache has stub "
"or fwd but no addresses, "
"fallback to config");
iq->dp = delegpt_copy(retdp,
qstate->region);
if(!iq->dp) {
log_err("out of memory in "
"stub/fwd fallback");
errinf(qstate, "malloc failure, for fallback to config");
return error_response(qstate,
id, LDNS_RCODE_SERVFAIL);
}
break;
}
verbose(VERB_ALGO, "useless dp "
"but cannot go up, servfail");
delegpt_log(VERB_ALGO, iq->dp);
errinf(qstate, "no useful nameservers, "
"and cannot go up");
errinf_dname(qstate, "for zone", iq->dp->name);
return error_response(qstate, id,
LDNS_RCODE_SERVFAIL);
}
if(dname_is_root(iq->dp->name)) {
/* use safety belt */
verbose(VERB_QUERY, "Cache has root NS but "
"no addresses. Fallback to the safety belt.");
iq->dp = hints_lookup_root(qstate->env->hints,
iq->qchase.qclass);
/* note deleg_msg is from previous lookup,
* but RD is on, so it is not used */
if(!iq->dp) {
log_err("internal error: no hints dp");
return error_response(qstate, id,
LDNS_RCODE_REFUSED);
}
iq->dp = delegpt_copy(iq->dp, qstate->region);
if(!iq->dp) {
log_err("out of memory in safety belt");
errinf(qstate, "malloc failure, in safety belt, for root");
return error_response(qstate, id,
LDNS_RCODE_SERVFAIL);
}
break;
} else {
verbose(VERB_ALGO,
"cache delegation was useless:");
delegpt_log(VERB_ALGO, iq->dp);
/* go up */
delname = iq->dp->name;
delnamelen = iq->dp->namelen;
dname_remove_label(&delname, &delnamelen);
}
} else break;
}
verbose(VERB_ALGO, "cache delegation returns delegpt");
delegpt_log(VERB_ALGO, iq->dp);
/* Otherwise, set the current delegation point and move on to the
* next state. */
return next_state(iq, INIT_REQUEST_2_STATE);
}
/**
* Process the second part of the initial request handling. This state
* basically exists so that queries that generate root priming events have
* the same init processing as ones that do not. Request events that reach
* this state must have a valid currentDelegationPoint set.
*
* This part is primarily handling stub zone priming. Events that reach this
* state must have a current delegation point.
*
* @param qstate: query state.
* @param iq: iterator query state.
* @param id: module id.
* @return true if the event needs more request processing immediately,
* false if not.
*/
static int
processInitRequest2(struct module_qstate* qstate, struct iter_qstate* iq,
int id)
{
uint8_t* delname;
size_t delnamelen;
log_query_info(VERB_QUERY, "resolving (init part 2): ",
&qstate->qinfo);
delname = iq->qchase.qname;
delnamelen = iq->qchase.qname_len;
if(iq->refetch_glue) {
struct iter_hints_stub* stub;
if(!iq->dp) {
log_err("internal or malloc fail: no dp for refetch");
errinf(qstate, "malloc failure, no delegation info");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
/* Do not send queries above stub, do not set delname to dp if
* this is above stub without stub-first. */
stub = hints_lookup_stub(
qstate->env->hints, iq->qchase.qname, iq->qchase.qclass,
iq->dp);
if(!stub || !stub->dp->has_parent_side_NS ||
dname_subdomain_c(iq->dp->name, stub->dp->name)) {
delname = iq->dp->name;
delnamelen = iq->dp->namelen;
}
}
if(iq->qchase.qtype == LDNS_RR_TYPE_DS || iq->refetch_glue) {
if(!dname_is_root(delname))
dname_remove_label(&delname, &delnamelen);
iq->refetch_glue = 0; /* if CNAME causes restart, no refetch */
}
/* see if we have an auth zone to answer from, improves dp from cache
* (if any dp from cache) with auth zone dp, if that is lower */
if(!auth_zone_delegpt(qstate, iq, delname, delnamelen))
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
/* Check to see if we need to prime a stub zone. */
if(prime_stub(qstate, iq, id, delname, iq->qchase.qclass)) {
/* A priming sub request was made */
return 0;
}
/* most events just get forwarded to the next state. */
return next_state(iq, INIT_REQUEST_3_STATE);
}
/**
* Process the third part of the initial request handling. This state exists
* as a separate state so that queries that generate stub priming events
* will get the tail end of the init process but not repeat the stub priming
* check.
*
* @param qstate: query state.
* @param iq: iterator query state.
* @param id: module id.
* @return true, advancing the event to the QUERYTARGETS_STATE.
*/
static int
processInitRequest3(struct module_qstate* qstate, struct iter_qstate* iq,
int id)
{
log_query_info(VERB_QUERY, "resolving (init part 3): ",
&qstate->qinfo);
/* if the cache reply dp equals a validation anchor or msg has DS,
* then DNSSEC RRSIGs are expected in the reply */
iq->dnssec_expected = iter_indicates_dnssec(qstate->env, iq->dp,
iq->deleg_msg, iq->qchase.qclass);
/* If the RD flag wasn't set, then we just finish with the
* cached referral as the response. */
if(!(qstate->query_flags & BIT_RD) && iq->deleg_msg) {
iq->response = iq->deleg_msg;
if(verbosity >= VERB_ALGO && iq->response)
log_dns_msg("no RD requested, using delegation msg",
&iq->response->qinfo, iq->response->rep);
if(qstate->reply_origin)
sock_list_insert(&qstate->reply_origin, NULL, 0, qstate->region);
return final_state(iq);
}
/* After this point, unset the RD flag -- this query is going to
* be sent to an auth. server. */
iq->chase_flags &= ~BIT_RD;
/* if dnssec expected, fetch key for the trust-anchor or cached-DS */
if(iq->dnssec_expected && qstate->env->cfg->prefetch_key &&
!(qstate->query_flags&BIT_CD)) {
generate_dnskey_prefetch(qstate, iq, id);
fptr_ok(fptr_whitelist_modenv_detach_subs(
qstate->env->detach_subs));
(*qstate->env->detach_subs)(qstate);
}
/* Jump to the next state. */
return next_state(iq, QUERYTARGETS_STATE);
}
/**
* Given a basic query, generate a parent-side "target" query.
* These are subordinate queries for missing delegation point target addresses,
* for which only the parent of the delegation provides correct IP addresses.
*
* @param qstate: query state.
* @param iq: iterator query state.
* @param id: module id.
* @param name: target qname.
* @param namelen: target qname length.
* @param qtype: target qtype (either A or AAAA).
* @param qclass: target qclass.
* @return true on success, false on failure.
*/
static int
generate_parentside_target_query(struct module_qstate* qstate,
struct iter_qstate* iq, int id, uint8_t* name, size_t namelen,
uint16_t qtype, uint16_t qclass)
{
struct module_qstate* subq;
if(!generate_sub_request(name, namelen, qtype, qclass, qstate,
id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0, 0))
return 0;
if(subq) {
struct iter_qstate* subiq =
(struct iter_qstate*)subq->minfo[id];
/* blacklist the cache - we want to fetch parent stuff */
sock_list_insert(&subq->blacklist, NULL, 0, subq->region);
subiq->query_for_pside_glue = 1;
if(dname_subdomain_c(name, iq->dp->name)) {
subiq->dp = delegpt_copy(iq->dp, subq->region);
subiq->dnssec_expected = iter_indicates_dnssec(
qstate->env, subiq->dp, NULL,
subq->qinfo.qclass);
subiq->refetch_glue = 1;
} else {
subiq->dp = dns_cache_find_delegation(qstate->env,
name, namelen, qtype, qclass, subq->region,
&subiq->deleg_msg,
*qstate->env->now+subq->prefetch_leeway,
1, NULL, 0);
/* if no dp, then it's from root, refetch unneeded */
if(subiq->dp) {
subiq->dnssec_expected = iter_indicates_dnssec(
qstate->env, subiq->dp, NULL,
subq->qinfo.qclass);
subiq->refetch_glue = 1;
}
}
}
log_nametypeclass(VERB_QUERY, "new pside target", name, qtype, qclass);
return 1;
}
/**
* Given a basic query, generate a "target" query. These are subordinate
* queries for missing delegation point target addresses.
*
* @param qstate: query state.
* @param iq: iterator query state.
* @param id: module id.
* @param name: target qname.
* @param namelen: target qname length.
* @param qtype: target qtype (either A or AAAA).
* @param qclass: target qclass.
* @return true on success, false on failure.
*/
static int
generate_target_query(struct module_qstate* qstate, struct iter_qstate* iq,
int id, uint8_t* name, size_t namelen, uint16_t qtype, uint16_t qclass)
{
struct module_qstate* subq;
if(!generate_sub_request(name, namelen, qtype, qclass, qstate,
id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0, 0))
return 0;
log_nametypeclass(VERB_QUERY, "new target", name, qtype, qclass);
return 1;
}
/**
* Given an event at a certain state, generate zero or more target queries
* for it's current delegation point.
*
* @param qstate: query state.
* @param iq: iterator query state.
* @param ie: iterator shared global environment.
* @param id: module id.
* @param maxtargets: The maximum number of targets to query for.
* if it is negative, there is no maximum number of targets.
* @param num: returns the number of queries generated and processed,
* which may be zero if there were no missing targets.
* @return false on error.
*/
static int
query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq,
struct iter_env* ie, int id, int maxtargets, int* num)
{
int query_count = 0;
struct delegpt_ns* ns;
int missing;
int toget = 0;
iter_mark_cycle_targets(qstate, iq->dp);
missing = (int)delegpt_count_missing_targets(iq->dp, NULL);
log_assert(maxtargets != 0); /* that would not be useful */
/* Generate target requests. Basically, any missing targets
* are queried for here, regardless if it is necessary to do
* so to continue processing. */
if(maxtargets < 0 || maxtargets > missing)
toget = missing;
else toget = maxtargets;
if(toget == 0) {
*num = 0;
return 1;
}
/* now that we are sure that a target query is going to be made,
* check the limits. */
if(iq->depth == ie->max_dependency_depth)
return 0;
if(iq->depth > 0 && iq->target_count &&
iq->target_count[TARGET_COUNT_QUERIES] > MAX_TARGET_COUNT) {
char s[LDNS_MAX_DOMAINLEN+1];
dname_str(qstate->qinfo.qname, s);
verbose(VERB_QUERY, "request %s has exceeded the maximum "
"number of glue fetches %d", s,
iq->target_count[TARGET_COUNT_QUERIES]);
return 0;
}
if(iq->dp_target_count > MAX_DP_TARGET_COUNT) {
char s[LDNS_MAX_DOMAINLEN+1];
dname_str(qstate->qinfo.qname, s);
verbose(VERB_QUERY, "request %s has exceeded the maximum "
"number of glue fetches %d to a single delegation point",
s, iq->dp_target_count);
return 0;
}
/* select 'toget' items from the total of 'missing' items */
log_assert(toget <= missing);
/* loop over missing targets */
for(ns = iq->dp->nslist; ns; ns = ns->next) {
if(ns->resolved)
continue;
/* randomly select this item with probability toget/missing */
if(!iter_ns_probability(qstate->env->rnd, toget, missing)) {
/* do not select this one, next; select toget number
* of items from a list one less in size */
missing --;
continue;
}
if(ie->supports_ipv6 &&
((ns->lame && !ns->done_pside6) ||
(!ns->lame && !ns->got6))) {
/* Send the AAAA request. */
if(!generate_target_query(qstate, iq, id,
ns->name, ns->namelen,
LDNS_RR_TYPE_AAAA, iq->qchase.qclass)) {
*num = query_count;
if(query_count > 0)
qstate->ext_state[id] = module_wait_subquery;
return 0;
}
query_count++;
/* If the mesh query list is full, exit the loop here.
* This makes the routine spawn one query at a time,
* and this means there is no query state load
* increase, because the spawned state uses cpu and a
* socket while this state waits for that spawned
* state. Next time we can look up further targets */
if(mesh_jostle_exceeded(qstate->env->mesh))
break;
}
/* Send the A request. */
if((ie->supports_ipv4 || ie->use_nat64) &&
((ns->lame && !ns->done_pside4) ||
(!ns->lame && !ns->got4))) {
if(!generate_target_query(qstate, iq, id,
ns->name, ns->namelen,
LDNS_RR_TYPE_A, iq->qchase.qclass)) {
*num = query_count;
if(query_count > 0)
qstate->ext_state[id] = module_wait_subquery;
return 0;
}
query_count++;
/* If the mesh query list is full, exit the loop. */
if(mesh_jostle_exceeded(qstate->env->mesh))
break;
}
/* mark this target as in progress. */
ns->resolved = 1;
missing--;
toget--;
if(toget == 0)
break;
}
*num = query_count;
if(query_count > 0)
qstate->ext_state[id] = module_wait_subquery;
return 1;
}
/**
* Called by processQueryTargets when it would like extra targets to query
* but it seems to be out of options. At last resort some less appealing
* options are explored. If there are no more options, the result is SERVFAIL
*
* @param qstate: query state.
* @param iq: iterator query state.
* @param ie: iterator shared global environment.
* @param id: module id.
* @return true if the event requires more request processing immediately,
* false if not.
*/
static int
processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
struct iter_env* ie, int id)
{
struct delegpt_ns* ns;
int query_count = 0;
verbose(VERB_ALGO, "No more query targets, attempting last resort");
log_assert(iq->dp);
if(!can_have_last_resort(qstate->env, iq->dp->name, iq->dp->namelen,
iq->qchase.qclass, NULL)) {
/* fail -- no more targets, no more hope of targets, no hope
* of a response. */
errinf(qstate, "all the configured stub or forward servers failed,");
errinf_dname(qstate, "at zone", iq->dp->name);
errinf_reply(qstate, iq);
verbose(VERB_QUERY, "configured stub or forward servers failed -- returning SERVFAIL");
return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
if(!iq->dp->has_parent_side_NS && dname_is_root(iq->dp->name)) {
struct delegpt* p = hints_lookup_root(qstate->env->hints,
iq->qchase.qclass);
if(p) {
struct delegpt_addr* a;
iq->chase_flags &= ~BIT_RD; /* go to authorities */
for(ns = p->nslist; ns; ns=ns->next) {
(void)delegpt_add_ns(iq->dp, qstate->region,
ns->name, ns->lame, ns->tls_auth_name,
ns->port);
}
for(a = p->target_list; a; a=a->next_target) {
(void)delegpt_add_addr(iq->dp, qstate->region,
&a->addr, a->addrlen, a->bogus,
a->lame, a->tls_auth_name, -1, NULL);
}
}
iq->dp->has_parent_side_NS = 1;
} else if(!iq->dp->has_parent_side_NS) {
if(!iter_lookup_parent_NS_from_cache(qstate->env, iq->dp,
qstate->region, &qstate->qinfo)
|| !iq->dp->has_parent_side_NS) {
/* if: malloc failure in lookup go up to try */
/* if: no parent NS in cache - go up one level */
verbose(VERB_ALGO, "try to grab parent NS");
iq->store_parent_NS = iq->dp;
iq->chase_flags &= ~BIT_RD; /* go to authorities */
iq->deleg_msg = NULL;
iq->refetch_glue = 1;
iq->query_restart_count++;
iq->sent_count = 0;
iq->dp_target_count = 0;
if(qstate->env->cfg->qname_minimisation)
iq->minimisation_state = INIT_MINIMISE_STATE;
return next_state(iq, INIT_REQUEST_STATE);
}
}
/* see if that makes new names available */
if(!cache_fill_missing(qstate->env, iq->qchase.qclass,
qstate->region, iq->dp))
log_err("out of memory in cache_fill_missing");
if(iq->dp->usable_list) {
verbose(VERB_ALGO, "try parent-side-name, w. glue from cache");
return next_state(iq, QUERYTARGETS_STATE);
}
/* try to fill out parent glue from cache */
if(iter_lookup_parent_glue_from_cache(qstate->env, iq->dp,
qstate->region, &qstate->qinfo)) {
/* got parent stuff from cache, see if we can continue */
verbose(VERB_ALGO, "try parent-side glue from cache");
return next_state(iq, QUERYTARGETS_STATE);
}
/* query for an extra name added by the parent-NS record */
if(delegpt_count_missing_targets(iq->dp, NULL) > 0) {
int qs = 0;
verbose(VERB_ALGO, "try parent-side target name");
if(!query_for_targets(qstate, iq, ie, id, 1, &qs)) {
errinf(qstate, "could not fetch nameserver");
errinf_dname(qstate, "at zone", iq->dp->name);
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
iq->num_target_queries += qs;
target_count_increase(iq, qs);
if(qs != 0) {
qstate->ext_state[id] = module_wait_subquery;
return 0; /* and wait for them */
}
}
if(iq->depth == ie->max_dependency_depth) {
verbose(VERB_QUERY, "maxdepth and need more nameservers, fail");
errinf(qstate, "cannot fetch more nameservers because at max dependency depth");
return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
if(iq->depth > 0 && iq->target_count &&
iq->target_count[TARGET_COUNT_QUERIES] > MAX_TARGET_COUNT) {
char s[LDNS_MAX_DOMAINLEN+1];
dname_str(qstate->qinfo.qname, s);
verbose(VERB_QUERY, "request %s has exceeded the maximum "
"number of glue fetches %d", s,
iq->target_count[TARGET_COUNT_QUERIES]);
errinf(qstate, "exceeded the maximum number of glue fetches");
return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
/* mark cycle targets for parent-side lookups */
iter_mark_pside_cycle_targets(qstate, iq->dp);
/* see if we can issue queries to get nameserver addresses */
/* this lookup is not randomized, but sequential. */
for(ns = iq->dp->nslist; ns; ns = ns->next) {
/* 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 || 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 || ie->use_nat64) ns->done_pside4 = 1;
continue;
}
/* query for parent-side A and AAAA for nameservers */
if(ie->supports_ipv6 && !ns->done_pside6) {
/* Send the AAAA request. */
if(!generate_parentside_target_query(qstate, iq, id,
ns->name, ns->namelen,
LDNS_RR_TYPE_AAAA, iq->qchase.qclass)) {
errinf_dname(qstate, "could not generate nameserver AAAA lookup for", ns->name);
return error_response(qstate, id,
LDNS_RCODE_SERVFAIL);
}
ns->done_pside6 = 1;
query_count++;
if(mesh_jostle_exceeded(qstate->env->mesh)) {
/* Wait for the lookup; do not spawn multiple
* lookups at a time. */
verbose(VERB_ALGO, "try parent-side glue lookup");
iq->num_target_queries += query_count;
target_count_increase(iq, query_count);
qstate->ext_state[id] = module_wait_subquery;
return 0;
}
}
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,
LDNS_RR_TYPE_A, iq->qchase.qclass)) {
errinf_dname(qstate, "could not generate nameserver A lookup for", ns->name);
return error_response(qstate, id,
LDNS_RCODE_SERVFAIL);
}
ns->done_pside4 = 1;
query_count++;
}
if(query_count != 0) { /* suspend to await results */
verbose(VERB_ALGO, "try parent-side glue lookup");
iq->num_target_queries += query_count;
target_count_increase(iq, query_count);
qstate->ext_state[id] = module_wait_subquery;
return 0;
}
}
/* if this was a parent-side glue query itself, then store that
* failure in cache. */
if(!qstate->no_cache_store && iq->query_for_pside_glue
&& !iq->pside_glue)
iter_store_parentside_neg(qstate->env, &qstate->qinfo,
iq->deleg_msg?iq->deleg_msg->rep:
(iq->response?iq->response->rep:NULL));
errinf(qstate, "all servers for this domain failed,");
errinf_dname(qstate, "at zone", iq->dp->name);
errinf_reply(qstate, iq);
verbose(VERB_QUERY, "out of query targets -- returning SERVFAIL");
/* fail -- no more targets, no more hope of targets, no hope
* of a response. */
return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
/**
* Try to find the NS record set that will resolve a qtype DS query. Due
* to grandparent/grandchild reasons we did not get a proper lookup right
* away. We need to create type NS queries until we get the right parent
* for this lookup. We remove labels from the query to find the right point.
* If we end up at the old dp name, then there is no solution.
*
* @param qstate: query state.
* @param iq: iterator query state.
* @param id: module id.
* @return true if the event requires more immediate processing, false if
* not. This is generally only true when forwarding the request to
* the final state (i.e., on answer).
*/
static int
processDSNSFind(struct module_qstate* qstate, struct iter_qstate* iq, int id)
{
struct module_qstate* subq = NULL;
verbose(VERB_ALGO, "processDSNSFind");
if(!iq->dsns_point) {
/* initialize */
iq->dsns_point = iq->qchase.qname;
iq->dsns_point_len = iq->qchase.qname_len;
}
/* robustcheck for internal error: we are not underneath the dp */
if(!dname_subdomain_c(iq->dsns_point, iq->dp->name)) {
errinf_dname(qstate, "for DS query parent-child nameserver search the query is not under the zone", iq->dp->name);
return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
/* go up one (more) step, until we hit the dp, if so, end */
dname_remove_label(&iq->dsns_point, &iq->dsns_point_len);
if(query_dname_compare(iq->dsns_point, iq->dp->name) == 0) {
/* there was no inbetween nameserver, use the old delegation
* point again. And this time, because dsns_point is nonNULL
* we are going to accept the (bad) result */
iq->state = QUERYTARGETS_STATE;
return 1;
}
iq->state = DSNS_FIND_STATE;
/* spawn NS lookup (validation not needed, this is for DS lookup) */
log_nametypeclass(VERB_ALGO, "fetch nameservers",
iq->dsns_point, LDNS_RR_TYPE_NS, iq->qchase.qclass);
if(!generate_sub_request(iq->dsns_point, iq->dsns_point_len,
LDNS_RR_TYPE_NS, iq->qchase.qclass, qstate, id, iq,
INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0, 0)) {
errinf_dname(qstate, "for DS query parent-child nameserver search, could not generate NS lookup for", iq->dsns_point);
return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
return 0;
}
/**
* Check if we wait responses for sent queries and update the iterator's
* external state.
*/
static void
check_waiting_queries(struct iter_qstate* iq, struct module_qstate* qstate,
int id)
{
if(iq->num_target_queries>0 && iq->num_current_queries>0) {
verbose(VERB_ALGO, "waiting for %d targets to "
"resolve or %d outstanding queries to "
"respond", iq->num_target_queries,
iq->num_current_queries);
qstate->ext_state[id] = module_wait_reply;
} else if(iq->num_target_queries>0) {
verbose(VERB_ALGO, "waiting for %d targets to "
"resolve", iq->num_target_queries);
qstate->ext_state[id] = module_wait_subquery;
} else {
verbose(VERB_ALGO, "waiting for %d "
"outstanding queries to respond",
iq->num_current_queries);
qstate->ext_state[id] = module_wait_reply;
}
}
/**
* This is the request event state where the request will be sent to one of
* its current query targets. This state also handles issuing target lookup
* queries for missing target IP addresses. Queries typically iterate on
* this state, both when they are just trying different targets for a given
* delegation point, and when they change delegation points. This state
* roughly corresponds to RFC 1034 algorithm steps 3 and 4.
*
* @param qstate: query state.
* @param iq: iterator query state.
* @param ie: iterator shared global environment.
* @param id: module id.
* @return true if the event requires more request processing immediately,
* false if not. This state only returns true when it is generating
* a SERVFAIL response because the query has hit a dead end.
*/
static int
processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
struct iter_env* ie, int id)
{
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;
int sq_check_ratelimit = 1;
int sq_was_ratelimited = 0;
int can_do_promisc = 0;
/* NOTE: a request will encounter this state for each target it
* needs to send a query to. That is, at least one per referral,
* more if some targets timeout or return throwaway answers. */
log_query_info(VERB_QUERY, "processQueryTargets:", &qstate->qinfo);
verbose(VERB_ALGO, "processQueryTargets: targetqueries %d, "
"currentqueries %d sentcount %d", iq->num_target_queries,
iq->num_current_queries, iq->sent_count);
/* Make sure that we haven't run away */
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 > 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");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
/* Check if we reached MAX_TARGET_NX limit without a fallback activation. */
if(iq->target_count && !*iq->nxns_dp &&
iq->target_count[TARGET_COUNT_NX] > MAX_TARGET_NX) {
struct delegpt_ns* ns;
/* If we can wait for resolution, do so. */
if(iq->num_target_queries>0 || iq->num_current_queries>0) {
check_waiting_queries(iq, qstate, id);
return 0;
}
verbose(VERB_ALGO, "request has exceeded the maximum "
"number of nxdomain nameserver lookups (%d) with %d",
MAX_TARGET_NX, iq->target_count[TARGET_COUNT_NX]);
/* Check for dp because we require one below */
if(!iq->dp) {
verbose(VERB_QUERY, "Failed to get a delegation, "
"giving up");
errinf(qstate, "failed to get a delegation (eg. prime "
"failure)");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
/* We reached the limit but we already have parent side
* information; stop resolution */
if(iq->dp->has_parent_side_NS) {
verbose(VERB_ALGO, "parent-side information is "
"already present for the delegation point, no "
"fallback possible");
errinf(qstate, "exceeded the maximum nameserver nxdomains");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
verbose(VERB_ALGO, "initiating parent-side fallback for "
"nxdomain nameserver lookups");
/* Mark all the current NSes as resolved to allow for parent
* fallback */
for(ns=iq->dp->nslist; ns; ns=ns->next) {
ns->resolved = 1;
}
/* Note the delegation point that triggered the NXNS fallback;
* no reason for shared queries to keep trying there.
* This also marks the fallback activation. */
*iq->nxns_dp = malloc(iq->dp->namelen);
if(!*iq->nxns_dp) {
verbose(VERB_ALGO, "out of memory while initiating "
"fallback");
errinf(qstate, "exceeded the maximum nameserver "
"nxdomains (malloc)");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
memcpy(*iq->nxns_dp, iq->dp->name, iq->dp->namelen);
} else if(iq->target_count && *iq->nxns_dp) {
/* Handle the NXNS fallback case. */
/* If we can wait for resolution, do so. */
if(iq->num_target_queries>0 || iq->num_current_queries>0) {
check_waiting_queries(iq, qstate, id);
return 0;
}
/* Check for dp because we require one below */
if(!iq->dp) {
verbose(VERB_QUERY, "Failed to get a delegation, "
"giving up");
errinf(qstate, "failed to get a delegation (eg. prime "
"failure)");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
if(iq->target_count[TARGET_COUNT_NX] > MAX_TARGET_NX_FALLBACK) {
verbose(VERB_ALGO, "request has exceeded the maximum "
"number of fallback nxdomain nameserver "
"lookups (%d) with %d", MAX_TARGET_NX_FALLBACK,
iq->target_count[TARGET_COUNT_NX]);
errinf(qstate, "exceeded the maximum nameserver nxdomains");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
if(!iq->dp->has_parent_side_NS) {
struct delegpt_ns* ns;
if(!dname_canonical_compare(*iq->nxns_dp, iq->dp->name)) {
verbose(VERB_ALGO, "this delegation point "
"initiated the fallback, marking the "
"nslist as resolved");
for(ns=iq->dp->nslist; ns; ns=ns->next) {
ns->resolved = 1;
}
}
}
}
/* Make sure we have a delegation point, otherwise priming failed
* or another failure occurred */
if(!iq->dp) {
verbose(VERB_QUERY, "Failed to get a delegation, giving up");
errinf(qstate, "failed to get a delegation (eg. prime failure)");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
if(!ie->supports_ipv6)
delegpt_no_ipv6(iq->dp);
if(!ie->supports_ipv4 && !ie->use_nat64)
delegpt_no_ipv4(iq->dp);
delegpt_log(VERB_ALGO, iq->dp);
if(iq->num_current_queries>0) {
/* already busy answering a query, this restart is because
* more delegpt addrs became available, wait for existing
* query. */
verbose(VERB_ALGO, "woke up, but wait for outstanding query");
qstate->ext_state[id] = module_wait_reply;
return 0;
}
if(iq->minimisation_state == INIT_MINIMISE_STATE
&& !(iq->chase_flags & BIT_RD)) {
/* (Re)set qinfo_out to (new) delegation point, except when
* qinfo_out is already a subdomain of dp. This happens when
* increasing by more than one label at once (QNAMEs with more
* than MAX_MINIMISE_COUNT labels). */
if(!(iq->qinfo_out.qname_len
&& dname_subdomain_c(iq->qchase.qname,
iq->qinfo_out.qname)
&& dname_subdomain_c(iq->qinfo_out.qname,
iq->dp->name))) {
iq->qinfo_out.qname = iq->dp->name;
iq->qinfo_out.qname_len = iq->dp->namelen;
iq->qinfo_out.qtype = LDNS_RR_TYPE_A;
iq->qinfo_out.qclass = iq->qchase.qclass;
iq->qinfo_out.local_alias = NULL;
iq->minimise_count = 0;
}
iq->minimisation_state = MINIMISE_STATE;
}
if(iq->minimisation_state == MINIMISE_STATE) {
int qchaselabs = dname_count_labels(iq->qchase.qname);
int labdiff = qchaselabs -
dname_count_labels(iq->qinfo_out.qname);
qout_orig = iq->qinfo_out.qname;
qout_orig_len = iq->qinfo_out.qname_len;
iq->qinfo_out.qname = iq->qchase.qname;
iq->qinfo_out.qname_len = iq->qchase.qname_len;
iq->minimise_count++;
iq->timeout_count = 0;
iter_dec_attempts(iq->dp, 1, ie->outbound_msg_retry);
/* Limit number of iterations for QNAMEs with more
* than MAX_MINIMISE_COUNT labels. Send first MINIMISE_ONE_LAB
* labels of QNAME always individually.
*/
if(qchaselabs > MAX_MINIMISE_COUNT && labdiff > 1 &&
iq->minimise_count > MINIMISE_ONE_LAB) {
if(iq->minimise_count < MAX_MINIMISE_COUNT) {
int multilabs = qchaselabs - 1 -
MINIMISE_ONE_LAB;
int extralabs = multilabs /
MINIMISE_MULTIPLE_LABS;
if (MAX_MINIMISE_COUNT - iq->minimise_count >=
multilabs % MINIMISE_MULTIPLE_LABS)
/* Default behaviour is to add 1 label
* every iteration. Therefore, decrement
* the extralabs by 1 */
extralabs--;
if (extralabs < labdiff)
labdiff -= extralabs;
else
labdiff = 1;
}
/* Last minimised iteration, send all labels with
* QTYPE=NS */
else
labdiff = 1;
}
if(labdiff > 1) {
verbose(VERB_QUERY, "removing %d labels", labdiff-1);
dname_remove_labels(&iq->qinfo_out.qname,
&iq->qinfo_out.qname_len,
labdiff-1);
}
if(labdiff < 1 || (labdiff < 2
&& (iq->qchase.qtype == LDNS_RR_TYPE_DS
|| iq->qchase.qtype == LDNS_RR_TYPE_A)))
/* Stop minimising this query, resolve "as usual" */
iq->minimisation_state = DONOT_MINIMISE_STATE;
else if(!qstate->no_cache_lookup) {
struct dns_msg* msg = dns_cache_lookup(qstate->env,
iq->qinfo_out.qname, iq->qinfo_out.qname_len,
iq->qinfo_out.qtype, iq->qinfo_out.qclass,
qstate->query_flags, qstate->region,
qstate->env->scratch, 0, iq->dp->name,
iq->dp->namelen);
if(msg && FLAGS_GET_RCODE(msg->rep->flags) ==
LDNS_RCODE_NOERROR)
/* no need to send query if it is already
* cached as NOERROR */
return 1;
if(msg && FLAGS_GET_RCODE(msg->rep->flags) ==
LDNS_RCODE_NXDOMAIN &&
qstate->env->need_to_validate &&
qstate->env->cfg->harden_below_nxdomain) {
if(msg->rep->security == sec_status_secure) {
iq->response = msg;
return final_state(iq);
}
if(msg->rep->security == sec_status_unchecked) {
struct module_qstate* subq = NULL;
if(!generate_sub_request(
iq->qinfo_out.qname,
iq->qinfo_out.qname_len,
iq->qinfo_out.qtype,
iq->qinfo_out.qclass,
qstate, id, iq,
INIT_REQUEST_STATE,
FINISHED_STATE, &subq, 1, 1))
verbose(VERB_ALGO,
"could not validate NXDOMAIN "
"response");
}
}
if(msg && FLAGS_GET_RCODE(msg->rep->flags) ==
LDNS_RCODE_NXDOMAIN) {
/* return and add a label in the next
* minimisation iteration.
*/
return 1;
}
}
}
if(iq->minimisation_state == SKIP_MINIMISE_STATE) {
if(iq->timeout_count < MAX_MINIMISE_TIMEOUT_COUNT)
/* Do not increment qname, continue incrementing next
* iteration */
iq->minimisation_state = MINIMISE_STATE;
else if(!qstate->env->cfg->qname_minimisation_strict)
/* Too many time-outs detected for this QNAME and QTYPE.
* We give up, disable QNAME minimisation. */
iq->minimisation_state = DONOT_MINIMISE_STATE;
}
if(iq->minimisation_state == DONOT_MINIMISE_STATE)
iq->qinfo_out = iq->qchase;
/* now find an answer to this query */
/* see if authority zones have an answer */
/* now we know the dp, we can check the auth zone for locally hosted
* contents */
if(!iq->auth_zone_avoid && qstate->blacklist) {
if(auth_zones_can_fallback(qstate->env->auth_zones,
iq->dp->name, iq->dp->namelen, iq->qinfo_out.qclass)) {
/* if cache is blacklisted and this zone allows us
* to fallback to the internet, then do so, and
* fetch results from the internet servers */
iq->auth_zone_avoid = 1;
}
}
if(iq->auth_zone_avoid) {
iq->auth_zone_avoid = 0;
auth_fallback = 1;
} else if(auth_zones_lookup(qstate->env->auth_zones, &iq->qinfo_out,
qstate->region, &iq->response, &auth_fallback, iq->dp->name,
iq->dp->namelen)) {
/* use this as a response to be processed by the iterator */
if(verbosity >= VERB_ALGO) {
log_dns_msg("msg from auth zone",
&iq->response->qinfo, iq->response->rep);
}
if((iq->chase_flags&BIT_RD) && !(iq->response->rep->flags&BIT_AA)) {
verbose(VERB_ALGO, "forwarder, ignoring referral from auth zone");
} else {
lock_rw_wrlock(&qstate->env->auth_zones->lock);
qstate->env->auth_zones->num_query_up++;
lock_rw_unlock(&qstate->env->auth_zones->lock);
iq->num_current_queries++;
iq->chase_to_rd = 0;
iq->dnssec_lame_query = 0;
iq->auth_zone_response = 1;
return next_state(iq, QUERY_RESP_STATE);
}
}
iq->auth_zone_response = 0;
if(auth_fallback == 0) {
/* like we got servfail from the auth zone lookup, and
* no internet fallback */
verbose(VERB_ALGO, "auth zone lookup failed, no fallback,"
" servfail");
errinf(qstate, "auth zone lookup failed, fallback is off");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
if(iq->dp->auth_dp) {
/* we wanted to fallback, but had no delegpt, only the
* auth zone generated delegpt, create an actual one */
iq->auth_zone_avoid = 1;
return next_state(iq, INIT_REQUEST_STATE);
}
/* but mostly, fallback==1 (like, when no such auth zone exists)
* and we continue with lookups */
tf_policy = 0;
/* < not <=, because although the array is large enough for <=, the
* generated query will immediately be discarded due to depth and
* that servfail is cached, which is not good as opportunism goes. */
if(iq->depth < ie->max_dependency_depth
&& iq->num_target_queries == 0
&& (!iq->target_count || iq->target_count[TARGET_COUNT_NX]==0)
&& iq->sent_count < TARGET_FETCH_STOP) {
can_do_promisc = 1;
}
/* if the mesh query list is full, then do not waste cpu and sockets to
* fetch promiscuous targets. They can be looked up when needed. */
if(can_do_promisc && !mesh_jostle_exceeded(qstate->env->mesh)) {
tf_policy = ie->target_fetch_policy[iq->depth];
}
/* if in 0x20 fallback get as many targets as possible */
if(iq->caps_fallback) {
int extra = 0;
size_t naddr, nres, navail;
if(!query_for_targets(qstate, iq, ie, id, -1, &extra)) {
errinf(qstate, "could not fetch nameservers for 0x20 fallback");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
iq->num_target_queries += extra;
target_count_increase(iq, extra);
if(iq->num_target_queries > 0) {
/* wait to get all targets, we want to try em */
verbose(VERB_ALGO, "wait for all targets for fallback");
qstate->ext_state[id] = module_wait_reply;
/* undo qname minimise step because we'll get back here
* to do it again */
if(qout_orig && iq->minimise_count > 0) {
iq->minimise_count--;
iq->qinfo_out.qname = qout_orig;
iq->qinfo_out.qname_len = qout_orig_len;
}
return 0;
}
/* did we do enough fallback queries already? */
delegpt_count_addr(iq->dp, &naddr, &nres, &navail);
/* the current caps_server is the number of fallbacks sent.
* 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 >= (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 "
"match for %d wanted, done.",
(int)iq->caps_server+1, (int)naddr*3);
iq->response = iq->caps_response;
iq->caps_fallback = 0;
iter_dec_attempts(iq->dp, 3, ie->outbound_msg_retry); /* space for fallback */
iq->num_current_queries++; /* RespState decrements it*/
iq->referral_count++; /* make sure we don't loop */
iq->sent_count = 0;
iq->dp_target_count = 0;
iq->state = QUERY_RESP_STATE;
return 1;
}
verbose(VERB_ALGO, "0x20 fallback number %d",
(int)iq->caps_server);
/* if there is a policy to fetch missing targets
* opportunistically, do it. we rely on the fact that once a
* query (or queries) for a missing name have been issued,
* they will not show up again. */
} else if(tf_policy != 0) {
int extra = 0;
verbose(VERB_ALGO, "attempt to get extra %d targets",
tf_policy);
(void)query_for_targets(qstate, iq, ie, id, tf_policy, &extra);
/* errors ignored, these targets are not strictly necessary for
* this result, we do not have to reply with SERVFAIL */
iq->num_target_queries += extra;
target_count_increase(iq, extra);
}
/* Add the current set of unused targets to our queue. */
delegpt_add_unused_targets(iq->dp);
if(qstate->env->auth_zones) {
/* apply rpz triggers at query time */
struct dns_msg* forged_response = rpz_callback_from_iterator_module(qstate, iq);
if(forged_response != NULL) {
qstate->ext_state[id] = module_finished;
qstate->return_rcode = LDNS_RCODE_NOERROR;
qstate->return_msg = forged_response;
iq->response = forged_response;
next_state(iq, FINISHED_STATE);
if(!iter_prepend(iq, qstate->return_msg, qstate->region)) {
log_err("rpz: prepend rrsets: out of memory");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
return 0;
}
}
/* Select the next usable target, filtering out unsuitable targets. */
target = iter_server_selection(ie, qstate->env, iq->dp,
iq->dp->name, iq->dp->namelen, iq->qchase.qtype,
&iq->dnssec_lame_query, &iq->chase_to_rd,
iq->num_target_queries, qstate->blacklist,
qstate->prefetch_leeway);
/* If no usable target was selected... */
if(!target) {
/* Here we distinguish between three states: generate a new
* target query, just wait, or quit (with a SERVFAIL).
* We have the following information: number of active
* target queries, number of active current queries,
* the presence of missing targets at this delegation
* point, and the given query target policy. */
/* Check for the wait condition. If this is true, then
* an action must be taken. */
if(iq->num_target_queries==0 && iq->num_current_queries==0) {
/* If there is nothing to wait for, then we need
* to distinguish between generating (a) new target
* query, or failing. */
if(delegpt_count_missing_targets(iq->dp, NULL) > 0) {
int qs = 0;
verbose(VERB_ALGO, "querying for next "
"missing target");
if(!query_for_targets(qstate, iq, ie, id,
1, &qs)) {
errinf(qstate, "could not fetch nameserver");
errinf_dname(qstate, "at zone", iq->dp->name);
return error_response(qstate, id,
LDNS_RCODE_SERVFAIL);
}
if(qs == 0 &&
delegpt_count_missing_targets(iq->dp, NULL) == 0){
/* it looked like there were missing
* targets, but they did not turn up.
* Try the bad choices again (if any),
* when we get back here missing==0,
* so this is not a loop. */
return 1;
}
iq->num_target_queries += qs;
target_count_increase(iq, qs);
}
/* Since a target query might have been made, we
* need to check again. */
if(iq->num_target_queries == 0) {
/* if in capsforid fallback, instead of last
* resort, we agree with the current reply
* we have (if any) (our count of addrs bad)*/
if(iq->caps_fallback && iq->caps_reply) {
/* we're done, process the response */
verbose(VERB_ALGO, "0x20 fallback had %d responses, "
"but no more servers except "
"last resort, done.",
(int)iq->caps_server+1);
iq->response = iq->caps_response;
iq->caps_fallback = 0;
iter_dec_attempts(iq->dp, 3, ie->outbound_msg_retry); /* space for fallback */
iq->num_current_queries++; /* RespState decrements it*/
iq->referral_count++; /* make sure we don't loop */
iq->sent_count = 0;
iq->dp_target_count = 0;
iq->state = QUERY_RESP_STATE;
return 1;
}
return processLastResort(qstate, iq, ie, id);
}
}
/* otherwise, we have no current targets, so submerge
* until one of the target or direct queries return. */
verbose(VERB_ALGO, "no current targets");
check_waiting_queries(iq, qstate, id);
/* undo qname minimise step because we'll get back here
* to do it again */
if(qout_orig && iq->minimise_count > 0) {
iq->minimise_count--;
iq->qinfo_out.qname = qout_orig;
iq->qinfo_out.qname_len = qout_orig_len;
}
return 0;
}
/* We have a target. We could have created promiscuous target
* queries but we are currently under pressure (mesh_jostle_exceeded).
* If we are configured to allow promiscuous target queries and haven't
* gone out to the network for a target query for this delegation, then
* it is possible to slip in a promiscuous one with a 1/10 chance. */
if(can_do_promisc && tf_policy == 0 && iq->depth == 0
&& iq->depth < ie->max_dependency_depth
&& ie->target_fetch_policy[iq->depth] != 0
&& iq->dp_target_count == 0
&& !ub_random_max(qstate->env->rnd, 10)) {
int extra = 0;
verbose(VERB_ALGO, "available target exists in cache but "
"attempt to get extra 1 target");
(void)query_for_targets(qstate, iq, ie, id, 1, &extra);
/* errors ignored, these targets are not strictly necessary for
* this result, we do not have to reply with SERVFAIL */
if(extra > 0) {
iq->num_target_queries += extra;
target_count_increase(iq, extra);
check_waiting_queries(iq, qstate, id);
/* undo qname minimise step because we'll get back here
* to do it again */
if(qout_orig && iq->minimise_count > 0) {
iq->minimise_count--;
iq->qinfo_out.qname = qout_orig;
iq->qinfo_out.qname_len = qout_orig_len;
}
return 0;
}
}
/* Do not check ratelimit for forwarding queries or if we already got a
* pass. */
sq_check_ratelimit = (!(iq->chase_flags & BIT_RD) && !iq->ratelimit_ok);
/* 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,
&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),
/* unset CD if to forwarder(RD set) and not dnssec retry
* (blacklist nonempty) and no trust-anchors are configured
* above the qname or on the first attempt when dnssec is on */
- EDNS_DO| ((iq->chase_to_rd||(iq->chase_flags&BIT_RD)!=0)&&
+ (qstate->env->cfg->disable_edns_do?0:EDNS_DO)|
+ ((iq->chase_to_rd||(iq->chase_flags&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, &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),
target->tls_auth_name, qstate, &sq_was_ratelimited);
if(!outq) {
if(sq_was_ratelimited) {
lock_basic_lock(&ie->queries_ratelimit_lock);
ie->num_queries_ratelimited++;
lock_basic_unlock(&ie->queries_ratelimit_lock);
verbose(VERB_ALGO, "query exceeded ratelimits");
qstate->was_ratelimited = 1;
errinf_dname(qstate, "exceeded ratelimit for zone",
iq->dp->name);
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
log_addr(VERB_QUERY, "error sending query to auth server",
&real_addr, real_addrlen);
if(qstate->env->cfg->qname_minimisation)
iq->minimisation_state = SKIP_MINIMISE_STATE;
return next_state(iq, QUERYTARGETS_STATE);
}
outbound_list_insert(&iq->outlist, outq);
iq->num_current_queries++;
iq->sent_count++;
qstate->ext_state[id] = module_wait_reply;
return 0;
}
/** find NS rrset in given list */
static struct ub_packed_rrset_key*
find_NS(struct reply_info* rep, size_t from, size_t to)
{
size_t i;
for(i=from; i<to; i++) {
if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NS)
return rep->rrsets[i];
}
return NULL;
}
/**
* Process the query response. All queries end up at this state first. This
* process generally consists of analyzing the response and routing the
* event to the next state (either bouncing it back to a request state, or
* terminating the processing for this event).
*
* @param qstate: query state.
* @param iq: iterator query state.
* @param ie: iterator shared global environment.
* @param id: module id.
* @return true if the event requires more immediate processing, false if
* not. This is generally only true when forwarding the request to
* the final state (i.e., on answer).
*/
static int
processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
struct iter_env* ie, int id)
{
- int dnsseclame = 0, origtypecname = 0;
+ int dnsseclame = 0, origtypecname = 0, orig_empty_nodata_found;
enum response_type type;
iq->num_current_queries--;
if(!inplace_cb_query_response_call(qstate->env, qstate, iq->response))
log_err("unable to call query_response callback");
if(iq->response == NULL) {
/* Don't increment qname when QNAME minimisation is enabled */
if(qstate->env->cfg->qname_minimisation) {
iq->minimisation_state = SKIP_MINIMISE_STATE;
}
iq->timeout_count++;
iq->chase_to_rd = 0;
iq->dnssec_lame_query = 0;
verbose(VERB_ALGO, "query response was timeout");
return next_state(iq, QUERYTARGETS_STATE);
}
iq->timeout_count = 0;
+ orig_empty_nodata_found = iq->empty_nodata_found;
type = response_type_from_server(
(int)((iq->chase_flags&BIT_RD) || iq->chase_to_rd),
- iq->response, &iq->qinfo_out, iq->dp);
+ iq->response, &iq->qinfo_out, iq->dp, &iq->empty_nodata_found);
iq->chase_to_rd = 0;
/* remove TC flag, if this is erroneously set by TCP upstream */
iq->response->rep->flags &= ~BIT_TC;
+ if(orig_empty_nodata_found != iq->empty_nodata_found &&
+ iq->empty_nodata_found < EMPTY_NODATA_RETRY_COUNT) {
+ /* try to search at another server */
+ if(qstate->reply) {
+ struct delegpt_addr* a = delegpt_find_addr(
+ iq->dp, &qstate->reply->remote_addr,
+ qstate->reply->remote_addrlen);
+ /* make selection disprefer it */
+ if(a) a->lame = 1;
+ }
+ return next_state(iq, QUERYTARGETS_STATE);
+ }
if(type == RESPONSE_TYPE_REFERRAL && (iq->chase_flags&BIT_RD) &&
!iq->auth_zone_response) {
/* When forwarding (RD bit is set), we handle referrals
* differently. No queries should be sent elsewhere */
type = RESPONSE_TYPE_ANSWER;
}
if(!qstate->env->cfg->disable_dnssec_lame_check && iq->dnssec_expected
&& !iq->dnssec_lame_query &&
!(iq->chase_flags&BIT_RD)
&& iq->sent_count < DNSSEC_LAME_DETECT_COUNT
&& type != RESPONSE_TYPE_LAME
&& type != RESPONSE_TYPE_REC_LAME
&& type != RESPONSE_TYPE_THROWAWAY
&& type != RESPONSE_TYPE_UNTYPED) {
/* a possible answer, see if it is missing DNSSEC */
/* but not when forwarding, so we dont mark fwder lame */
if(!iter_msg_has_dnssec(iq->response)) {
/* Mark this address as dnsseclame in this dp,
* because that will make serverselection disprefer
* it, but also, once it is the only final option,
* use dnssec-lame-bypass if it needs to query there.*/
if(qstate->reply) {
struct delegpt_addr* a = delegpt_find_addr(
iq->dp, &qstate->reply->remote_addr,
qstate->reply->remote_addrlen);
if(a) a->dnsseclame = 1;
}
/* test the answer is from the zone we expected,
* otherwise, (due to parent,child on same server), we
* might mark the server,zone lame inappropriately */
if(!iter_msg_from_zone(iq->response, iq->dp, type,
iq->qchase.qclass))
qstate->reply = NULL;
type = RESPONSE_TYPE_LAME;
dnsseclame = 1;
}
} else iq->dnssec_lame_query = 0;
/* see if referral brings us close to the target */
if(type == RESPONSE_TYPE_REFERRAL) {
struct ub_packed_rrset_key* ns = find_NS(
iq->response->rep, iq->response->rep->an_numrrsets,
iq->response->rep->an_numrrsets
+ iq->response->rep->ns_numrrsets);
if(!ns) ns = find_NS(iq->response->rep, 0,
iq->response->rep->an_numrrsets);
if(!ns || !dname_strict_subdomain_c(ns->rk.dname, iq->dp->name)
|| !dname_subdomain_c(iq->qchase.qname, ns->rk.dname)){
verbose(VERB_ALGO, "bad referral, throwaway");
type = RESPONSE_TYPE_THROWAWAY;
} else
iter_scrub_ds(iq->response, ns, iq->dp->name);
} else iter_scrub_ds(iq->response, NULL, NULL);
if(type == RESPONSE_TYPE_THROWAWAY &&
FLAGS_GET_RCODE(iq->response->rep->flags) == LDNS_RCODE_YXDOMAIN) {
/* 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;
size_t snamelen = 0;
get_cname_target(iq->response->rep->rrsets[0], &sname,
&snamelen);
if(snamelen && dname_subdomain_c(sname, iq->response->rep->rrsets[0]->rk.dname)) {
/* DNAME to a subdomain loop; do not recurse */
type = RESPONSE_TYPE_ANSWER;
}
} else if(type == RESPONSE_TYPE_CNAME &&
iq->qchase.qtype == LDNS_RR_TYPE_CNAME &&
iq->minimisation_state == MINIMISE_STATE &&
query_dname_compare(iq->qchase.qname, iq->qinfo_out.qname) == 0) {
/* The minimised query for full QTYPE and hidden QTYPE can be
* classified as CNAME response type, even when the original
* QTYPE=CNAME. This should be treated as answer response type.
*/
type = RESPONSE_TYPE_ANSWER;
}
/* handle each of the type cases */
if(type == RESPONSE_TYPE_ANSWER) {
/* ANSWER type responses terminate the query algorithm,
* so they sent on their */
if(verbosity >= VERB_DETAIL) {
verbose(VERB_DETAIL, "query response was %s",
FLAGS_GET_RCODE(iq->response->rep->flags)
==LDNS_RCODE_NXDOMAIN?"NXDOMAIN ANSWER":
(iq->response->rep->an_numrrsets?"ANSWER":
"nodata ANSWER"));
}
/* if qtype is DS, check we have the right level of answer,
* like grandchild answer but we need the middle, reject it */
if(iq->qchase.qtype == LDNS_RR_TYPE_DS && !iq->dsns_point
&& !(iq->chase_flags&BIT_RD)
&& iter_ds_toolow(iq->response, iq->dp)
&& iter_dp_cangodown(&iq->qchase, iq->dp)) {
/* close down outstanding requests to be discarded */
outbound_list_clear(&iq->outlist);
iq->num_current_queries = 0;
fptr_ok(fptr_whitelist_modenv_detach_subs(
qstate->env->detach_subs));
(*qstate->env->detach_subs)(qstate);
iq->num_target_queries = 0;
return processDSNSFind(qstate, iq, id);
}
if(!qstate->no_cache_store)
iter_dns_store(qstate->env, &iq->response->qinfo,
iq->response->rep,
iq->qchase.qtype != iq->response->qinfo.qtype,
qstate->prefetch_leeway,
iq->dp&&iq->dp->has_parent_side_NS,
qstate->region, qstate->query_flags,
qstate->qstarttime);
/* close down outstanding requests to be discarded */
outbound_list_clear(&iq->outlist);
iq->num_current_queries = 0;
fptr_ok(fptr_whitelist_modenv_detach_subs(
qstate->env->detach_subs));
(*qstate->env->detach_subs)(qstate);
iq->num_target_queries = 0;
if(qstate->reply)
sock_list_insert(&qstate->reply_origin,
&qstate->reply->remote_addr,
qstate->reply->remote_addrlen, qstate->region);
if(iq->minimisation_state != DONOT_MINIMISE_STATE
&& !(iq->chase_flags & BIT_RD)) {
if(FLAGS_GET_RCODE(iq->response->rep->flags) !=
LDNS_RCODE_NOERROR) {
if(qstate->env->cfg->qname_minimisation_strict) {
if(FLAGS_GET_RCODE(iq->response->rep->flags) ==
LDNS_RCODE_NXDOMAIN) {
iter_scrub_nxdomain(iq->response);
return final_state(iq);
}
return error_response(qstate, id,
LDNS_RCODE_SERVFAIL);
}
/* Best effort qname-minimisation.
* Stop minimising and send full query when
* RCODE is not NOERROR. */
iq->minimisation_state = DONOT_MINIMISE_STATE;
}
if(FLAGS_GET_RCODE(iq->response->rep->flags) ==
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
* NXDOMAIN if harden-below-nxdomain is
* enabled. */
if(qstate->env->cfg->harden_below_nxdomain &&
qstate->env->need_to_validate) {
struct module_qstate* subq = NULL;
log_query_info(VERB_QUERY,
"schedule NXDOMAIN validation:",
&iq->response->qinfo);
if(!generate_sub_request(
iq->response->qinfo.qname,
iq->response->qinfo.qname_len,
iq->response->qinfo.qtype,
iq->response->qinfo.qclass,
qstate, id, iq,
INIT_REQUEST_STATE,
FINISHED_STATE, &subq, 1, 1))
verbose(VERB_ALGO,
"could not validate NXDOMAIN "
"response");
}
}
return next_state(iq, QUERYTARGETS_STATE);
}
return final_state(iq);
} else if(type == RESPONSE_TYPE_REFERRAL) {
/* REFERRAL type responses get a reset of the
* delegation point, and back to the QUERYTARGETS_STATE. */
verbose(VERB_DETAIL, "query response was REFERRAL");
/* if hardened, only store referral if we asked for it */
if(!qstate->no_cache_store &&
(!qstate->env->cfg->harden_referral_path ||
( qstate->qinfo.qtype == LDNS_RR_TYPE_NS
&& (qstate->query_flags&BIT_RD)
&& !(qstate->query_flags&BIT_CD)
/* we know that all other NS rrsets are scrubbed
* away, thus on referral only one is left.
* see if that equals the query name... */
&& ( /* auth section, but sometimes in answer section*/
reply_find_rrset_section_ns(iq->response->rep,
iq->qchase.qname, iq->qchase.qname_len,
LDNS_RR_TYPE_NS, iq->qchase.qclass)
|| reply_find_rrset_section_an(iq->response->rep,
iq->qchase.qname, iq->qchase.qname_len,
LDNS_RR_TYPE_NS, iq->qchase.qclass)
)
))) {
/* Store the referral under the current query */
/* no prefetch-leeway, since its not the answer */
iter_dns_store(qstate->env, &iq->response->qinfo,
iq->response->rep, 1, 0, 0, NULL, 0,
qstate->qstarttime);
if(iq->store_parent_NS)
iter_store_parentside_NS(qstate->env,
iq->response->rep);
if(qstate->env->neg_cache)
val_neg_addreferral(qstate->env->neg_cache,
iq->response->rep, iq->dp->name);
}
/* store parent-side-in-zone-glue, if directly queried for */
if(!qstate->no_cache_store && iq->query_for_pside_glue
&& !iq->pside_glue) {
iq->pside_glue = reply_find_rrset(iq->response->rep,
iq->qchase.qname, iq->qchase.qname_len,
iq->qchase.qtype, iq->qchase.qclass);
if(iq->pside_glue) {
log_rrset_key(VERB_ALGO, "found parent-side "
"glue", iq->pside_glue);
iter_store_parentside_rrset(qstate->env,
iq->pside_glue);
}
}
/* Reset the event state, setting the current delegation
* point to the referral. */
iq->deleg_msg = iq->response;
iq->dp = delegpt_from_message(iq->response, qstate->region);
if (qstate->env->cfg->qname_minimisation)
iq->minimisation_state = INIT_MINIMISE_STATE;
if(!iq->dp) {
errinf(qstate, "malloc failure, for delegation point");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
if(!cache_fill_missing(qstate->env, iq->qchase.qclass,
qstate->region, iq->dp)) {
errinf(qstate, "malloc failure, copy extra info into delegation point");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
if(iq->store_parent_NS && query_dname_compare(iq->dp->name,
iq->store_parent_NS->name) == 0)
iter_merge_retry_counts(iq->dp, iq->store_parent_NS,
ie->outbound_msg_retry);
delegpt_log(VERB_ALGO, iq->dp);
/* Count this as a referral. */
iq->referral_count++;
iq->sent_count = 0;
iq->dp_target_count = 0;
/* see if the next dp is a trust anchor, or a DS was sent
* along, indicating dnssec is expected for next zone */
iq->dnssec_expected = iter_indicates_dnssec(qstate->env,
iq->dp, iq->response, iq->qchase.qclass);
/* if dnssec, validating then also fetch the key for the DS */
if(iq->dnssec_expected && qstate->env->cfg->prefetch_key &&
!(qstate->query_flags&BIT_CD))
generate_dnskey_prefetch(qstate, iq, id);
/* spawn off NS and addr to auth servers for the NS we just
* got in the referral. This gets authoritative answer
* (answer section trust level) rrset.
* right after, we detach the subs, answer goes to cache. */
if(qstate->env->cfg->harden_referral_path)
generate_ns_check(qstate, iq, id);
/* stop current outstanding queries.
* FIXME: should the outstanding queries be waited for and
* handled? Say by a subquery that inherits the outbound_entry.
*/
outbound_list_clear(&iq->outlist);
iq->num_current_queries = 0;
fptr_ok(fptr_whitelist_modenv_detach_subs(
qstate->env->detach_subs));
(*qstate->env->detach_subs)(qstate);
iq->num_target_queries = 0;
iq->response = 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) {
uint8_t* sname = NULL;
size_t snamelen = 0;
/* CNAME type responses get a query restart (i.e., get a
* reset of the query state and go back to INIT_REQUEST_STATE).
*/
verbose(VERB_DETAIL, "query response was CNAME");
if(verbosity >= VERB_ALGO)
log_dns_msg("cname msg", &iq->response->qinfo,
iq->response->rep);
/* if qtype is DS, check we have the right level of answer,
* like grandchild answer but we need the middle, reject it */
if(iq->qchase.qtype == LDNS_RR_TYPE_DS && !iq->dsns_point
&& !(iq->chase_flags&BIT_RD)
&& iter_ds_toolow(iq->response, iq->dp)
&& iter_dp_cangodown(&iq->qchase, iq->dp)) {
outbound_list_clear(&iq->outlist);
iq->num_current_queries = 0;
fptr_ok(fptr_whitelist_modenv_detach_subs(
qstate->env->detach_subs));
(*qstate->env->detach_subs)(qstate);
iq->num_target_queries = 0;
return processDSNSFind(qstate, iq, id);
}
/* Process the CNAME response. */
if(!handle_cname_response(qstate, iq, iq->response,
&sname, &snamelen)) {
errinf(qstate, "malloc failure, CNAME info");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
/* cache the CNAME response under the current query */
/* NOTE : set referral=1, so that rrsets get stored but not
* the partial query answer (CNAME only). */
/* prefetchleeway applied because this updates answer parts */
if(!qstate->no_cache_store)
iter_dns_store(qstate->env, &iq->response->qinfo,
iq->response->rep, 1, qstate->prefetch_leeway,
iq->dp&&iq->dp->has_parent_side_NS, NULL,
qstate->query_flags, qstate->qstarttime);
/* set the current request's qname to the new value. */
iq->qchase.qname = sname;
iq->qchase.qname_len = snamelen;
if(qstate->env->auth_zones) {
/* apply rpz qname triggers after cname */
struct dns_msg* forged_response =
rpz_callback_from_iterator_cname(qstate, iq);
while(forged_response && reply_find_rrset_section_an(
forged_response->rep, iq->qchase.qname,
iq->qchase.qname_len, LDNS_RR_TYPE_CNAME,
iq->qchase.qclass)) {
/* another cname to follow */
if(!handle_cname_response(qstate, iq, forged_response,
&sname, &snamelen)) {
errinf(qstate, "malloc failure, CNAME info");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
iq->qchase.qname = sname;
iq->qchase.qname_len = snamelen;
forged_response =
rpz_callback_from_iterator_cname(qstate, iq);
}
if(forged_response != NULL) {
qstate->ext_state[id] = module_finished;
qstate->return_rcode = LDNS_RCODE_NOERROR;
qstate->return_msg = forged_response;
iq->response = forged_response;
next_state(iq, FINISHED_STATE);
if(!iter_prepend(iq, qstate->return_msg, qstate->region)) {
log_err("rpz: after cname, prepend rrsets: out of memory");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
qstate->return_msg->qinfo = qstate->qinfo;
return 0;
}
}
/* Clear the query state, since this is a query restart. */
iq->deleg_msg = NULL;
iq->dp = NULL;
iq->dsns_point = NULL;
iq->auth_zone_response = 0;
iq->sent_count = 0;
iq->dp_target_count = 0;
if(iq->minimisation_state != MINIMISE_STATE)
/* Only count as query restart when it is not an extra
* query as result of qname minimisation. */
iq->query_restart_count++;
if(qstate->env->cfg->qname_minimisation)
iq->minimisation_state = INIT_MINIMISE_STATE;
/* stop current outstanding queries.
* FIXME: should the outstanding queries be waited for and
* handled? Say by a subquery that inherits the outbound_entry.
*/
outbound_list_clear(&iq->outlist);
iq->num_current_queries = 0;
fptr_ok(fptr_whitelist_modenv_detach_subs(
qstate->env->detach_subs));
(*qstate->env->detach_subs)(qstate);
iq->num_target_queries = 0;
if(qstate->reply)
sock_list_insert(&qstate->reply_origin,
&qstate->reply->remote_addr,
qstate->reply->remote_addrlen, qstate->region);
verbose(VERB_ALGO, "cleared outbound list for query restart");
/* go to INIT_REQUEST_STATE for new qname. */
return next_state(iq, INIT_REQUEST_STATE);
} else if(type == RESPONSE_TYPE_LAME) {
/* Cache the LAMEness. */
verbose(VERB_DETAIL, "query response was %sLAME",
dnsseclame?"DNSSEC ":"");
if(!dname_subdomain_c(iq->qchase.qname, iq->dp->name)) {
log_err("mark lame: mismatch in qname and dpname");
/* throwaway this reply below */
} else if(qstate->reply) {
/* need addr for lameness cache, but we may have
* gotten this from cache, so test to be sure */
if(!infra_set_lame(qstate->env->infra_cache,
&qstate->reply->remote_addr,
qstate->reply->remote_addrlen,
iq->dp->name, iq->dp->namelen,
*qstate->env->now, dnsseclame, 0,
iq->qchase.qtype))
log_err("mark host lame: out of memory");
}
} else if(type == RESPONSE_TYPE_REC_LAME) {
/* Cache the LAMEness. */
verbose(VERB_DETAIL, "query response REC_LAME: "
"recursive but not authoritative server");
if(!dname_subdomain_c(iq->qchase.qname, iq->dp->name)) {
log_err("mark rec_lame: mismatch in qname and dpname");
/* throwaway this reply below */
} else if(qstate->reply) {
/* need addr for lameness cache, but we may have
* gotten this from cache, so test to be sure */
verbose(VERB_DETAIL, "mark as REC_LAME");
if(!infra_set_lame(qstate->env->infra_cache,
&qstate->reply->remote_addr,
qstate->reply->remote_addrlen,
iq->dp->name, iq->dp->namelen,
*qstate->env->now, 0, 1, iq->qchase.qtype))
log_err("mark host lame: out of memory");
}
} else if(type == RESPONSE_TYPE_THROWAWAY) {
/* LAME and THROWAWAY responses are handled the same way.
* In this case, the event is just sent directly back to
* the QUERYTARGETS_STATE without resetting anything,
* because, clearly, the next target must be tried. */
verbose(VERB_DETAIL, "query response was THROWAWAY");
} else {
log_warn("A query response came back with an unknown type: %d",
(int)type);
}
/* LAME, THROWAWAY and "unknown" all end up here.
* Recycle to the QUERYTARGETS state to hopefully try a
* different target. */
if (qstate->env->cfg->qname_minimisation &&
!qstate->env->cfg->qname_minimisation_strict)
iq->minimisation_state = DONOT_MINIMISE_STATE;
if(iq->auth_zone_response) {
/* can we fallback? */
iq->auth_zone_response = 0;
if(!auth_zones_can_fallback(qstate->env->auth_zones,
iq->dp->name, iq->dp->namelen, qstate->qinfo.qclass)) {
verbose(VERB_ALGO, "auth zone response bad, and no"
" fallback possible, servfail");
errinf_dname(qstate, "response is bad, no fallback, "
"for auth zone", iq->dp->name);
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
verbose(VERB_ALGO, "auth zone response was bad, "
"fallback enabled");
iq->auth_zone_avoid = 1;
if(iq->dp->auth_dp) {
/* we are using a dp for the auth zone, with no
* nameservers, get one first */
iq->dp = NULL;
return next_state(iq, INIT_REQUEST_STATE);
}
}
return next_state(iq, QUERYTARGETS_STATE);
}
/**
* Return priming query results to interested super querystates.
*
* Sets the delegation point and delegation message (not nonRD queries).
* This is a callback from walk_supers.
*
* @param qstate: priming query state that finished.
* @param id: module id.
* @param forq: the qstate for which priming has been done.
*/
static void
prime_supers(struct module_qstate* qstate, int id, struct module_qstate* forq)
{
struct iter_qstate* foriq = (struct iter_qstate*)forq->minfo[id];
struct delegpt* dp = NULL;
log_assert(qstate->is_priming || foriq->wait_priming_stub);
log_assert(qstate->return_rcode == LDNS_RCODE_NOERROR);
/* Convert our response to a delegation point */
dp = delegpt_from_message(qstate->return_msg, forq->region);
if(!dp) {
/* if there is no convertible delegation point, then
* the ANSWER type was (presumably) a negative answer. */
verbose(VERB_ALGO, "prime response was not a positive "
"ANSWER; failing");
foriq->dp = NULL;
foriq->state = QUERYTARGETS_STATE;
return;
}
log_query_info(VERB_DETAIL, "priming successful for", &qstate->qinfo);
delegpt_log(VERB_ALGO, dp);
foriq->dp = dp;
foriq->deleg_msg = dns_copy_msg(qstate->return_msg, forq->region);
if(!foriq->deleg_msg) {
log_err("copy prime response: out of memory");
foriq->dp = NULL;
foriq->state = QUERYTARGETS_STATE;
return;
}
/* root priming responses go to init stage 2, priming stub
* responses to to stage 3. */
if(foriq->wait_priming_stub) {
foriq->state = INIT_REQUEST_3_STATE;
foriq->wait_priming_stub = 0;
} else foriq->state = INIT_REQUEST_2_STATE;
/* because we are finished, the parent will be reactivated */
}
/**
* This handles the response to a priming query. This is used to handle both
* root and stub priming responses. This is basically the equivalent of the
* QUERY_RESP_STATE, but will not handle CNAME responses and will treat
* REFERRALs as ANSWERS. It will also update and reactivate the originating
* event.
*
* @param qstate: query state.
* @param id: module id.
* @return true if the event needs more immediate processing, false if not.
* This state always returns false.
*/
static int
processPrimeResponse(struct module_qstate* qstate, int id)
{
struct iter_qstate* iq = (struct iter_qstate*)qstate->minfo[id];
enum response_type type;
iq->response->rep->flags &= ~(BIT_RD|BIT_RA); /* ignore rec-lame */
type = response_type_from_server(
(int)((iq->chase_flags&BIT_RD) || iq->chase_to_rd),
- iq->response, &iq->qchase, iq->dp);
+ iq->response, &iq->qchase, iq->dp, NULL);
if(type == RESPONSE_TYPE_ANSWER) {
qstate->return_rcode = LDNS_RCODE_NOERROR;
qstate->return_msg = iq->response;
} else {
errinf(qstate, "prime response did not get an answer");
errinf_dname(qstate, "for", qstate->qinfo.qname);
qstate->return_rcode = LDNS_RCODE_SERVFAIL;
qstate->return_msg = NULL;
}
/* validate the root or stub after priming (if enabled).
* This is the same query as the prime query, but with validation.
* Now that we are primed, the additional queries that validation
* may need can be resolved. */
if(qstate->env->cfg->harden_referral_path) {
struct module_qstate* subq = NULL;
log_nametypeclass(VERB_ALGO, "schedule prime validation",
qstate->qinfo.qname, qstate->qinfo.qtype,
qstate->qinfo.qclass);
if(!generate_sub_request(qstate->qinfo.qname,
qstate->qinfo.qname_len, qstate->qinfo.qtype,
qstate->qinfo.qclass, qstate, id, iq,
INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1, 0)) {
verbose(VERB_ALGO, "could not generate prime check");
}
generate_a_aaaa_check(qstate, iq, id);
}
/* This event is finished. */
qstate->ext_state[id] = module_finished;
return 0;
}
/**
* Do final processing on responses to target queries. Events reach this
* state after the iterative resolution algorithm terminates. This state is
* responsible for reactivating the original event, and housekeeping related
* to received target responses (caching, updating the current delegation
* point, etc).
* Callback from walk_supers for every super state that is interested in
* the results from this query.
*
* @param qstate: query state.
* @param id: module id.
* @param forq: super query state.
*/
static void
processTargetResponse(struct module_qstate* qstate, int id,
struct module_qstate* forq)
{
struct iter_env* ie = (struct iter_env*)qstate->env->modinfo[id];
struct iter_qstate* iq = (struct iter_qstate*)qstate->minfo[id];
struct iter_qstate* foriq = (struct iter_qstate*)forq->minfo[id];
struct ub_packed_rrset_key* rrset;
struct delegpt_ns* dpns;
log_assert(qstate->return_rcode == LDNS_RCODE_NOERROR);
foriq->state = QUERYTARGETS_STATE;
log_query_info(VERB_ALGO, "processTargetResponse", &qstate->qinfo);
log_query_info(VERB_ALGO, "processTargetResponse super", &forq->qinfo);
/* Tell the originating event that this target query has finished
* (regardless if it succeeded or not). */
foriq->num_target_queries--;
/* check to see if parent event is still interested (in orig name). */
if(!foriq->dp) {
verbose(VERB_ALGO, "subq: parent not interested, was reset");
return; /* not interested anymore */
}
dpns = delegpt_find_ns(foriq->dp, qstate->qinfo.qname,
qstate->qinfo.qname_len);
if(!dpns) {
/* If not interested, just stop processing this event */
verbose(VERB_ALGO, "subq: parent not interested anymore");
/* could be because parent was jostled out of the cache,
and a new identical query arrived, that does not want it*/
return;
}
/* if iq->query_for_pside_glue then add the pside_glue (marked lame) */
if(iq->pside_glue) {
/* if the pside_glue is NULL, then it could not be found,
* the done_pside is already set when created and a cache
* entry created in processFinished so nothing to do here */
log_rrset_key(VERB_ALGO, "add parentside glue to dp",
iq->pside_glue);
if(!delegpt_add_rrset(foriq->dp, forq->region,
iq->pside_glue, 1, NULL))
log_err("out of memory adding pside glue");
}
/* This response is relevant to the current query, so we
* add (attempt to add, anyway) this target(s) and reactivate
* the original event.
* NOTE: we could only look for the AnswerRRset if the
* response type was ANSWER. */
rrset = reply_find_answer_rrset(&iq->qchase, qstate->return_msg->rep);
if(rrset) {
int additions = 0;
/* if CNAMEs have been followed - add new NS to delegpt. */
/* BTW. RFC 1918 says NS should not have got CNAMEs. Robust. */
if(!delegpt_find_ns(foriq->dp, rrset->rk.dname,
rrset->rk.dname_len)) {
/* if dpns->lame then set newcname ns lame too */
if(!delegpt_add_ns(foriq->dp, forq->region,
rrset->rk.dname, dpns->lame, dpns->tls_auth_name,
dpns->port))
log_err("out of memory adding cnamed-ns");
}
/* if dpns->lame then set the address(es) lame too */
if(!delegpt_add_rrset(foriq->dp, forq->region, rrset,
dpns->lame, &additions))
log_err("out of memory adding targets");
if(!additions) {
/* no new addresses, increase the nxns counter, like
* this could be a list of wildcards with no new
* addresses */
target_count_increase_nx(foriq, 1);
}
verbose(VERB_ALGO, "added target response");
delegpt_log(VERB_ALGO, foriq->dp);
} else {
verbose(VERB_ALGO, "iterator TargetResponse failed");
delegpt_mark_neg(dpns, qstate->qinfo.qtype);
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 */
if(qstate->reply_origin && qstate->reply_origin->len != 0) {
target_count_increase_nx(foriq, 1);
}
}
}
}
/**
* Process response for DS NS Find queries, that attempt to find the delegation
* point where we ask the DS query from.
*
* @param qstate: query state.
* @param id: module id.
* @param forq: super query state.
*/
static void
processDSNSResponse(struct module_qstate* qstate, int id,
struct module_qstate* forq)
{
struct iter_qstate* foriq = (struct iter_qstate*)forq->minfo[id];
/* if the finished (iq->response) query has no NS set: continue
* up to look for the right dp; nothing to change, do DPNSstate */
if(qstate->return_rcode != LDNS_RCODE_NOERROR)
return; /* seek further */
/* find the NS RRset (without allowing CNAMEs) */
if(!reply_find_rrset(qstate->return_msg->rep, qstate->qinfo.qname,
qstate->qinfo.qname_len, LDNS_RR_TYPE_NS,
qstate->qinfo.qclass)){
return; /* seek further */
}
/* else, store as DP and continue at querytargets */
foriq->state = QUERYTARGETS_STATE;
foriq->dp = delegpt_from_message(qstate->return_msg, forq->region);
if(!foriq->dp) {
log_err("out of memory in dsns dp alloc");
errinf(qstate, "malloc failure, in DS search");
return; /* dp==NULL in QUERYTARGETS makes SERVFAIL */
}
/* success, go query the querytargets in the new dp (and go down) */
}
/**
* Process response for qclass=ANY queries for a particular class.
* Append to result or error-exit.
*
* @param qstate: query state.
* @param id: module id.
* @param forq: super query state.
*/
static void
processClassResponse(struct module_qstate* qstate, int id,
struct module_qstate* forq)
{
struct iter_qstate* foriq = (struct iter_qstate*)forq->minfo[id];
struct dns_msg* from = qstate->return_msg;
log_query_info(VERB_ALGO, "processClassResponse", &qstate->qinfo);
log_query_info(VERB_ALGO, "processClassResponse super", &forq->qinfo);
if(qstate->return_rcode != LDNS_RCODE_NOERROR) {
/* cause servfail for qclass ANY query */
foriq->response = NULL;
foriq->state = FINISHED_STATE;
return;
}
/* append result */
if(!foriq->response) {
/* allocate the response: copy RCODE, sec_state */
foriq->response = dns_copy_msg(from, forq->region);
if(!foriq->response) {
log_err("malloc failed for qclass ANY response");
foriq->state = FINISHED_STATE;
return;
}
foriq->response->qinfo.qclass = forq->qinfo.qclass;
/* qclass ANY does not receive the AA flag on replies */
foriq->response->rep->authoritative = 0;
} else {
struct dns_msg* to = foriq->response;
/* add _from_ this response _to_ existing collection */
/* if there are records, copy RCODE */
/* lower sec_state if this message is lower */
if(from->rep->rrset_count != 0) {
size_t n = from->rep->rrset_count+to->rep->rrset_count;
struct ub_packed_rrset_key** dest, **d;
/* copy appropriate rcode */
to->rep->flags = from->rep->flags;
/* copy rrsets */
if(from->rep->rrset_count > RR_COUNT_MAX ||
to->rep->rrset_count > RR_COUNT_MAX) {
log_err("malloc failed (too many rrsets) in collect ANY");
foriq->state = FINISHED_STATE;
return; /* integer overflow protection */
}
dest = regional_alloc(forq->region, sizeof(dest[0])*n);
if(!dest) {
log_err("malloc failed in collect ANY");
foriq->state = FINISHED_STATE;
return;
}
d = dest;
/* copy AN */
memcpy(dest, to->rep->rrsets, to->rep->an_numrrsets
* sizeof(dest[0]));
dest += to->rep->an_numrrsets;
memcpy(dest, from->rep->rrsets, from->rep->an_numrrsets
* sizeof(dest[0]));
dest += from->rep->an_numrrsets;
/* copy NS */
memcpy(dest, to->rep->rrsets+to->rep->an_numrrsets,
to->rep->ns_numrrsets * sizeof(dest[0]));
dest += to->rep->ns_numrrsets;
memcpy(dest, from->rep->rrsets+from->rep->an_numrrsets,
from->rep->ns_numrrsets * sizeof(dest[0]));
dest += from->rep->ns_numrrsets;
/* copy AR */
memcpy(dest, to->rep->rrsets+to->rep->an_numrrsets+
to->rep->ns_numrrsets,
to->rep->ar_numrrsets * sizeof(dest[0]));
dest += to->rep->ar_numrrsets;
memcpy(dest, from->rep->rrsets+from->rep->an_numrrsets+
from->rep->ns_numrrsets,
from->rep->ar_numrrsets * sizeof(dest[0]));
/* update counts */
to->rep->rrsets = d;
to->rep->an_numrrsets += from->rep->an_numrrsets;
to->rep->ns_numrrsets += from->rep->ns_numrrsets;
to->rep->ar_numrrsets += from->rep->ar_numrrsets;
to->rep->rrset_count = n;
}
if(from->rep->security < to->rep->security) /* lowest sec */
to->rep->security = from->rep->security;
if(from->rep->qdcount != 0) /* insert qd if appropriate */
to->rep->qdcount = from->rep->qdcount;
if(from->rep->ttl < to->rep->ttl) /* use smallest TTL */
to->rep->ttl = from->rep->ttl;
if(from->rep->prefetch_ttl < to->rep->prefetch_ttl)
to->rep->prefetch_ttl = from->rep->prefetch_ttl;
if(from->rep->serve_expired_ttl < to->rep->serve_expired_ttl)
to->rep->serve_expired_ttl = from->rep->serve_expired_ttl;
}
/* are we done? */
foriq->num_current_queries --;
if(foriq->num_current_queries == 0)
foriq->state = FINISHED_STATE;
}
/**
* Collect class ANY responses and make them into one response. This
* state is started and it creates queries for all classes (that have
* root hints). The answers are then collected.
*
* @param qstate: query state.
* @param id: module id.
* @return true if the event needs more immediate processing, false if not.
*/
static int
processCollectClass(struct module_qstate* qstate, int id)
{
struct iter_qstate* iq = (struct iter_qstate*)qstate->minfo[id];
struct module_qstate* subq;
/* If qchase.qclass == 0 then send out queries for all classes.
* Otherwise, do nothing (wait for all answers to arrive and the
* processClassResponse to put them together, and that moves us
* towards the Finished state when done. */
if(iq->qchase.qclass == 0) {
uint16_t c = 0;
iq->qchase.qclass = LDNS_RR_CLASS_ANY;
while(iter_get_next_root(qstate->env->hints,
qstate->env->fwds, &c)) {
/* generate query for this class */
log_nametypeclass(VERB_ALGO, "spawn collect query",
qstate->qinfo.qname, qstate->qinfo.qtype, c);
if(!generate_sub_request(qstate->qinfo.qname,
qstate->qinfo.qname_len, qstate->qinfo.qtype,
c, qstate, id, iq, INIT_REQUEST_STATE,
FINISHED_STATE, &subq,
(int)!(qstate->query_flags&BIT_CD), 0)) {
errinf(qstate, "could not generate class ANY"
" lookup query");
return error_response(qstate, id,
LDNS_RCODE_SERVFAIL);
}
/* ignore subq, no special init required */
iq->num_current_queries ++;
if(c == 0xffff)
break;
else c++;
}
/* if no roots are configured at all, return */
if(iq->num_current_queries == 0) {
verbose(VERB_ALGO, "No root hints or fwds, giving up "
"on qclass ANY");
return error_response(qstate, id, LDNS_RCODE_REFUSED);
}
/* return false, wait for queries to return */
}
/* if woke up here because of an answer, wait for more answers */
return 0;
}
/**
* This handles the final state for first-tier responses (i.e., responses to
* externally generated queries).
*
* @param qstate: query state.
* @param iq: iterator query state.
* @param id: module id.
* @return true if the event needs more processing, false if not. Since this
* is the final state for an event, it always returns false.
*/
static int
processFinished(struct module_qstate* qstate, struct iter_qstate* iq,
int id)
{
log_query_info(VERB_QUERY, "finishing processing for",
&qstate->qinfo);
/* store negative cache element for parent side glue. */
if(!qstate->no_cache_store && iq->query_for_pside_glue
&& !iq->pside_glue)
iter_store_parentside_neg(qstate->env, &qstate->qinfo,
iq->deleg_msg?iq->deleg_msg->rep:
(iq->response?iq->response->rep:NULL));
if(!iq->response) {
verbose(VERB_ALGO, "No response is set, servfail");
errinf(qstate, "(no response found at query finish)");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
/* Make sure that the RA flag is set (since the presence of
* this module means that recursion is available) */
iq->response->rep->flags |= BIT_RA;
/* Clear the AA flag */
/* FIXME: does this action go here or in some other module? */
iq->response->rep->flags &= ~BIT_AA;
/* 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;
+ if((qstate->env->cfg->val_log_level >= 2 ||
+ qstate->env->cfg->log_servfail) && qstate->errinf &&
+ !qstate->env->cfg->val_log_squelch) {
+ char* err_str = errinf_to_str_misc(qstate);
+ if(err_str) {
+ size_t err_str_len = strlen(err_str);
+ verbose(VERB_ALGO, "iterator EDE: %s", err_str);
+ /* allocate space and store the error
+ * string */
+ iq->response->rep->reason_bogus_str = regional_alloc(
+ qstate->region,
+ sizeof(char) * (err_str_len+1));
+ memcpy(iq->response->rep->reason_bogus_str,
+ err_str, err_str_len+1);
+ }
+ free(err_str);
+ }
/* we have finished processing this query */
qstate->ext_state[id] = module_finished;
/* TODO: we are using a private TTL, trim the response. */
/* if (mPrivateTTL > 0){IterUtils.setPrivateTTL(resp, mPrivateTTL); } */
/* prepend any items we have accumulated */
if(iq->an_prepend_list || iq->ns_prepend_list) {
if(!iter_prepend(iq, iq->response, qstate->region)) {
log_err("prepend rrsets: out of memory");
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
/* reset the query name back */
iq->response->qinfo = qstate->qinfo;
/* the security state depends on the combination */
iq->response->rep->security = sec_status_unchecked;
/* store message with the finished prepended items,
* but only if we did recursion. The nonrecursion referral
* from cache does not need to be stored in the msg cache. */
if(!qstate->no_cache_store && qstate->query_flags&BIT_RD) {
iter_dns_store(qstate->env, &qstate->qinfo,
iq->response->rep, 0, qstate->prefetch_leeway,
iq->dp&&iq->dp->has_parent_side_NS,
qstate->region, qstate->query_flags,
qstate->qstarttime);
}
}
qstate->return_rcode = LDNS_RCODE_NOERROR;
qstate->return_msg = iq->response;
return 0;
}
/*
* Return priming query results to interested super querystates.
*
* Sets the delegation point and delegation message (not nonRD queries).
* This is a callback from walk_supers.
*
* @param qstate: query state that finished.
* @param id: module id.
* @param super: the qstate to inform.
*/
void
iter_inform_super(struct module_qstate* qstate, int id,
struct module_qstate* super)
{
if(!qstate->is_priming && super->qinfo.qclass == LDNS_RR_CLASS_ANY)
processClassResponse(qstate, id, super);
else if(super->qinfo.qtype == LDNS_RR_TYPE_DS && ((struct iter_qstate*)
super->minfo[id])->state == DSNS_FIND_STATE)
processDSNSResponse(qstate, id, super);
else if(qstate->return_rcode != LDNS_RCODE_NOERROR)
error_supers(qstate, id, super);
else if(qstate->is_priming)
prime_supers(qstate, id, super);
else processTargetResponse(qstate, id, super);
}
/**
* Handle iterator state.
* Handle events. This is the real processing loop for events, responsible
* for moving events through the various states. If a processing method
* returns true, then it will be advanced to the next state. If false, then
* processing will stop.
*
* @param qstate: query state.
* @param ie: iterator shared global environment.
* @param iq: iterator query state.
* @param id: module id.
*/
static void
iter_handle(struct module_qstate* qstate, struct iter_qstate* iq,
struct iter_env* ie, int id)
{
int cont = 1;
while(cont) {
verbose(VERB_ALGO, "iter_handle processing q with state %s",
iter_state_to_string(iq->state));
switch(iq->state) {
case INIT_REQUEST_STATE:
cont = processInitRequest(qstate, iq, ie, id);
break;
case INIT_REQUEST_2_STATE:
cont = processInitRequest2(qstate, iq, id);
break;
case INIT_REQUEST_3_STATE:
cont = processInitRequest3(qstate, iq, id);
break;
case QUERYTARGETS_STATE:
cont = processQueryTargets(qstate, iq, ie, id);
break;
case QUERY_RESP_STATE:
cont = processQueryResponse(qstate, iq, ie, id);
break;
case PRIME_RESP_STATE:
cont = processPrimeResponse(qstate, id);
break;
case COLLECT_CLASS_STATE:
cont = processCollectClass(qstate, id);
break;
case DSNS_FIND_STATE:
cont = processDSNSFind(qstate, iq, id);
break;
case FINISHED_STATE:
cont = processFinished(qstate, iq, id);
break;
default:
log_warn("iterator: invalid state: %d",
iq->state);
cont = 0;
break;
}
}
}
/**
* This is the primary entry point for processing request events. Note that
* this method should only be used by external modules.
* @param qstate: query state.
* @param ie: iterator shared global environment.
* @param iq: iterator query state.
* @param id: module id.
*/
static void
process_request(struct module_qstate* qstate, struct iter_qstate* iq,
struct iter_env* ie, int id)
{
/* external requests start in the INIT state, and finish using the
* FINISHED state. */
iq->state = INIT_REQUEST_STATE;
iq->final_state = FINISHED_STATE;
verbose(VERB_ALGO, "process_request: new external request event");
iter_handle(qstate, iq, ie, id);
}
/** process authoritative server reply */
static void
process_response(struct module_qstate* qstate, struct iter_qstate* iq,
struct iter_env* ie, int id, struct outbound_entry* outbound,
enum module_ev event)
{
struct msg_parse* prs;
struct edns_data edns;
sldns_buffer* pkt;
verbose(VERB_ALGO, "process_response: new external response event");
iq->response = NULL;
iq->state = QUERY_RESP_STATE;
if(event == module_event_noreply || event == module_event_error) {
if(event == module_event_noreply && iq->timeout_count >= 3 &&
qstate->env->cfg->use_caps_bits_for_id &&
!iq->caps_fallback && !is_caps_whitelisted(ie, iq)) {
/* start fallback */
iq->caps_fallback = 1;
iq->caps_server = 0;
iq->caps_reply = NULL;
iq->caps_response = NULL;
iq->caps_minimisation_state = DONOT_MINIMISE_STATE;
iq->state = QUERYTARGETS_STATE;
iq->num_current_queries--;
/* need fresh attempts for the 0x20 fallback, if
* that was the cause for the failure */
iter_dec_attempts(iq->dp, 3, ie->outbound_msg_retry);
verbose(VERB_DETAIL, "Capsforid: timeouts, starting fallback");
goto handle_it;
}
goto handle_it;
}
if( (event != module_event_reply && event != module_event_capsfail)
|| !qstate->reply) {
log_err("Bad event combined with response");
outbound_list_remove(&iq->outlist, outbound);
errinf(qstate, "module iterator received wrong internal event with a response message");
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
return;
}
/* parse message */
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) {
log_err("out of memory on incoming message");
/* like packet got dropped */
goto handle_it;
}
memset(prs, 0, sizeof(*prs));
memset(&edns, 0, sizeof(edns));
pkt = qstate->reply->c->buffer;
sldns_buffer_set_position(pkt, 0);
if(parse_packet(pkt, prs, qstate->env->scratch) != LDNS_RCODE_NOERROR) {
verbose(VERB_ALGO, "parse error on reply packet");
iq->parse_failures++;
goto handle_it;
}
/* edns is not examined, but removed from message to help cache */
if(parse_extract_edns_from_response_msg(prs, &edns, qstate->env->scratch) !=
LDNS_RCODE_NOERROR) {
iq->parse_failures++;
goto handle_it;
}
/* Copy the edns options we may got from the back end */
if(edns.opt_list_in) {
qstate->edns_opts_back_in = edns_opt_copy_region(edns.opt_list_in,
qstate->region);
if(!qstate->edns_opts_back_in) {
log_err("out of memory on incoming message");
/* like packet got dropped */
goto handle_it;
}
if(!inplace_cb_edns_back_parsed_call(qstate->env, qstate)) {
log_err("unable to call edns_back_parsed callback");
goto handle_it;
}
}
/* remove CD-bit, we asked for in case we handle validation ourself */
prs->flags &= ~BIT_CD;
/* normalize and sanitize: easy to delete items from linked lists */
if(!scrub_message(pkt, prs, &iq->qinfo_out, iq->dp->name,
- qstate->env->scratch, qstate->env, ie)) {
+ qstate->env->scratch, qstate->env, qstate, ie)) {
/* if 0x20 enabled, start fallback, but we have no message */
if(event == module_event_capsfail && !iq->caps_fallback) {
iq->caps_fallback = 1;
iq->caps_server = 0;
iq->caps_reply = NULL;
iq->caps_response = NULL;
iq->caps_minimisation_state = DONOT_MINIMISE_STATE;
iq->state = QUERYTARGETS_STATE;
iq->num_current_queries--;
verbose(VERB_DETAIL, "Capsforid: scrub failed, starting fallback with no response");
}
iq->scrub_failures++;
goto handle_it;
}
/* allocate response dns_msg in region */
iq->response = dns_alloc_msg(pkt, prs, qstate->region);
if(!iq->response)
goto handle_it;
log_query_info(VERB_DETAIL, "response for", &qstate->qinfo);
log_name_addr(VERB_DETAIL, "reply from", iq->dp->name,
&qstate->reply->remote_addr, qstate->reply->remote_addrlen);
if(verbosity >= VERB_ALGO)
log_dns_msg("incoming scrubbed packet:", &iq->response->qinfo,
iq->response->rep);
if(event == module_event_capsfail || iq->caps_fallback) {
if(qstate->env->cfg->qname_minimisation &&
iq->minimisation_state != DONOT_MINIMISE_STATE) {
/* Skip QNAME minimisation for next query, since that
* one has to match the current query. */
iq->minimisation_state = SKIP_MINIMISE_STATE;
}
/* for fallback we care about main answer, not additionals */
/* removing that makes comparison more likely to succeed */
caps_strip_reply(iq->response->rep);
if(iq->caps_fallback &&
iq->caps_minimisation_state != iq->minimisation_state) {
/* QNAME minimisation state has changed, restart caps
* fallback. */
iq->caps_fallback = 0;
}
if(!iq->caps_fallback) {
/* start fallback */
iq->caps_fallback = 1;
iq->caps_server = 0;
iq->caps_reply = iq->response->rep;
iq->caps_response = iq->response;
iq->caps_minimisation_state = iq->minimisation_state;
iq->state = QUERYTARGETS_STATE;
iq->num_current_queries--;
verbose(VERB_DETAIL, "Capsforid: starting fallback");
goto handle_it;
} else {
/* check if reply is the same, otherwise, fail */
if(!iq->caps_reply) {
iq->caps_reply = iq->response->rep;
iq->caps_response = iq->response;
iq->caps_server = -1; /*become zero at ++,
so that we start the full set of trials */
} else if(caps_failed_rcode(iq->caps_reply) &&
!caps_failed_rcode(iq->response->rep)) {
/* prefer to upgrade to non-SERVFAIL */
iq->caps_reply = iq->response->rep;
iq->caps_response = iq->response;
} else if(!caps_failed_rcode(iq->caps_reply) &&
caps_failed_rcode(iq->response->rep)) {
/* if we have non-SERVFAIL as answer then
* we can ignore SERVFAILs for the equality
* comparison */
/* no instructions here, skip other else */
} else if(caps_failed_rcode(iq->caps_reply) &&
caps_failed_rcode(iq->response->rep)) {
/* failure is same as other failure in fallbk*/
/* no instructions here, skip other else */
} else if(!reply_equal(iq->response->rep, iq->caps_reply,
qstate->env->scratch)) {
verbose(VERB_DETAIL, "Capsforid fallback: "
"getting different replies, failed");
outbound_list_remove(&iq->outlist, outbound);
errinf(qstate, "0x20 failed, then got different replies in fallback");
(void)error_response(qstate, id,
LDNS_RCODE_SERVFAIL);
return;
}
/* continue the fallback procedure at next server */
iq->caps_server++;
iq->state = QUERYTARGETS_STATE;
iq->num_current_queries--;
verbose(VERB_DETAIL, "Capsforid: reply is equal. "
"go to next fallback");
goto handle_it;
}
}
iq->caps_fallback = 0; /* if we were in fallback, 0x20 is OK now */
handle_it:
outbound_list_remove(&iq->outlist, outbound);
iter_handle(qstate, iq, ie, id);
}
void
iter_operate(struct module_qstate* qstate, enum module_ev event, int id,
struct outbound_entry* outbound)
{
struct iter_env* ie = (struct iter_env*)qstate->env->modinfo[id];
struct iter_qstate* iq = (struct iter_qstate*)qstate->minfo[id];
verbose(VERB_QUERY, "iterator[module %d] operate: extstate:%s event:%s",
id, strextstate(qstate->ext_state[id]), strmodulevent(event));
if(iq) log_query_info(VERB_QUERY, "iterator operate: query",
&qstate->qinfo);
if(iq && qstate->qinfo.qname != iq->qchase.qname)
log_query_info(VERB_QUERY, "iterator operate: chased to",
&iq->qchase);
/* perform iterator state machine */
if((event == module_event_new || event == module_event_pass) &&
iq == NULL) {
if(!iter_new(qstate, id)) {
errinf(qstate, "malloc failure, new iterator module allocation");
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
return;
}
iq = (struct iter_qstate*)qstate->minfo[id];
process_request(qstate, iq, ie, id);
return;
}
if(iq && event == module_event_pass) {
iter_handle(qstate, iq, ie, id);
return;
}
if(iq && outbound) {
process_response(qstate, iq, ie, id, outbound, event);
return;
}
if(event == module_event_error) {
verbose(VERB_ALGO, "got called with event error, giving up");
errinf(qstate, "iterator module got the error event");
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
return;
}
log_err("bad event for iterator");
errinf(qstate, "iterator module received wrong event");
(void)error_response(qstate, id, LDNS_RCODE_SERVFAIL);
}
void
iter_clear(struct module_qstate* qstate, int id)
{
struct iter_qstate* iq;
if(!qstate)
return;
iq = (struct iter_qstate*)qstate->minfo[id];
if(iq) {
outbound_list_clear(&iq->outlist);
if(iq->target_count && --iq->target_count[TARGET_COUNT_REF] == 0) {
free(iq->target_count);
if(*iq->nxns_dp) free(*iq->nxns_dp);
free(iq->nxns_dp);
}
iq->num_current_queries = 0;
}
qstate->minfo[id] = NULL;
}
size_t
iter_get_mem(struct module_env* env, int id)
{
struct iter_env* ie = (struct iter_env*)env->modinfo[id];
if(!ie)
return 0;
return sizeof(*ie) + sizeof(int)*((size_t)ie->max_dependency_depth+1)
+ donotq_get_mem(ie->donotq) + priv_get_mem(ie->priv);
}
/**
* The iterator function block
*/
static struct module_func_block iter_block = {
"iterator",
&iter_init, &iter_deinit, &iter_operate, &iter_inform_super,
&iter_clear, &iter_get_mem
};
struct module_func_block*
iter_get_funcblock(void)
{
return &iter_block;
}
const char*
iter_state_to_string(enum iter_state state)
{
switch (state)
{
case INIT_REQUEST_STATE :
return "INIT REQUEST STATE";
case INIT_REQUEST_2_STATE :
return "INIT REQUEST STATE (stage 2)";
case INIT_REQUEST_3_STATE:
return "INIT REQUEST STATE (stage 3)";
case QUERYTARGETS_STATE :
return "QUERY TARGETS STATE";
case PRIME_RESP_STATE :
return "PRIME RESPONSE STATE";
case COLLECT_CLASS_STATE :
return "COLLECT CLASS STATE";
case DSNS_FIND_STATE :
return "DSNS FIND STATE";
case QUERY_RESP_STATE :
return "QUERY RESPONSE STATE";
case FINISHED_STATE :
return "FINISHED RESPONSE STATE";
default :
return "UNKNOWN ITER STATE";
}
}
int
iter_state_is_responsestate(enum iter_state s)
{
switch(s) {
case INIT_REQUEST_STATE :
case INIT_REQUEST_2_STATE :
case INIT_REQUEST_3_STATE :
case QUERYTARGETS_STATE :
case COLLECT_CLASS_STATE :
return 0;
default:
break;
}
return 1;
}
diff --git a/contrib/unbound/iterator/iterator.h b/contrib/unbound/iterator/iterator.h
index fad7f03e63de..e253f3f7e2bd 100644
--- a/contrib/unbound/iterator/iterator.h
+++ b/contrib/unbound/iterator/iterator.h
@@ -1,523 +1,530 @@
/*
* iterator/iterator.h - iterative resolver DNS query response module
*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file contains a module that performs recursive iterative DNS query
* processing.
*/
#ifndef ITERATOR_ITERATOR_H
#define ITERATOR_ITERATOR_H
#include "services/outbound_list.h"
#include "util/data/msgreply.h"
#include "util/module.h"
struct delegpt;
struct iter_hints;
struct iter_forwards;
struct iter_donotq;
struct iter_prep_list;
struct iter_priv;
struct rbtree_type;
/** max number of targets spawned for a query and its subqueries */
#define MAX_TARGET_COUNT 64
/** max number of target lookups per qstate, per delegation point */
#define MAX_DP_TARGET_COUNT 16
/** max number of nxdomains allowed for target lookups for a query and
* its subqueries */
#define MAX_TARGET_NX 5
/** max number of nxdomains allowed for target lookups for a query and
* its subqueries when fallback has kicked in */
#define MAX_TARGET_NX_FALLBACK (MAX_TARGET_NX*2)
/** max number of referrals. Makes sure resolver does not run away */
#define MAX_REFERRAL_COUNT 130
/** 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
/**
* max number of QNAME minimisation iterations. Limits number of queries for
* QNAMEs with a lot of labels.
*/
#define MAX_MINIMISE_COUNT 10
/* max number of time-outs for minimised query. Prevents resolving failures
* when the QNAME minimisation QTYPE is blocked. */
#define MAX_MINIMISE_TIMEOUT_COUNT 3
/**
* number of labels from QNAME that are always send individually when using
* QNAME minimisation, even when the number of labels of the QNAME is bigger
* than MAX_MINIMISE_COUNT */
#define MINIMISE_ONE_LAB 4
#define MINIMISE_MULTIPLE_LABS (MAX_MINIMISE_COUNT - MINIMISE_ONE_LAB)
/** at what query-sent-count to stop target fetch policy */
#define TARGET_FETCH_STOP 3
/** how nice is a server without further information, in msec
* Equals rtt initial timeout value.
*/
extern int UNKNOWN_SERVER_NICENESS;
/** maximum timeout before a host is deemed unsuitable, in msec.
* After host_ttl this will be timed out and the host will be tried again.
* Equals RTT_MAX_TIMEOUT, and thus when RTT_MAX_TIMEOUT is overwritten by
* config infra_cache_max_rtt, it will be overwritten as well. */
extern int USEFUL_SERVER_TOP_TIMEOUT;
/** penalty to validation failed blacklisted IPs
* Equals USEFUL_SERVER_TOP_TIMEOUT*4, and thus when RTT_MAX_TIMEOUT is
* overwritten by config infra_cache_max_rtt, it will be overwritten as well. */
extern int BLACKLIST_PENALTY;
/** RTT band, within this amount from the best, servers are chosen randomly.
* Chosen so that the UNKNOWN_SERVER_NICENESS falls within the band of a
* fast server, this causes server exploration as a side benefit. msec. */
#define RTT_BAND 400
+/** Number of retries for empty nodata packets before it is accepted. */
+#define EMPTY_NODATA_RETRY_COUNT 2
/**
* Global state for the iterator.
*/
struct iter_env {
/** A flag to indicate whether or not we have an IPv6 route */
int supports_ipv6;
/** 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;
/** private address space and private domains */
struct iter_priv* priv;
/** whitelist for capsforid names */
struct rbtree_type* caps_white;
/** The maximum dependency depth that this resolver will pursue. */
int max_dependency_depth;
/**
* The target fetch policy for each dependency level. This is
* described as a simple number (per dependency level):
* negative numbers (usually just -1) mean fetch-all,
* 0 means only fetch on demand, and
* positive numbers mean to fetch at most that many targets.
* array of max_dependency_depth+1 size.
*/
int* target_fetch_policy;
/** lock on ratelimit counter */
lock_basic_type queries_ratelimit_lock;
/** number of queries that have been ratelimited */
size_t num_queries_ratelimited;
/** 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;
};
/**
* QNAME minimisation state
*/
enum minimisation_state {
/**
* (Re)start minimisation. Outgoing QNAME should be set to dp->name.
* State entered on new query or after following referral or CNAME.
*/
INIT_MINIMISE_STATE = 0,
/**
* QNAME minimisation ongoing. Increase QNAME on every iteration.
*/
MINIMISE_STATE,
/**
* Don't increment QNAME this iteration
*/
SKIP_MINIMISE_STATE,
/**
* Send out full QNAME + original QTYPE
*/
DONOT_MINIMISE_STATE,
};
/**
* State of the iterator for a query.
*/
enum iter_state {
/**
* Externally generated queries start at this state. Query restarts are
* reset to this state.
*/
INIT_REQUEST_STATE = 0,
/**
* Root priming events reactivate here, most other events pass
* through this naturally as the 2nd part of the INIT_REQUEST_STATE.
*/
INIT_REQUEST_2_STATE,
/**
* Stub priming events reactivate here, most other events pass
* through this naturally as the 3rd part of the INIT_REQUEST_STATE.
*/
INIT_REQUEST_3_STATE,
/**
* Each time a delegation point changes for a given query or a
* query times out and/or wakes up, this state is (re)visited.
* This state is responsible for iterating through a list of
* nameserver targets.
*/
QUERYTARGETS_STATE,
/**
* Responses to queries start at this state. This state handles
* the decision tree associated with handling responses.
*/
QUERY_RESP_STATE,
/** Responses to priming queries finish at this state. */
PRIME_RESP_STATE,
/** Collecting query class information, for qclass=ANY, when
* it spawns off queries for every class, it returns here. */
COLLECT_CLASS_STATE,
/** Find NS record to resolve DS record from, walking to the right
* NS spot until we find it */
DSNS_FIND_STATE,
/** Responses that are to be returned upstream end at this state.
* As well as responses to target queries. */
FINISHED_STATE
};
/**
* Shared counters for queries.
*/
enum target_count_variables {
/** Reference count for the shared iter_qstate->target_count. */
TARGET_COUNT_REF = 0,
/** Number of target queries spawned for the query and subqueries. */
TARGET_COUNT_QUERIES,
/** Number of nxdomain responses encountered. */
TARGET_COUNT_NX,
/** This should stay last here, it is used for the allocation */
TARGET_COUNT_MAX,
};
/**
* Per query state for the iterator module.
*/
struct iter_qstate {
/**
* State of the iterator module.
* This is the state that event is in or should sent to -- all
* requests should start with the INIT_REQUEST_STATE. All
* responses should start with QUERY_RESP_STATE. Subsequent
* processing of the event will change this state.
*/
enum iter_state state;
/**
* Final state for the iterator module.
* This is the state that responses should be routed to once the
* response is final. For externally initiated queries, this
* will be FINISHED_STATE, locally initiated queries will have
* different final states.
*/
enum iter_state final_state;
/**
* The depth of this query, this means the depth of recursion.
* This address is needed for another query, which is an address
* needed for another query, etc. Original client query has depth 0.
*/
int depth;
/**
* The response
*/
struct dns_msg* response;
/**
* This is a list of RRsets that must be prepended to the
* ANSWER section of a response before being sent upstream.
*/
struct iter_prep_list* an_prepend_list;
/** Last element of the prepend list */
struct iter_prep_list* an_prepend_last;
/**
* This is the list of RRsets that must be prepended to the
* AUTHORITY section of the response before being sent upstream.
*/
struct iter_prep_list* ns_prepend_list;
/** Last element of the authority prepend list */
struct iter_prep_list* ns_prepend_last;
/** query name used for chasing the results. Initially the same as
* the state qinfo, but after CNAMEs this will be different.
* The query info used to elicit the results needed. */
struct query_info qchase;
/** query flags to use when chasing the answer (i.e. RD flag) */
uint16_t chase_flags;
/** true if we set RD bit because of last resort recursion lame query*/
int chase_to_rd;
/**
* This is the current delegation point for an in-progress query. This
* object retains state as to which delegation targets need to be
* (sub)queried for vs which ones have already been visited.
*/
struct delegpt* dp;
/** state for 0x20 fallback when capsfail happens, 0 not a fallback */
int caps_fallback;
/** state for capsfail: current server number to try */
size_t caps_server;
/** state for capsfail: stored query for comparisons. Can be NULL if
* no response had been seen prior to starting the fallback. */
struct reply_info* caps_reply;
struct dns_msg* caps_response;
/** Current delegation message - returned for non-RD queries */
struct dns_msg* deleg_msg;
/** number of outstanding target sub queries */
int num_target_queries;
/** outstanding direct queries */
int num_current_queries;
/** the number of times this query has been restarted. */
int query_restart_count;
/** the number of times this query has followed a referral. */
int referral_count;
/** number of queries fired off */
int sent_count;
/** malloced-array shared with this query and its subqueries. It keeps
* track of the defined enum target_count_variables counters. */
int* target_count;
/** number of target lookups per delegation point. Reset to 0 after
* receiving referral answer. Not shared with subqueries. */
int dp_target_count;
/** Delegation point that triggered the NXNS fallback; shared with
* this query and its subqueries, count-referenced by the reference
* counter in target_count.
* This also marks the fallback activation. */
uint8_t** nxns_dp;
/** if true, already tested for ratelimiting and passed the test */
int ratelimit_ok;
/**
* The query must store NS records from referrals as parentside RRs
* Enabled once it hits resolution problems, to throttle retries.
* If enabled it is the pointer to the old delegation point with
* the old retry counts for bad-nameserver-addresses.
*/
struct delegpt* store_parent_NS;
/**
* The query is for parent-side glue(A or AAAA) for a nameserver.
* If the item is seen as glue in a referral, and pside_glue is NULL,
* then it is stored in pside_glue for later.
* If it was never seen, at the end, then a negative caching element
* must be created.
* The (data or negative) RR cache element then throttles retries.
*/
int query_for_pside_glue;
/** the parent-side-glue element (NULL if none, its first match) */
struct ub_packed_rrset_key* pside_glue;
/** If nonNULL we are walking upwards from DS query to find NS */
uint8_t* dsns_point;
/** length of the dname in dsns_point */
size_t dsns_point_len;
/**
* expected dnssec information for this iteration step.
* If dnssec rrsigs are expected and not given, the server is marked
* lame (dnssec-lame).
*/
int dnssec_expected;
/**
* We are expecting dnssec information, but we also know the server
* is DNSSEC lame. The response need not be marked dnssec-lame again.
*/
int dnssec_lame_query;
/**
* This is flag that, if true, means that this event is
* waiting for a stub priming query.
*/
int wait_priming_stub;
/**
* This is a flag that, if true, means that this query is
* for (re)fetching glue from a zone. Since the address should
* have been glue, query again to the servers that should have
* been returning it as glue.
* The delegation point must be set to the one that should *not*
* be used when creating the state. A higher one will be attempted.
*/
int refetch_glue;
+ /**
+ * This flag detects that a completely empty nodata was received,
+ * already so that it is accepted later. */
+ int empty_nodata_found;
+
/** list of pending queries to authoritative servers. */
struct outbound_list outlist;
/** QNAME minimisation state, RFC9156 */
enum minimisation_state minimisation_state;
/** State for capsfail: QNAME minimisation state for comparisons. */
enum minimisation_state caps_minimisation_state;
/**
* The query info that is sent upstream. Will be a subset of qchase
* when qname minimisation is enabled.
*/
struct query_info qinfo_out;
/**
* Count number of QNAME minimisation iterations. Used to limit number of
* outgoing queries when QNAME minimisation is enabled.
*/
int minimise_count;
/**
* Count number of time-outs. Used to prevent resolving failures when
* the QNAME minimisation QTYPE is blocked. Used to determine if
* capsforid fallback should be started.*/
int timeout_count;
/** True if the current response is from auth_zone */
int auth_zone_response;
/** True if the auth_zones should not be consulted for the query */
int auth_zone_avoid;
/** true if there have been scrubbing failures of reply packets */
int scrub_failures;
/** true if there have been parse failures of reply packets */
int parse_failures;
/** a failure printout address for last received answer */
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;
};
/**
* List of prepend items
*/
struct iter_prep_list {
/** next in list */
struct iter_prep_list* next;
/** rrset */
struct ub_packed_rrset_key* rrset;
};
/**
* Get the iterator function block.
* @return: function block with function pointers to iterator methods.
*/
struct module_func_block* iter_get_funcblock(void);
/**
* Get iterator state as a string
* @param state: to convert
* @return constant string that is printable.
*/
const char* iter_state_to_string(enum iter_state state);
/**
* See if iterator state is a response state
* @param s: to inspect
* @return true if response state.
*/
int iter_state_is_responsestate(enum iter_state s);
/** iterator init */
int iter_init(struct module_env* env, int id);
/** iterator deinit */
void iter_deinit(struct module_env* env, int id);
/** iterator operate on a query */
void iter_operate(struct module_qstate* qstate, enum module_ev event, int id,
struct outbound_entry* outbound);
/**
* Return priming query results to interested super querystates.
*
* Sets the delegation point and delegation message (not nonRD queries).
* This is a callback from walk_supers.
*
* @param qstate: query state that finished.
* @param id: module id.
* @param super: the qstate to inform.
*/
void iter_inform_super(struct module_qstate* qstate, int id,
struct module_qstate* super);
/** iterator cleanup query state */
void iter_clear(struct module_qstate* qstate, int id);
/** iterator alloc size routine */
size_t iter_get_mem(struct module_env* env, int id);
#endif /* ITERATOR_ITERATOR_H */
diff --git a/contrib/unbound/libunbound/libworker.c b/contrib/unbound/libunbound/libworker.c
index 104244937bf0..0e1c40393763 100644
--- a/contrib/unbound/libunbound/libworker.c
+++ b/contrib/unbound/libunbound/libworker.c
@@ -1,1070 +1,1074 @@
/*
* libunbound/worker.c - worker thread or process that resolves
*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file contains the worker process or thread that performs
* the DNS resolving and validation. The worker is called by a procedure
* and if in the background continues until exit, if in the foreground
* returns from the procedure when done.
*/
#include "config.h"
#ifdef HAVE_SSL
#include <openssl/ssl.h>
#endif
#include "libunbound/libworker.h"
#include "libunbound/context.h"
#include "libunbound/unbound.h"
#include "libunbound/worker.h"
#include "libunbound/unbound-event.h"
#include "services/outside_network.h"
#include "services/mesh.h"
#include "services/localzone.h"
#include "services/cache/rrset.h"
#include "services/outbound_list.h"
#include "services/authzone.h"
#include "util/fptr_wlist.h"
#include "util/module.h"
#include "util/regional.h"
#include "util/random.h"
#include "util/config_file.h"
#include "util/netevent.h"
+#include "util/proxy_protocol.h"
#include "util/storage/lookup3.h"
#include "util/storage/slabhash.h"
#include "util/net_help.h"
#include "util/data/dname.h"
#include "util/data/msgreply.h"
#include "util/data/msgencode.h"
#include "util/tube.h"
#include "iterator/iter_fwd.h"
#include "iterator/iter_hints.h"
#include "sldns/sbuffer.h"
#include "sldns/str2wire.h"
#ifdef USE_DNSTAP
#include "dnstap/dtstream.h"
#endif
#ifdef HAVE_TARGETCONDITIONALS_H
#include <TargetConditionals.h>
#endif
#if (defined(TARGET_OS_TV) && TARGET_OS_TV) || (defined(TARGET_OS_WATCH) && TARGET_OS_WATCH)
#undef HAVE_FORK
#endif
/** handle new query command for bg worker */
static void handle_newq(struct libworker* w, uint8_t* buf, uint32_t len);
/** delete libworker env */
static void
libworker_delete_env(struct libworker* w)
{
if(w->env) {
outside_network_quit_prepare(w->back);
mesh_delete(w->env->mesh);
context_release_alloc(w->ctx, w->env->alloc,
!w->is_bg || w->is_bg_thread);
sldns_buffer_free(w->env->scratch_buffer);
regional_destroy(w->env->scratch);
forwards_delete(w->env->fwds);
hints_delete(w->env->hints);
ub_randfree(w->env->rnd);
free(w->env);
}
#ifdef HAVE_SSL
SSL_CTX_free(w->sslctx);
#endif
outside_network_delete(w->back);
}
/** delete libworker struct */
static void
libworker_delete(struct libworker* w)
{
if(!w) return;
libworker_delete_env(w);
comm_base_delete(w->base);
free(w);
}
void
libworker_delete_event(struct libworker* w)
{
if(!w) return;
libworker_delete_env(w);
comm_base_delete_no_base(w->base);
free(w);
}
/** setup fresh libworker struct */
static struct libworker*
libworker_setup(struct ub_ctx* ctx, int is_bg, struct ub_event_base* eb)
{
struct libworker* w = (struct libworker*)calloc(1, sizeof(*w));
struct config_file* cfg = ctx->env->cfg;
int* ports;
int numports;
if(!w) return NULL;
w->is_bg = is_bg;
w->ctx = ctx;
w->env = (struct module_env*)malloc(sizeof(*w->env));
if(!w->env) {
free(w);
return NULL;
}
*w->env = *ctx->env;
w->env->alloc = context_obtain_alloc(ctx, !w->is_bg || w->is_bg_thread);
if(!w->env->alloc) {
libworker_delete(w);
return NULL;
}
w->thread_num = w->env->alloc->thread_num;
alloc_set_id_cleanup(w->env->alloc, &libworker_alloc_cleanup, w);
if(!w->is_bg || w->is_bg_thread) {
lock_basic_lock(&ctx->cfglock);
}
w->env->scratch = regional_create_custom(cfg->msg_buffer_size);
w->env->scratch_buffer = sldns_buffer_new(cfg->msg_buffer_size);
w->env->fwds = forwards_create();
if(w->env->fwds && !forwards_apply_cfg(w->env->fwds, cfg)) {
forwards_delete(w->env->fwds);
w->env->fwds = NULL;
}
w->env->hints = hints_create();
if(w->env->hints && !hints_apply_cfg(w->env->hints, cfg)) {
hints_delete(w->env->hints);
w->env->hints = NULL;
}
+#ifdef HAVE_SSL
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;
}
+#endif
if(!w->is_bg || w->is_bg_thread) {
lock_basic_unlock(&ctx->cfglock);
}
if(!w->env->scratch || !w->env->scratch_buffer || !w->env->fwds ||
!w->env->hints) {
libworker_delete(w);
return NULL;
}
w->env->worker = (struct worker*)w;
w->env->probe_timer = NULL;
if(!w->is_bg || w->is_bg_thread) {
lock_basic_lock(&ctx->cfglock);
}
if(!(w->env->rnd = ub_initstate(ctx->seed_rnd))) {
if(!w->is_bg || w->is_bg_thread) {
lock_basic_unlock(&ctx->cfglock);
}
libworker_delete(w);
return NULL;
}
if(!w->is_bg || w->is_bg_thread) {
lock_basic_unlock(&ctx->cfglock);
}
if(1) {
/* primitive lockout for threading: if it overwrites another
* thread it is like wiping the cache (which is likely empty
* at the start) */
/* note we are holding the ctx lock in normal threaded
* cases so that is solved properly, it is only for many ctx
* in different threads that this may clash */
static int done_raninit = 0;
if(!done_raninit) {
done_raninit = 1;
hash_set_raninit((uint32_t)ub_random(w->env->rnd));
}
}
if(eb)
w->base = comm_base_create_event(eb);
else w->base = comm_base_create(0);
if(!w->base) {
libworker_delete(w);
return NULL;
}
w->env->worker_base = w->base;
if(!w->is_bg || w->is_bg_thread) {
lock_basic_lock(&ctx->cfglock);
}
numports = cfg_condense_ports(cfg, &ports);
if(numports == 0) {
if(!w->is_bg || w->is_bg_thread) {
lock_basic_unlock(&ctx->cfglock);
}
libworker_delete(w);
return NULL;
}
w->back = outside_network_create(w->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->do_tcp?cfg->outgoing_num_tcp:0, cfg->ip_dscp,
w->env->infra_cache, w->env->rnd, cfg->use_caps_bits_for_id,
ports, numports, cfg->unwanted_threshold,
cfg->outgoing_tcp_mss, &libworker_alloc_cleanup, w,
cfg->do_udp || cfg->udp_upstream_without_downstream, w->sslctx,
cfg->delay_close, cfg->tls_use_sni, NULL, cfg->udp_connect,
cfg->max_reuse_tcp_queries, cfg->tcp_reuse_timeout,
cfg->tcp_auth_query_timeout);
w->env->outnet = w->back;
if(!w->is_bg || w->is_bg_thread) {
lock_basic_unlock(&ctx->cfglock);
}
free(ports);
if(!w->back) {
libworker_delete(w);
return NULL;
}
w->env->mesh = mesh_create(&ctx->mods, w->env);
if(!w->env->mesh) {
libworker_delete(w);
return NULL;
}
w->env->send_query = &libworker_send_query;
w->env->detach_subs = &mesh_detach_subs;
w->env->attach_sub = &mesh_attach_sub;
w->env->add_sub = &mesh_add_sub;
w->env->kill_sub = &mesh_state_delete;
w->env->detect_cycle = &mesh_detect_cycle;
comm_base_timept(w->base, &w->env->now, &w->env->now_tv);
+ pp_init(&sldns_write_uint16, &sldns_write_uint32);
return w;
}
struct libworker* libworker_create_event(struct ub_ctx* ctx,
struct ub_event_base* eb)
{
return libworker_setup(ctx, 0, eb);
}
/** handle cancel command for bg worker */
static void
handle_cancel(struct libworker* w, uint8_t* buf, uint32_t len)
{
struct ctx_query* q;
if(w->is_bg_thread) {
lock_basic_lock(&w->ctx->cfglock);
q = context_deserialize_cancel(w->ctx, buf, len);
lock_basic_unlock(&w->ctx->cfglock);
} else {
q = context_deserialize_cancel(w->ctx, buf, len);
}
if(!q) {
/* probably simply lookup failed, i.e. the message had been
* processed and answered before the cancel arrived */
return;
}
q->cancelled = 1;
free(buf);
}
/** do control command coming into bg server */
static void
libworker_do_cmd(struct libworker* w, uint8_t* msg, uint32_t len)
{
switch(context_serial_getcmd(msg, len)) {
default:
case UB_LIBCMD_ANSWER:
log_err("unknown command for bg worker %d",
(int)context_serial_getcmd(msg, len));
/* and fall through to quit */
/* fallthrough */
case UB_LIBCMD_QUIT:
free(msg);
comm_base_exit(w->base);
break;
case UB_LIBCMD_NEWQUERY:
handle_newq(w, msg, len);
break;
case UB_LIBCMD_CANCEL:
handle_cancel(w, msg, len);
break;
}
}
/** handle control command coming into server */
void
libworker_handle_control_cmd(struct tube* ATTR_UNUSED(tube),
uint8_t* msg, size_t len, int err, void* arg)
{
struct libworker* w = (struct libworker*)arg;
if(err != 0) {
free(msg);
/* it is of no use to go on, exit */
comm_base_exit(w->base);
return;
}
libworker_do_cmd(w, msg, len); /* also frees the buf */
}
/** the background thread func */
static void*
libworker_dobg(void* arg)
{
/* setup */
uint32_t m;
struct libworker* w = (struct libworker*)arg;
struct ub_ctx* ctx;
if(!w) {
log_err("libunbound bg worker init failed, nomem");
return NULL;
}
ctx = w->ctx;
log_thread_set(&w->thread_num);
#ifdef THREADS_DISABLED
/* we are forked */
w->is_bg_thread = 0;
/* close non-used parts of the pipes */
tube_close_write(ctx->qq_pipe);
tube_close_read(ctx->rr_pipe);
#endif
if(!tube_setup_bg_listen(ctx->qq_pipe, w->base,
libworker_handle_control_cmd, w)) {
log_err("libunbound bg worker init failed, no bglisten");
return NULL;
}
if(!tube_setup_bg_write(ctx->rr_pipe, w->base)) {
log_err("libunbound bg worker init failed, no bgwrite");
return NULL;
}
/* do the work */
comm_base_dispatch(w->base);
/* cleanup */
m = UB_LIBCMD_QUIT;
w->want_quit = 1;
tube_remove_bg_listen(w->ctx->qq_pipe);
tube_remove_bg_write(w->ctx->rr_pipe);
libworker_delete(w);
(void)tube_write_msg(ctx->rr_pipe, (uint8_t*)&m,
(uint32_t)sizeof(m), 0);
#ifdef THREADS_DISABLED
/* close pipes from forked process before exit */
tube_close_read(ctx->qq_pipe);
tube_close_write(ctx->rr_pipe);
#endif
return NULL;
}
int libworker_bg(struct ub_ctx* ctx)
{
struct libworker* w;
/* fork or threadcreate */
lock_basic_lock(&ctx->cfglock);
if(ctx->dothread) {
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
ub_thread_create(&ctx->bg_tid, libworker_dobg, w);
} else {
lock_basic_unlock(&ctx->cfglock);
#ifndef HAVE_FORK
/* no fork on windows */
return UB_FORKFAIL;
#else /* HAVE_FORK */
switch((ctx->bg_pid=fork())) {
case 0:
w = libworker_setup(ctx, 1, NULL);
if(!w) fatal_exit("out of memory");
/* close non-used parts of the pipes */
tube_close_write(ctx->qq_pipe);
tube_close_read(ctx->rr_pipe);
(void)libworker_dobg(w);
exit(0);
break;
case -1:
return UB_FORKFAIL;
default:
/* close non-used parts, so that the worker
* bgprocess gets 'pipe closed' when the
* main process exits */
tube_close_read(ctx->qq_pipe);
tube_close_write(ctx->rr_pipe);
break;
}
#endif /* HAVE_FORK */
}
return UB_NOERROR;
}
/** insert canonname */
static int
fill_canon(struct ub_result* res, uint8_t* s)
{
char buf[255+2];
dname_str(s, buf);
res->canonname = strdup(buf);
return res->canonname != 0;
}
/** fill data into result */
static int
fill_res(struct ub_result* res, struct ub_packed_rrset_key* answer,
uint8_t* finalcname, struct query_info* rq, struct reply_info* rep)
{
size_t i;
struct packed_rrset_data* data;
res->ttl = 0;
if(!answer) {
if(finalcname) {
if(!fill_canon(res, finalcname))
return 0; /* out of memory */
}
if(rep->rrset_count != 0)
res->ttl = (int)rep->ttl;
res->data = (char**)calloc(1, sizeof(char*));
if(!res->data)
return 0; /* out of memory */
res->len = (int*)calloc(1, sizeof(int));
if(!res->len) {
free(res->data);
res->data = NULL;
return 0; /* out of memory */
}
return 1;
}
data = (struct packed_rrset_data*)answer->entry.data;
if(query_dname_compare(rq->qname, answer->rk.dname) != 0) {
if(!fill_canon(res, answer->rk.dname))
return 0; /* out of memory */
} else res->canonname = NULL;
res->data = (char**)calloc(data->count+1, sizeof(char*));
if(!res->data)
return 0; /* out of memory */
res->len = (int*)calloc(data->count+1, sizeof(int));
if(!res->len) {
free(res->data);
res->data = NULL;
return 0; /* out of memory */
}
for(i=0; i<data->count; i++) {
/* remove rdlength from rdata */
res->len[i] = (int)(data->rr_len[i] - 2);
res->data[i] = memdup(data->rr_data[i]+2, (size_t)res->len[i]);
if(!res->data[i]) {
size_t j;
for(j=0; j<i; j++) {
free(res->data[j]);
res->data[j] = NULL;
}
free(res->data);
res->data = NULL;
free(res->len);
res->len = NULL;
return 0; /* out of memory */
}
}
/* ttl for positive answers, from CNAME and answer RRs */
if(data->count != 0) {
size_t j;
res->ttl = (int)data->ttl;
for(j=0; j<rep->an_numrrsets; j++) {
struct packed_rrset_data* d =
(struct packed_rrset_data*)rep->rrsets[j]->
entry.data;
if((int)d->ttl < res->ttl)
res->ttl = (int)d->ttl;
}
}
/* ttl for negative answers */
if(data->count == 0 && rep->rrset_count != 0)
res->ttl = (int)rep->ttl;
res->data[data->count] = NULL;
res->len[data->count] = 0;
return 1;
}
/** fill result from parsed message, on error fills servfail */
void
libworker_enter_result(struct ub_result* res, sldns_buffer* buf,
struct regional* temp, enum sec_status msg_security)
{
struct query_info rq;
struct reply_info* rep;
res->rcode = LDNS_RCODE_SERVFAIL;
rep = parse_reply_in_temp_region(buf, temp, &rq);
if(!rep) {
log_err("cannot parse buf");
return; /* error parsing buf, or out of memory */
}
if(!fill_res(res, reply_find_answer_rrset(&rq, rep),
reply_find_final_cname_target(&rq, rep), &rq, rep))
return; /* out of memory */
/* rcode, havedata, nxdomain, secure, bogus */
res->rcode = (int)FLAGS_GET_RCODE(rep->flags);
if(res->data && res->data[0])
res->havedata = 1;
if(res->rcode == LDNS_RCODE_NXDOMAIN)
res->nxdomain = 1;
if(msg_security == sec_status_secure)
res->secure = 1;
if(msg_security == sec_status_bogus ||
msg_security == sec_status_secure_sentinel_fail)
res->bogus = 1;
}
/** fillup fg results */
static void
libworker_fillup_fg(struct ctx_query* q, int rcode, sldns_buffer* buf,
enum sec_status s, char* why_bogus, int was_ratelimited)
{
q->res->was_ratelimited = was_ratelimited;
if(why_bogus)
q->res->why_bogus = strdup(why_bogus);
if(rcode != 0) {
q->res->rcode = rcode;
q->msg_security = s;
return;
}
q->res->rcode = LDNS_RCODE_SERVFAIL;
q->msg_security = sec_status_unchecked;
q->msg = memdup(sldns_buffer_begin(buf), sldns_buffer_limit(buf));
q->msg_len = sldns_buffer_limit(buf);
if(!q->msg) {
return; /* the error is in the rcode */
}
/* canonname and results */
q->msg_security = s;
libworker_enter_result(q->res, buf, q->w->env->scratch, s);
}
void
libworker_fg_done_cb(void* arg, int rcode, sldns_buffer* buf, enum sec_status s,
char* why_bogus, int was_ratelimited)
{
struct ctx_query* q = (struct ctx_query*)arg;
/* fg query is done; exit comm base */
comm_base_exit(q->w->base);
libworker_fillup_fg(q, rcode, buf, s, why_bogus, was_ratelimited);
}
/** setup qinfo and edns */
static int
setup_qinfo_edns(struct libworker* w, struct ctx_query* q,
struct query_info* qinfo, struct edns_data* edns)
{
qinfo->qtype = (uint16_t)q->res->qtype;
qinfo->qclass = (uint16_t)q->res->qclass;
qinfo->local_alias = NULL;
qinfo->qname = sldns_str2wire_dname(q->res->qname, &qinfo->qname_len);
if(!qinfo->qname) {
return 0;
}
edns->edns_present = 1;
edns->ext_rcode = 0;
edns->edns_version = 0;
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;
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);
else edns->udp_size = 65535;
return 1;
}
int libworker_fg(struct ub_ctx* ctx, struct ctx_query* q)
{
struct libworker* w = libworker_setup(ctx, 0, NULL);
uint16_t qflags, qid;
struct query_info qinfo;
struct edns_data edns;
if(!w)
return UB_INITFAIL;
if(!setup_qinfo_edns(w, q, &qinfo, &edns)) {
libworker_delete(w);
return UB_SYNTAX;
}
qid = 0;
qflags = BIT_RD;
q->w = w;
/* see if there is a fixed answer */
sldns_buffer_write_u16_at(w->back->udp_buff, 0, qid);
sldns_buffer_write_u16_at(w->back->udp_buff, 2, qflags);
if(local_zones_answer(ctx->local_zones, w->env, &qinfo, &edns,
w->back->udp_buff, w->env->scratch, NULL, NULL, 0, NULL, 0,
NULL, 0, NULL, 0, NULL)) {
regional_free_all(w->env->scratch);
libworker_fillup_fg(q, LDNS_RCODE_NOERROR,
w->back->udp_buff, sec_status_insecure, NULL, 0);
libworker_delete(w);
free(qinfo.qname);
return UB_NOERROR;
}
if(ctx->env->auth_zones && auth_zones_answer(ctx->env->auth_zones,
w->env, &qinfo, &edns, NULL, w->back->udp_buff, w->env->scratch)) {
regional_free_all(w->env->scratch);
libworker_fillup_fg(q, LDNS_RCODE_NOERROR,
w->back->udp_buff, sec_status_insecure, NULL, 0);
libworker_delete(w);
free(qinfo.qname);
return UB_NOERROR;
}
/* process new query */
if(!mesh_new_callback(w->env->mesh, &qinfo, qflags, &edns,
w->back->udp_buff, qid, libworker_fg_done_cb, q, 0)) {
free(qinfo.qname);
return UB_NOMEM;
}
free(qinfo.qname);
/* wait for reply */
comm_base_dispatch(w->base);
libworker_delete(w);
return UB_NOERROR;
}
void
libworker_event_done_cb(void* arg, int rcode, sldns_buffer* buf,
enum sec_status s, char* why_bogus, int was_ratelimited)
{
struct ctx_query* q = (struct ctx_query*)arg;
ub_event_callback_type cb = q->cb_event;
void* cb_arg = q->cb_arg;
int cancelled = q->cancelled;
/* delete it now */
struct ub_ctx* ctx = q->w->ctx;
lock_basic_lock(&ctx->cfglock);
(void)rbtree_delete(&ctx->queries, q->node.key);
ctx->num_async--;
context_query_delete(q);
lock_basic_unlock(&ctx->cfglock);
if(!cancelled) {
/* call callback */
int sec = 0;
if(s == sec_status_bogus)
sec = 1;
else if(s == sec_status_secure)
sec = 2;
(*cb)(cb_arg, rcode, (buf?(void*)sldns_buffer_begin(buf):NULL),
(buf?(int)sldns_buffer_limit(buf):0), sec, why_bogus, was_ratelimited);
}
}
int libworker_attach_mesh(struct ub_ctx* ctx, struct ctx_query* q,
int* async_id)
{
struct libworker* w = ctx->event_worker;
uint16_t qflags, qid;
struct query_info qinfo;
struct edns_data edns;
if(!w)
return UB_INITFAIL;
if(!setup_qinfo_edns(w, q, &qinfo, &edns))
return UB_SYNTAX;
qid = 0;
qflags = BIT_RD;
q->w = w;
/* see if there is a fixed answer */
sldns_buffer_write_u16_at(w->back->udp_buff, 0, qid);
sldns_buffer_write_u16_at(w->back->udp_buff, 2, qflags);
if(local_zones_answer(ctx->local_zones, w->env, &qinfo, &edns,
w->back->udp_buff, w->env->scratch, NULL, NULL, 0, NULL, 0,
NULL, 0, NULL, 0, NULL)) {
regional_free_all(w->env->scratch);
free(qinfo.qname);
libworker_event_done_cb(q, LDNS_RCODE_NOERROR,
w->back->udp_buff, sec_status_insecure, NULL, 0);
return UB_NOERROR;
}
if(ctx->env->auth_zones && auth_zones_answer(ctx->env->auth_zones,
w->env, &qinfo, &edns, NULL, w->back->udp_buff, w->env->scratch)) {
regional_free_all(w->env->scratch);
free(qinfo.qname);
libworker_event_done_cb(q, LDNS_RCODE_NOERROR,
w->back->udp_buff, sec_status_insecure, NULL, 0);
return UB_NOERROR;
}
/* process new query */
if(async_id)
*async_id = q->querynum;
if(!mesh_new_callback(w->env->mesh, &qinfo, qflags, &edns,
w->back->udp_buff, qid, libworker_event_done_cb, q, 0)) {
free(qinfo.qname);
return UB_NOMEM;
}
free(qinfo.qname);
return UB_NOERROR;
}
/** add result to the bg worker result queue */
static void
add_bg_result(struct libworker* w, struct ctx_query* q, sldns_buffer* pkt,
int err, char* reason, int was_ratelimited)
{
uint8_t* msg = NULL;
uint32_t len = 0;
if(w->want_quit) {
context_query_delete(q);
return;
}
/* serialize and delete unneeded q */
if(w->is_bg_thread) {
lock_basic_lock(&w->ctx->cfglock);
if(reason)
q->res->why_bogus = strdup(reason);
q->res->was_ratelimited = was_ratelimited;
if(pkt) {
q->msg_len = sldns_buffer_remaining(pkt);
q->msg = memdup(sldns_buffer_begin(pkt), q->msg_len);
if(!q->msg) {
msg = context_serialize_answer(q, UB_NOMEM, NULL, &len);
} else {
msg = context_serialize_answer(q, err, NULL, &len);
}
} else {
msg = context_serialize_answer(q, err, NULL, &len);
}
lock_basic_unlock(&w->ctx->cfglock);
} else {
if(reason)
q->res->why_bogus = strdup(reason);
q->res->was_ratelimited = was_ratelimited;
msg = context_serialize_answer(q, err, pkt, &len);
(void)rbtree_delete(&w->ctx->queries, q->node.key);
w->ctx->num_async--;
context_query_delete(q);
}
if(!msg) {
log_err("out of memory for async answer");
return;
}
if(!tube_queue_item(w->ctx->rr_pipe, msg, len)) {
log_err("out of memory for async answer");
return;
}
}
void
libworker_bg_done_cb(void* arg, int rcode, sldns_buffer* buf, enum sec_status s,
char* why_bogus, int was_ratelimited)
{
struct ctx_query* q = (struct ctx_query*)arg;
if(q->cancelled || q->w->back->want_to_quit) {
if(q->w->is_bg_thread) {
/* delete it now */
struct ub_ctx* ctx = q->w->ctx;
lock_basic_lock(&ctx->cfglock);
(void)rbtree_delete(&ctx->queries, q->node.key);
ctx->num_async--;
context_query_delete(q);
lock_basic_unlock(&ctx->cfglock);
}
/* cancelled, do not give answer */
return;
}
q->msg_security = s;
if(!buf) {
buf = q->w->env->scratch_buffer;
}
if(rcode != 0) {
error_encode(buf, rcode, NULL, 0, BIT_RD, NULL);
}
add_bg_result(q->w, q, buf, UB_NOERROR, why_bogus, was_ratelimited);
}
/** handle new query command for bg worker */
static void
handle_newq(struct libworker* w, uint8_t* buf, uint32_t len)
{
uint16_t qflags, qid;
struct query_info qinfo;
struct edns_data edns;
struct ctx_query* q;
if(w->is_bg_thread) {
lock_basic_lock(&w->ctx->cfglock);
q = context_lookup_new_query(w->ctx, buf, len);
lock_basic_unlock(&w->ctx->cfglock);
} else {
q = context_deserialize_new_query(w->ctx, buf, len);
}
free(buf);
if(!q) {
log_err("failed to deserialize newq");
return;
}
if(!setup_qinfo_edns(w, q, &qinfo, &edns)) {
add_bg_result(w, q, NULL, UB_SYNTAX, NULL, 0);
return;
}
qid = 0;
qflags = BIT_RD;
/* see if there is a fixed answer */
sldns_buffer_write_u16_at(w->back->udp_buff, 0, qid);
sldns_buffer_write_u16_at(w->back->udp_buff, 2, qflags);
if(local_zones_answer(w->ctx->local_zones, w->env, &qinfo, &edns,
w->back->udp_buff, w->env->scratch, NULL, NULL, 0, NULL, 0,
NULL, 0, NULL, 0, NULL)) {
regional_free_all(w->env->scratch);
q->msg_security = sec_status_insecure;
add_bg_result(w, q, w->back->udp_buff, UB_NOERROR, NULL, 0);
free(qinfo.qname);
return;
}
if(w->ctx->env->auth_zones && auth_zones_answer(w->ctx->env->auth_zones,
w->env, &qinfo, &edns, NULL, w->back->udp_buff, w->env->scratch)) {
regional_free_all(w->env->scratch);
q->msg_security = sec_status_insecure;
add_bg_result(w, q, w->back->udp_buff, UB_NOERROR, NULL, 0);
free(qinfo.qname);
return;
}
q->w = w;
/* process new query */
if(!mesh_new_callback(w->env->mesh, &qinfo, qflags, &edns,
w->back->udp_buff, qid, libworker_bg_done_cb, q, 0)) {
add_bg_result(w, q, NULL, UB_NOMEM, NULL, 0);
}
free(qinfo.qname);
}
void libworker_alloc_cleanup(void* arg)
{
struct libworker* w = (struct libworker*)arg;
slabhash_clear(&w->env->rrset_cache->table);
slabhash_clear(w->env->msg_cache);
}
struct outbound_entry* libworker_send_query(struct query_info* qinfo,
uint16_t flags, int dnssec, int want_dnssec, int nocaps,
int check_ratelimit,
struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
size_t zonelen, int tcp_upstream, int ssl_upstream, char* tls_auth_name,
struct module_qstate* q, int* was_ratelimited)
{
struct libworker* w = (struct libworker*)q->env->worker;
struct outbound_entry* e = (struct outbound_entry*)regional_alloc(
q->region, sizeof(*e));
if(!e)
return NULL;
e->qstate = q;
e->qsent = outnet_serviced_query(w->back, qinfo, flags, dnssec,
want_dnssec, nocaps, check_ratelimit, tcp_upstream, ssl_upstream,
tls_auth_name, addr, addrlen, zone, zonelen, q,
libworker_handle_service_reply, e, w->back->udp_buff, q->env,
was_ratelimited);
if(!e->qsent) {
return NULL;
}
return e;
}
int
libworker_handle_service_reply(struct comm_point* c, void* arg, int error,
struct comm_reply* reply_info)
{
struct outbound_entry* e = (struct outbound_entry*)arg;
struct libworker* lw = (struct libworker*)e->qstate->env->worker;
if(error != 0) {
mesh_report_reply(lw->env->mesh, e, reply_info, error);
return 0;
}
/* sanity check. */
if(!LDNS_QR_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. */
mesh_report_reply(lw->env->mesh, e, reply_info,
NETEVENT_TIMEOUT);
return 0;
}
mesh_report_reply(lw->env->mesh, e, reply_info, NETEVENT_NOERROR);
return 0;
}
/* --- fake callbacks for fptr_wlist to work --- */
void worker_handle_control_cmd(struct tube* ATTR_UNUSED(tube),
uint8_t* ATTR_UNUSED(buffer), size_t ATTR_UNUSED(len),
int ATTR_UNUSED(error), void* ATTR_UNUSED(arg))
{
log_assert(0);
}
int worker_handle_request(struct comm_point* ATTR_UNUSED(c),
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
struct comm_reply* ATTR_UNUSED(repinfo))
{
log_assert(0);
return 0;
}
int worker_handle_service_reply(struct comm_point* ATTR_UNUSED(c),
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
struct comm_reply* ATTR_UNUSED(reply_info))
{
log_assert(0);
return 0;
}
int remote_accept_callback(struct comm_point* ATTR_UNUSED(c),
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
struct comm_reply* ATTR_UNUSED(repinfo))
{
log_assert(0);
return 0;
}
int remote_control_callback(struct comm_point* ATTR_UNUSED(c),
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
struct comm_reply* ATTR_UNUSED(repinfo))
{
log_assert(0);
return 0;
}
void worker_sighandler(int ATTR_UNUSED(sig), void* ATTR_UNUSED(arg))
{
log_assert(0);
}
struct outbound_entry* worker_send_query(struct query_info* ATTR_UNUSED(qinfo),
uint16_t ATTR_UNUSED(flags), int ATTR_UNUSED(dnssec),
int ATTR_UNUSED(want_dnssec), int ATTR_UNUSED(nocaps),
int ATTR_UNUSED(check_ratelimit),
struct sockaddr_storage* ATTR_UNUSED(addr), socklen_t ATTR_UNUSED(addrlen),
uint8_t* ATTR_UNUSED(zone), size_t ATTR_UNUSED(zonelen), int ATTR_UNUSED(tcp_upstream),
int ATTR_UNUSED(ssl_upstream), char* ATTR_UNUSED(tls_auth_name),
struct module_qstate* ATTR_UNUSED(q), int* ATTR_UNUSED(was_ratelimited))
{
log_assert(0);
return 0;
}
void
worker_alloc_cleanup(void* ATTR_UNUSED(arg))
{
log_assert(0);
}
void worker_stat_timer_cb(void* ATTR_UNUSED(arg))
{
log_assert(0);
}
void worker_probe_timer_cb(void* ATTR_UNUSED(arg))
{
log_assert(0);
}
void worker_start_accept(void* ATTR_UNUSED(arg))
{
log_assert(0);
}
void worker_stop_accept(void* ATTR_UNUSED(arg))
{
log_assert(0);
}
int order_lock_cmp(const void* ATTR_UNUSED(e1), const void* ATTR_UNUSED(e2))
{
log_assert(0);
return 0;
}
int
codeline_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
{
log_assert(0);
return 0;
}
int replay_var_compare(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
{
log_assert(0);
return 0;
}
void remote_get_opt_ssl(char* ATTR_UNUSED(str), void* ATTR_UNUSED(arg))
{
log_assert(0);
}
#ifdef UB_ON_WINDOWS
void
worker_win_stop_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev), void*
ATTR_UNUSED(arg)) {
log_assert(0);
}
void
wsvc_cron_cb(void* ATTR_UNUSED(arg))
{
log_assert(0);
}
#endif /* UB_ON_WINDOWS */
#ifdef USE_DNSTAP
void dtio_tap_callback(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev),
void* ATTR_UNUSED(arg))
{
log_assert(0);
}
#endif
#ifdef USE_DNSTAP
void dtio_mainfdcallback(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev),
void* ATTR_UNUSED(arg))
{
log_assert(0);
}
#endif
diff --git a/contrib/unbound/services/authzone.c b/contrib/unbound/services/authzone.c
index 33cbef2c742b..87844870a25a 100644
--- a/contrib/unbound/services/authzone.c
+++ b/contrib/unbound/services/authzone.c
@@ -1,8533 +1,8536 @@
/*
* services/authzone.c - authoritative zone that is locally hosted.
*
* Copyright (c) 2017, 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file contains the functions for an authority zone. This zone
* is queried by the iterator, just like a stub or forward zone, but then
* the data is locally held.
*/
#include "config.h"
#include "services/authzone.h"
#include "util/data/dname.h"
#include "util/data/msgparse.h"
#include "util/data/msgreply.h"
#include "util/data/msgencode.h"
#include "util/data/packed_rrset.h"
#include "util/regional.h"
#include "util/net_help.h"
#include "util/netevent.h"
#include "util/config_file.h"
#include "util/log.h"
#include "util/module.h"
#include "util/random.h"
#include "services/cache/dns.h"
#include "services/outside_network.h"
#include "services/listen_dnsport.h"
#include "services/mesh.h"
#include "sldns/rrdef.h"
#include "sldns/pkthdr.h"
#include "sldns/sbuffer.h"
#include "sldns/str2wire.h"
#include "sldns/wire2str.h"
#include "sldns/parseutil.h"
#include "sldns/keyraw.h"
#include "validator/val_nsec3.h"
#include "validator/val_nsec.h"
#include "validator/val_secalgo.h"
#include "validator/val_sigcrypt.h"
#include "validator/val_anchor.h"
#include "validator/val_utils.h"
#include <ctype.h>
/** bytes to use for NSEC3 hash buffer. 20 for sha1 */
#define N3HASHBUFLEN 32
/** max number of CNAMEs we are willing to follow (in one answer) */
#define MAX_CNAME_CHAIN 8
/** timeout for probe packets for SOA */
#define AUTH_PROBE_TIMEOUT 100 /* msec */
/** when to stop with SOA probes (when exponential timeouts exceed this) */
#define AUTH_PROBE_TIMEOUT_STOP 1000 /* msec */
/* auth transfer timeout for TCP connections, in msec */
#define AUTH_TRANSFER_TIMEOUT 10000 /* msec */
/* auth transfer max backoff for failed transfers and probes */
#define AUTH_TRANSFER_MAX_BACKOFF 86400 /* sec */
/* auth http port number */
#define AUTH_HTTP_PORT 80
/* auth https port number */
#define AUTH_HTTPS_PORT 443
/* max depth for nested $INCLUDEs */
#define MAX_INCLUDE_DEPTH 10
/** number of timeouts before we fallback from IXFR to AXFR,
* because some versions of servers (eg. dnsmasq) drop IXFR packets. */
#define NUM_TIMEOUTS_FALLBACK_IXFR 3
/** pick up nextprobe task to start waiting to perform transfer actions */
static void xfr_set_timeout(struct auth_xfer* xfr, struct module_env* env,
int failure, int lookup_only);
/** move to sending the probe packets, next if fails. task_probe */
static void xfr_probe_send_or_end(struct auth_xfer* xfr,
struct module_env* env);
/** pick up probe task with specified(or NULL) destination first,
* or transfer task if nothing to probe, or false if already in progress */
static int xfr_start_probe(struct auth_xfer* xfr, struct module_env* env,
struct auth_master* spec);
/** delete xfer structure (not its tree entry) */
static void auth_xfer_delete(struct auth_xfer* xfr);
/** create new dns_msg */
static struct dns_msg*
msg_create(struct regional* region, struct query_info* qinfo)
{
struct dns_msg* msg = (struct dns_msg*)regional_alloc(region,
sizeof(struct dns_msg));
if(!msg)
return NULL;
msg->qinfo.qname = regional_alloc_init(region, qinfo->qname,
qinfo->qname_len);
if(!msg->qinfo.qname)
return NULL;
msg->qinfo.qname_len = qinfo->qname_len;
msg->qinfo.qtype = qinfo->qtype;
msg->qinfo.qclass = qinfo->qclass;
msg->qinfo.local_alias = NULL;
/* non-packed reply_info, because it needs to grow the array */
msg->rep = (struct reply_info*)regional_alloc_zero(region,
sizeof(struct reply_info)-sizeof(struct rrset_ref));
if(!msg->rep)
return NULL;
msg->rep->flags = (uint16_t)(BIT_QR | BIT_AA);
msg->rep->authoritative = 1;
msg->rep->reason_bogus = LDNS_EDE_NONE;
msg->rep->qdcount = 1;
/* rrsets is NULL, no rrsets yet */
return msg;
}
/** grow rrset array by one in msg */
static int
msg_grow_array(struct regional* region, struct dns_msg* msg)
{
if(msg->rep->rrsets == NULL) {
msg->rep->rrsets = regional_alloc_zero(region,
sizeof(struct ub_packed_rrset_key*)*(msg->rep->rrset_count+1));
if(!msg->rep->rrsets)
return 0;
} else {
struct ub_packed_rrset_key** rrsets_old = msg->rep->rrsets;
msg->rep->rrsets = regional_alloc_zero(region,
sizeof(struct ub_packed_rrset_key*)*(msg->rep->rrset_count+1));
if(!msg->rep->rrsets)
return 0;
memmove(msg->rep->rrsets, rrsets_old,
sizeof(struct ub_packed_rrset_key*)*msg->rep->rrset_count);
}
return 1;
}
/** get ttl of rrset */
static time_t
get_rrset_ttl(struct ub_packed_rrset_key* k)
{
struct packed_rrset_data* d = (struct packed_rrset_data*)
k->entry.data;
return d->ttl;
}
/** Copy rrset into region from domain-datanode and packet rrset */
static struct ub_packed_rrset_key*
auth_packed_rrset_copy_region(struct auth_zone* z, struct auth_data* node,
struct auth_rrset* rrset, struct regional* region, time_t adjust)
{
struct ub_packed_rrset_key key;
memset(&key, 0, sizeof(key));
key.entry.key = &key;
key.entry.data = rrset->data;
key.rk.dname = node->name;
key.rk.dname_len = node->namelen;
key.rk.type = htons(rrset->type);
key.rk.rrset_class = htons(z->dclass);
key.entry.hash = rrset_key_hash(&key.rk);
return packed_rrset_copy_region(&key, region, adjust);
}
/** fix up msg->rep TTL and prefetch ttl */
static void
msg_ttl(struct dns_msg* msg)
{
if(msg->rep->rrset_count == 0) return;
if(msg->rep->rrset_count == 1) {
msg->rep->ttl = get_rrset_ttl(msg->rep->rrsets[0]);
msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl);
msg->rep->serve_expired_ttl = msg->rep->ttl + SERVE_EXPIRED_TTL;
} else if(get_rrset_ttl(msg->rep->rrsets[msg->rep->rrset_count-1]) <
msg->rep->ttl) {
msg->rep->ttl = get_rrset_ttl(msg->rep->rrsets[
msg->rep->rrset_count-1]);
msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl);
msg->rep->serve_expired_ttl = msg->rep->ttl + SERVE_EXPIRED_TTL;
}
}
/** see if rrset is a duplicate in the answer message */
static int
msg_rrset_duplicate(struct dns_msg* msg, uint8_t* nm, size_t nmlen,
uint16_t type, uint16_t dclass)
{
size_t i;
for(i=0; i<msg->rep->rrset_count; i++) {
struct ub_packed_rrset_key* k = msg->rep->rrsets[i];
if(ntohs(k->rk.type) == type && k->rk.dname_len == nmlen &&
ntohs(k->rk.rrset_class) == dclass &&
query_dname_compare(k->rk.dname, nm) == 0)
return 1;
}
return 0;
}
/** add rrset to answer section (no auth, add rrsets yet) */
static int
msg_add_rrset_an(struct auth_zone* z, struct regional* region,
struct dns_msg* msg, struct auth_data* node, struct auth_rrset* rrset)
{
log_assert(msg->rep->ns_numrrsets == 0);
log_assert(msg->rep->ar_numrrsets == 0);
if(!rrset || !node)
return 1;
if(msg_rrset_duplicate(msg, node->name, node->namelen, rrset->type,
z->dclass))
return 1;
/* grow array */
if(!msg_grow_array(region, msg))
return 0;
/* copy it */
if(!(msg->rep->rrsets[msg->rep->rrset_count] =
auth_packed_rrset_copy_region(z, node, rrset, region, 0)))
return 0;
msg->rep->rrset_count++;
msg->rep->an_numrrsets++;
msg_ttl(msg);
return 1;
}
/** add rrset to authority section (no additional section rrsets yet) */
static int
msg_add_rrset_ns(struct auth_zone* z, struct regional* region,
struct dns_msg* msg, struct auth_data* node, struct auth_rrset* rrset)
{
log_assert(msg->rep->ar_numrrsets == 0);
if(!rrset || !node)
return 1;
if(msg_rrset_duplicate(msg, node->name, node->namelen, rrset->type,
z->dclass))
return 1;
/* grow array */
if(!msg_grow_array(region, msg))
return 0;
/* copy it */
if(!(msg->rep->rrsets[msg->rep->rrset_count] =
auth_packed_rrset_copy_region(z, node, rrset, region, 0)))
return 0;
msg->rep->rrset_count++;
msg->rep->ns_numrrsets++;
msg_ttl(msg);
return 1;
}
/** add rrset to additional section */
static int
msg_add_rrset_ar(struct auth_zone* z, struct regional* region,
struct dns_msg* msg, struct auth_data* node, struct auth_rrset* rrset)
{
if(!rrset || !node)
return 1;
if(msg_rrset_duplicate(msg, node->name, node->namelen, rrset->type,
z->dclass))
return 1;
/* grow array */
if(!msg_grow_array(region, msg))
return 0;
/* copy it */
if(!(msg->rep->rrsets[msg->rep->rrset_count] =
auth_packed_rrset_copy_region(z, node, rrset, region, 0)))
return 0;
msg->rep->rrset_count++;
msg->rep->ar_numrrsets++;
msg_ttl(msg);
return 1;
}
struct auth_zones* auth_zones_create(void)
{
struct auth_zones* az = (struct auth_zones*)calloc(1, sizeof(*az));
if(!az) {
log_err("out of memory");
return NULL;
}
rbtree_init(&az->ztree, &auth_zone_cmp);
rbtree_init(&az->xtree, &auth_xfer_cmp);
lock_rw_init(&az->lock);
lock_protect(&az->lock, &az->ztree, sizeof(az->ztree));
lock_protect(&az->lock, &az->xtree, sizeof(az->xtree));
/* also lock protects the rbnode's in struct auth_zone, auth_xfer */
lock_rw_init(&az->rpz_lock);
lock_protect(&az->rpz_lock, &az->rpz_first, sizeof(az->rpz_first));
return az;
}
int auth_zone_cmp(const void* z1, const void* z2)
{
/* first sort on class, so that hierarchy can be maintained within
* a class */
struct auth_zone* a = (struct auth_zone*)z1;
struct auth_zone* b = (struct auth_zone*)z2;
int m;
if(a->dclass != b->dclass) {
if(a->dclass < b->dclass)
return -1;
return 1;
}
/* sorted such that higher zones sort before lower zones (their
* contents) */
return dname_lab_cmp(a->name, a->namelabs, b->name, b->namelabs, &m);
}
int auth_data_cmp(const void* z1, const void* z2)
{
struct auth_data* a = (struct auth_data*)z1;
struct auth_data* b = (struct auth_data*)z2;
int m;
/* canonical sort, because DNSSEC needs that */
return dname_canon_lab_cmp(a->name, a->namelabs, b->name,
b->namelabs, &m);
}
int auth_xfer_cmp(const void* z1, const void* z2)
{
/* first sort on class, so that hierarchy can be maintained within
* a class */
struct auth_xfer* a = (struct auth_xfer*)z1;
struct auth_xfer* b = (struct auth_xfer*)z2;
int m;
if(a->dclass != b->dclass) {
if(a->dclass < b->dclass)
return -1;
return 1;
}
/* sorted such that higher zones sort before lower zones (their
* contents) */
return dname_lab_cmp(a->name, a->namelabs, b->name, b->namelabs, &m);
}
/** delete auth rrset node */
static void
auth_rrset_delete(struct auth_rrset* rrset)
{
if(!rrset) return;
free(rrset->data);
free(rrset);
}
/** delete auth data domain node */
static void
auth_data_delete(struct auth_data* n)
{
struct auth_rrset* p, *np;
if(!n) return;
p = n->rrsets;
while(p) {
np = p->next;
auth_rrset_delete(p);
p = np;
}
free(n->name);
free(n);
}
/** helper traverse to delete zones */
static void
auth_data_del(rbnode_type* n, void* ATTR_UNUSED(arg))
{
struct auth_data* z = (struct auth_data*)n->key;
auth_data_delete(z);
}
/** delete an auth zone structure (tree remove must be done elsewhere) */
static void
auth_zone_delete(struct auth_zone* z, struct auth_zones* az)
{
if(!z) return;
lock_rw_destroy(&z->lock);
traverse_postorder(&z->data, auth_data_del, NULL);
if(az && z->rpz) {
/* keep RPZ linked list intact */
lock_rw_wrlock(&az->rpz_lock);
if(z->rpz_az_prev)
z->rpz_az_prev->rpz_az_next = z->rpz_az_next;
else
az->rpz_first = z->rpz_az_next;
if(z->rpz_az_next)
z->rpz_az_next->rpz_az_prev = z->rpz_az_prev;
lock_rw_unlock(&az->rpz_lock);
}
if(z->rpz)
rpz_delete(z->rpz);
free(z->name);
free(z->zonefile);
free(z);
}
struct auth_zone*
auth_zone_create(struct auth_zones* az, uint8_t* nm, size_t nmlen,
uint16_t dclass)
{
struct auth_zone* z = (struct auth_zone*)calloc(1, sizeof(*z));
if(!z) {
return NULL;
}
z->node.key = z;
z->dclass = dclass;
z->namelen = nmlen;
z->namelabs = dname_count_labels(nm);
z->name = memdup(nm, nmlen);
if(!z->name) {
free(z);
return NULL;
}
rbtree_init(&z->data, &auth_data_cmp);
lock_rw_init(&z->lock);
lock_protect(&z->lock, &z->name, sizeof(*z)-sizeof(rbnode_type)-
sizeof(&z->rpz_az_next)-sizeof(&z->rpz_az_prev));
lock_rw_wrlock(&z->lock);
/* z lock protects all, except rbtree itself and the rpz linked list
* pointers, which are protected using az->lock */
if(!rbtree_insert(&az->ztree, &z->node)) {
lock_rw_unlock(&z->lock);
auth_zone_delete(z, NULL);
log_warn("duplicate auth zone");
return NULL;
}
return z;
}
struct auth_zone*
auth_zone_find(struct auth_zones* az, uint8_t* nm, size_t nmlen,
uint16_t dclass)
{
struct auth_zone key;
key.node.key = &key;
key.dclass = dclass;
key.name = nm;
key.namelen = nmlen;
key.namelabs = dname_count_labels(nm);
return (struct auth_zone*)rbtree_search(&az->ztree, &key);
}
struct auth_xfer*
auth_xfer_find(struct auth_zones* az, uint8_t* nm, size_t nmlen,
uint16_t dclass)
{
struct auth_xfer key;
key.node.key = &key;
key.dclass = dclass;
key.name = nm;
key.namelen = nmlen;
key.namelabs = dname_count_labels(nm);
return (struct auth_xfer*)rbtree_search(&az->xtree, &key);
}
/** find an auth zone or sorted less-or-equal, return true if exact */
static int
auth_zone_find_less_equal(struct auth_zones* az, uint8_t* nm, size_t nmlen,
uint16_t dclass, struct auth_zone** z)
{
struct auth_zone key;
key.node.key = &key;
key.dclass = dclass;
key.name = nm;
key.namelen = nmlen;
key.namelabs = dname_count_labels(nm);
return rbtree_find_less_equal(&az->ztree, &key, (rbnode_type**)z);
}
/** find the auth zone that is above the given name */
struct auth_zone*
auth_zones_find_zone(struct auth_zones* az, uint8_t* name, size_t name_len,
uint16_t dclass)
{
uint8_t* nm = name;
size_t nmlen = name_len;
struct auth_zone* z;
if(auth_zone_find_less_equal(az, nm, nmlen, dclass, &z)) {
/* exact match */
return z;
} else {
/* less-or-nothing */
if(!z) return NULL; /* nothing smaller, nothing above it */
/* we found smaller name; smaller may be above the name,
* but not below it. */
nm = dname_get_shared_topdomain(z->name, name);
dname_count_size_labels(nm, &nmlen);
z = NULL;
}
/* search up */
while(!z) {
z = auth_zone_find(az, nm, nmlen, dclass);
if(z) return z;
if(dname_is_root(nm)) break;
dname_remove_label(&nm, &nmlen);
}
return NULL;
}
/** find or create zone with name str. caller must have lock on az.
* returns a wrlocked zone */
static struct auth_zone*
auth_zones_find_or_add_zone(struct auth_zones* az, char* name)
{
uint8_t nm[LDNS_MAX_DOMAINLEN+1];
size_t nmlen = sizeof(nm);
struct auth_zone* z;
if(sldns_str2wire_dname_buf(name, nm, &nmlen) != 0) {
log_err("cannot parse auth zone name: %s", name);
return 0;
}
z = auth_zone_find(az, nm, nmlen, LDNS_RR_CLASS_IN);
if(!z) {
/* not found, create the zone */
z = auth_zone_create(az, nm, nmlen, LDNS_RR_CLASS_IN);
} else {
lock_rw_wrlock(&z->lock);
}
return z;
}
/** find or create xfer zone with name str. caller must have lock on az.
* returns a locked xfer */
static struct auth_xfer*
auth_zones_find_or_add_xfer(struct auth_zones* az, struct auth_zone* z)
{
struct auth_xfer* x;
x = auth_xfer_find(az, z->name, z->namelen, z->dclass);
if(!x) {
/* not found, create the zone */
x = auth_xfer_create(az, z);
} else {
lock_basic_lock(&x->lock);
}
return x;
}
int
auth_zone_set_zonefile(struct auth_zone* z, char* zonefile)
{
if(z->zonefile) free(z->zonefile);
if(zonefile == NULL) {
z->zonefile = NULL;
} else {
z->zonefile = strdup(zonefile);
if(!z->zonefile) {
log_err("malloc failure");
return 0;
}
}
return 1;
}
/** set auth zone fallback. caller must have lock on zone */
int
auth_zone_set_fallback(struct auth_zone* z, char* fallbackstr)
{
if(strcmp(fallbackstr, "yes") != 0 && strcmp(fallbackstr, "no") != 0){
log_err("auth zone fallback, expected yes or no, got %s",
fallbackstr);
return 0;
}
z->fallback_enabled = (strcmp(fallbackstr, "yes")==0);
return 1;
}
/** create domain with the given name */
static struct auth_data*
az_domain_create(struct auth_zone* z, uint8_t* nm, size_t nmlen)
{
struct auth_data* n = (struct auth_data*)malloc(sizeof(*n));
if(!n) return NULL;
memset(n, 0, sizeof(*n));
n->node.key = n;
n->name = memdup(nm, nmlen);
if(!n->name) {
free(n);
return NULL;
}
n->namelen = nmlen;
n->namelabs = dname_count_labels(nm);
if(!rbtree_insert(&z->data, &n->node)) {
log_warn("duplicate auth domain name");
free(n->name);
free(n);
return NULL;
}
return n;
}
/** find domain with exactly the given name */
static struct auth_data*
az_find_name(struct auth_zone* z, uint8_t* nm, size_t nmlen)
{
struct auth_zone key;
key.node.key = &key;
key.name = nm;
key.namelen = nmlen;
key.namelabs = dname_count_labels(nm);
return (struct auth_data*)rbtree_search(&z->data, &key);
}
/** Find domain name (or closest match) */
static void
az_find_domain(struct auth_zone* z, struct query_info* qinfo, int* node_exact,
struct auth_data** node)
{
struct auth_zone key;
key.node.key = &key;
key.name = qinfo->qname;
key.namelen = qinfo->qname_len;
key.namelabs = dname_count_labels(key.name);
*node_exact = rbtree_find_less_equal(&z->data, &key,
(rbnode_type**)node);
}
/** find or create domain with name in zone */
static struct auth_data*
az_domain_find_or_create(struct auth_zone* z, uint8_t* dname,
size_t dname_len)
{
struct auth_data* n = az_find_name(z, dname, dname_len);
if(!n) {
n = az_domain_create(z, dname, dname_len);
}
return n;
}
/** find rrset of given type in the domain */
static struct auth_rrset*
az_domain_rrset(struct auth_data* n, uint16_t t)
{
struct auth_rrset* rrset;
if(!n) return NULL;
rrset = n->rrsets;
while(rrset) {
if(rrset->type == t)
return rrset;
rrset = rrset->next;
}
return NULL;
}
/** remove rrset of this type from domain */
static void
domain_remove_rrset(struct auth_data* node, uint16_t rr_type)
{
struct auth_rrset* rrset, *prev;
if(!node) return;
prev = NULL;
rrset = node->rrsets;
while(rrset) {
if(rrset->type == rr_type) {
/* found it, now delete it */
if(prev) prev->next = rrset->next;
else node->rrsets = rrset->next;
auth_rrset_delete(rrset);
return;
}
prev = rrset;
rrset = rrset->next;
}
}
/** find an rrsig index in the rrset. returns true if found */
static int
az_rrset_find_rrsig(struct packed_rrset_data* d, uint8_t* rdata, size_t len,
size_t* index)
{
size_t i;
for(i=d->count; i<d->count + d->rrsig_count; i++) {
if(d->rr_len[i] != len)
continue;
if(memcmp(d->rr_data[i], rdata, len) == 0) {
*index = i;
return 1;
}
}
return 0;
}
/** see if rdata is duplicate */
static int
rdata_duplicate(struct packed_rrset_data* d, uint8_t* rdata, size_t len)
{
size_t i;
for(i=0; i<d->count + d->rrsig_count; i++) {
if(d->rr_len[i] != len)
continue;
if(memcmp(d->rr_data[i], rdata, len) == 0)
return 1;
}
return 0;
}
/** get rrsig type covered from rdata.
* @param rdata: rdata in wireformat, starting with 16bit rdlength.
* @param rdatalen: length of rdata buffer.
* @return type covered (or 0).
*/
static uint16_t
rrsig_rdata_get_type_covered(uint8_t* rdata, size_t rdatalen)
{
if(rdatalen < 4)
return 0;
return sldns_read_uint16(rdata+2);
}
/** remove RR from existing RRset. Also sig, if it is a signature.
* reallocates the packed rrset for a new one, false on alloc failure */
static int
rrset_remove_rr(struct auth_rrset* rrset, size_t index)
{
struct packed_rrset_data* d, *old = rrset->data;
size_t i;
if(index >= old->count + old->rrsig_count)
return 0; /* index out of bounds */
d = (struct packed_rrset_data*)calloc(1, packed_rrset_sizeof(old) - (
sizeof(size_t) + sizeof(uint8_t*) + sizeof(time_t) +
old->rr_len[index]));
if(!d) {
log_err("malloc failure");
return 0;
}
d->ttl = old->ttl;
d->count = old->count;
d->rrsig_count = old->rrsig_count;
if(index < d->count) d->count--;
else d->rrsig_count--;
d->trust = old->trust;
d->security = old->security;
/* set rr_len, needed for ptr_fixup */
d->rr_len = (size_t*)((uint8_t*)d +
sizeof(struct packed_rrset_data));
if(index > 0)
memmove(d->rr_len, old->rr_len, (index)*sizeof(size_t));
if(index+1 < old->count+old->rrsig_count)
memmove(&d->rr_len[index], &old->rr_len[index+1],
(old->count+old->rrsig_count - (index+1))*sizeof(size_t));
packed_rrset_ptr_fixup(d);
/* move over ttls */
if(index > 0)
memmove(d->rr_ttl, old->rr_ttl, (index)*sizeof(time_t));
if(index+1 < old->count+old->rrsig_count)
memmove(&d->rr_ttl[index], &old->rr_ttl[index+1],
(old->count+old->rrsig_count - (index+1))*sizeof(time_t));
/* move over rr_data */
for(i=0; i<d->count+d->rrsig_count; i++) {
size_t oldi;
if(i < index) oldi = i;
else oldi = i+1;
memmove(d->rr_data[i], old->rr_data[oldi], d->rr_len[i]);
}
/* recalc ttl (lowest of remaining RR ttls) */
if(d->count + d->rrsig_count > 0)
d->ttl = d->rr_ttl[0];
for(i=0; i<d->count+d->rrsig_count; i++) {
if(d->rr_ttl[i] < d->ttl)
d->ttl = d->rr_ttl[i];
}
free(rrset->data);
rrset->data = d;
return 1;
}
/** add RR to existing RRset. If insert_sig is true, add to rrsigs.
* This reallocates the packed rrset for a new one */
static int
rrset_add_rr(struct auth_rrset* rrset, uint32_t rr_ttl, uint8_t* rdata,
size_t rdatalen, int insert_sig)
{
struct packed_rrset_data* d, *old = rrset->data;
size_t total, old_total;
d = (struct packed_rrset_data*)calloc(1, packed_rrset_sizeof(old)
+ sizeof(size_t) + sizeof(uint8_t*) + sizeof(time_t)
+ rdatalen);
if(!d) {
log_err("out of memory");
return 0;
}
/* copy base values */
memcpy(d, old, sizeof(struct packed_rrset_data));
if(!insert_sig) {
d->count++;
} else {
d->rrsig_count++;
}
old_total = old->count + old->rrsig_count;
total = d->count + d->rrsig_count;
/* set rr_len, needed for ptr_fixup */
d->rr_len = (size_t*)((uint8_t*)d +
sizeof(struct packed_rrset_data));
if(old->count != 0)
memmove(d->rr_len, old->rr_len, old->count*sizeof(size_t));
if(old->rrsig_count != 0)
memmove(d->rr_len+d->count, old->rr_len+old->count,
old->rrsig_count*sizeof(size_t));
if(!insert_sig)
d->rr_len[d->count-1] = rdatalen;
else d->rr_len[total-1] = rdatalen;
packed_rrset_ptr_fixup(d);
if((time_t)rr_ttl < d->ttl)
d->ttl = rr_ttl;
/* copy old values into new array */
if(old->count != 0) {
memmove(d->rr_ttl, old->rr_ttl, old->count*sizeof(time_t));
/* all the old rr pieces are allocated sequential, so we
* can copy them in one go */
memmove(d->rr_data[0], old->rr_data[0],
(old->rr_data[old->count-1] - old->rr_data[0]) +
old->rr_len[old->count-1]);
}
if(old->rrsig_count != 0) {
memmove(d->rr_ttl+d->count, old->rr_ttl+old->count,
old->rrsig_count*sizeof(time_t));
memmove(d->rr_data[d->count], old->rr_data[old->count],
(old->rr_data[old_total-1] - old->rr_data[old->count]) +
old->rr_len[old_total-1]);
}
/* insert new value */
if(!insert_sig) {
d->rr_ttl[d->count-1] = rr_ttl;
memmove(d->rr_data[d->count-1], rdata, rdatalen);
} else {
d->rr_ttl[total-1] = rr_ttl;
memmove(d->rr_data[total-1], rdata, rdatalen);
}
rrset->data = d;
free(old);
return 1;
}
/** Create new rrset for node with packed rrset with one RR element */
static struct auth_rrset*
rrset_create(struct auth_data* node, uint16_t rr_type, uint32_t rr_ttl,
uint8_t* rdata, size_t rdatalen)
{
struct auth_rrset* rrset = (struct auth_rrset*)calloc(1,
sizeof(*rrset));
struct auth_rrset* p, *prev;
struct packed_rrset_data* d;
if(!rrset) {
log_err("out of memory");
return NULL;
}
rrset->type = rr_type;
/* the rrset data structure, with one RR */
d = (struct packed_rrset_data*)calloc(1,
sizeof(struct packed_rrset_data) + sizeof(size_t) +
sizeof(uint8_t*) + sizeof(time_t) + rdatalen);
if(!d) {
free(rrset);
log_err("out of memory");
return NULL;
}
rrset->data = d;
d->ttl = rr_ttl;
d->trust = rrset_trust_prim_noglue;
d->rr_len = (size_t*)((uint8_t*)d + sizeof(struct packed_rrset_data));
d->rr_data = (uint8_t**)&(d->rr_len[1]);
d->rr_ttl = (time_t*)&(d->rr_data[1]);
d->rr_data[0] = (uint8_t*)&(d->rr_ttl[1]);
/* insert the RR */
d->rr_len[0] = rdatalen;
d->rr_ttl[0] = rr_ttl;
memmove(d->rr_data[0], rdata, rdatalen);
d->count++;
/* insert rrset into linked list for domain */
/* find sorted place to link the rrset into the list */
prev = NULL;
p = node->rrsets;
while(p && p->type<=rr_type) {
prev = p;
p = p->next;
}
/* so, prev is smaller, and p is larger than rr_type */
rrset->next = p;
if(prev) prev->next = rrset;
else node->rrsets = rrset;
return rrset;
}
/** count number (and size) of rrsigs that cover a type */
static size_t
rrsig_num_that_cover(struct auth_rrset* rrsig, uint16_t rr_type, size_t* sigsz)
{
struct packed_rrset_data* d = rrsig->data;
size_t i, num = 0;
*sigsz = 0;
log_assert(d && rrsig->type == LDNS_RR_TYPE_RRSIG);
for(i=0; i<d->count+d->rrsig_count; i++) {
if(rrsig_rdata_get_type_covered(d->rr_data[i],
d->rr_len[i]) == rr_type) {
num++;
(*sigsz) += d->rr_len[i];
}
}
return num;
}
/** See if rrsig set has covered sigs for rrset and move them over */
static int
rrset_moveover_rrsigs(struct auth_data* node, uint16_t rr_type,
struct auth_rrset* rrset, struct auth_rrset* rrsig)
{
size_t sigs, sigsz, i, j, total;
struct packed_rrset_data* sigold = rrsig->data;
struct packed_rrset_data* old = rrset->data;
struct packed_rrset_data* d, *sigd;
log_assert(rrset->type == rr_type);
log_assert(rrsig->type == LDNS_RR_TYPE_RRSIG);
sigs = rrsig_num_that_cover(rrsig, rr_type, &sigsz);
if(sigs == 0) {
/* 0 rrsigs to move over, done */
return 1;
}
/* allocate rrset sigsz larger for extra sigs elements, and
* allocate rrsig sigsz smaller for less sigs elements. */
d = (struct packed_rrset_data*)calloc(1, packed_rrset_sizeof(old)
+ sigs*(sizeof(size_t) + sizeof(uint8_t*) + sizeof(time_t))
+ sigsz);
if(!d) {
log_err("out of memory");
return 0;
}
/* copy base values */
total = old->count + old->rrsig_count;
memcpy(d, old, sizeof(struct packed_rrset_data));
d->rrsig_count += sigs;
/* setup rr_len */
d->rr_len = (size_t*)((uint8_t*)d +
sizeof(struct packed_rrset_data));
if(total != 0)
memmove(d->rr_len, old->rr_len, total*sizeof(size_t));
j = d->count+d->rrsig_count-sigs;
for(i=0; i<sigold->count+sigold->rrsig_count; i++) {
if(rrsig_rdata_get_type_covered(sigold->rr_data[i],
sigold->rr_len[i]) == rr_type) {
d->rr_len[j] = sigold->rr_len[i];
j++;
}
}
packed_rrset_ptr_fixup(d);
/* copy old values into new array */
if(total != 0) {
memmove(d->rr_ttl, old->rr_ttl, total*sizeof(time_t));
/* all the old rr pieces are allocated sequential, so we
* can copy them in one go */
memmove(d->rr_data[0], old->rr_data[0],
(old->rr_data[total-1] - old->rr_data[0]) +
old->rr_len[total-1]);
}
/* move over the rrsigs to the larger rrset*/
j = d->count+d->rrsig_count-sigs;
for(i=0; i<sigold->count+sigold->rrsig_count; i++) {
if(rrsig_rdata_get_type_covered(sigold->rr_data[i],
sigold->rr_len[i]) == rr_type) {
/* move this one over to location j */
d->rr_ttl[j] = sigold->rr_ttl[i];
memmove(d->rr_data[j], sigold->rr_data[i],
sigold->rr_len[i]);
if(d->rr_ttl[j] < d->ttl)
d->ttl = d->rr_ttl[j];
j++;
}
}
/* put it in and deallocate the old rrset */
rrset->data = d;
free(old);
/* now make rrsig set smaller */
if(sigold->count+sigold->rrsig_count == sigs) {
/* remove all sigs from rrsig, remove it entirely */
domain_remove_rrset(node, LDNS_RR_TYPE_RRSIG);
return 1;
}
log_assert(packed_rrset_sizeof(sigold) > sigs*(sizeof(size_t) +
sizeof(uint8_t*) + sizeof(time_t)) + sigsz);
sigd = (struct packed_rrset_data*)calloc(1, packed_rrset_sizeof(sigold)
- sigs*(sizeof(size_t) + sizeof(uint8_t*) + sizeof(time_t))
- sigsz);
if(!sigd) {
/* no need to free up d, it has already been placed in the
* node->rrset structure */
log_err("out of memory");
return 0;
}
/* copy base values */
memcpy(sigd, sigold, sizeof(struct packed_rrset_data));
/* in sigd the RRSIGs are stored in the base of the RR, in count */
sigd->count -= sigs;
/* setup rr_len */
sigd->rr_len = (size_t*)((uint8_t*)sigd +
sizeof(struct packed_rrset_data));
j = 0;
for(i=0; i<sigold->count+sigold->rrsig_count; i++) {
if(rrsig_rdata_get_type_covered(sigold->rr_data[i],
sigold->rr_len[i]) != rr_type) {
sigd->rr_len[j] = sigold->rr_len[i];
j++;
}
}
packed_rrset_ptr_fixup(sigd);
/* copy old values into new rrsig array */
j = 0;
for(i=0; i<sigold->count+sigold->rrsig_count; i++) {
if(rrsig_rdata_get_type_covered(sigold->rr_data[i],
sigold->rr_len[i]) != rr_type) {
/* move this one over to location j */
sigd->rr_ttl[j] = sigold->rr_ttl[i];
memmove(sigd->rr_data[j], sigold->rr_data[i],
sigold->rr_len[i]);
if(j==0) sigd->ttl = sigd->rr_ttl[j];
else {
if(sigd->rr_ttl[j] < sigd->ttl)
sigd->ttl = sigd->rr_ttl[j];
}
j++;
}
}
/* put it in and deallocate the old rrset */
rrsig->data = sigd;
free(sigold);
return 1;
}
/** copy the rrsigs from the rrset to the rrsig rrset, because the rrset
* is going to be deleted. reallocates the RRSIG rrset data. */
static int
rrsigs_copy_from_rrset_to_rrsigset(struct auth_rrset* rrset,
struct auth_rrset* rrsigset)
{
size_t i;
if(rrset->data->rrsig_count == 0)
return 1;
/* move them over one by one, because there might be duplicates,
* duplicates are ignored */
for(i=rrset->data->count;
i<rrset->data->count+rrset->data->rrsig_count; i++) {
uint8_t* rdata = rrset->data->rr_data[i];
size_t rdatalen = rrset->data->rr_len[i];
time_t rr_ttl = rrset->data->rr_ttl[i];
if(rdata_duplicate(rrsigset->data, rdata, rdatalen)) {
continue;
}
if(!rrset_add_rr(rrsigset, rr_ttl, rdata, rdatalen, 0))
return 0;
}
return 1;
}
/** Add rr to node, ignores duplicate RRs,
* rdata points to buffer with rdatalen octets, starts with 2bytelength. */
static int
az_domain_add_rr(struct auth_data* node, uint16_t rr_type, uint32_t rr_ttl,
uint8_t* rdata, size_t rdatalen, int* duplicate)
{
struct auth_rrset* rrset;
/* packed rrsets have their rrsigs along with them, sort them out */
if(rr_type == LDNS_RR_TYPE_RRSIG) {
uint16_t ctype = rrsig_rdata_get_type_covered(rdata, rdatalen);
if((rrset=az_domain_rrset(node, ctype))!= NULL) {
/* a node of the correct type exists, add the RRSIG
* to the rrset of the covered data type */
if(rdata_duplicate(rrset->data, rdata, rdatalen)) {
if(duplicate) *duplicate = 1;
return 1;
}
if(!rrset_add_rr(rrset, rr_ttl, rdata, rdatalen, 1))
return 0;
} else if((rrset=az_domain_rrset(node, rr_type))!= NULL) {
/* add RRSIG to rrset of type RRSIG */
if(rdata_duplicate(rrset->data, rdata, rdatalen)) {
if(duplicate) *duplicate = 1;
return 1;
}
if(!rrset_add_rr(rrset, rr_ttl, rdata, rdatalen, 0))
return 0;
} else {
/* create rrset of type RRSIG */
if(!rrset_create(node, rr_type, rr_ttl, rdata,
rdatalen))
return 0;
}
} else {
/* normal RR type */
if((rrset=az_domain_rrset(node, rr_type))!= NULL) {
/* add data to existing node with data type */
if(rdata_duplicate(rrset->data, rdata, rdatalen)) {
if(duplicate) *duplicate = 1;
return 1;
}
if(!rrset_add_rr(rrset, rr_ttl, rdata, rdatalen, 0))
return 0;
} else {
struct auth_rrset* rrsig;
/* create new node with data type */
if(!(rrset=rrset_create(node, rr_type, rr_ttl, rdata,
rdatalen)))
return 0;
/* see if node of type RRSIG has signatures that
* cover the data type, and move them over */
/* and then make the RRSIG type smaller */
if((rrsig=az_domain_rrset(node, LDNS_RR_TYPE_RRSIG))
!= NULL) {
if(!rrset_moveover_rrsigs(node, rr_type,
rrset, rrsig))
return 0;
}
}
}
return 1;
}
/** insert RR into zone, ignore duplicates */
static int
az_insert_rr(struct auth_zone* z, uint8_t* rr, size_t rr_len,
size_t dname_len, int* duplicate)
{
struct auth_data* node;
uint8_t* dname = rr;
uint16_t rr_type = sldns_wirerr_get_type(rr, rr_len, dname_len);
uint16_t rr_class = sldns_wirerr_get_class(rr, rr_len, dname_len);
uint32_t rr_ttl = sldns_wirerr_get_ttl(rr, rr_len, dname_len);
size_t rdatalen = ((size_t)sldns_wirerr_get_rdatalen(rr, rr_len,
dname_len))+2;
/* rdata points to rdata prefixed with uint16 rdatalength */
uint8_t* rdata = sldns_wirerr_get_rdatawl(rr, rr_len, dname_len);
if(rr_class != z->dclass) {
log_err("wrong class for RR");
return 0;
}
if(!(node=az_domain_find_or_create(z, dname, dname_len))) {
log_err("cannot create domain");
return 0;
}
if(!az_domain_add_rr(node, rr_type, rr_ttl, rdata, rdatalen,
duplicate)) {
log_err("cannot add RR to domain");
return 0;
}
if(z->rpz) {
if(!(rpz_insert_rr(z->rpz, z->name, z->namelen, dname,
dname_len, rr_type, rr_class, rr_ttl, rdata, rdatalen,
rr, rr_len)))
return 0;
}
return 1;
}
/** Remove rr from node, ignores nonexisting RRs,
* rdata points to buffer with rdatalen octets, starts with 2bytelength. */
static int
az_domain_remove_rr(struct auth_data* node, uint16_t rr_type,
uint8_t* rdata, size_t rdatalen, int* nonexist)
{
struct auth_rrset* rrset;
size_t index = 0;
/* find the plain RR of the given type */
if((rrset=az_domain_rrset(node, rr_type))!= NULL) {
if(packed_rrset_find_rr(rrset->data, rdata, rdatalen, &index)) {
if(rrset->data->count == 1 &&
rrset->data->rrsig_count == 0) {
/* last RR, delete the rrset */
domain_remove_rrset(node, rr_type);
} else if(rrset->data->count == 1 &&
rrset->data->rrsig_count != 0) {
/* move RRSIGs to the RRSIG rrset, or
* this one becomes that RRset */
struct auth_rrset* rrsigset = az_domain_rrset(
node, LDNS_RR_TYPE_RRSIG);
if(rrsigset) {
/* move left over rrsigs to the
* existing rrset of type RRSIG */
rrsigs_copy_from_rrset_to_rrsigset(
rrset, rrsigset);
/* and then delete the rrset */
domain_remove_rrset(node, rr_type);
} else {
/* no rrset of type RRSIG, this
* set is now of that type,
* just remove the rr */
if(!rrset_remove_rr(rrset, index))
return 0;
rrset->type = LDNS_RR_TYPE_RRSIG;
rrset->data->count = rrset->data->rrsig_count;
rrset->data->rrsig_count = 0;
}
} else {
/* remove the RR from the rrset */
if(!rrset_remove_rr(rrset, index))
return 0;
}
return 1;
}
/* rr not found in rrset */
}
/* is it a type RRSIG, look under the covered type */
if(rr_type == LDNS_RR_TYPE_RRSIG) {
uint16_t ctype = rrsig_rdata_get_type_covered(rdata, rdatalen);
if((rrset=az_domain_rrset(node, ctype))!= NULL) {
if(az_rrset_find_rrsig(rrset->data, rdata, rdatalen,
&index)) {
/* rrsig should have d->count > 0, be
* over some rr of that type */
/* remove the rrsig from the rrsigs list of the
* rrset */
if(!rrset_remove_rr(rrset, index))
return 0;
return 1;
}
}
/* also RRSIG not found */
}
/* nothing found to delete */
if(nonexist) *nonexist = 1;
return 1;
}
/** remove RR from zone, ignore if it does not exist, false on alloc failure*/
static int
az_remove_rr(struct auth_zone* z, uint8_t* rr, size_t rr_len,
size_t dname_len, int* nonexist)
{
struct auth_data* node;
uint8_t* dname = rr;
uint16_t rr_type = sldns_wirerr_get_type(rr, rr_len, dname_len);
uint16_t rr_class = sldns_wirerr_get_class(rr, rr_len, dname_len);
size_t rdatalen = ((size_t)sldns_wirerr_get_rdatalen(rr, rr_len,
dname_len))+2;
/* rdata points to rdata prefixed with uint16 rdatalength */
uint8_t* rdata = sldns_wirerr_get_rdatawl(rr, rr_len, dname_len);
if(rr_class != z->dclass) {
log_err("wrong class for RR");
/* really also a nonexisting entry, because no records
* of that class in the zone, but return an error because
* getting records of the wrong class is a failure of the
* zone transfer */
return 0;
}
node = az_find_name(z, dname, dname_len);
if(!node) {
/* node with that name does not exist */
/* nonexisting entry, because no such name */
*nonexist = 1;
return 1;
}
if(!az_domain_remove_rr(node, rr_type, rdata, rdatalen, nonexist)) {
/* alloc failure or so */
return 0;
}
/* remove the node, if necessary */
/* an rrsets==NULL entry is not kept around for empty nonterminals,
* and also parent nodes are not kept around, so we just delete it */
if(node->rrsets == NULL) {
(void)rbtree_delete(&z->data, node);
auth_data_delete(node);
}
if(z->rpz) {
rpz_remove_rr(z->rpz, z->name, z->namelen, dname, dname_len,
rr_type, rr_class, rdata, rdatalen);
}
return 1;
}
/** decompress an RR into the buffer where it'll be an uncompressed RR
* with uncompressed dname and uncompressed rdata (dnames) */
static int
decompress_rr_into_buffer(struct sldns_buffer* buf, uint8_t* pkt,
size_t pktlen, uint8_t* dname, uint16_t rr_type, uint16_t rr_class,
uint32_t rr_ttl, uint8_t* rr_data, uint16_t rr_rdlen)
{
sldns_buffer pktbuf;
size_t dname_len = 0;
size_t rdlenpos;
size_t rdlen;
uint8_t* rd;
const sldns_rr_descriptor* desc;
sldns_buffer_init_frm_data(&pktbuf, pkt, pktlen);
sldns_buffer_clear(buf);
/* decompress dname */
sldns_buffer_set_position(&pktbuf,
(size_t)(dname - sldns_buffer_current(&pktbuf)));
dname_len = pkt_dname_len(&pktbuf);
if(dname_len == 0) return 0; /* parse fail on dname */
if(!sldns_buffer_available(buf, dname_len)) return 0;
dname_pkt_copy(&pktbuf, sldns_buffer_current(buf), dname);
sldns_buffer_skip(buf, (ssize_t)dname_len);
/* type, class, ttl and rdatalength fields */
if(!sldns_buffer_available(buf, 10)) return 0;
sldns_buffer_write_u16(buf, rr_type);
sldns_buffer_write_u16(buf, rr_class);
sldns_buffer_write_u32(buf, rr_ttl);
rdlenpos = sldns_buffer_position(buf);
sldns_buffer_write_u16(buf, 0); /* rd length position */
/* decompress rdata */
desc = sldns_rr_descript(rr_type);
rd = rr_data;
rdlen = rr_rdlen;
if(rdlen > 0 && desc && desc->_dname_count > 0) {
int count = (int)desc->_dname_count;
int rdf = 0;
size_t len; /* how much rdata to plain copy */
size_t uncompressed_len, compressed_len;
size_t oldpos;
/* decompress dnames. */
while(rdlen > 0 && count) {
switch(desc->_wireformat[rdf]) {
case LDNS_RDF_TYPE_DNAME:
sldns_buffer_set_position(&pktbuf,
(size_t)(rd -
sldns_buffer_begin(&pktbuf)));
oldpos = sldns_buffer_position(&pktbuf);
/* moves pktbuf to right after the
* compressed dname, and returns uncompressed
* dname length */
uncompressed_len = pkt_dname_len(&pktbuf);
if(!uncompressed_len)
return 0; /* parse error in dname */
if(!sldns_buffer_available(buf,
uncompressed_len))
/* dname too long for buffer */
return 0;
dname_pkt_copy(&pktbuf,
sldns_buffer_current(buf), rd);
sldns_buffer_skip(buf, (ssize_t)uncompressed_len);
compressed_len = sldns_buffer_position(
&pktbuf) - oldpos;
rd += compressed_len;
rdlen -= compressed_len;
count--;
len = 0;
break;
case LDNS_RDF_TYPE_STR:
len = rd[0] + 1;
break;
default:
len = get_rdf_size(desc->_wireformat[rdf]);
break;
}
if(len) {
if(!sldns_buffer_available(buf, len))
return 0; /* too long for buffer */
sldns_buffer_write(buf, rd, len);
rd += len;
rdlen -= len;
}
rdf++;
}
}
/* copy remaining data */
if(rdlen > 0) {
if(!sldns_buffer_available(buf, rdlen)) return 0;
sldns_buffer_write(buf, rd, rdlen);
}
/* fixup rdlength */
sldns_buffer_write_u16_at(buf, rdlenpos,
sldns_buffer_position(buf)-rdlenpos-2);
sldns_buffer_flip(buf);
return 1;
}
/** insert RR into zone, from packet, decompress RR,
* if duplicate is nonNULL set the flag but otherwise ignore duplicates */
static int
az_insert_rr_decompress(struct auth_zone* z, uint8_t* pkt, size_t pktlen,
struct sldns_buffer* scratch_buffer, uint8_t* dname, uint16_t rr_type,
uint16_t rr_class, uint32_t rr_ttl, uint8_t* rr_data,
uint16_t rr_rdlen, int* duplicate)
{
uint8_t* rr;
size_t rr_len;
size_t dname_len;
if(!decompress_rr_into_buffer(scratch_buffer, pkt, pktlen, dname,
rr_type, rr_class, rr_ttl, rr_data, rr_rdlen)) {
log_err("could not decompress RR");
return 0;
}
rr = sldns_buffer_begin(scratch_buffer);
rr_len = sldns_buffer_limit(scratch_buffer);
dname_len = dname_valid(rr, rr_len);
return az_insert_rr(z, rr, rr_len, dname_len, duplicate);
}
/** remove RR from zone, from packet, decompress RR,
* if nonexist is nonNULL set the flag but otherwise ignore nonexisting entries*/
static int
az_remove_rr_decompress(struct auth_zone* z, uint8_t* pkt, size_t pktlen,
struct sldns_buffer* scratch_buffer, uint8_t* dname, uint16_t rr_type,
uint16_t rr_class, uint32_t rr_ttl, uint8_t* rr_data,
uint16_t rr_rdlen, int* nonexist)
{
uint8_t* rr;
size_t rr_len;
size_t dname_len;
if(!decompress_rr_into_buffer(scratch_buffer, pkt, pktlen, dname,
rr_type, rr_class, rr_ttl, rr_data, rr_rdlen)) {
log_err("could not decompress RR");
return 0;
}
rr = sldns_buffer_begin(scratch_buffer);
rr_len = sldns_buffer_limit(scratch_buffer);
dname_len = dname_valid(rr, rr_len);
return az_remove_rr(z, rr, rr_len, dname_len, nonexist);
}
/**
* Parse zonefile
* @param z: zone to read in.
* @param in: file to read from (just opened).
* @param rr: buffer to use for RRs, 64k.
* passed so that recursive includes can use the same buffer and do
* not grow the stack too much.
* @param rrbuflen: sizeof rr buffer.
* @param state: parse state with $ORIGIN, $TTL and 'prev-dname' and so on,
* that is kept between includes.
* The lineno is set at 1 and then increased by the function.
* @param fname: file name.
* @param depth: recursion depth for includes
* @param cfg: config for chroot.
* returns false on failure, has printed an error message
*/
static int
az_parse_file(struct auth_zone* z, FILE* in, uint8_t* rr, size_t rrbuflen,
struct sldns_file_parse_state* state, char* fname, int depth,
struct config_file* cfg)
{
size_t rr_len, dname_len;
int status;
state->lineno = 1;
while(!feof(in)) {
rr_len = rrbuflen;
dname_len = 0;
status = sldns_fp2wire_rr_buf(in, rr, &rr_len, &dname_len,
state);
if(status == LDNS_WIREPARSE_ERR_INCLUDE && rr_len == 0) {
/* we have $INCLUDE or $something */
if(strncmp((char*)rr, "$INCLUDE ", 9) == 0 ||
strncmp((char*)rr, "$INCLUDE\t", 9) == 0) {
FILE* inc;
int lineno_orig = state->lineno;
char* incfile = (char*)rr + 8;
if(depth > MAX_INCLUDE_DEPTH) {
log_err("%s:%d max include depth"
"exceeded", fname, state->lineno);
return 0;
}
/* skip spaces */
while(*incfile == ' ' || *incfile == '\t')
incfile++;
/* adjust for chroot on include file */
if(cfg->chrootdir && cfg->chrootdir[0] &&
strncmp(incfile, cfg->chrootdir,
strlen(cfg->chrootdir)) == 0)
incfile += strlen(cfg->chrootdir);
incfile = strdup(incfile);
if(!incfile) {
log_err("malloc failure");
return 0;
}
verbose(VERB_ALGO, "opening $INCLUDE %s",
incfile);
inc = fopen(incfile, "r");
if(!inc) {
log_err("%s:%d cannot open include "
"file %s: %s", fname,
lineno_orig, incfile,
strerror(errno));
free(incfile);
return 0;
}
/* recurse read that file now */
if(!az_parse_file(z, inc, rr, rrbuflen,
state, incfile, depth+1, cfg)) {
log_err("%s:%d cannot parse include "
"file %s", fname,
lineno_orig, incfile);
fclose(inc);
free(incfile);
return 0;
}
fclose(inc);
verbose(VERB_ALGO, "done with $INCLUDE %s",
incfile);
free(incfile);
state->lineno = lineno_orig;
}
continue;
}
if(status != 0) {
log_err("parse error %s %d:%d: %s", fname,
state->lineno, LDNS_WIREPARSE_OFFSET(status),
sldns_get_errorstr_parse(status));
return 0;
}
if(rr_len == 0) {
/* EMPTY line, TTL or ORIGIN */
continue;
}
/* insert wirerr in rrbuf */
if(!az_insert_rr(z, rr, rr_len, dname_len, NULL)) {
char buf[17];
sldns_wire2str_type_buf(sldns_wirerr_get_type(rr,
rr_len, dname_len), buf, sizeof(buf));
log_err("%s:%d cannot insert RR of type %s",
fname, state->lineno, buf);
return 0;
}
}
return 1;
}
int
auth_zone_read_zonefile(struct auth_zone* z, struct config_file* cfg)
{
uint8_t rr[LDNS_RR_BUF_SIZE];
struct sldns_file_parse_state state;
char* zfilename;
FILE* in;
if(!z || !z->zonefile || z->zonefile[0]==0)
return 1; /* no file, or "", nothing to read */
zfilename = z->zonefile;
if(cfg->chrootdir && cfg->chrootdir[0] && strncmp(zfilename,
cfg->chrootdir, strlen(cfg->chrootdir)) == 0)
zfilename += strlen(cfg->chrootdir);
if(verbosity >= VERB_ALGO) {
char nm[255+1];
dname_str(z->name, nm);
verbose(VERB_ALGO, "read zonefile %s for %s", zfilename, nm);
}
in = fopen(zfilename, "r");
if(!in) {
char* n = sldns_wire2str_dname(z->name, z->namelen);
if(z->zone_is_slave && errno == ENOENT) {
/* we fetch the zone contents later, no file yet */
verbose(VERB_ALGO, "no zonefile %s for %s",
zfilename, n?n:"error");
free(n);
return 1;
}
log_err("cannot open zonefile %s for %s: %s",
zfilename, n?n:"error", strerror(errno));
free(n);
return 0;
}
/* clear the data tree */
traverse_postorder(&z->data, auth_data_del, NULL);
rbtree_init(&z->data, &auth_data_cmp);
/* clear the RPZ policies */
if(z->rpz)
rpz_clear(z->rpz);
memset(&state, 0, sizeof(state));
/* default TTL to 3600 */
state.default_ttl = 3600;
/* set $ORIGIN to the zone name */
if(z->namelen <= sizeof(state.origin)) {
memcpy(state.origin, z->name, z->namelen);
state.origin_len = z->namelen;
}
/* parse the (toplevel) file */
if(!az_parse_file(z, in, rr, sizeof(rr), &state, zfilename, 0, cfg)) {
char* n = sldns_wire2str_dname(z->name, z->namelen);
log_err("error parsing zonefile %s for %s",
zfilename, n?n:"error");
free(n);
fclose(in);
return 0;
}
fclose(in);
if(z->rpz)
rpz_finish_config(z->rpz);
return 1;
}
/** write buffer to file and check return codes */
static int
write_out(FILE* out, const char* str, size_t len)
{
size_t r;
if(len == 0)
return 1;
r = fwrite(str, 1, len, out);
if(r == 0) {
log_err("write failed: %s", strerror(errno));
return 0;
} else if(r < len) {
log_err("write failed: too short (disk full?)");
return 0;
}
return 1;
}
/** convert auth rr to string */
static int
auth_rr_to_string(uint8_t* nm, size_t nmlen, uint16_t tp, uint16_t cl,
struct packed_rrset_data* data, size_t i, char* s, size_t buflen)
{
int w = 0;
size_t slen = buflen, datlen;
uint8_t* dat;
if(i >= data->count) tp = LDNS_RR_TYPE_RRSIG;
dat = nm;
datlen = nmlen;
w += sldns_wire2str_dname_scan(&dat, &datlen, &s, &slen, NULL, 0, NULL);
w += sldns_str_print(&s, &slen, "\t");
w += sldns_str_print(&s, &slen, "%lu\t", (unsigned long)data->rr_ttl[i]);
w += sldns_wire2str_class_print(&s, &slen, cl);
w += sldns_str_print(&s, &slen, "\t");
w += sldns_wire2str_type_print(&s, &slen, tp);
w += sldns_str_print(&s, &slen, "\t");
datlen = data->rr_len[i]-2;
dat = data->rr_data[i]+2;
w += sldns_wire2str_rdata_scan(&dat, &datlen, &s, &slen, tp, NULL, 0, NULL);
if(tp == LDNS_RR_TYPE_DNSKEY) {
w += sldns_str_print(&s, &slen, " ;{id = %u}",
sldns_calc_keytag_raw(data->rr_data[i]+2,
data->rr_len[i]-2));
}
w += sldns_str_print(&s, &slen, "\n");
if(w >= (int)buflen) {
log_nametypeclass(NO_VERBOSE, "RR too long to print", nm, tp, cl);
return 0;
}
return 1;
}
/** write rrset to file */
static int
auth_zone_write_rrset(struct auth_zone* z, struct auth_data* node,
struct auth_rrset* r, FILE* out)
{
size_t i, count = r->data->count + r->data->rrsig_count;
char buf[LDNS_RR_BUF_SIZE];
for(i=0; i<count; i++) {
if(!auth_rr_to_string(node->name, node->namelen, r->type,
z->dclass, r->data, i, buf, sizeof(buf))) {
verbose(VERB_ALGO, "failed to rr2str rr %d", (int)i);
continue;
}
if(!write_out(out, buf, strlen(buf)))
return 0;
}
return 1;
}
/** write domain to file */
static int
auth_zone_write_domain(struct auth_zone* z, struct auth_data* n, FILE* out)
{
struct auth_rrset* r;
/* if this is zone apex, write SOA first */
if(z->namelen == n->namelen) {
struct auth_rrset* soa = az_domain_rrset(n, LDNS_RR_TYPE_SOA);
if(soa) {
if(!auth_zone_write_rrset(z, n, soa, out))
return 0;
}
}
/* write all the RRsets for this domain */
for(r = n->rrsets; r; r = r->next) {
if(z->namelen == n->namelen &&
r->type == LDNS_RR_TYPE_SOA)
continue; /* skip SOA here */
if(!auth_zone_write_rrset(z, n, r, out))
return 0;
}
return 1;
}
int auth_zone_write_file(struct auth_zone* z, const char* fname)
{
FILE* out;
struct auth_data* n;
out = fopen(fname, "w");
if(!out) {
log_err("could not open %s: %s", fname, strerror(errno));
return 0;
}
RBTREE_FOR(n, struct auth_data*, &z->data) {
if(!auth_zone_write_domain(z, n, out)) {
log_err("could not write domain to %s", fname);
fclose(out);
return 0;
}
}
fclose(out);
return 1;
}
/** offline verify for zonemd, while reading a zone file to immediately
* spot bad hashes in zonefile as they are read.
* Creates temp buffers, but uses anchors and validation environment
* from the module_env. */
static void
zonemd_offline_verify(struct auth_zone* z, struct module_env* env_for_val,
struct module_stack* mods)
{
struct module_env env;
time_t now = 0;
if(!z->zonemd_check)
return;
env = *env_for_val;
env.scratch_buffer = sldns_buffer_new(env.cfg->msg_buffer_size);
if(!env.scratch_buffer) {
log_err("out of memory");
goto clean_exit;
}
env.scratch = regional_create();
if(!env.now) {
env.now = &now;
now = time(NULL);
}
if(!env.scratch) {
log_err("out of memory");
goto clean_exit;
}
auth_zone_verify_zonemd(z, &env, mods, NULL, 1, 0);
clean_exit:
/* clean up and exit */
sldns_buffer_free(env.scratch_buffer);
regional_destroy(env.scratch);
}
/** read all auth zones from file (if they have) */
static int
auth_zones_read_zones(struct auth_zones* az, struct config_file* cfg,
struct module_env* env, struct module_stack* mods)
{
struct auth_zone* z;
lock_rw_wrlock(&az->lock);
RBTREE_FOR(z, struct auth_zone*, &az->ztree) {
lock_rw_wrlock(&z->lock);
if(!auth_zone_read_zonefile(z, cfg)) {
lock_rw_unlock(&z->lock);
lock_rw_unlock(&az->lock);
return 0;
}
if(z->zonefile && z->zonefile[0]!=0 && env)
zonemd_offline_verify(z, env, mods);
lock_rw_unlock(&z->lock);
}
lock_rw_unlock(&az->lock);
return 1;
}
/** fetch the content of a ZONEMD RR from the rdata */
static int zonemd_fetch_parameters(struct auth_rrset* zonemd_rrset, size_t i,
uint32_t* serial, int* scheme, int* hashalgo, uint8_t** hash,
size_t* hashlen)
{
size_t rr_len;
uint8_t* rdata;
if(i >= zonemd_rrset->data->count)
return 0;
rr_len = zonemd_rrset->data->rr_len[i];
if(rr_len < 2+4+1+1)
return 0; /* too short, for rdlen+serial+scheme+algo */
rdata = zonemd_rrset->data->rr_data[i];
*serial = sldns_read_uint32(rdata+2);
*scheme = rdata[6];
*hashalgo = rdata[7];
*hashlen = rr_len - 8;
if(*hashlen == 0)
*hash = NULL;
else *hash = rdata+8;
return 1;
}
/**
* See if the ZONEMD scheme, hash occurs more than once.
* @param zonemd_rrset: the zonemd rrset to check with the RRs in it.
* @param index: index of the original, this is allowed to have that
* scheme and hashalgo, but other RRs should not have it.
* @param scheme: the scheme to check for.
* @param hashalgo: the hash algorithm to check for.
* @return true if it occurs more than once.
*/
static int zonemd_is_duplicate_scheme_hash(struct auth_rrset* zonemd_rrset,
size_t index, int scheme, int hashalgo)
{
size_t j;
for(j=0; j<zonemd_rrset->data->count; j++) {
uint32_t serial2 = 0;
int scheme2 = 0, hashalgo2 = 0;
uint8_t* hash2 = NULL;
size_t hashlen2 = 0;
if(index == j) {
/* this is the original */
continue;
}
if(!zonemd_fetch_parameters(zonemd_rrset, j, &serial2,
&scheme2, &hashalgo2, &hash2, &hashlen2)) {
/* malformed, skip it */
continue;
}
if(scheme == scheme2 && hashalgo == hashalgo2) {
/* duplicate scheme, hash */
verbose(VERB_ALGO, "zonemd duplicate for scheme %d "
"and hash %d", scheme, hashalgo);
return 1;
}
}
return 0;
}
/**
* Check ZONEMDs if present for the auth zone. Depending on config
* it can warn or fail on that. Checks the hash of the ZONEMD.
* @param z: auth zone to check for.
* caller must hold lock on zone.
* @param env: module env for temp buffers.
* @param reason: returned on failure.
* @return false on failure, true if hash checks out.
*/
static int auth_zone_zonemd_check_hash(struct auth_zone* z,
struct module_env* env, char** reason)
{
/* loop over ZONEMDs and see which one is valid. if not print
* failure (depending on config) */
struct auth_data* apex;
struct auth_rrset* zonemd_rrset;
size_t i;
struct regional* region = NULL;
struct sldns_buffer* buf = NULL;
uint32_t soa_serial = 0;
char* unsupported_reason = NULL;
int only_unsupported = 1;
region = env->scratch;
regional_free_all(region);
buf = env->scratch_buffer;
if(!auth_zone_get_serial(z, &soa_serial)) {
*reason = "zone has no SOA serial";
return 0;
}
apex = az_find_name(z, z->name, z->namelen);
if(!apex) {
*reason = "zone has no apex";
return 0;
}
zonemd_rrset = az_domain_rrset(apex, LDNS_RR_TYPE_ZONEMD);
if(!zonemd_rrset || zonemd_rrset->data->count==0) {
*reason = "zone has no ZONEMD";
return 0; /* no RRset or no RRs in rrset */
}
/* we have a ZONEMD, check if it is correct */
for(i=0; i<zonemd_rrset->data->count; i++) {
uint32_t serial = 0;
int scheme = 0, hashalgo = 0;
uint8_t* hash = NULL;
size_t hashlen = 0;
if(!zonemd_fetch_parameters(zonemd_rrset, i, &serial, &scheme,
&hashalgo, &hash, &hashlen)) {
/* malformed RR */
*reason = "ZONEMD rdata malformed";
only_unsupported = 0;
continue;
}
/* check for duplicates */
if(zonemd_is_duplicate_scheme_hash(zonemd_rrset, i, scheme,
hashalgo)) {
/* duplicate hash of the same scheme,hash
* is not allowed. */
*reason = "ZONEMD RRSet contains more than one RR "
"with the same scheme and hash algorithm";
only_unsupported = 0;
continue;
}
regional_free_all(region);
if(serial != soa_serial) {
*reason = "ZONEMD serial is wrong";
only_unsupported = 0;
continue;
}
*reason = NULL;
if(auth_zone_generate_zonemd_check(z, scheme, hashalgo,
hash, hashlen, region, buf, reason)) {
/* success */
if(*reason) {
if(!unsupported_reason)
unsupported_reason = *reason;
/* continue to check for valid ZONEMD */
if(verbosity >= VERB_ALGO) {
char zstr[255+1];
dname_str(z->name, zstr);
verbose(VERB_ALGO, "auth-zone %s ZONEMD %d %d is unsupported: %s", zstr, (int)scheme, (int)hashalgo, *reason);
}
*reason = NULL;
continue;
}
if(verbosity >= VERB_ALGO) {
char zstr[255+1];
dname_str(z->name, zstr);
if(!*reason)
verbose(VERB_ALGO, "auth-zone %s ZONEMD hash is correct", zstr);
}
return 1;
}
only_unsupported = 0;
/* try next one */
}
/* have we seen no failures but only unsupported algo,
* and one unsupported algorithm, or more. */
if(only_unsupported && unsupported_reason) {
/* only unsupported algorithms, with valid serial, not
* malformed. Did not see supported algorithms, failed or
* successful ones. */
*reason = unsupported_reason;
return 1;
}
/* fail, we may have reason */
if(!*reason)
*reason = "no ZONEMD records found";
if(verbosity >= VERB_ALGO) {
char zstr[255+1];
dname_str(z->name, zstr);
verbose(VERB_ALGO, "auth-zone %s ZONEMD failed: %s", zstr, *reason);
}
return 0;
}
/** find the apex SOA RRset, if it exists */
struct auth_rrset* auth_zone_get_soa_rrset(struct auth_zone* z)
{
struct auth_data* apex;
struct auth_rrset* soa;
apex = az_find_name(z, z->name, z->namelen);
if(!apex) return NULL;
soa = az_domain_rrset(apex, LDNS_RR_TYPE_SOA);
return soa;
}
/** find serial number of zone or false if none */
int
auth_zone_get_serial(struct auth_zone* z, uint32_t* serial)
{
struct auth_data* apex;
struct auth_rrset* soa;
struct packed_rrset_data* d;
apex = az_find_name(z, z->name, z->namelen);
if(!apex) return 0;
soa = az_domain_rrset(apex, LDNS_RR_TYPE_SOA);
if(!soa || soa->data->count==0)
return 0; /* no RRset or no RRs in rrset */
if(soa->data->rr_len[0] < 2+4*5) return 0; /* SOA too short */
d = soa->data;
*serial = sldns_read_uint32(d->rr_data[0]+(d->rr_len[0]-20));
return 1;
}
/** Find auth_zone SOA and populate the values in xfr(soa values). */
int
xfr_find_soa(struct auth_zone* z, struct auth_xfer* xfr)
{
struct auth_data* apex;
struct auth_rrset* soa;
struct packed_rrset_data* d;
apex = az_find_name(z, z->name, z->namelen);
if(!apex) return 0;
soa = az_domain_rrset(apex, LDNS_RR_TYPE_SOA);
if(!soa || soa->data->count==0)
return 0; /* no RRset or no RRs in rrset */
if(soa->data->rr_len[0] < 2+4*5) return 0; /* SOA too short */
/* SOA record ends with serial, refresh, retry, expiry, minimum,
* as 4 byte fields */
d = soa->data;
xfr->have_zone = 1;
xfr->serial = sldns_read_uint32(d->rr_data[0]+(d->rr_len[0]-20));
xfr->refresh = sldns_read_uint32(d->rr_data[0]+(d->rr_len[0]-16));
xfr->retry = sldns_read_uint32(d->rr_data[0]+(d->rr_len[0]-12));
xfr->expiry = sldns_read_uint32(d->rr_data[0]+(d->rr_len[0]-8));
/* soa minimum at d->rr_len[0]-4 */
return 1;
}
/**
* Setup auth_xfer zone
* This populates the have_zone, soa values, and so on times.
* Doesn't do network traffic yet, can set option flags.
* @param z: locked by caller, and modified for setup
* @param x: locked by caller, and modified.
* @return false on failure.
*/
static int
auth_xfer_setup(struct auth_zone* z, struct auth_xfer* x)
{
/* for a zone without zone transfers, x==NULL, so skip them,
* i.e. the zone config is fixed with no masters or urls */
if(!z || !x) return 1;
if(!xfr_find_soa(z, x)) {
return 1;
}
/* nothing for probe, nextprobe and transfer tasks */
return 1;
}
/**
* Setup all zones
* @param az: auth zones structure
* @return false on failure.
*/
static int
auth_zones_setup_zones(struct auth_zones* az)
{
struct auth_zone* z;
struct auth_xfer* x;
lock_rw_wrlock(&az->lock);
RBTREE_FOR(z, struct auth_zone*, &az->ztree) {
lock_rw_wrlock(&z->lock);
x = auth_xfer_find(az, z->name, z->namelen, z->dclass);
if(x) {
lock_basic_lock(&x->lock);
}
if(!auth_xfer_setup(z, x)) {
if(x) {
lock_basic_unlock(&x->lock);
}
lock_rw_unlock(&z->lock);
lock_rw_unlock(&az->lock);
return 0;
}
if(x) {
lock_basic_unlock(&x->lock);
}
lock_rw_unlock(&z->lock);
}
lock_rw_unlock(&az->lock);
return 1;
}
/** set config items and create zones */
static int
auth_zones_cfg(struct auth_zones* az, struct config_auth* c)
{
struct auth_zone* z;
struct auth_xfer* x = NULL;
/* create zone */
if(c->isrpz) {
/* if the rpz lock is needed, grab it before the other
* locks to avoid a lock dependency cycle */
lock_rw_wrlock(&az->rpz_lock);
}
lock_rw_wrlock(&az->lock);
if(!(z=auth_zones_find_or_add_zone(az, c->name))) {
lock_rw_unlock(&az->lock);
if(c->isrpz) {
lock_rw_unlock(&az->rpz_lock);
}
return 0;
}
if(c->masters || c->urls) {
if(!(x=auth_zones_find_or_add_xfer(az, z))) {
lock_rw_unlock(&az->lock);
lock_rw_unlock(&z->lock);
if(c->isrpz) {
lock_rw_unlock(&az->rpz_lock);
}
return 0;
}
}
if(c->for_downstream)
az->have_downstream = 1;
lock_rw_unlock(&az->lock);
/* set options */
z->zone_deleted = 0;
if(!auth_zone_set_zonefile(z, c->zonefile)) {
if(x) {
lock_basic_unlock(&x->lock);
}
lock_rw_unlock(&z->lock);
if(c->isrpz) {
lock_rw_unlock(&az->rpz_lock);
}
return 0;
}
z->for_downstream = c->for_downstream;
z->for_upstream = c->for_upstream;
z->fallback_enabled = c->fallback_enabled;
z->zonemd_check = c->zonemd_check;
z->zonemd_reject_absence = c->zonemd_reject_absence;
if(c->isrpz && !z->rpz){
if(!(z->rpz = rpz_create(c))){
fatal_exit("Could not setup RPZ zones");
return 0;
}
lock_protect(&z->lock, &z->rpz->local_zones, sizeof(*z->rpz));
/* the az->rpz_lock is locked above */
z->rpz_az_next = az->rpz_first;
if(az->rpz_first)
az->rpz_first->rpz_az_prev = z;
az->rpz_first = z;
}
if(c->isrpz) {
lock_rw_unlock(&az->rpz_lock);
}
/* xfer zone */
if(x) {
z->zone_is_slave = 1;
/* set options on xfer zone */
if(!xfer_set_masters(&x->task_probe->masters, c, 0)) {
lock_basic_unlock(&x->lock);
lock_rw_unlock(&z->lock);
return 0;
}
if(!xfer_set_masters(&x->task_transfer->masters, c, 1)) {
lock_basic_unlock(&x->lock);
lock_rw_unlock(&z->lock);
return 0;
}
lock_basic_unlock(&x->lock);
}
lock_rw_unlock(&z->lock);
return 1;
}
/** set all auth zones deleted, then in auth_zones_cfg, it marks them
* as nondeleted (if they are still in the config), and then later
* we can find deleted zones */
static void
az_setall_deleted(struct auth_zones* az)
{
struct auth_zone* z;
lock_rw_wrlock(&az->lock);
RBTREE_FOR(z, struct auth_zone*, &az->ztree) {
lock_rw_wrlock(&z->lock);
z->zone_deleted = 1;
lock_rw_unlock(&z->lock);
}
lock_rw_unlock(&az->lock);
}
/** find zones that are marked deleted and delete them.
* This is called from apply_cfg, and there are no threads and no
* workers, so the xfr can just be deleted. */
static void
az_delete_deleted_zones(struct auth_zones* az)
{
struct auth_zone* z;
struct auth_zone* delete_list = NULL, *next;
struct auth_xfer* xfr;
lock_rw_wrlock(&az->lock);
RBTREE_FOR(z, struct auth_zone*, &az->ztree) {
lock_rw_wrlock(&z->lock);
if(z->zone_deleted) {
/* we cannot alter the rbtree right now, but
* we can put it on a linked list and then
* delete it */
z->delete_next = delete_list;
delete_list = z;
}
lock_rw_unlock(&z->lock);
}
/* now we are out of the tree loop and we can loop and delete
* the zones */
z = delete_list;
while(z) {
next = z->delete_next;
xfr = auth_xfer_find(az, z->name, z->namelen, z->dclass);
if(xfr) {
(void)rbtree_delete(&az->xtree, &xfr->node);
auth_xfer_delete(xfr);
}
(void)rbtree_delete(&az->ztree, &z->node);
auth_zone_delete(z, az);
z = next;
}
lock_rw_unlock(&az->lock);
}
int auth_zones_apply_cfg(struct auth_zones* az, struct config_file* cfg,
int setup, int* is_rpz, struct module_env* env,
struct module_stack* mods)
{
struct config_auth* p;
az_setall_deleted(az);
for(p = cfg->auths; p; p = p->next) {
if(!p->name || p->name[0] == 0) {
log_warn("auth-zone without a name, skipped");
continue;
}
*is_rpz = (*is_rpz || p->isrpz);
if(!auth_zones_cfg(az, p)) {
log_err("cannot config auth zone %s", p->name);
return 0;
}
}
az_delete_deleted_zones(az);
if(!auth_zones_read_zones(az, cfg, env, mods))
return 0;
if(setup) {
if(!auth_zones_setup_zones(az))
return 0;
}
return 1;
}
/** delete chunks
* @param at: transfer structure with chunks list. The chunks and their
* data are freed.
*/
static void
auth_chunks_delete(struct auth_transfer* at)
{
if(at->chunks_first) {
struct auth_chunk* c, *cn;
c = at->chunks_first;
while(c) {
cn = c->next;
free(c->data);
free(c);
c = cn;
}
}
at->chunks_first = NULL;
at->chunks_last = NULL;
}
/** free master addr list */
static void
auth_free_master_addrs(struct auth_addr* list)
{
struct auth_addr *n;
while(list) {
n = list->next;
free(list);
list = n;
}
}
/** free the masters list */
static void
auth_free_masters(struct auth_master* list)
{
struct auth_master* n;
while(list) {
n = list->next;
auth_free_master_addrs(list->list);
free(list->host);
free(list->file);
free(list);
list = n;
}
}
/** delete auth xfer structure
* @param xfr: delete this xfer and its tasks.
*/
static void
auth_xfer_delete(struct auth_xfer* xfr)
{
if(!xfr) return;
lock_basic_destroy(&xfr->lock);
free(xfr->name);
if(xfr->task_nextprobe) {
comm_timer_delete(xfr->task_nextprobe->timer);
free(xfr->task_nextprobe);
}
if(xfr->task_probe) {
auth_free_masters(xfr->task_probe->masters);
comm_point_delete(xfr->task_probe->cp);
comm_timer_delete(xfr->task_probe->timer);
free(xfr->task_probe);
}
if(xfr->task_transfer) {
auth_free_masters(xfr->task_transfer->masters);
comm_point_delete(xfr->task_transfer->cp);
comm_timer_delete(xfr->task_transfer->timer);
if(xfr->task_transfer->chunks_first) {
auth_chunks_delete(xfr->task_transfer);
}
free(xfr->task_transfer);
}
auth_free_masters(xfr->allow_notify_list);
free(xfr);
}
/** helper traverse to delete zones */
static void
auth_zone_del(rbnode_type* n, void* ATTR_UNUSED(arg))
{
struct auth_zone* z = (struct auth_zone*)n->key;
auth_zone_delete(z, NULL);
}
/** helper traverse to delete xfer zones */
static void
auth_xfer_del(rbnode_type* n, void* ATTR_UNUSED(arg))
{
struct auth_xfer* z = (struct auth_xfer*)n->key;
auth_xfer_delete(z);
}
void auth_zones_delete(struct auth_zones* az)
{
if(!az) return;
lock_rw_destroy(&az->lock);
lock_rw_destroy(&az->rpz_lock);
traverse_postorder(&az->ztree, auth_zone_del, NULL);
traverse_postorder(&az->xtree, auth_xfer_del, NULL);
free(az);
}
/** true if domain has only nsec3 */
static int
domain_has_only_nsec3(struct auth_data* n)
{
struct auth_rrset* rrset = n->rrsets;
int nsec3_seen = 0;
while(rrset) {
if(rrset->type == LDNS_RR_TYPE_NSEC3) {
nsec3_seen = 1;
} else if(rrset->type != LDNS_RR_TYPE_RRSIG) {
return 0;
}
rrset = rrset->next;
}
return nsec3_seen;
}
/** see if the domain has a wildcard child '*.domain' */
static struct auth_data*
az_find_wildcard_domain(struct auth_zone* z, uint8_t* nm, size_t nmlen)
{
uint8_t wc[LDNS_MAX_DOMAINLEN];
if(nmlen+2 > sizeof(wc))
return NULL; /* result would be too long */
wc[0] = 1; /* length of wildcard label */
wc[1] = (uint8_t)'*'; /* wildcard label */
memmove(wc+2, nm, nmlen);
return az_find_name(z, wc, nmlen+2);
}
/** find wildcard between qname and cename */
static struct auth_data*
az_find_wildcard(struct auth_zone* z, struct query_info* qinfo,
struct auth_data* ce)
{
uint8_t* nm = qinfo->qname;
size_t nmlen = qinfo->qname_len;
struct auth_data* node;
if(!dname_subdomain_c(nm, z->name))
return NULL; /* out of zone */
while((node=az_find_wildcard_domain(z, nm, nmlen))==NULL) {
/* see if we can go up to find the wildcard */
if(nmlen == z->namelen)
return NULL; /* top of zone reached */
if(ce && nmlen == ce->namelen)
return NULL; /* ce reached */
if(dname_is_root(nm))
return NULL; /* cannot go up */
dname_remove_label(&nm, &nmlen);
}
return node;
}
/** domain is not exact, find first candidate ce (name that matches
* a part of qname) in tree */
static struct auth_data*
az_find_candidate_ce(struct auth_zone* z, struct query_info* qinfo,
struct auth_data* n)
{
uint8_t* nm;
size_t nmlen;
if(n) {
nm = dname_get_shared_topdomain(qinfo->qname, n->name);
} else {
nm = qinfo->qname;
}
dname_count_size_labels(nm, &nmlen);
n = az_find_name(z, nm, nmlen);
/* delete labels and go up on name */
while(!n) {
if(dname_is_root(nm))
return NULL; /* cannot go up */
dname_remove_label(&nm, &nmlen);
n = az_find_name(z, nm, nmlen);
}
return n;
}
/** go up the auth tree to next existing name. */
static struct auth_data*
az_domain_go_up(struct auth_zone* z, struct auth_data* n)
{
uint8_t* nm = n->name;
size_t nmlen = n->namelen;
while(!dname_is_root(nm)) {
dname_remove_label(&nm, &nmlen);
if((n=az_find_name(z, nm, nmlen)) != NULL)
return n;
}
return NULL;
}
/** Find the closest encloser, an name that exists and is above the
* qname.
* return true if the node (param node) is existing, nonobscured and
* can be used to generate answers from. It is then also node_exact.
* returns false if the node is not good enough (or it wasn't node_exact)
* in this case the ce can be filled.
* if ce is NULL, no ce exists, and likely the zone is completely empty,
* not even with a zone apex.
* if ce is nonNULL it is the closest enclosing upper name (that exists
* itself for answer purposes). That name may have DNAME, NS or wildcard
* rrset is the closest DNAME or NS rrset that was found.
*/
static int
az_find_ce(struct auth_zone* z, struct query_info* qinfo,
struct auth_data* node, int node_exact, struct auth_data** ce,
struct auth_rrset** rrset)
{
struct auth_data* n = node;
+ struct auth_rrset* lookrrset;
*ce = NULL;
*rrset = NULL;
if(!node_exact) {
/* if not exact, lookup closest exact match */
n = az_find_candidate_ce(z, qinfo, n);
} else {
/* if exact, the node itself is the first candidate ce */
*ce = n;
}
/* no direct answer from nsec3-only domains */
if(n && domain_has_only_nsec3(n)) {
node_exact = 0;
*ce = NULL;
}
/* with exact matches, walk up the labels until we find the
* delegation, or DNAME or zone end */
while(n) {
/* see if the current candidate has issues */
/* not zone apex and has type NS */
if(n->namelen != z->namelen &&
- (*rrset=az_domain_rrset(n, LDNS_RR_TYPE_NS)) &&
+ (lookrrset=az_domain_rrset(n, LDNS_RR_TYPE_NS)) &&
/* delegate here, but DS at exact the dp has notype */
(qinfo->qtype != LDNS_RR_TYPE_DS ||
n->namelen != qinfo->qname_len)) {
/* referral */
/* this is ce and the lowernode is nonexisting */
*ce = n;
- return 0;
+ *rrset = lookrrset;
+ node_exact = 0;
}
/* not equal to qname and has type DNAME */
if(n->namelen != qinfo->qname_len &&
- (*rrset=az_domain_rrset(n, LDNS_RR_TYPE_DNAME))) {
+ (lookrrset=az_domain_rrset(n, LDNS_RR_TYPE_DNAME))) {
/* this is ce and the lowernode is nonexisting */
*ce = n;
- return 0;
+ *rrset = lookrrset;
+ node_exact = 0;
}
if(*ce == NULL && !domain_has_only_nsec3(n)) {
/* if not found yet, this exact name must be
* our lowest match (but not nsec3onlydomain) */
*ce = n;
}
/* walk up the tree by removing labels from name and lookup */
n = az_domain_go_up(z, n);
}
/* found no problems, if it was an exact node, it is fine to use */
return node_exact;
}
/** add additional A/AAAA from domain names in rrset rdata (+offset)
* offset is number of bytes in rdata where the dname is located. */
static int
az_add_additionals_from(struct auth_zone* z, struct regional* region,
struct dns_msg* msg, struct auth_rrset* rrset, size_t offset)
{
struct packed_rrset_data* d = rrset->data;
size_t i;
if(!d) return 0;
for(i=0; i<d->count; i++) {
size_t dlen;
struct auth_data* domain;
struct auth_rrset* ref;
if(d->rr_len[i] < 2+offset)
continue; /* too short */
if(!(dlen = dname_valid(d->rr_data[i]+2+offset,
d->rr_len[i]-2-offset)))
continue; /* malformed */
domain = az_find_name(z, d->rr_data[i]+2+offset, dlen);
if(!domain)
continue;
if((ref=az_domain_rrset(domain, LDNS_RR_TYPE_A)) != NULL) {
if(!msg_add_rrset_ar(z, region, msg, domain, ref))
return 0;
}
if((ref=az_domain_rrset(domain, LDNS_RR_TYPE_AAAA)) != NULL) {
if(!msg_add_rrset_ar(z, region, msg, domain, ref))
return 0;
}
}
return 1;
}
/** add negative SOA record (with negative TTL) */
static int
az_add_negative_soa(struct auth_zone* z, struct regional* region,
struct dns_msg* msg)
{
time_t minimum;
size_t i;
struct packed_rrset_data* d;
struct auth_rrset* soa;
struct auth_data* apex = az_find_name(z, z->name, z->namelen);
if(!apex) return 0;
soa = az_domain_rrset(apex, LDNS_RR_TYPE_SOA);
if(!soa) return 0;
/* must be first to put in message; we want to fix the TTL with
* one RRset here, otherwise we'd need to loop over the RRs to get
* the resulting lower TTL */
log_assert(msg->rep->rrset_count == 0);
if(!msg_add_rrset_ns(z, region, msg, apex, soa)) return 0;
/* fixup TTL */
d = (struct packed_rrset_data*)msg->rep->rrsets[msg->rep->rrset_count-1]->entry.data;
/* last 4 bytes are minimum ttl in network format */
if(d->count == 0) return 0;
if(d->rr_len[0] < 2+4) return 0;
minimum = (time_t)sldns_read_uint32(d->rr_data[0]+(d->rr_len[0]-4));
minimum = d->ttl<minimum?d->ttl:minimum;
d->ttl = minimum;
for(i=0; i < d->count + d->rrsig_count; i++)
d->rr_ttl[i] = minimum;
msg->rep->ttl = get_rrset_ttl(msg->rep->rrsets[0]);
msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl);
msg->rep->serve_expired_ttl = msg->rep->ttl + SERVE_EXPIRED_TTL;
return 1;
}
/** See if the query goes to empty nonterminal (that has no auth_data,
* but there are nodes underneath. We already checked that there are
* not NS, or DNAME above, so that we only need to check if some node
* exists below (with nonempty rr list), return true if emptynonterminal */
static int
az_empty_nonterminal(struct auth_zone* z, struct query_info* qinfo,
struct auth_data* node)
{
struct auth_data* next;
if(!node) {
/* no smaller was found, use first (smallest) node as the
* next one */
next = (struct auth_data*)rbtree_first(&z->data);
} else {
next = (struct auth_data*)rbtree_next(&node->node);
}
while(next && (rbnode_type*)next != RBTREE_NULL && next->rrsets == NULL) {
/* the next name has empty rrsets, is an empty nonterminal
* itself, see if there exists something below it */
next = (struct auth_data*)rbtree_next(&node->node);
}
if((rbnode_type*)next == RBTREE_NULL || !next) {
/* there is no next node, so something below it cannot
* exist */
return 0;
}
/* a next node exists, if there was something below the query,
* this node has to be it. See if it is below the query name */
if(dname_strict_subdomain_c(next->name, qinfo->qname))
return 1;
return 0;
}
/** create synth cname target name in buffer, or fail if too long */
static size_t
synth_cname_buf(uint8_t* qname, size_t qname_len, size_t dname_len,
uint8_t* dtarg, size_t dtarglen, uint8_t* buf, size_t buflen)
{
size_t newlen = qname_len + dtarglen - dname_len;
if(newlen > buflen) {
/* YXDOMAIN error */
return 0;
}
/* new name is concatenation of qname front (without DNAME owner)
* and DNAME target name */
memcpy(buf, qname, qname_len-dname_len);
memmove(buf+(qname_len-dname_len), dtarg, dtarglen);
return newlen;
}
/** create synthetic CNAME rrset for in a DNAME answer in region,
* false on alloc failure, cname==NULL when name too long. */
static int
create_synth_cname(uint8_t* qname, size_t qname_len, struct regional* region,
struct auth_data* node, struct auth_rrset* dname, uint16_t dclass,
struct ub_packed_rrset_key** cname)
{
uint8_t buf[LDNS_MAX_DOMAINLEN];
uint8_t* dtarg;
size_t dtarglen, newlen;
struct packed_rrset_data* d;
/* get DNAME target name */
if(dname->data->count < 1) return 0;
if(dname->data->rr_len[0] < 3) return 0; /* at least rdatalen +1 */
dtarg = dname->data->rr_data[0]+2;
dtarglen = dname->data->rr_len[0]-2;
if(sldns_read_uint16(dname->data->rr_data[0]) != dtarglen)
return 0; /* rdatalen in DNAME rdata is malformed */
if(dname_valid(dtarg, dtarglen) != dtarglen)
return 0; /* DNAME RR has malformed rdata */
if(qname_len == 0)
return 0; /* too short */
if(qname_len <= node->namelen)
return 0; /* qname too short for dname removal */
/* synthesize a CNAME */
newlen = synth_cname_buf(qname, qname_len, node->namelen,
dtarg, dtarglen, buf, sizeof(buf));
if(newlen == 0) {
/* YXDOMAIN error */
*cname = NULL;
return 1;
}
*cname = (struct ub_packed_rrset_key*)regional_alloc(region,
sizeof(struct ub_packed_rrset_key));
if(!*cname)
return 0; /* out of memory */
memset(&(*cname)->entry, 0, sizeof((*cname)->entry));
(*cname)->entry.key = (*cname);
(*cname)->rk.type = htons(LDNS_RR_TYPE_CNAME);
(*cname)->rk.rrset_class = htons(dclass);
(*cname)->rk.flags = 0;
(*cname)->rk.dname = regional_alloc_init(region, qname, qname_len);
if(!(*cname)->rk.dname)
return 0; /* out of memory */
(*cname)->rk.dname_len = qname_len;
(*cname)->entry.hash = rrset_key_hash(&(*cname)->rk);
d = (struct packed_rrset_data*)regional_alloc_zero(region,
sizeof(struct packed_rrset_data) + sizeof(size_t) +
sizeof(uint8_t*) + sizeof(time_t) + sizeof(uint16_t)
+ newlen);
if(!d)
return 0; /* out of memory */
(*cname)->entry.data = d;
d->ttl = 0; /* 0 for synthesized CNAME TTL */
d->count = 1;
d->rrsig_count = 0;
d->trust = rrset_trust_ans_noAA;
d->rr_len = (size_t*)((uint8_t*)d +
sizeof(struct packed_rrset_data));
d->rr_len[0] = newlen + sizeof(uint16_t);
packed_rrset_ptr_fixup(d);
d->rr_ttl[0] = d->ttl;
sldns_write_uint16(d->rr_data[0], newlen);
memmove(d->rr_data[0] + sizeof(uint16_t), buf, newlen);
return 1;
}
/** add a synthesized CNAME to the answer section */
static int
add_synth_cname(struct auth_zone* z, uint8_t* qname, size_t qname_len,
struct regional* region, struct dns_msg* msg, struct auth_data* dname,
struct auth_rrset* rrset)
{
struct ub_packed_rrset_key* cname;
/* synthesize a CNAME */
if(!create_synth_cname(qname, qname_len, region, dname, rrset,
z->dclass, &cname)) {
/* out of memory */
return 0;
}
if(!cname) {
/* cname cannot be create because of YXDOMAIN */
msg->rep->flags |= LDNS_RCODE_YXDOMAIN;
return 1;
}
/* add cname to message */
if(!msg_grow_array(region, msg))
return 0;
msg->rep->rrsets[msg->rep->rrset_count] = cname;
msg->rep->rrset_count++;
msg->rep->an_numrrsets++;
msg_ttl(msg);
return 1;
}
/** Change a dname to a different one, for wildcard namechange */
static void
az_change_dnames(struct dns_msg* msg, uint8_t* oldname, uint8_t* newname,
size_t newlen, int an_only)
{
size_t i;
size_t start = 0, end = msg->rep->rrset_count;
if(!an_only) start = msg->rep->an_numrrsets;
if(an_only) end = msg->rep->an_numrrsets;
for(i=start; i<end; i++) {
/* allocated in region so we can change the ptrs */
if(query_dname_compare(msg->rep->rrsets[i]->rk.dname, oldname)
== 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);
}
}
}
/** find NSEC record covering the query */
static struct auth_rrset*
az_find_nsec_cover(struct auth_zone* z, struct auth_data** node)
{
uint8_t* nm = (*node)->name;
size_t nmlen = (*node)->namelen;
struct auth_rrset* rrset;
/* find the NSEC for the smallest-or-equal node */
/* if node == NULL, we did not find a smaller name. But the zone
* name is the smallest name and should have an NSEC. So there is
* no NSEC to return (for a properly signed zone) */
/* for empty nonterminals, the auth-data node should not exist,
* and thus we don't need to go rbtree_previous here to find
* a domain with an NSEC record */
/* but there could be glue, and if this is node, then it has no NSEC.
* Go up to find nonglue (previous) NSEC-holding nodes */
while((rrset=az_domain_rrset(*node, LDNS_RR_TYPE_NSEC)) == NULL) {
if(dname_is_root(nm)) return NULL;
if(nmlen == z->namelen) return NULL;
dname_remove_label(&nm, &nmlen);
/* adjust *node for the nsec rrset to find in */
*node = az_find_name(z, nm, nmlen);
}
return rrset;
}
/** Find NSEC and add for wildcard denial */
static int
az_nsec_wildcard_denial(struct auth_zone* z, struct regional* region,
struct dns_msg* msg, uint8_t* cenm, size_t cenmlen)
{
struct query_info qinfo;
int node_exact;
struct auth_data* node;
struct auth_rrset* nsec;
uint8_t wc[LDNS_MAX_DOMAINLEN];
if(cenmlen+2 > sizeof(wc))
return 0; /* result would be too long */
wc[0] = 1; /* length of wildcard label */
wc[1] = (uint8_t)'*'; /* wildcard label */
memmove(wc+2, cenm, cenmlen);
/* we have '*.ce' in wc wildcard name buffer */
/* get nsec cover for that */
qinfo.qname = wc;
qinfo.qname_len = cenmlen+2;
qinfo.qtype = 0;
qinfo.qclass = 0;
az_find_domain(z, &qinfo, &node_exact, &node);
if((nsec=az_find_nsec_cover(z, &node)) != NULL) {
if(!msg_add_rrset_ns(z, region, msg, node, nsec)) return 0;
}
return 1;
}
/** Find the NSEC3PARAM rrset (if any) and if true you have the parameters */
static int
az_nsec3_param(struct auth_zone* z, int* algo, size_t* iter, uint8_t** salt,
size_t* saltlen)
{
struct auth_data* apex;
struct auth_rrset* param;
size_t i;
apex = az_find_name(z, z->name, z->namelen);
if(!apex) return 0;
param = az_domain_rrset(apex, LDNS_RR_TYPE_NSEC3PARAM);
if(!param || param->data->count==0)
return 0; /* no RRset or no RRs in rrset */
/* find out which NSEC3PARAM RR has supported parameters */
/* skip unknown flags (dynamic signer is recalculating nsec3 chain) */
for(i=0; i<param->data->count; i++) {
uint8_t* rdata = param->data->rr_data[i]+2;
size_t rdatalen = param->data->rr_len[i];
if(rdatalen < 2+5)
continue; /* too short */
if(!nsec3_hash_algo_size_supported((int)(rdata[0])))
continue; /* unsupported algo */
if(rdatalen < (size_t)(2+5+(size_t)rdata[4]))
continue; /* salt missing */
if((rdata[1]&NSEC3_UNKNOWN_FLAGS)!=0)
continue; /* unknown flags */
*algo = (int)(rdata[0]);
*iter = sldns_read_uint16(rdata+2);
*saltlen = rdata[4];
if(*saltlen == 0)
*salt = NULL;
else *salt = rdata+5;
return 1;
}
/* no supported params */
return 0;
}
/** Hash a name with nsec3param into buffer, it has zone name appended.
* return length of hash */
static size_t
az_nsec3_hash(uint8_t* buf, size_t buflen, uint8_t* nm, size_t nmlen,
int algo, size_t iter, uint8_t* salt, size_t saltlen)
{
size_t hlen = nsec3_hash_algo_size_supported(algo);
/* buffer has domain name, nsec3hash, and 256 is for max saltlen
* (salt has 0-255 length) */
unsigned char p[LDNS_MAX_DOMAINLEN+1+N3HASHBUFLEN+256];
size_t i;
if(nmlen+saltlen > sizeof(p) || hlen+saltlen > sizeof(p))
return 0;
if(hlen > buflen)
return 0; /* somehow too large for destination buffer */
/* hashfunc(name, salt) */
memmove(p, nm, nmlen);
query_dname_tolower(p);
if(salt && saltlen > 0)
memmove(p+nmlen, salt, saltlen);
(void)secalgo_nsec3_hash(algo, p, nmlen+saltlen, (unsigned char*)buf);
for(i=0; i<iter; i++) {
/* hashfunc(hash, salt) */
memmove(p, buf, hlen);
if(salt && saltlen > 0)
memmove(p+hlen, salt, saltlen);
(void)secalgo_nsec3_hash(algo, p, hlen+saltlen,
(unsigned char*)buf);
}
return hlen;
}
/** Hash name and return b32encoded hashname for lookup, zone name appended */
static int
az_nsec3_hashname(struct auth_zone* z, uint8_t* hashname, size_t* hashnmlen,
uint8_t* nm, size_t nmlen, int algo, size_t iter, uint8_t* salt,
size_t saltlen)
{
uint8_t hash[N3HASHBUFLEN];
size_t hlen;
int ret;
hlen = az_nsec3_hash(hash, sizeof(hash), nm, nmlen, algo, iter,
salt, saltlen);
if(!hlen) return 0;
/* b32 encode */
if(*hashnmlen < hlen*2+1+z->namelen) /* approx b32 as hexb16 */
return 0;
ret = sldns_b32_ntop_extended_hex(hash, hlen, (char*)(hashname+1),
(*hashnmlen)-1);
if(ret<1)
return 0;
hashname[0] = (uint8_t)ret;
ret++;
if((*hashnmlen) - ret < z->namelen)
return 0;
memmove(hashname+ret, z->name, z->namelen);
*hashnmlen = z->namelen+(size_t)ret;
return 1;
}
/** Find the datanode that covers the nsec3hash-name */
static struct auth_data*
az_nsec3_findnode(struct auth_zone* z, uint8_t* hashnm, size_t hashnmlen)
{
struct query_info qinfo;
struct auth_data* node;
int node_exact;
qinfo.qclass = 0;
qinfo.qtype = 0;
qinfo.qname = hashnm;
qinfo.qname_len = hashnmlen;
/* because canonical ordering and b32 nsec3 ordering are the same.
* this is a good lookup to find the nsec3 name. */
az_find_domain(z, &qinfo, &node_exact, &node);
/* but we may have to skip non-nsec3 nodes */
/* this may be a lot, the way to speed that up is to have a
* separate nsec3 tree with nsec3 nodes */
while(node && (rbnode_type*)node != RBTREE_NULL &&
!az_domain_rrset(node, LDNS_RR_TYPE_NSEC3)) {
node = (struct auth_data*)rbtree_previous(&node->node);
}
if((rbnode_type*)node == RBTREE_NULL)
node = NULL;
return node;
}
/** Find cover for hashed(nm, nmlen) (or NULL) */
static struct auth_data*
az_nsec3_find_cover(struct auth_zone* z, uint8_t* nm, size_t nmlen,
int algo, size_t iter, uint8_t* salt, size_t saltlen)
{
struct auth_data* node;
uint8_t hname[LDNS_MAX_DOMAINLEN];
size_t hlen = sizeof(hname);
if(!az_nsec3_hashname(z, hname, &hlen, nm, nmlen, algo, iter,
salt, saltlen))
return NULL;
node = az_nsec3_findnode(z, hname, hlen);
if(node)
return node;
/* we did not find any, perhaps because the NSEC3 hash is before
* the first hash, we have to find the 'last hash' in the zone */
node = (struct auth_data*)rbtree_last(&z->data);
while(node && (rbnode_type*)node != RBTREE_NULL &&
!az_domain_rrset(node, LDNS_RR_TYPE_NSEC3)) {
node = (struct auth_data*)rbtree_previous(&node->node);
}
if((rbnode_type*)node == RBTREE_NULL)
node = NULL;
return node;
}
/** Find exact match for hashed(nm, nmlen) NSEC3 record or NULL */
static struct auth_data*
az_nsec3_find_exact(struct auth_zone* z, uint8_t* nm, size_t nmlen,
int algo, size_t iter, uint8_t* salt, size_t saltlen)
{
struct auth_data* node;
uint8_t hname[LDNS_MAX_DOMAINLEN];
size_t hlen = sizeof(hname);
if(!az_nsec3_hashname(z, hname, &hlen, nm, nmlen, algo, iter,
salt, saltlen))
return NULL;
node = az_find_name(z, hname, hlen);
if(az_domain_rrset(node, LDNS_RR_TYPE_NSEC3))
return node;
return NULL;
}
/** Return nextcloser name (as a ref into the qname). This is one label
* more than the cenm (cename must be a suffix of qname) */
static void
az_nsec3_get_nextcloser(uint8_t* cenm, uint8_t* qname, size_t qname_len,
uint8_t** nx, size_t* nxlen)
{
int celabs = dname_count_labels(cenm);
int qlabs = dname_count_labels(qname);
int strip = qlabs - celabs -1;
log_assert(dname_strict_subdomain(qname, qlabs, cenm, celabs));
*nx = qname;
*nxlen = qname_len;
if(strip>0)
dname_remove_labels(nx, nxlen, strip);
}
/** Find the closest encloser that has exact NSEC3.
* updated cenm to the new name. If it went up no-exact-ce is true. */
static struct auth_data*
az_nsec3_find_ce(struct auth_zone* z, uint8_t** cenm, size_t* cenmlen,
int* no_exact_ce, int algo, size_t iter, uint8_t* salt, size_t saltlen)
{
struct auth_data* node;
while((node = az_nsec3_find_exact(z, *cenm, *cenmlen,
algo, iter, salt, saltlen)) == NULL) {
if(*cenmlen == z->namelen) {
/* next step up would take us out of the zone. fail */
return NULL;
}
*no_exact_ce = 1;
dname_remove_label(cenm, cenmlen);
}
return node;
}
/* Insert NSEC3 record in authority section, if NULL does nothing */
static int
az_nsec3_insert(struct auth_zone* z, struct regional* region,
struct dns_msg* msg, struct auth_data* node)
{
struct auth_rrset* nsec3;
if(!node) return 1; /* no node, skip this */
nsec3 = az_domain_rrset(node, LDNS_RR_TYPE_NSEC3);
if(!nsec3) return 1; /* if no nsec3 RR, skip it */
if(!msg_add_rrset_ns(z, region, msg, node, nsec3)) return 0;
return 1;
}
/** add NSEC3 records to the zone for the nsec3 proof.
* Specify with the flags with parts of the proof are required.
* the ce is the exact matching name (for notype) but also delegation points.
* qname is the one where the nextcloser name can be derived from.
* If NSEC3 is not properly there (in the zone) nothing is added.
* always enabled: include nsec3 proving about the Closest Encloser.
* that is an exact match that should exist for it.
* If that does not exist, a higher exact match + nxproof is enabled
* (for some sort of opt-out empty nonterminal cases).
* nodataproof: search for exact match and include that instead.
* ceproof: include ce proof NSEC3 (omitted for wildcard replies).
* nxproof: include denial of the qname.
* wcproof: include denial of wildcard (wildcard.ce).
*/
static int
az_add_nsec3_proof(struct auth_zone* z, struct regional* region,
struct dns_msg* msg, uint8_t* cenm, size_t cenmlen, uint8_t* qname,
size_t qname_len, int nodataproof, int ceproof, int nxproof,
int wcproof)
{
int algo;
size_t iter, saltlen;
uint8_t* salt;
int no_exact_ce = 0;
struct auth_data* node;
/* find parameters of nsec3 proof */
if(!az_nsec3_param(z, &algo, &iter, &salt, &saltlen))
return 1; /* no nsec3 */
if(nodataproof) {
/* see if the node has a hash of itself for the nodata
* proof nsec3, this has to be an exact match nsec3. */
struct auth_data* match;
match = az_nsec3_find_exact(z, qname, qname_len, algo,
iter, salt, saltlen);
if(match) {
if(!az_nsec3_insert(z, region, msg, match))
return 0;
/* only nodata NSEC3 needed, no CE or others. */
return 1;
}
}
/* find ce that has an NSEC3 */
if(ceproof) {
node = az_nsec3_find_ce(z, &cenm, &cenmlen, &no_exact_ce,
algo, iter, salt, saltlen);
if(no_exact_ce) nxproof = 1;
if(!az_nsec3_insert(z, region, msg, node))
return 0;
}
if(nxproof) {
uint8_t* nx;
size_t nxlen;
/* create nextcloser domain name */
az_nsec3_get_nextcloser(cenm, qname, qname_len, &nx, &nxlen);
/* find nsec3 that matches or covers it */
node = az_nsec3_find_cover(z, nx, nxlen, algo, iter, salt,
saltlen);
if(!az_nsec3_insert(z, region, msg, node))
return 0;
}
if(wcproof) {
/* create wildcard name *.ce */
uint8_t wc[LDNS_MAX_DOMAINLEN];
size_t wclen;
if(cenmlen+2 > sizeof(wc))
return 0; /* result would be too long */
wc[0] = 1; /* length of wildcard label */
wc[1] = (uint8_t)'*'; /* wildcard label */
memmove(wc+2, cenm, cenmlen);
wclen = cenmlen+2;
/* find nsec3 that matches or covers it */
node = az_nsec3_find_cover(z, wc, wclen, algo, iter, salt,
saltlen);
if(!az_nsec3_insert(z, region, msg, node))
return 0;
}
return 1;
}
/** generate answer for positive answer */
static int
az_generate_positive_answer(struct auth_zone* z, struct regional* region,
struct dns_msg* msg, struct auth_data* node, struct auth_rrset* rrset)
{
if(!msg_add_rrset_an(z, region, msg, node, rrset)) return 0;
/* see if we want additional rrs */
if(rrset->type == LDNS_RR_TYPE_MX) {
if(!az_add_additionals_from(z, region, msg, rrset, 2))
return 0;
} else if(rrset->type == LDNS_RR_TYPE_SRV) {
if(!az_add_additionals_from(z, region, msg, rrset, 6))
return 0;
} else if(rrset->type == LDNS_RR_TYPE_NS) {
if(!az_add_additionals_from(z, region, msg, rrset, 0))
return 0;
}
return 1;
}
/** generate answer for type ANY answer */
static int
az_generate_any_answer(struct auth_zone* z, struct regional* region,
struct dns_msg* msg, struct auth_data* node)
{
struct auth_rrset* rrset;
int added = 0;
/* add a couple (at least one) RRs */
if((rrset=az_domain_rrset(node, LDNS_RR_TYPE_SOA)) != NULL) {
if(!msg_add_rrset_an(z, region, msg, node, rrset)) return 0;
added++;
}
if((rrset=az_domain_rrset(node, LDNS_RR_TYPE_MX)) != NULL) {
if(!msg_add_rrset_an(z, region, msg, node, rrset)) return 0;
added++;
}
if((rrset=az_domain_rrset(node, LDNS_RR_TYPE_A)) != NULL) {
if(!msg_add_rrset_an(z, region, msg, node, rrset)) return 0;
added++;
}
if((rrset=az_domain_rrset(node, LDNS_RR_TYPE_AAAA)) != NULL) {
if(!msg_add_rrset_an(z, region, msg, node, rrset)) return 0;
added++;
}
if(added == 0 && node && node->rrsets) {
if(!msg_add_rrset_an(z, region, msg, node,
node->rrsets)) return 0;
}
return 1;
}
/** follow cname chain and add more data to the answer section */
static int
follow_cname_chain(struct auth_zone* z, uint16_t qtype,
struct regional* region, struct dns_msg* msg,
struct packed_rrset_data* d)
{
int maxchain = 0;
/* see if we can add the target of the CNAME into the answer */
while(maxchain++ < MAX_CNAME_CHAIN) {
struct auth_data* node;
struct auth_rrset* rrset;
size_t clen;
/* d has cname rdata */
if(d->count == 0) break; /* no CNAME */
if(d->rr_len[0] < 2+1) break; /* too small */
if((clen=dname_valid(d->rr_data[0]+2, d->rr_len[0]-2))==0)
break; /* malformed */
if(!dname_subdomain_c(d->rr_data[0]+2, z->name))
break; /* target out of zone */
if((node = az_find_name(z, d->rr_data[0]+2, clen))==NULL)
break; /* no such target name */
if((rrset=az_domain_rrset(node, qtype))!=NULL) {
/* done we found the target */
if(!msg_add_rrset_an(z, region, msg, node, rrset))
return 0;
break;
}
if((rrset=az_domain_rrset(node, LDNS_RR_TYPE_CNAME))==NULL)
break; /* no further CNAME chain, notype */
if(!msg_add_rrset_an(z, region, msg, node, rrset)) return 0;
d = rrset->data;
}
return 1;
}
/** generate answer for cname answer */
static int
az_generate_cname_answer(struct auth_zone* z, struct query_info* qinfo,
struct regional* region, struct dns_msg* msg,
struct auth_data* node, struct auth_rrset* rrset)
{
if(!msg_add_rrset_an(z, region, msg, node, rrset)) return 0;
if(!rrset) return 1;
if(!follow_cname_chain(z, qinfo->qtype, region, msg, rrset->data))
return 0;
return 1;
}
/** generate answer for notype answer */
static int
az_generate_notype_answer(struct auth_zone* z, struct regional* region,
struct dns_msg* msg, struct auth_data* node)
{
struct auth_rrset* rrset;
if(!az_add_negative_soa(z, region, msg)) return 0;
/* DNSSEC denial NSEC */
if((rrset=az_domain_rrset(node, LDNS_RR_TYPE_NSEC))!=NULL) {
if(!msg_add_rrset_ns(z, region, msg, node, rrset)) return 0;
} else if(node) {
/* DNSSEC denial NSEC3 */
if(!az_add_nsec3_proof(z, region, msg, node->name,
node->namelen, msg->qinfo.qname,
msg->qinfo.qname_len, 1, 1, 0, 0))
return 0;
}
return 1;
}
/** generate answer for referral answer */
static int
az_generate_referral_answer(struct auth_zone* z, struct regional* region,
struct dns_msg* msg, struct auth_data* ce, struct auth_rrset* rrset)
{
struct auth_rrset* ds, *nsec;
/* turn off AA flag, referral is nonAA because it leaves the zone */
log_assert(ce);
msg->rep->flags &= ~BIT_AA;
if(!msg_add_rrset_ns(z, region, msg, ce, rrset)) return 0;
/* add DS or deny it */
if((ds=az_domain_rrset(ce, LDNS_RR_TYPE_DS))!=NULL) {
if(!msg_add_rrset_ns(z, region, msg, ce, ds)) return 0;
} else {
/* deny the DS */
if((nsec=az_domain_rrset(ce, LDNS_RR_TYPE_NSEC))!=NULL) {
if(!msg_add_rrset_ns(z, region, msg, ce, nsec))
return 0;
} else {
if(!az_add_nsec3_proof(z, region, msg, ce->name,
ce->namelen, msg->qinfo.qname,
msg->qinfo.qname_len, 1, 1, 0, 0))
return 0;
}
}
/* add additional rrs for type NS */
if(!az_add_additionals_from(z, region, msg, rrset, 0)) return 0;
return 1;
}
/** generate answer for DNAME answer */
static int
az_generate_dname_answer(struct auth_zone* z, struct query_info* qinfo,
struct regional* region, struct dns_msg* msg, struct auth_data* ce,
struct auth_rrset* rrset)
{
log_assert(ce);
/* add the DNAME and then a CNAME */
if(!msg_add_rrset_an(z, region, msg, ce, rrset)) return 0;
if(!add_synth_cname(z, qinfo->qname, qinfo->qname_len, region,
msg, ce, rrset)) return 0;
if(FLAGS_GET_RCODE(msg->rep->flags) == LDNS_RCODE_YXDOMAIN)
return 1;
if(msg->rep->rrset_count == 0 ||
!msg->rep->rrsets[msg->rep->rrset_count-1])
return 0;
if(!follow_cname_chain(z, qinfo->qtype, region, msg,
(struct packed_rrset_data*)msg->rep->rrsets[
msg->rep->rrset_count-1]->entry.data))
return 0;
return 1;
}
/** generate answer for wildcard answer */
static int
az_generate_wildcard_answer(struct auth_zone* z, struct query_info* qinfo,
struct regional* region, struct dns_msg* msg, struct auth_data* ce,
struct auth_data* wildcard, struct auth_data* node)
{
struct auth_rrset* rrset, *nsec;
int insert_ce = 0;
if((rrset=az_domain_rrset(wildcard, qinfo->qtype)) != NULL) {
/* wildcard has type, add it */
if(!msg_add_rrset_an(z, region, msg, wildcard, rrset))
return 0;
az_change_dnames(msg, wildcard->name, msg->qinfo.qname,
msg->qinfo.qname_len, 1);
} else if((rrset=az_domain_rrset(wildcard, LDNS_RR_TYPE_CNAME))!=NULL) {
/* wildcard has cname instead, do that */
if(!msg_add_rrset_an(z, region, msg, wildcard, rrset))
return 0;
az_change_dnames(msg, wildcard->name, msg->qinfo.qname,
msg->qinfo.qname_len, 1);
if(!follow_cname_chain(z, qinfo->qtype, region, msg,
rrset->data))
return 0;
} else if(qinfo->qtype == LDNS_RR_TYPE_ANY && wildcard->rrsets) {
/* add ANY rrsets from wildcard node */
if(!az_generate_any_answer(z, region, msg, wildcard))
return 0;
az_change_dnames(msg, wildcard->name, msg->qinfo.qname,
msg->qinfo.qname_len, 1);
} else {
/* wildcard has nodata, notype answer */
/* call other notype routine for dnssec notype denials */
if(!az_generate_notype_answer(z, region, msg, wildcard))
return 0;
/* because the notype, there is no positive data with an
* RRSIG that indicates the wildcard position. Thus the
* wildcard qname denial needs to have a CE nsec3. */
insert_ce = 1;
}
/* ce and node for dnssec denial of wildcard original name */
if((nsec=az_find_nsec_cover(z, &node)) != NULL) {
if(!msg_add_rrset_ns(z, region, msg, node, nsec)) return 0;
} else if(ce) {
uint8_t* wildup = wildcard->name;
size_t wilduplen= wildcard->namelen;
dname_remove_label(&wildup, &wilduplen);
if(!az_add_nsec3_proof(z, region, msg, wildup,
wilduplen, msg->qinfo.qname,
msg->qinfo.qname_len, 0, insert_ce, 1, 0))
return 0;
}
/* fixup name of wildcard from *.zone to qname, use already allocated
* pointer to msg qname */
az_change_dnames(msg, wildcard->name, msg->qinfo.qname,
msg->qinfo.qname_len, 0);
return 1;
}
/** generate answer for nxdomain answer */
static int
az_generate_nxdomain_answer(struct auth_zone* z, struct regional* region,
struct dns_msg* msg, struct auth_data* ce, struct auth_data* node)
{
struct auth_rrset* nsec;
msg->rep->flags |= LDNS_RCODE_NXDOMAIN;
if(!az_add_negative_soa(z, region, msg)) return 0;
if((nsec=az_find_nsec_cover(z, &node)) != NULL) {
if(!msg_add_rrset_ns(z, region, msg, node, nsec)) return 0;
if(ce && !az_nsec_wildcard_denial(z, region, msg, ce->name,
ce->namelen)) return 0;
} else if(ce) {
if(!az_add_nsec3_proof(z, region, msg, ce->name,
ce->namelen, msg->qinfo.qname,
msg->qinfo.qname_len, 0, 1, 1, 1))
return 0;
}
return 1;
}
/** Create answers when an exact match exists for the domain name */
static int
az_generate_answer_with_node(struct auth_zone* z, struct query_info* qinfo,
struct regional* region, struct dns_msg* msg, struct auth_data* node)
{
struct auth_rrset* rrset;
/* positive answer, rrset we are looking for exists */
if((rrset=az_domain_rrset(node, qinfo->qtype)) != NULL) {
return az_generate_positive_answer(z, region, msg, node, rrset);
}
/* CNAME? */
if((rrset=az_domain_rrset(node, LDNS_RR_TYPE_CNAME)) != NULL) {
return az_generate_cname_answer(z, qinfo, region, msg,
node, rrset);
}
/* type ANY ? */
if(qinfo->qtype == LDNS_RR_TYPE_ANY) {
return az_generate_any_answer(z, region, msg, node);
}
/* NOERROR/NODATA (no such type at domain name) */
return az_generate_notype_answer(z, region, msg, node);
}
/** Generate answer without an existing-node that we can use.
* So it'll be a referral, DNAME or nxdomain */
static int
az_generate_answer_nonexistnode(struct auth_zone* z, struct query_info* qinfo,
struct regional* region, struct dns_msg* msg, struct auth_data* ce,
struct auth_rrset* rrset, struct auth_data* node)
{
struct auth_data* wildcard;
/* we do not have an exact matching name (that exists) */
/* see if we have a NS or DNAME in the ce */
if(ce && rrset && rrset->type == LDNS_RR_TYPE_NS) {
return az_generate_referral_answer(z, region, msg, ce, rrset);
}
if(ce && rrset && rrset->type == LDNS_RR_TYPE_DNAME) {
return az_generate_dname_answer(z, qinfo, region, msg, ce,
rrset);
}
/* if there is an empty nonterminal, wildcard and nxdomain don't
* happen, it is a notype answer */
if(az_empty_nonterminal(z, qinfo, node)) {
return az_generate_notype_answer(z, region, msg, node);
}
/* see if we have a wildcard under the ce */
if((wildcard=az_find_wildcard(z, qinfo, ce)) != NULL) {
return az_generate_wildcard_answer(z, qinfo, region, msg,
ce, wildcard, node);
}
/* generate nxdomain answer */
return az_generate_nxdomain_answer(z, region, msg, ce, node);
}
/** Lookup answer in a zone. */
static int
auth_zone_generate_answer(struct auth_zone* z, struct query_info* qinfo,
struct regional* region, struct dns_msg** msg, int* fallback)
{
struct auth_data* node, *ce;
struct auth_rrset* rrset;
int node_exact, node_exists;
/* does the zone want fallback in case of failure? */
*fallback = z->fallback_enabled;
if(!(*msg=msg_create(region, qinfo))) return 0;
/* lookup if there is a matching domain name for the query */
az_find_domain(z, qinfo, &node_exact, &node);
/* see if node exists for generating answers from (i.e. not glue and
* obscured by NS or DNAME or NSEC3-only), and also return the
* closest-encloser from that, closest node that should be used
* to generate answers from that is above the query */
node_exists = az_find_ce(z, qinfo, node, node_exact, &ce, &rrset);
if(verbosity >= VERB_ALGO) {
char zname[256], qname[256], nname[256], cename[256],
tpstr[32], rrstr[32];
sldns_wire2str_dname_buf(qinfo->qname, qinfo->qname_len, qname,
sizeof(qname));
sldns_wire2str_type_buf(qinfo->qtype, tpstr, sizeof(tpstr));
sldns_wire2str_dname_buf(z->name, z->namelen, zname,
sizeof(zname));
if(node)
sldns_wire2str_dname_buf(node->name, node->namelen,
nname, sizeof(nname));
else snprintf(nname, sizeof(nname), "NULL");
if(ce)
sldns_wire2str_dname_buf(ce->name, ce->namelen,
cename, sizeof(cename));
else snprintf(cename, sizeof(cename), "NULL");
if(rrset) sldns_wire2str_type_buf(rrset->type, rrstr,
sizeof(rrstr));
else snprintf(rrstr, sizeof(rrstr), "NULL");
log_info("auth_zone %s query %s %s, domain %s %s %s, "
"ce %s, rrset %s", zname, qname, tpstr, nname,
(node_exact?"exact":"notexact"),
(node_exists?"exist":"notexist"), cename, rrstr);
}
if(node_exists) {
/* the node is fine, generate answer from node */
return az_generate_answer_with_node(z, qinfo, region, *msg,
node);
}
return az_generate_answer_nonexistnode(z, qinfo, region, *msg,
ce, rrset, node);
}
int auth_zones_lookup(struct auth_zones* az, struct query_info* qinfo,
struct regional* region, struct dns_msg** msg, int* fallback,
uint8_t* dp_nm, size_t dp_nmlen)
{
int r;
struct auth_zone* z;
/* find the zone that should contain the answer. */
lock_rw_rdlock(&az->lock);
z = auth_zone_find(az, dp_nm, dp_nmlen, qinfo->qclass);
if(!z) {
lock_rw_unlock(&az->lock);
/* no auth zone, fallback to internet */
*fallback = 1;
return 0;
}
lock_rw_rdlock(&z->lock);
lock_rw_unlock(&az->lock);
/* if not for upstream queries, fallback */
if(!z->for_upstream) {
lock_rw_unlock(&z->lock);
*fallback = 1;
return 0;
}
if(z->zone_expired) {
*fallback = z->fallback_enabled;
lock_rw_unlock(&z->lock);
return 0;
}
/* see what answer that zone would generate */
r = auth_zone_generate_answer(z, qinfo, region, msg, fallback);
lock_rw_unlock(&z->lock);
return r;
}
/** encode auth answer */
static void
auth_answer_encode(struct query_info* qinfo, struct module_env* env,
struct edns_data* edns, struct comm_reply* repinfo, sldns_buffer* buf,
struct regional* temp, struct dns_msg* msg)
{
uint16_t udpsize;
udpsize = edns->udp_size;
edns->edns_version = EDNS_ADVERTISED_VERSION;
edns->udp_size = EDNS_ADVERTISED_SIZE;
edns->ext_rcode = 0;
edns->bits &= EDNS_DO;
if(!inplace_cb_reply_local_call(env, qinfo, NULL, msg->rep,
(int)FLAGS_GET_RCODE(msg->rep->flags), edns, repinfo, temp, env->now_tv)
|| !reply_info_answer_encode(qinfo, msg->rep,
*(uint16_t*)sldns_buffer_begin(buf),
sldns_buffer_read_u16_at(buf, 2),
buf, 0, 0, temp, udpsize, edns,
(int)(edns->bits&EDNS_DO), 0)) {
error_encode(buf, (LDNS_RCODE_SERVFAIL|BIT_AA), qinfo,
*(uint16_t*)sldns_buffer_begin(buf),
sldns_buffer_read_u16_at(buf, 2), edns);
}
}
/** encode auth error answer */
static void
auth_error_encode(struct query_info* qinfo, struct module_env* env,
struct edns_data* edns, struct comm_reply* repinfo, sldns_buffer* buf,
struct regional* temp, int rcode)
{
edns->edns_version = EDNS_ADVERTISED_VERSION;
edns->udp_size = EDNS_ADVERTISED_SIZE;
edns->ext_rcode = 0;
edns->bits &= EDNS_DO;
if(!inplace_cb_reply_local_call(env, qinfo, NULL, NULL,
rcode, edns, repinfo, temp, env->now_tv))
edns->opt_list_inplace_cb_out = NULL;
error_encode(buf, rcode|BIT_AA, qinfo,
*(uint16_t*)sldns_buffer_begin(buf),
sldns_buffer_read_u16_at(buf, 2), edns);
}
int auth_zones_answer(struct auth_zones* az, struct module_env* env,
struct query_info* qinfo, struct edns_data* edns,
struct comm_reply* repinfo, struct sldns_buffer* buf, struct regional* temp)
{
struct dns_msg* msg = NULL;
struct auth_zone* z;
int r;
int fallback = 0;
lock_rw_rdlock(&az->lock);
if(!az->have_downstream) {
/* no downstream auth zones */
lock_rw_unlock(&az->lock);
return 0;
}
if(qinfo->qtype == LDNS_RR_TYPE_DS) {
uint8_t* delname = qinfo->qname;
size_t delnamelen = qinfo->qname_len;
dname_remove_label(&delname, &delnamelen);
z = auth_zones_find_zone(az, delname, delnamelen,
qinfo->qclass);
} else {
z = auth_zones_find_zone(az, qinfo->qname, qinfo->qname_len,
qinfo->qclass);
}
if(!z) {
/* no zone above it */
lock_rw_unlock(&az->lock);
return 0;
}
lock_rw_rdlock(&z->lock);
lock_rw_unlock(&az->lock);
if(!z->for_downstream) {
lock_rw_unlock(&z->lock);
return 0;
}
if(z->zone_expired) {
if(z->fallback_enabled) {
lock_rw_unlock(&z->lock);
return 0;
}
lock_rw_unlock(&z->lock);
lock_rw_wrlock(&az->lock);
az->num_query_down++;
lock_rw_unlock(&az->lock);
auth_error_encode(qinfo, env, edns, repinfo, buf, temp,
LDNS_RCODE_SERVFAIL);
return 1;
}
/* answer it from zone z */
r = auth_zone_generate_answer(z, qinfo, temp, &msg, &fallback);
lock_rw_unlock(&z->lock);
if(!r && fallback) {
/* fallback to regular answering (recursive) */
return 0;
}
lock_rw_wrlock(&az->lock);
az->num_query_down++;
lock_rw_unlock(&az->lock);
/* encode answer */
if(!r)
auth_error_encode(qinfo, env, edns, repinfo, buf, temp,
LDNS_RCODE_SERVFAIL);
else auth_answer_encode(qinfo, env, edns, repinfo, buf, temp, msg);
return 1;
}
int auth_zones_can_fallback(struct auth_zones* az, uint8_t* nm, size_t nmlen,
uint16_t dclass)
{
int r;
struct auth_zone* z;
lock_rw_rdlock(&az->lock);
z = auth_zone_find(az, nm, nmlen, dclass);
if(!z) {
lock_rw_unlock(&az->lock);
/* no such auth zone, fallback */
return 1;
}
lock_rw_rdlock(&z->lock);
lock_rw_unlock(&az->lock);
r = z->fallback_enabled || (!z->for_upstream);
lock_rw_unlock(&z->lock);
return r;
}
int
auth_zone_parse_notify_serial(sldns_buffer* pkt, uint32_t *serial)
{
struct query_info q;
uint16_t rdlen;
memset(&q, 0, sizeof(q));
sldns_buffer_set_position(pkt, 0);
if(!query_info_parse(&q, pkt)) return 0;
if(LDNS_ANCOUNT(sldns_buffer_begin(pkt)) == 0) return 0;
/* skip name of RR in answer section */
if(sldns_buffer_remaining(pkt) < 1) return 0;
if(pkt_dname_len(pkt) == 0) return 0;
/* check type */
if(sldns_buffer_remaining(pkt) < 10 /* type,class,ttl,rdatalen*/)
return 0;
if(sldns_buffer_read_u16(pkt) != LDNS_RR_TYPE_SOA) return 0;
sldns_buffer_skip(pkt, 2); /* class */
sldns_buffer_skip(pkt, 4); /* ttl */
rdlen = sldns_buffer_read_u16(pkt); /* rdatalen */
if(sldns_buffer_remaining(pkt) < rdlen) return 0;
if(rdlen < 22) return 0; /* bad soa length */
sldns_buffer_skip(pkt, (ssize_t)(rdlen-20));
*serial = sldns_buffer_read_u32(pkt);
/* return true when has serial in answer section */
return 1;
}
/** see if addr appears in the list */
static int
addr_in_list(struct auth_addr* list, struct sockaddr_storage* addr,
socklen_t addrlen)
{
struct auth_addr* p;
for(p=list; p; p=p->next) {
if(sockaddr_cmp_addr(addr, addrlen, &p->addr, p->addrlen)==0)
return 1;
}
return 0;
}
/** check if an address matches a master specification (or one of its
* addresses in the addr list) */
static int
addr_matches_master(struct auth_master* master, struct sockaddr_storage* addr,
socklen_t addrlen, struct auth_master** fromhost)
{
struct sockaddr_storage a;
socklen_t alen = 0;
int net = 0;
if(addr_in_list(master->list, addr, addrlen)) {
*fromhost = master;
return 1;
}
/* compare address (but not port number, that is the destination
* port of the master, the port number of the received notify is
* allowed to by any port on that master) */
if(extstrtoaddr(master->host, &a, &alen, UNBOUND_DNS_PORT) &&
sockaddr_cmp_addr(addr, addrlen, &a, alen)==0) {
*fromhost = master;
return 1;
}
/* prefixes, addr/len, like 10.0.0.0/8 */
/* not http and has a / and there is one / */
if(master->allow_notify && !master->http &&
strchr(master->host, '/') != NULL &&
strchr(master->host, '/') == strrchr(master->host, '/') &&
netblockstrtoaddr(master->host, UNBOUND_DNS_PORT, &a, &alen,
&net) && alen == addrlen) {
if(addr_in_common(addr, (addr_is_ip6(addr, addrlen)?128:32),
&a, net, alen) >= net) {
*fromhost = NULL; /* prefix does not have destination
to send the probe or transfer with */
return 1; /* matches the netblock */
}
}
return 0;
}
/** check access list for notifies */
static int
az_xfr_allowed_notify(struct auth_xfer* xfr, struct sockaddr_storage* addr,
socklen_t addrlen, struct auth_master** fromhost)
{
struct auth_master* p;
for(p=xfr->allow_notify_list; p; p=p->next) {
if(addr_matches_master(p, addr, addrlen, fromhost)) {
return 1;
}
}
return 0;
}
/** see if the serial means the zone has to be updated, i.e. the serial
* is newer than the zone serial, or we have no zone */
static int
xfr_serial_means_update(struct auth_xfer* xfr, uint32_t serial)
{
if(!xfr->have_zone)
return 1; /* no zone, anything is better */
if(xfr->zone_expired)
return 1; /* expired, the sent serial is better than expired
data */
if(compare_serial(xfr->serial, serial) < 0)
return 1; /* our serial is smaller than the sent serial,
the data is newer, fetch it */
return 0;
}
/** note notify serial, updates the notify information in the xfr struct */
static void
xfr_note_notify_serial(struct auth_xfer* xfr, int has_serial, uint32_t serial)
{
if(xfr->notify_received && xfr->notify_has_serial && has_serial) {
/* see if this serial is newer */
if(compare_serial(xfr->notify_serial, serial) < 0)
xfr->notify_serial = serial;
} else if(xfr->notify_received && xfr->notify_has_serial &&
!has_serial) {
/* remove serial, we have notify without serial */
xfr->notify_has_serial = 0;
xfr->notify_serial = 0;
} else if(xfr->notify_received && !xfr->notify_has_serial) {
/* we already have notify without serial, keep it
* that way; no serial check when current operation
* is done */
} else {
xfr->notify_received = 1;
xfr->notify_has_serial = has_serial;
xfr->notify_serial = serial;
}
}
/** process a notify serial, start new probe or note serial. xfr is locked */
static void
xfr_process_notify(struct auth_xfer* xfr, struct module_env* env,
int has_serial, uint32_t serial, struct auth_master* fromhost)
{
/* if the serial of notify is older than we have, don't fetch
* a zone, we already have it */
if(has_serial && !xfr_serial_means_update(xfr, serial)) {
lock_basic_unlock(&xfr->lock);
return;
}
/* start new probe with this addr src, or note serial */
if(!xfr_start_probe(xfr, env, fromhost)) {
/* not started because already in progress, note the serial */
xfr_note_notify_serial(xfr, has_serial, serial);
lock_basic_unlock(&xfr->lock);
}
/* successful end of start_probe unlocked xfr->lock */
}
int auth_zones_notify(struct auth_zones* az, struct module_env* env,
uint8_t* nm, size_t nmlen, uint16_t dclass,
struct sockaddr_storage* addr, socklen_t addrlen, int has_serial,
uint32_t serial, int* refused)
{
struct auth_xfer* xfr;
struct auth_master* fromhost = NULL;
/* see which zone this is */
lock_rw_rdlock(&az->lock);
xfr = auth_xfer_find(az, nm, nmlen, dclass);
if(!xfr) {
lock_rw_unlock(&az->lock);
/* no such zone, refuse the notify */
*refused = 1;
return 0;
}
lock_basic_lock(&xfr->lock);
lock_rw_unlock(&az->lock);
/* check access list for notifies */
if(!az_xfr_allowed_notify(xfr, addr, addrlen, &fromhost)) {
lock_basic_unlock(&xfr->lock);
/* notify not allowed, refuse the notify */
*refused = 1;
return 0;
}
/* process the notify */
xfr_process_notify(xfr, env, has_serial, serial, fromhost);
return 1;
}
int auth_zones_startprobesequence(struct auth_zones* az,
struct module_env* env, uint8_t* nm, size_t nmlen, uint16_t dclass)
{
struct auth_xfer* xfr;
lock_rw_rdlock(&az->lock);
xfr = auth_xfer_find(az, nm, nmlen, dclass);
if(!xfr) {
lock_rw_unlock(&az->lock);
return 0;
}
lock_basic_lock(&xfr->lock);
lock_rw_unlock(&az->lock);
xfr_process_notify(xfr, env, 0, 0, NULL);
return 1;
}
/** set a zone expired */
static void
auth_xfer_set_expired(struct auth_xfer* xfr, struct module_env* env,
int expired)
{
struct auth_zone* z;
/* expire xfr */
lock_basic_lock(&xfr->lock);
xfr->zone_expired = expired;
lock_basic_unlock(&xfr->lock);
/* find auth_zone */
lock_rw_rdlock(&env->auth_zones->lock);
z = auth_zone_find(env->auth_zones, xfr->name, xfr->namelen,
xfr->dclass);
if(!z) {
lock_rw_unlock(&env->auth_zones->lock);
return;
}
lock_rw_wrlock(&z->lock);
lock_rw_unlock(&env->auth_zones->lock);
/* expire auth_zone */
z->zone_expired = expired;
lock_rw_unlock(&z->lock);
}
/** find master (from notify or probe) in list of masters */
static struct auth_master*
find_master_by_host(struct auth_master* list, char* host)
{
struct auth_master* p;
for(p=list; p; p=p->next) {
if(strcmp(p->host, host) == 0)
return p;
}
return NULL;
}
/** delete the looked up auth_addrs for all the masters in the list */
static void
xfr_masterlist_free_addrs(struct auth_master* list)
{
struct auth_master* m;
for(m=list; m; m=m->next) {
if(m->list) {
auth_free_master_addrs(m->list);
m->list = NULL;
}
}
}
/** copy a list of auth_addrs */
static struct auth_addr*
auth_addr_list_copy(struct auth_addr* source)
{
struct auth_addr* list = NULL, *last = NULL;
struct auth_addr* p;
for(p=source; p; p=p->next) {
struct auth_addr* a = (struct auth_addr*)memdup(p, sizeof(*p));
if(!a) {
log_err("malloc failure");
auth_free_master_addrs(list);
return NULL;
}
a->next = NULL;
if(last) last->next = a;
if(!list) list = a;
last = a;
}
return list;
}
/** copy a master to a new structure, NULL on alloc failure */
static struct auth_master*
auth_master_copy(struct auth_master* o)
{
struct auth_master* m;
if(!o) return NULL;
m = (struct auth_master*)memdup(o, sizeof(*o));
if(!m) {
log_err("malloc failure");
return NULL;
}
m->next = NULL;
if(m->host) {
m->host = strdup(m->host);
if(!m->host) {
free(m);
log_err("malloc failure");
return NULL;
}
}
if(m->file) {
m->file = strdup(m->file);
if(!m->file) {
free(m->host);
free(m);
log_err("malloc failure");
return NULL;
}
}
if(m->list) {
m->list = auth_addr_list_copy(m->list);
if(!m->list) {
free(m->file);
free(m->host);
free(m);
return NULL;
}
}
return m;
}
/** copy the master addresses from the task_probe lookups to the allow_notify
* list of masters */
static void
probe_copy_masters_for_allow_notify(struct auth_xfer* xfr)
{
struct auth_master* list = NULL, *last = NULL;
struct auth_master* p;
/* build up new list with copies */
for(p = xfr->task_transfer->masters; p; p=p->next) {
struct auth_master* m = auth_master_copy(p);
if(!m) {
auth_free_masters(list);
/* failed because of malloc failure, use old list */
return;
}
m->next = NULL;
if(last) last->next = m;
if(!list) list = m;
last = m;
}
/* success, replace list */
auth_free_masters(xfr->allow_notify_list);
xfr->allow_notify_list = list;
}
/** start the lookups for task_transfer */
static void
xfr_transfer_start_lookups(struct auth_xfer* xfr)
{
/* delete all the looked up addresses in the list */
xfr->task_transfer->scan_addr = NULL;
xfr_masterlist_free_addrs(xfr->task_transfer->masters);
/* start lookup at the first master */
xfr->task_transfer->lookup_target = xfr->task_transfer->masters;
xfr->task_transfer->lookup_aaaa = 0;
}
/** move to the next lookup of hostname for task_transfer */
static void
xfr_transfer_move_to_next_lookup(struct auth_xfer* xfr, struct module_env* env)
{
if(!xfr->task_transfer->lookup_target)
return; /* already at end of list */
if(!xfr->task_transfer->lookup_aaaa && env->cfg->do_ip6) {
/* move to lookup AAAA */
xfr->task_transfer->lookup_aaaa = 1;
return;
}
xfr->task_transfer->lookup_target =
xfr->task_transfer->lookup_target->next;
xfr->task_transfer->lookup_aaaa = 0;
if(!env->cfg->do_ip4 && xfr->task_transfer->lookup_target!=NULL)
xfr->task_transfer->lookup_aaaa = 1;
}
/** start the lookups for task_probe */
static void
xfr_probe_start_lookups(struct auth_xfer* xfr)
{
/* delete all the looked up addresses in the list */
xfr->task_probe->scan_addr = NULL;
xfr_masterlist_free_addrs(xfr->task_probe->masters);
/* start lookup at the first master */
xfr->task_probe->lookup_target = xfr->task_probe->masters;
xfr->task_probe->lookup_aaaa = 0;
}
/** move to the next lookup of hostname for task_probe */
static void
xfr_probe_move_to_next_lookup(struct auth_xfer* xfr, struct module_env* env)
{
if(!xfr->task_probe->lookup_target)
return; /* already at end of list */
if(!xfr->task_probe->lookup_aaaa && env->cfg->do_ip6) {
/* move to lookup AAAA */
xfr->task_probe->lookup_aaaa = 1;
return;
}
xfr->task_probe->lookup_target = xfr->task_probe->lookup_target->next;
xfr->task_probe->lookup_aaaa = 0;
if(!env->cfg->do_ip4 && xfr->task_probe->lookup_target!=NULL)
xfr->task_probe->lookup_aaaa = 1;
}
/** start the iteration of the task_transfer list of masters */
static void
xfr_transfer_start_list(struct auth_xfer* xfr, struct auth_master* spec)
{
if(spec) {
xfr->task_transfer->scan_specific = find_master_by_host(
xfr->task_transfer->masters, spec->host);
if(xfr->task_transfer->scan_specific) {
xfr->task_transfer->scan_target = NULL;
xfr->task_transfer->scan_addr = NULL;
if(xfr->task_transfer->scan_specific->list)
xfr->task_transfer->scan_addr =
xfr->task_transfer->scan_specific->list;
return;
}
}
/* no specific (notified) host to scan */
xfr->task_transfer->scan_specific = NULL;
xfr->task_transfer->scan_addr = NULL;
/* pick up first scan target */
xfr->task_transfer->scan_target = xfr->task_transfer->masters;
if(xfr->task_transfer->scan_target && xfr->task_transfer->
scan_target->list)
xfr->task_transfer->scan_addr =
xfr->task_transfer->scan_target->list;
}
/** start the iteration of the task_probe list of masters */
static void
xfr_probe_start_list(struct auth_xfer* xfr, struct auth_master* spec)
{
if(spec) {
xfr->task_probe->scan_specific = find_master_by_host(
xfr->task_probe->masters, spec->host);
if(xfr->task_probe->scan_specific) {
xfr->task_probe->scan_target = NULL;
xfr->task_probe->scan_addr = NULL;
if(xfr->task_probe->scan_specific->list)
xfr->task_probe->scan_addr =
xfr->task_probe->scan_specific->list;
return;
}
}
/* no specific (notified) host to scan */
xfr->task_probe->scan_specific = NULL;
xfr->task_probe->scan_addr = NULL;
/* pick up first scan target */
xfr->task_probe->scan_target = xfr->task_probe->masters;
if(xfr->task_probe->scan_target && xfr->task_probe->scan_target->list)
xfr->task_probe->scan_addr =
xfr->task_probe->scan_target->list;
}
/** pick up the master that is being scanned right now, task_transfer */
static struct auth_master*
xfr_transfer_current_master(struct auth_xfer* xfr)
{
if(xfr->task_transfer->scan_specific)
return xfr->task_transfer->scan_specific;
return xfr->task_transfer->scan_target;
}
/** pick up the master that is being scanned right now, task_probe */
static struct auth_master*
xfr_probe_current_master(struct auth_xfer* xfr)
{
if(xfr->task_probe->scan_specific)
return xfr->task_probe->scan_specific;
return xfr->task_probe->scan_target;
}
/** true if at end of list, task_transfer */
static int
xfr_transfer_end_of_list(struct auth_xfer* xfr)
{
return !xfr->task_transfer->scan_specific &&
!xfr->task_transfer->scan_target;
}
/** true if at end of list, task_probe */
static int
xfr_probe_end_of_list(struct auth_xfer* xfr)
{
return !xfr->task_probe->scan_specific && !xfr->task_probe->scan_target;
}
/** move to next master in list, task_transfer */
static void
xfr_transfer_nextmaster(struct auth_xfer* xfr)
{
if(!xfr->task_transfer->scan_specific &&
!xfr->task_transfer->scan_target)
return;
if(xfr->task_transfer->scan_addr) {
xfr->task_transfer->scan_addr =
xfr->task_transfer->scan_addr->next;
if(xfr->task_transfer->scan_addr)
return;
}
if(xfr->task_transfer->scan_specific) {
xfr->task_transfer->scan_specific = NULL;
xfr->task_transfer->scan_target = xfr->task_transfer->masters;
if(xfr->task_transfer->scan_target && xfr->task_transfer->
scan_target->list)
xfr->task_transfer->scan_addr =
xfr->task_transfer->scan_target->list;
return;
}
if(!xfr->task_transfer->scan_target)
return;
xfr->task_transfer->scan_target = xfr->task_transfer->scan_target->next;
if(xfr->task_transfer->scan_target && xfr->task_transfer->
scan_target->list)
xfr->task_transfer->scan_addr =
xfr->task_transfer->scan_target->list;
return;
}
/** move to next master in list, task_probe */
static void
xfr_probe_nextmaster(struct auth_xfer* xfr)
{
if(!xfr->task_probe->scan_specific && !xfr->task_probe->scan_target)
return;
if(xfr->task_probe->scan_addr) {
xfr->task_probe->scan_addr = xfr->task_probe->scan_addr->next;
if(xfr->task_probe->scan_addr)
return;
}
if(xfr->task_probe->scan_specific) {
xfr->task_probe->scan_specific = NULL;
xfr->task_probe->scan_target = xfr->task_probe->masters;
if(xfr->task_probe->scan_target && xfr->task_probe->
scan_target->list)
xfr->task_probe->scan_addr =
xfr->task_probe->scan_target->list;
return;
}
if(!xfr->task_probe->scan_target)
return;
xfr->task_probe->scan_target = xfr->task_probe->scan_target->next;
if(xfr->task_probe->scan_target && xfr->task_probe->
scan_target->list)
xfr->task_probe->scan_addr =
xfr->task_probe->scan_target->list;
return;
}
/** create SOA probe packet for xfr */
static void
xfr_create_soa_probe_packet(struct auth_xfer* xfr, sldns_buffer* buf,
uint16_t id)
{
struct query_info qinfo;
memset(&qinfo, 0, sizeof(qinfo));
qinfo.qname = xfr->name;
qinfo.qname_len = xfr->namelen;
qinfo.qtype = LDNS_RR_TYPE_SOA;
qinfo.qclass = xfr->dclass;
qinfo_query_encode(buf, &qinfo);
sldns_buffer_write_u16_at(buf, 0, id);
}
/** create IXFR/AXFR packet for xfr */
static void
xfr_create_ixfr_packet(struct auth_xfer* xfr, sldns_buffer* buf, uint16_t id,
struct auth_master* master)
{
struct query_info qinfo;
uint32_t serial;
int have_zone;
have_zone = xfr->have_zone;
serial = xfr->serial;
memset(&qinfo, 0, sizeof(qinfo));
qinfo.qname = xfr->name;
qinfo.qname_len = xfr->namelen;
xfr->task_transfer->got_xfr_serial = 0;
xfr->task_transfer->rr_scan_num = 0;
xfr->task_transfer->incoming_xfr_serial = 0;
xfr->task_transfer->on_ixfr_is_axfr = 0;
xfr->task_transfer->on_ixfr = 1;
qinfo.qtype = LDNS_RR_TYPE_IXFR;
if(!have_zone || xfr->task_transfer->ixfr_fail || !master->ixfr) {
qinfo.qtype = LDNS_RR_TYPE_AXFR;
xfr->task_transfer->ixfr_fail = 0;
xfr->task_transfer->on_ixfr = 0;
}
qinfo.qclass = xfr->dclass;
qinfo_query_encode(buf, &qinfo);
sldns_buffer_write_u16_at(buf, 0, id);
/* append serial for IXFR */
if(qinfo.qtype == LDNS_RR_TYPE_IXFR) {
size_t end = sldns_buffer_limit(buf);
sldns_buffer_clear(buf);
sldns_buffer_set_position(buf, end);
/* auth section count 1 */
sldns_buffer_write_u16_at(buf, LDNS_NSCOUNT_OFF, 1);
/* write SOA */
sldns_buffer_write_u8(buf, 0xC0); /* compressed ptr to qname */
sldns_buffer_write_u8(buf, 0x0C);
sldns_buffer_write_u16(buf, LDNS_RR_TYPE_SOA);
sldns_buffer_write_u16(buf, qinfo.qclass);
sldns_buffer_write_u32(buf, 0); /* ttl */
sldns_buffer_write_u16(buf, 22); /* rdata length */
sldns_buffer_write_u8(buf, 0); /* . */
sldns_buffer_write_u8(buf, 0); /* . */
sldns_buffer_write_u32(buf, serial); /* serial */
sldns_buffer_write_u32(buf, 0); /* refresh */
sldns_buffer_write_u32(buf, 0); /* retry */
sldns_buffer_write_u32(buf, 0); /* expire */
sldns_buffer_write_u32(buf, 0); /* minimum */
sldns_buffer_flip(buf);
}
}
/** check if returned packet is OK */
static int
check_packet_ok(sldns_buffer* pkt, uint16_t qtype, struct auth_xfer* xfr,
uint32_t* serial)
{
/* parse to see if packet worked, valid reply */
/* check serial number of SOA */
if(sldns_buffer_limit(pkt) < LDNS_HEADER_SIZE)
return 0;
/* check ID */
if(LDNS_ID_WIRE(sldns_buffer_begin(pkt)) != xfr->task_probe->id)
return 0;
/* check flag bits and rcode */
if(!LDNS_QR_WIRE(sldns_buffer_begin(pkt)))
return 0;
if(LDNS_OPCODE_WIRE(sldns_buffer_begin(pkt)) != LDNS_PACKET_QUERY)
return 0;
if(LDNS_RCODE_WIRE(sldns_buffer_begin(pkt)) != LDNS_RCODE_NOERROR)
return 0;
/* check qname */
if(LDNS_QDCOUNT(sldns_buffer_begin(pkt)) != 1)
return 0;
sldns_buffer_skip(pkt, LDNS_HEADER_SIZE);
if(sldns_buffer_remaining(pkt) < xfr->namelen)
return 0;
if(query_dname_compare(sldns_buffer_current(pkt), xfr->name) != 0)
return 0;
sldns_buffer_skip(pkt, (ssize_t)xfr->namelen);
/* check qtype, qclass */
if(sldns_buffer_remaining(pkt) < 4)
return 0;
if(sldns_buffer_read_u16(pkt) != qtype)
return 0;
if(sldns_buffer_read_u16(pkt) != xfr->dclass)
return 0;
if(serial) {
uint16_t rdlen;
/* read serial number, from answer section SOA */
if(LDNS_ANCOUNT(sldns_buffer_begin(pkt)) == 0)
return 0;
/* read from first record SOA record */
if(sldns_buffer_remaining(pkt) < 1)
return 0;
if(dname_pkt_compare(pkt, sldns_buffer_current(pkt),
xfr->name) != 0)
return 0;
if(!pkt_dname_len(pkt))
return 0;
/* type, class, ttl, rdatalen */
if(sldns_buffer_remaining(pkt) < 4+4+2)
return 0;
if(sldns_buffer_read_u16(pkt) != qtype)
return 0;
if(sldns_buffer_read_u16(pkt) != xfr->dclass)
return 0;
sldns_buffer_skip(pkt, 4); /* ttl */
rdlen = sldns_buffer_read_u16(pkt);
if(sldns_buffer_remaining(pkt) < rdlen)
return 0;
if(sldns_buffer_remaining(pkt) < 1)
return 0;
if(!pkt_dname_len(pkt)) /* soa name */
return 0;
if(sldns_buffer_remaining(pkt) < 1)
return 0;
if(!pkt_dname_len(pkt)) /* soa name */
return 0;
if(sldns_buffer_remaining(pkt) < 20)
return 0;
*serial = sldns_buffer_read_u32(pkt);
}
return 1;
}
/** read one line from chunks into buffer at current position */
static int
chunkline_get_line(struct auth_chunk** chunk, size_t* chunk_pos,
sldns_buffer* buf)
{
int readsome = 0;
while(*chunk) {
/* more text in this chunk? */
if(*chunk_pos < (*chunk)->len) {
readsome = 1;
while(*chunk_pos < (*chunk)->len) {
char c = (char)((*chunk)->data[*chunk_pos]);
(*chunk_pos)++;
if(sldns_buffer_remaining(buf) < 2) {
/* buffer too short */
verbose(VERB_ALGO, "http chunkline, "
"line too long");
return 0;
}
sldns_buffer_write_u8(buf, (uint8_t)c);
if(c == '\n') {
/* we are done */
return 1;
}
}
}
/* move to next chunk */
*chunk = (*chunk)->next;
*chunk_pos = 0;
}
/* no more text */
if(readsome) return 1;
return 0;
}
/** count number of open and closed parenthesis in a chunkline */
static int
chunkline_count_parens(sldns_buffer* buf, size_t start)
{
size_t end = sldns_buffer_position(buf);
size_t i;
int count = 0;
int squote = 0, dquote = 0;
for(i=start; i<end; i++) {
char c = (char)sldns_buffer_read_u8_at(buf, i);
if(squote && c != '\'') continue;
if(dquote && c != '"') continue;
if(c == '"')
dquote = !dquote; /* skip quoted part */
else if(c == '\'')
squote = !squote; /* skip quoted part */
else if(c == '(')
count ++;
else if(c == ')')
count --;
else if(c == ';') {
/* rest is a comment */
return count;
}
}
return count;
}
/** remove trailing ;... comment from a line in the chunkline buffer */
static void
chunkline_remove_trailcomment(sldns_buffer* buf, size_t start)
{
size_t end = sldns_buffer_position(buf);
size_t i;
int squote = 0, dquote = 0;
for(i=start; i<end; i++) {
char c = (char)sldns_buffer_read_u8_at(buf, i);
if(squote && c != '\'') continue;
if(dquote && c != '"') continue;
if(c == '"')
dquote = !dquote; /* skip quoted part */
else if(c == '\'')
squote = !squote; /* skip quoted part */
else if(c == ';') {
/* rest is a comment */
sldns_buffer_set_position(buf, i);
return;
}
}
/* nothing to remove */
}
/** see if a chunkline is a comment line (or empty line) */
static int
chunkline_is_comment_line_or_empty(sldns_buffer* buf)
{
size_t i, end = sldns_buffer_limit(buf);
for(i=0; i<end; i++) {
char c = (char)sldns_buffer_read_u8_at(buf, i);
if(c == ';')
return 1; /* comment */
else if(c != ' ' && c != '\t' && c != '\r' && c != '\n')
return 0; /* not a comment */
}
return 1; /* empty */
}
/** find a line with ( ) collated */
static int
chunkline_get_line_collated(struct auth_chunk** chunk, size_t* chunk_pos,
sldns_buffer* buf)
{
size_t pos;
int parens = 0;
sldns_buffer_clear(buf);
pos = sldns_buffer_position(buf);
if(!chunkline_get_line(chunk, chunk_pos, buf)) {
if(sldns_buffer_position(buf) < sldns_buffer_limit(buf))
sldns_buffer_write_u8_at(buf, sldns_buffer_position(buf), 0);
else sldns_buffer_write_u8_at(buf, sldns_buffer_position(buf)-1, 0);
sldns_buffer_flip(buf);
return 0;
}
parens += chunkline_count_parens(buf, pos);
while(parens > 0) {
chunkline_remove_trailcomment(buf, pos);
pos = sldns_buffer_position(buf);
if(!chunkline_get_line(chunk, chunk_pos, buf)) {
if(sldns_buffer_position(buf) < sldns_buffer_limit(buf))
sldns_buffer_write_u8_at(buf, sldns_buffer_position(buf), 0);
else sldns_buffer_write_u8_at(buf, sldns_buffer_position(buf)-1, 0);
sldns_buffer_flip(buf);
return 0;
}
parens += chunkline_count_parens(buf, pos);
}
if(sldns_buffer_remaining(buf) < 1) {
verbose(VERB_ALGO, "http chunkline: "
"line too long");
return 0;
}
sldns_buffer_write_u8_at(buf, sldns_buffer_position(buf), 0);
sldns_buffer_flip(buf);
return 1;
}
/** process $ORIGIN for http, 0 nothing, 1 done, 2 error */
static int
http_parse_origin(sldns_buffer* buf, struct sldns_file_parse_state* pstate)
{
char* line = (char*)sldns_buffer_begin(buf);
if(strncmp(line, "$ORIGIN", 7) == 0 &&
isspace((unsigned char)line[7])) {
int s;
pstate->origin_len = sizeof(pstate->origin);
s = sldns_str2wire_dname_buf(sldns_strip_ws(line+8),
pstate->origin, &pstate->origin_len);
if(s) {
pstate->origin_len = 0;
return 2;
}
return 1;
}
return 0;
}
/** process $TTL for http, 0 nothing, 1 done, 2 error */
static int
http_parse_ttl(sldns_buffer* buf, struct sldns_file_parse_state* pstate)
{
char* line = (char*)sldns_buffer_begin(buf);
if(strncmp(line, "$TTL", 4) == 0 &&
isspace((unsigned char)line[4])) {
const char* end = NULL;
int overflow = 0;
pstate->default_ttl = sldns_str2period(
sldns_strip_ws(line+5), &end, &overflow);
if(overflow) {
return 2;
}
return 1;
}
return 0;
}
/** find noncomment RR line in chunks, collates lines if ( ) format */
static int
chunkline_non_comment_RR(struct auth_chunk** chunk, size_t* chunk_pos,
sldns_buffer* buf, struct sldns_file_parse_state* pstate)
{
int ret;
while(chunkline_get_line_collated(chunk, chunk_pos, buf)) {
if(chunkline_is_comment_line_or_empty(buf)) {
/* a comment, go to next line */
continue;
}
if((ret=http_parse_origin(buf, pstate))!=0) {
if(ret == 2)
return 0;
continue; /* $ORIGIN has been handled */
}
if((ret=http_parse_ttl(buf, pstate))!=0) {
if(ret == 2)
return 0;
continue; /* $TTL has been handled */
}
return 1;
}
/* no noncomments, fail */
return 0;
}
/** check syntax of chunklist zonefile, parse first RR, return false on
* failure and return a string in the scratch buffer (first RR string)
* on failure. */
static int
http_zonefile_syntax_check(struct auth_xfer* xfr, sldns_buffer* buf)
{
uint8_t rr[LDNS_RR_BUF_SIZE];
size_t rr_len, dname_len = 0;
struct sldns_file_parse_state pstate;
struct auth_chunk* chunk;
size_t chunk_pos;
int e;
memset(&pstate, 0, sizeof(pstate));
pstate.default_ttl = 3600;
if(xfr->namelen < sizeof(pstate.origin)) {
pstate.origin_len = xfr->namelen;
memmove(pstate.origin, xfr->name, xfr->namelen);
}
chunk = xfr->task_transfer->chunks_first;
chunk_pos = 0;
if(!chunkline_non_comment_RR(&chunk, &chunk_pos, buf, &pstate)) {
return 0;
}
rr_len = sizeof(rr);
e=sldns_str2wire_rr_buf((char*)sldns_buffer_begin(buf), rr, &rr_len,
&dname_len, pstate.default_ttl,
pstate.origin_len?pstate.origin:NULL, pstate.origin_len,
pstate.prev_rr_len?pstate.prev_rr:NULL, pstate.prev_rr_len);
if(e != 0) {
log_err("parse failure on first RR[%d]: %s",
LDNS_WIREPARSE_OFFSET(e),
sldns_get_errorstr_parse(LDNS_WIREPARSE_ERROR(e)));
return 0;
}
/* check that class is correct */
if(sldns_wirerr_get_class(rr, rr_len, dname_len) != xfr->dclass) {
log_err("parse failure: first record in downloaded zonefile "
"from wrong RR class");
return 0;
}
return 1;
}
/** sum sizes of chunklist */
static size_t
chunklist_sum(struct auth_chunk* list)
{
struct auth_chunk* p;
size_t s = 0;
for(p=list; p; p=p->next) {
s += p->len;
}
return s;
}
/** remove newlines from collated line */
static void
chunkline_newline_removal(sldns_buffer* buf)
{
size_t i, end=sldns_buffer_limit(buf);
for(i=0; i<end; i++) {
char c = (char)sldns_buffer_read_u8_at(buf, i);
if(c == '\n' && i==end-1) {
sldns_buffer_write_u8_at(buf, i, 0);
sldns_buffer_set_limit(buf, end-1);
return;
}
if(c == '\n')
sldns_buffer_write_u8_at(buf, i, (uint8_t)' ');
}
}
/** for http download, parse and add RR to zone */
static int
http_parse_add_rr(struct auth_xfer* xfr, struct auth_zone* z,
sldns_buffer* buf, struct sldns_file_parse_state* pstate)
{
uint8_t rr[LDNS_RR_BUF_SIZE];
size_t rr_len, dname_len = 0;
int e;
char* line = (char*)sldns_buffer_begin(buf);
rr_len = sizeof(rr);
e = sldns_str2wire_rr_buf(line, rr, &rr_len, &dname_len,
pstate->default_ttl,
pstate->origin_len?pstate->origin:NULL, pstate->origin_len,
pstate->prev_rr_len?pstate->prev_rr:NULL, pstate->prev_rr_len);
if(e != 0) {
log_err("%s/%s parse failure RR[%d]: %s in '%s'",
xfr->task_transfer->master->host,
xfr->task_transfer->master->file,
LDNS_WIREPARSE_OFFSET(e),
sldns_get_errorstr_parse(LDNS_WIREPARSE_ERROR(e)),
line);
return 0;
}
if(rr_len == 0)
return 1; /* empty line or so */
/* set prev */
if(dname_len < sizeof(pstate->prev_rr)) {
memmove(pstate->prev_rr, rr, dname_len);
pstate->prev_rr_len = dname_len;
}
return az_insert_rr(z, rr, rr_len, dname_len, NULL);
}
/** RR list iterator, returns RRs from answer section one by one from the
* dns packets in the chunklist */
static void
chunk_rrlist_start(struct auth_xfer* xfr, struct auth_chunk** rr_chunk,
int* rr_num, size_t* rr_pos)
{
*rr_chunk = xfr->task_transfer->chunks_first;
*rr_num = 0;
*rr_pos = 0;
}
/** RR list iterator, see if we are at the end of the list */
static int
chunk_rrlist_end(struct auth_chunk* rr_chunk, int rr_num)
{
while(rr_chunk) {
if(rr_chunk->len < LDNS_HEADER_SIZE)
return 1;
if(rr_num < (int)LDNS_ANCOUNT(rr_chunk->data))
return 0;
/* no more RRs in this chunk */
/* continue with next chunk, see if it has RRs */
rr_chunk = rr_chunk->next;
rr_num = 0;
}
return 1;
}
/** RR list iterator, move to next RR */
static void
chunk_rrlist_gonext(struct auth_chunk** rr_chunk, int* rr_num,
size_t* rr_pos, size_t rr_nextpos)
{
/* already at end of chunks? */
if(!*rr_chunk)
return;
/* move within this chunk */
if((*rr_chunk)->len >= LDNS_HEADER_SIZE &&
(*rr_num)+1 < (int)LDNS_ANCOUNT((*rr_chunk)->data)) {
(*rr_num) += 1;
*rr_pos = rr_nextpos;
return;
}
/* no more RRs in this chunk */
/* continue with next chunk, see if it has RRs */
if(*rr_chunk)
*rr_chunk = (*rr_chunk)->next;
while(*rr_chunk) {
*rr_num = 0;
*rr_pos = 0;
if((*rr_chunk)->len >= LDNS_HEADER_SIZE &&
LDNS_ANCOUNT((*rr_chunk)->data) > 0) {
return;
}
*rr_chunk = (*rr_chunk)->next;
}
}
/** RR iterator, get current RR information, false on parse error */
static int
chunk_rrlist_get_current(struct auth_chunk* rr_chunk, int rr_num,
size_t rr_pos, uint8_t** rr_dname, uint16_t* rr_type,
uint16_t* rr_class, uint32_t* rr_ttl, uint16_t* rr_rdlen,
uint8_t** rr_rdata, size_t* rr_nextpos)
{
sldns_buffer pkt;
/* integrity checks on position */
if(!rr_chunk) return 0;
if(rr_chunk->len < LDNS_HEADER_SIZE) return 0;
if(rr_num >= (int)LDNS_ANCOUNT(rr_chunk->data)) return 0;
if(rr_pos >= rr_chunk->len) return 0;
/* fetch rr information */
sldns_buffer_init_frm_data(&pkt, rr_chunk->data, rr_chunk->len);
if(rr_pos == 0) {
size_t i;
/* skip question section */
sldns_buffer_set_position(&pkt, LDNS_HEADER_SIZE);
for(i=0; i<LDNS_QDCOUNT(rr_chunk->data); i++) {
if(pkt_dname_len(&pkt) == 0) return 0;
if(sldns_buffer_remaining(&pkt) < 4) return 0;
sldns_buffer_skip(&pkt, 4); /* type and class */
}
} else {
sldns_buffer_set_position(&pkt, rr_pos);
}
*rr_dname = sldns_buffer_current(&pkt);
if(pkt_dname_len(&pkt) == 0) return 0;
if(sldns_buffer_remaining(&pkt) < 10) return 0;
*rr_type = sldns_buffer_read_u16(&pkt);
*rr_class = sldns_buffer_read_u16(&pkt);
*rr_ttl = sldns_buffer_read_u32(&pkt);
*rr_rdlen = sldns_buffer_read_u16(&pkt);
if(sldns_buffer_remaining(&pkt) < (*rr_rdlen)) return 0;
*rr_rdata = sldns_buffer_current(&pkt);
sldns_buffer_skip(&pkt, (ssize_t)(*rr_rdlen));
*rr_nextpos = sldns_buffer_position(&pkt);
return 1;
}
/** print log message where we are in parsing the zone transfer */
static void
log_rrlist_position(const char* label, struct auth_chunk* rr_chunk,
uint8_t* rr_dname, uint16_t rr_type, size_t rr_counter)
{
sldns_buffer pkt;
size_t dlen;
uint8_t buf[256];
char str[256];
char typestr[32];
sldns_buffer_init_frm_data(&pkt, rr_chunk->data, rr_chunk->len);
sldns_buffer_set_position(&pkt, (size_t)(rr_dname -
sldns_buffer_begin(&pkt)));
if((dlen=pkt_dname_len(&pkt)) == 0) return;
if(dlen >= sizeof(buf)) return;
dname_pkt_copy(&pkt, buf, rr_dname);
dname_str(buf, str);
(void)sldns_wire2str_type_buf(rr_type, typestr, sizeof(typestr));
verbose(VERB_ALGO, "%s at[%d] %s %s", label, (int)rr_counter,
str, typestr);
}
/** check that start serial is OK for ixfr. we are at rr_counter == 0,
* and we are going to check rr_counter == 1 (has to be type SOA) serial */
static int
ixfr_start_serial(struct auth_chunk* rr_chunk, int rr_num, size_t rr_pos,
uint8_t* rr_dname, uint16_t rr_type, uint16_t rr_class,
uint32_t rr_ttl, uint16_t rr_rdlen, uint8_t* rr_rdata,
size_t rr_nextpos, uint32_t transfer_serial, uint32_t xfr_serial)
{
uint32_t startserial;
/* move forward on RR */
chunk_rrlist_gonext(&rr_chunk, &rr_num, &rr_pos, rr_nextpos);
if(chunk_rrlist_end(rr_chunk, rr_num)) {
/* no second SOA */
verbose(VERB_OPS, "IXFR has no second SOA record");
return 0;
}
if(!chunk_rrlist_get_current(rr_chunk, rr_num, rr_pos,
&rr_dname, &rr_type, &rr_class, &rr_ttl, &rr_rdlen,
&rr_rdata, &rr_nextpos)) {
verbose(VERB_OPS, "IXFR cannot parse second SOA record");
/* failed to parse RR */
return 0;
}
if(rr_type != LDNS_RR_TYPE_SOA) {
verbose(VERB_OPS, "IXFR second record is not type SOA");
return 0;
}
if(rr_rdlen < 22) {
verbose(VERB_OPS, "IXFR, second SOA has short rdlength");
return 0; /* bad SOA rdlen */
}
startserial = sldns_read_uint32(rr_rdata+rr_rdlen-20);
if(startserial == transfer_serial) {
/* empty AXFR, not an IXFR */
verbose(VERB_OPS, "IXFR second serial same as first");
return 0;
}
if(startserial != xfr_serial) {
/* wrong start serial, it does not match the serial in
* memory */
verbose(VERB_OPS, "IXFR is from serial %u to %u but %u "
"in memory, rejecting the zone transfer",
(unsigned)startserial, (unsigned)transfer_serial,
(unsigned)xfr_serial);
return 0;
}
/* everything OK in second SOA serial */
return 1;
}
/** apply IXFR to zone in memory. z is locked. false on failure(mallocfail) */
static int
apply_ixfr(struct auth_xfer* xfr, struct auth_zone* z,
struct sldns_buffer* scratch_buffer)
{
struct auth_chunk* rr_chunk;
int rr_num;
size_t rr_pos;
uint8_t* rr_dname, *rr_rdata;
uint16_t rr_type, rr_class, rr_rdlen;
uint32_t rr_ttl;
size_t rr_nextpos;
int have_transfer_serial = 0;
uint32_t transfer_serial = 0;
size_t rr_counter = 0;
int delmode = 0;
int softfail = 0;
/* start RR iterator over chunklist of packets */
chunk_rrlist_start(xfr, &rr_chunk, &rr_num, &rr_pos);
while(!chunk_rrlist_end(rr_chunk, rr_num)) {
if(!chunk_rrlist_get_current(rr_chunk, rr_num, rr_pos,
&rr_dname, &rr_type, &rr_class, &rr_ttl, &rr_rdlen,
&rr_rdata, &rr_nextpos)) {
/* failed to parse RR */
return 0;
}
if(verbosity>=7) log_rrlist_position("apply ixfr",
rr_chunk, rr_dname, rr_type, rr_counter);
/* twiddle add/del mode and check for start and end */
if(rr_counter == 0 && rr_type != LDNS_RR_TYPE_SOA)
return 0;
if(rr_counter == 1 && rr_type != LDNS_RR_TYPE_SOA) {
/* this is an AXFR returned from the IXFR master */
/* but that should already have been detected, by
* on_ixfr_is_axfr */
return 0;
}
if(rr_type == LDNS_RR_TYPE_SOA) {
uint32_t serial;
if(rr_rdlen < 22) return 0; /* bad SOA rdlen */
serial = sldns_read_uint32(rr_rdata+rr_rdlen-20);
if(have_transfer_serial == 0) {
have_transfer_serial = 1;
transfer_serial = serial;
delmode = 1; /* gets negated below */
/* check second RR before going any further */
if(!ixfr_start_serial(rr_chunk, rr_num, rr_pos,
rr_dname, rr_type, rr_class, rr_ttl,
rr_rdlen, rr_rdata, rr_nextpos,
transfer_serial, xfr->serial)) {
return 0;
}
} else if(transfer_serial == serial) {
have_transfer_serial++;
if(rr_counter == 1) {
/* empty AXFR, with SOA; SOA; */
/* should have been detected by
* on_ixfr_is_axfr */
return 0;
}
if(have_transfer_serial == 3) {
/* see serial three times for end */
/* eg. IXFR:
* SOA 3 start
* SOA 1 second RR, followed by del
* SOA 2 followed by add
* SOA 2 followed by del
* SOA 3 followed by add
* SOA 3 end */
/* ended by SOA record */
xfr->serial = transfer_serial;
break;
}
}
/* twiddle add/del mode */
/* switch from delete part to add part and back again
* just before the soa, it gets deleted and added too
* this means we switch to delete mode for the final
* SOA(so skip that one) */
delmode = !delmode;
}
/* process this RR */
/* if the RR is deleted twice or added twice, then we
* softfail, and continue with the rest of the IXFR, so
* that we serve something fairly nice during the refetch */
if(verbosity>=7) log_rrlist_position((delmode?"del":"add"),
rr_chunk, rr_dname, rr_type, rr_counter);
if(delmode) {
/* delete this RR */
int nonexist = 0;
if(!az_remove_rr_decompress(z, rr_chunk->data,
rr_chunk->len, scratch_buffer, rr_dname,
rr_type, rr_class, rr_ttl, rr_rdata, rr_rdlen,
&nonexist)) {
/* failed, malloc error or so */
return 0;
}
if(nonexist) {
/* it was removal of a nonexisting RR */
if(verbosity>=4) log_rrlist_position(
"IXFR error nonexistent RR",
rr_chunk, rr_dname, rr_type, rr_counter);
softfail = 1;
}
} else if(rr_counter != 0) {
/* skip first SOA RR for addition, it is added in
* the addition part near the end of the ixfr, when
* that serial is seen the second time. */
int duplicate = 0;
/* add this RR */
if(!az_insert_rr_decompress(z, rr_chunk->data,
rr_chunk->len, scratch_buffer, rr_dname,
rr_type, rr_class, rr_ttl, rr_rdata, rr_rdlen,
&duplicate)) {
/* failed, malloc error or so */
return 0;
}
if(duplicate) {
/* it was a duplicate */
if(verbosity>=4) log_rrlist_position(
"IXFR error duplicate RR",
rr_chunk, rr_dname, rr_type, rr_counter);
softfail = 1;
}
}
rr_counter++;
chunk_rrlist_gonext(&rr_chunk, &rr_num, &rr_pos, rr_nextpos);
}
if(softfail) {
verbose(VERB_ALGO, "IXFR did not apply cleanly, fetching full zone");
return 0;
}
return 1;
}
/** apply AXFR to zone in memory. z is locked. false on failure(mallocfail) */
static int
apply_axfr(struct auth_xfer* xfr, struct auth_zone* z,
struct sldns_buffer* scratch_buffer)
{
struct auth_chunk* rr_chunk;
int rr_num;
size_t rr_pos;
uint8_t* rr_dname, *rr_rdata;
uint16_t rr_type, rr_class, rr_rdlen;
uint32_t rr_ttl;
uint32_t serial = 0;
size_t rr_nextpos;
size_t rr_counter = 0;
int have_end_soa = 0;
/* clear the data tree */
traverse_postorder(&z->data, auth_data_del, NULL);
rbtree_init(&z->data, &auth_data_cmp);
/* clear the RPZ policies */
if(z->rpz)
rpz_clear(z->rpz);
xfr->have_zone = 0;
xfr->serial = 0;
/* insert all RRs in to the zone */
/* insert the SOA only once, skip the last one */
/* start RR iterator over chunklist of packets */
chunk_rrlist_start(xfr, &rr_chunk, &rr_num, &rr_pos);
while(!chunk_rrlist_end(rr_chunk, rr_num)) {
if(!chunk_rrlist_get_current(rr_chunk, rr_num, rr_pos,
&rr_dname, &rr_type, &rr_class, &rr_ttl, &rr_rdlen,
&rr_rdata, &rr_nextpos)) {
/* failed to parse RR */
return 0;
}
if(verbosity>=7) log_rrlist_position("apply_axfr",
rr_chunk, rr_dname, rr_type, rr_counter);
if(rr_type == LDNS_RR_TYPE_SOA) {
if(rr_counter != 0) {
/* end of the axfr */
have_end_soa = 1;
break;
}
if(rr_rdlen < 22) return 0; /* bad SOA rdlen */
serial = sldns_read_uint32(rr_rdata+rr_rdlen-20);
}
/* add this RR */
if(!az_insert_rr_decompress(z, rr_chunk->data, rr_chunk->len,
scratch_buffer, rr_dname, rr_type, rr_class, rr_ttl,
rr_rdata, rr_rdlen, NULL)) {
/* failed, malloc error or so */
return 0;
}
rr_counter++;
chunk_rrlist_gonext(&rr_chunk, &rr_num, &rr_pos, rr_nextpos);
}
if(!have_end_soa) {
log_err("no end SOA record for AXFR");
return 0;
}
xfr->serial = serial;
xfr->have_zone = 1;
return 1;
}
/** apply HTTP to zone in memory. z is locked. false on failure(mallocfail) */
static int
apply_http(struct auth_xfer* xfr, struct auth_zone* z,
struct sldns_buffer* scratch_buffer)
{
/* parse data in chunks */
/* parse RR's and read into memory. ignore $INCLUDE from the
* downloaded file*/
struct sldns_file_parse_state pstate;
struct auth_chunk* chunk;
size_t chunk_pos;
int ret;
memset(&pstate, 0, sizeof(pstate));
pstate.default_ttl = 3600;
if(xfr->namelen < sizeof(pstate.origin)) {
pstate.origin_len = xfr->namelen;
memmove(pstate.origin, xfr->name, xfr->namelen);
}
if(verbosity >= VERB_ALGO)
verbose(VERB_ALGO, "http download %s of size %d",
xfr->task_transfer->master->file,
(int)chunklist_sum(xfr->task_transfer->chunks_first));
if(xfr->task_transfer->chunks_first && verbosity >= VERB_ALGO) {
char preview[1024];
if(xfr->task_transfer->chunks_first->len+1 > sizeof(preview)) {
memmove(preview, xfr->task_transfer->chunks_first->data,
sizeof(preview)-1);
preview[sizeof(preview)-1]=0;
} else {
memmove(preview, xfr->task_transfer->chunks_first->data,
xfr->task_transfer->chunks_first->len);
preview[xfr->task_transfer->chunks_first->len]=0;
}
log_info("auth zone http downloaded content preview: %s",
preview);
}
/* perhaps a little syntax check before we try to apply the data? */
if(!http_zonefile_syntax_check(xfr, scratch_buffer)) {
log_err("http download %s/%s does not contain a zonefile, "
"but got '%s'", xfr->task_transfer->master->host,
xfr->task_transfer->master->file,
sldns_buffer_begin(scratch_buffer));
return 0;
}
/* clear the data tree */
traverse_postorder(&z->data, auth_data_del, NULL);
rbtree_init(&z->data, &auth_data_cmp);
/* clear the RPZ policies */
if(z->rpz)
rpz_clear(z->rpz);
xfr->have_zone = 0;
xfr->serial = 0;
chunk = xfr->task_transfer->chunks_first;
chunk_pos = 0;
pstate.lineno = 0;
while(chunkline_get_line_collated(&chunk, &chunk_pos, scratch_buffer)) {
/* process this line */
pstate.lineno++;
chunkline_newline_removal(scratch_buffer);
if(chunkline_is_comment_line_or_empty(scratch_buffer)) {
continue;
}
/* parse line and add RR */
if((ret=http_parse_origin(scratch_buffer, &pstate))!=0) {
if(ret == 2) {
verbose(VERB_ALGO, "error parsing ORIGIN on line [%s:%d] %s",
xfr->task_transfer->master->file,
pstate.lineno,
sldns_buffer_begin(scratch_buffer));
return 0;
}
continue; /* $ORIGIN has been handled */
}
if((ret=http_parse_ttl(scratch_buffer, &pstate))!=0) {
if(ret == 2) {
verbose(VERB_ALGO, "error parsing TTL on line [%s:%d] %s",
xfr->task_transfer->master->file,
pstate.lineno,
sldns_buffer_begin(scratch_buffer));
return 0;
}
continue; /* $TTL has been handled */
}
if(!http_parse_add_rr(xfr, z, scratch_buffer, &pstate)) {
verbose(VERB_ALGO, "error parsing line [%s:%d] %s",
xfr->task_transfer->master->file,
pstate.lineno,
sldns_buffer_begin(scratch_buffer));
return 0;
}
}
return 1;
}
/** write http chunks to zonefile to create downloaded file */
static int
auth_zone_write_chunks(struct auth_xfer* xfr, const char* fname)
{
FILE* out;
struct auth_chunk* p;
out = fopen(fname, "w");
if(!out) {
log_err("could not open %s: %s", fname, strerror(errno));
return 0;
}
for(p = xfr->task_transfer->chunks_first; p ; p = p->next) {
if(!write_out(out, (char*)p->data, p->len)) {
log_err("could not write http download to %s", fname);
fclose(out);
return 0;
}
}
fclose(out);
return 1;
}
/** write to zonefile after zone has been updated */
static void
xfr_write_after_update(struct auth_xfer* xfr, struct module_env* env)
{
struct config_file* cfg = env->cfg;
struct auth_zone* z;
char tmpfile[1024];
char* zfilename;
lock_basic_unlock(&xfr->lock);
/* get lock again, so it is a readlock and concurrently queries
* can be answered */
lock_rw_rdlock(&env->auth_zones->lock);
z = auth_zone_find(env->auth_zones, xfr->name, xfr->namelen,
xfr->dclass);
if(!z) {
lock_rw_unlock(&env->auth_zones->lock);
/* the zone is gone, ignore xfr results */
lock_basic_lock(&xfr->lock);
return;
}
lock_rw_rdlock(&z->lock);
lock_basic_lock(&xfr->lock);
lock_rw_unlock(&env->auth_zones->lock);
if(z->zonefile == NULL || z->zonefile[0] == 0) {
lock_rw_unlock(&z->lock);
/* no write needed, no zonefile set */
return;
}
zfilename = z->zonefile;
if(cfg->chrootdir && cfg->chrootdir[0] && strncmp(zfilename,
cfg->chrootdir, strlen(cfg->chrootdir)) == 0)
zfilename += strlen(cfg->chrootdir);
if(verbosity >= VERB_ALGO) {
char nm[255+1];
dname_str(z->name, nm);
verbose(VERB_ALGO, "write zonefile %s for %s", zfilename, nm);
}
/* write to tempfile first */
if((size_t)strlen(zfilename) + 16 > sizeof(tmpfile)) {
verbose(VERB_ALGO, "tmpfilename too long, cannot update "
" zonefile %s", zfilename);
lock_rw_unlock(&z->lock);
return;
}
snprintf(tmpfile, sizeof(tmpfile), "%s.tmp%u", zfilename,
(unsigned)getpid());
if(xfr->task_transfer->master->http) {
/* use the stored chunk list to write them */
if(!auth_zone_write_chunks(xfr, tmpfile)) {
unlink(tmpfile);
lock_rw_unlock(&z->lock);
return;
}
} else if(!auth_zone_write_file(z, tmpfile)) {
unlink(tmpfile);
lock_rw_unlock(&z->lock);
return;
}
#ifdef UB_ON_WINDOWS
(void)unlink(zfilename); /* windows does not replace file with rename() */
#endif
if(rename(tmpfile, zfilename) < 0) {
log_err("could not rename(%s, %s): %s", tmpfile, zfilename,
strerror(errno));
unlink(tmpfile);
lock_rw_unlock(&z->lock);
return;
}
lock_rw_unlock(&z->lock);
}
/** reacquire locks and structures. Starts with no locks, ends
* with xfr and z locks, if fail, no z lock */
static int xfr_process_reacquire_locks(struct auth_xfer* xfr,
struct module_env* env, struct auth_zone** z)
{
/* release xfr lock, then, while holding az->lock grab both
* z->lock and xfr->lock */
lock_rw_rdlock(&env->auth_zones->lock);
*z = auth_zone_find(env->auth_zones, xfr->name, xfr->namelen,
xfr->dclass);
if(!*z) {
lock_rw_unlock(&env->auth_zones->lock);
lock_basic_lock(&xfr->lock);
*z = NULL;
return 0;
}
lock_rw_wrlock(&(*z)->lock);
lock_basic_lock(&xfr->lock);
lock_rw_unlock(&env->auth_zones->lock);
return 1;
}
/** process chunk list and update zone in memory,
* return false if it did not work */
static int
xfr_process_chunk_list(struct auth_xfer* xfr, struct module_env* env,
int* ixfr_fail)
{
struct auth_zone* z;
/* obtain locks and structures */
lock_basic_unlock(&xfr->lock);
if(!xfr_process_reacquire_locks(xfr, env, &z)) {
/* the zone is gone, ignore xfr results */
return 0;
}
/* holding xfr and z locks */
/* apply data */
if(xfr->task_transfer->master->http) {
if(!apply_http(xfr, z, env->scratch_buffer)) {
lock_rw_unlock(&z->lock);
verbose(VERB_ALGO, "http from %s: could not store data",
xfr->task_transfer->master->host);
return 0;
}
} else if(xfr->task_transfer->on_ixfr &&
!xfr->task_transfer->on_ixfr_is_axfr) {
if(!apply_ixfr(xfr, z, env->scratch_buffer)) {
lock_rw_unlock(&z->lock);
verbose(VERB_ALGO, "xfr from %s: could not store IXFR"
" data", xfr->task_transfer->master->host);
*ixfr_fail = 1;
return 0;
}
} else {
if(!apply_axfr(xfr, z, env->scratch_buffer)) {
lock_rw_unlock(&z->lock);
verbose(VERB_ALGO, "xfr from %s: could not store AXFR"
" data", xfr->task_transfer->master->host);
return 0;
}
}
xfr->zone_expired = 0;
z->zone_expired = 0;
if(!xfr_find_soa(z, xfr)) {
lock_rw_unlock(&z->lock);
verbose(VERB_ALGO, "xfr from %s: no SOA in zone after update"
" (or malformed RR)", xfr->task_transfer->master->host);
return 0;
}
/* release xfr lock while verifying zonemd because it may have
* to spawn lookups in the state machines */
lock_basic_unlock(&xfr->lock);
/* holding z lock */
auth_zone_verify_zonemd(z, env, &env->mesh->mods, NULL, 0, 0);
if(z->zone_expired) {
char zname[256];
/* ZONEMD must have failed */
/* reacquire locks, so we hold xfr lock on exit of routine,
* and both xfr and z again after releasing xfr for potential
* state machine mesh callbacks */
lock_rw_unlock(&z->lock);
if(!xfr_process_reacquire_locks(xfr, env, &z))
return 0;
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "xfr from %s: ZONEMD failed for %s, transfer is failed", xfr->task_transfer->master->host, zname);
xfr->zone_expired = 1;
lock_rw_unlock(&z->lock);
return 0;
}
/* reacquire locks, so we hold xfr lock on exit of routine,
* and both xfr and z again after releasing xfr for potential
* state machine mesh callbacks */
lock_rw_unlock(&z->lock);
if(!xfr_process_reacquire_locks(xfr, env, &z))
return 0;
/* holding xfr and z locks */
if(xfr->have_zone)
xfr->lease_time = *env->now;
if(z->rpz)
rpz_finish_config(z->rpz);
/* unlock */
lock_rw_unlock(&z->lock);
if(verbosity >= VERB_QUERY && xfr->have_zone) {
char zname[256];
dname_str(xfr->name, zname);
verbose(VERB_QUERY, "auth zone %s updated to serial %u", zname,
(unsigned)xfr->serial);
}
/* see if we need to write to a zonefile */
xfr_write_after_update(xfr, env);
return 1;
}
/** disown task_transfer. caller must hold xfr.lock */
static void
xfr_transfer_disown(struct auth_xfer* xfr)
{
/* remove timer (from this worker's event base) */
comm_timer_delete(xfr->task_transfer->timer);
xfr->task_transfer->timer = NULL;
/* remove the commpoint */
comm_point_delete(xfr->task_transfer->cp);
xfr->task_transfer->cp = NULL;
/* we don't own this item anymore */
xfr->task_transfer->worker = NULL;
xfr->task_transfer->env = NULL;
}
/** lookup a host name for its addresses, if needed */
static int
xfr_transfer_lookup_host(struct auth_xfer* xfr, struct module_env* env)
{
struct sockaddr_storage addr;
socklen_t addrlen = 0;
struct auth_master* master = xfr->task_transfer->lookup_target;
struct query_info qinfo;
uint16_t qflags = BIT_RD;
uint8_t dname[LDNS_MAX_DOMAINLEN+1];
struct edns_data edns;
sldns_buffer* buf = env->scratch_buffer;
if(!master) return 0;
if(extstrtoaddr(master->host, &addr, &addrlen, UNBOUND_DNS_PORT)) {
/* not needed, host is in IP addr format */
return 0;
}
if(master->allow_notify)
return 0; /* allow-notifies are not transferred from, no
lookup is needed */
/* use mesh_new_callback to probe for non-addr hosts,
* and then wait for them to be looked up (in cache, or query) */
qinfo.qname_len = sizeof(dname);
if(sldns_str2wire_dname_buf(master->host, dname, &qinfo.qname_len)
!= 0) {
log_err("cannot parse host name of master %s", master->host);
return 0;
}
qinfo.qname = dname;
qinfo.qclass = xfr->dclass;
qinfo.qtype = LDNS_RR_TYPE_A;
if(xfr->task_transfer->lookup_aaaa)
qinfo.qtype = LDNS_RR_TYPE_AAAA;
qinfo.local_alias = NULL;
if(verbosity >= VERB_ALGO) {
char buf1[512];
char buf2[LDNS_MAX_DOMAINLEN+1];
dname_str(xfr->name, buf2);
snprintf(buf1, sizeof(buf1), "auth zone %s: master lookup"
" for task_transfer", buf2);
log_query_info(VERB_ALGO, buf1, &qinfo);
}
edns.edns_present = 1;
edns.ext_rcode = 0;
edns.edns_version = 0;
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;
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;
/* unlock xfr during mesh_new_callback() because the callback can be
* called straight away */
lock_basic_unlock(&xfr->lock);
if(!mesh_new_callback(env->mesh, &qinfo, qflags, &edns, buf, 0,
&auth_xfer_transfer_lookup_callback, xfr, 0)) {
lock_basic_lock(&xfr->lock);
log_err("out of memory lookup up master %s", master->host);
return 0;
}
lock_basic_lock(&xfr->lock);
return 1;
}
/** initiate TCP to the target and fetch zone.
* returns true if that was successfully started, and timeout setup. */
static int
xfr_transfer_init_fetch(struct auth_xfer* xfr, struct module_env* env)
{
struct sockaddr_storage addr;
socklen_t addrlen = 0;
struct auth_master* master = xfr->task_transfer->master;
char *auth_name = NULL;
struct timeval t;
int timeout;
if(!master) return 0;
if(master->allow_notify) return 0; /* only for notify */
/* get master addr */
if(xfr->task_transfer->scan_addr) {
addrlen = xfr->task_transfer->scan_addr->addrlen;
memmove(&addr, &xfr->task_transfer->scan_addr->addr, addrlen);
} else {
if(!authextstrtoaddr(master->host, &addr, &addrlen, &auth_name)) {
/* the ones that are not in addr format are supposed
* to be looked up. The lookup has failed however,
* so skip them */
char zname[255+1];
dname_str(xfr->name, zname);
log_err("%s: failed lookup, cannot transfer from master %s",
zname, master->host);
return 0;
}
}
/* remove previous TCP connection (if any) */
if(xfr->task_transfer->cp) {
comm_point_delete(xfr->task_transfer->cp);
xfr->task_transfer->cp = NULL;
}
if(!xfr->task_transfer->timer) {
xfr->task_transfer->timer = comm_timer_create(env->worker_base,
auth_xfer_transfer_timer_callback, xfr);
if(!xfr->task_transfer->timer) {
log_err("malloc failure");
return 0;
}
}
timeout = AUTH_TRANSFER_TIMEOUT;
#ifndef S_SPLINT_S
t.tv_sec = timeout/1000;
t.tv_usec = (timeout%1000)*1000;
#endif
if(master->http) {
/* perform http fetch */
/* store http port number into sockaddr,
* unless someone used unbound's host@port notation */
xfr->task_transfer->on_ixfr = 0;
if(strchr(master->host, '@') == NULL)
sockaddr_store_port(&addr, addrlen, master->port);
xfr->task_transfer->cp = outnet_comm_point_for_http(
env->outnet, auth_xfer_transfer_http_callback, xfr,
&addr, addrlen, -1, master->ssl, master->host,
master->file, env->cfg);
if(!xfr->task_transfer->cp) {
char zname[255+1], as[256];
dname_str(xfr->name, zname);
addr_to_str(&addr, addrlen, as, sizeof(as));
verbose(VERB_ALGO, "cannot create http cp "
"connection for %s to %s", zname, as);
return 0;
}
comm_timer_set(xfr->task_transfer->timer, &t);
if(verbosity >= VERB_ALGO) {
char zname[255+1], as[256];
dname_str(xfr->name, zname);
addr_to_str(&addr, addrlen, as, sizeof(as));
verbose(VERB_ALGO, "auth zone %s transfer next HTTP fetch from %s started", zname, as);
}
/* Create or refresh the list of allow_notify addrs */
probe_copy_masters_for_allow_notify(xfr);
return 1;
}
/* perform AXFR/IXFR */
/* set the packet to be written */
/* create new ID */
xfr->task_transfer->id = GET_RANDOM_ID(env->rnd);
xfr_create_ixfr_packet(xfr, env->scratch_buffer,
xfr->task_transfer->id, master);
/* connect on fd */
xfr->task_transfer->cp = outnet_comm_point_for_tcp(env->outnet,
auth_xfer_transfer_tcp_callback, xfr, &addr, addrlen,
env->scratch_buffer, -1,
auth_name != NULL, auth_name);
if(!xfr->task_transfer->cp) {
char zname[255+1], as[256];
dname_str(xfr->name, zname);
addr_to_str(&addr, addrlen, as, sizeof(as));
verbose(VERB_ALGO, "cannot create tcp cp connection for "
"xfr %s to %s", zname, as);
return 0;
}
comm_timer_set(xfr->task_transfer->timer, &t);
if(verbosity >= VERB_ALGO) {
char zname[255+1], as[256];
dname_str(xfr->name, zname);
addr_to_str(&addr, addrlen, as, sizeof(as));
verbose(VERB_ALGO, "auth zone %s transfer next %s fetch from %s started", zname,
(xfr->task_transfer->on_ixfr?"IXFR":"AXFR"), as);
}
return 1;
}
/** perform next lookup, next transfer TCP, or end and resume wait time task */
static void
xfr_transfer_nexttarget_or_end(struct auth_xfer* xfr, struct module_env* env)
{
log_assert(xfr->task_transfer->worker == env->worker);
/* are we performing lookups? */
while(xfr->task_transfer->lookup_target) {
if(xfr_transfer_lookup_host(xfr, env)) {
/* wait for lookup to finish,
* note that the hostname may be in unbound's cache
* and we may then get an instant cache response,
* and that calls the callback just like a full
* lookup and lookup failures also call callback */
if(verbosity >= VERB_ALGO) {
char zname[255+1];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s transfer next target lookup", zname);
}
lock_basic_unlock(&xfr->lock);
return;
}
xfr_transfer_move_to_next_lookup(xfr, env);
}
/* initiate TCP and fetch the zone from the master */
/* and set timeout on it */
while(!xfr_transfer_end_of_list(xfr)) {
xfr->task_transfer->master = xfr_transfer_current_master(xfr);
if(xfr_transfer_init_fetch(xfr, env)) {
/* successfully started, wait for callback */
lock_basic_unlock(&xfr->lock);
return;
}
/* failed to fetch, next master */
xfr_transfer_nextmaster(xfr);
}
if(verbosity >= VERB_ALGO) {
char zname[255+1];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s transfer failed, wait", zname);
}
/* we failed to fetch the zone, move to wait task
* use the shorter retry timeout */
xfr_transfer_disown(xfr);
/* pick up the nextprobe task and wait */
if(xfr->task_nextprobe->worker == NULL)
xfr_set_timeout(xfr, env, 1, 0);
lock_basic_unlock(&xfr->lock);
}
/** add addrs from A or AAAA rrset to the master */
static void
xfr_master_add_addrs(struct auth_master* m, struct ub_packed_rrset_key* rrset,
uint16_t rrtype)
{
size_t i;
struct packed_rrset_data* data;
if(!m || !rrset) return;
if(rrtype != LDNS_RR_TYPE_A && rrtype != LDNS_RR_TYPE_AAAA)
return;
data = (struct packed_rrset_data*)rrset->entry.data;
for(i=0; i<data->count; i++) {
struct auth_addr* a;
size_t len = data->rr_len[i] - 2;
uint8_t* rdata = data->rr_data[i]+2;
if(rrtype == LDNS_RR_TYPE_A && len != INET_SIZE)
continue; /* wrong length for A */
if(rrtype == LDNS_RR_TYPE_AAAA && len != INET6_SIZE)
continue; /* wrong length for AAAA */
/* add and alloc it */
a = (struct auth_addr*)calloc(1, sizeof(*a));
if(!a) {
log_err("out of memory");
return;
}
if(rrtype == LDNS_RR_TYPE_A) {
struct sockaddr_in* sa;
a->addrlen = (socklen_t)sizeof(*sa);
sa = (struct sockaddr_in*)&a->addr;
sa->sin_family = AF_INET;
sa->sin_port = (in_port_t)htons(UNBOUND_DNS_PORT);
memmove(&sa->sin_addr, rdata, INET_SIZE);
} else {
struct sockaddr_in6* sa;
a->addrlen = (socklen_t)sizeof(*sa);
sa = (struct sockaddr_in6*)&a->addr;
sa->sin6_family = AF_INET6;
sa->sin6_port = (in_port_t)htons(UNBOUND_DNS_PORT);
memmove(&sa->sin6_addr, rdata, INET6_SIZE);
}
if(verbosity >= VERB_ALGO) {
char s[64];
addr_to_str(&a->addr, a->addrlen, s, sizeof(s));
verbose(VERB_ALGO, "auth host %s lookup %s",
m->host, s);
}
/* append to list */
a->next = m->list;
m->list = a;
}
}
/** callback for task_transfer lookup of host name, of A or AAAA */
void auth_xfer_transfer_lookup_callback(void* arg, int rcode, sldns_buffer* buf,
enum sec_status ATTR_UNUSED(sec), char* ATTR_UNUSED(why_bogus),
int ATTR_UNUSED(was_ratelimited))
{
struct auth_xfer* xfr = (struct auth_xfer*)arg;
struct module_env* env;
log_assert(xfr->task_transfer);
lock_basic_lock(&xfr->lock);
env = xfr->task_transfer->env;
if(!env || env->outnet->want_to_quit) {
lock_basic_unlock(&xfr->lock);
return; /* stop on quit */
}
/* process result */
if(rcode == LDNS_RCODE_NOERROR) {
uint16_t wanted_qtype = LDNS_RR_TYPE_A;
struct regional* temp = env->scratch;
struct query_info rq;
struct reply_info* rep;
if(xfr->task_transfer->lookup_aaaa)
wanted_qtype = LDNS_RR_TYPE_AAAA;
memset(&rq, 0, sizeof(rq));
rep = parse_reply_in_temp_region(buf, temp, &rq);
if(rep && rq.qtype == wanted_qtype &&
FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NOERROR) {
/* parsed successfully */
struct ub_packed_rrset_key* answer =
reply_find_answer_rrset(&rq, rep);
if(answer) {
xfr_master_add_addrs(xfr->task_transfer->
lookup_target, answer, wanted_qtype);
} else {
if(verbosity >= VERB_ALGO) {
char zname[255+1];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s host %s type %s transfer lookup has nodata", zname, xfr->task_transfer->lookup_target->host, (xfr->task_transfer->lookup_aaaa?"AAAA":"A"));
}
}
} else {
if(verbosity >= VERB_ALGO) {
char zname[255+1];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s host %s type %s transfer lookup has no answer", zname, xfr->task_transfer->lookup_target->host, (xfr->task_transfer->lookup_aaaa?"AAAA":"A"));
}
}
regional_free_all(temp);
} else {
if(verbosity >= VERB_ALGO) {
char zname[255+1];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s host %s type %s transfer lookup failed", zname, xfr->task_transfer->lookup_target->host, (xfr->task_transfer->lookup_aaaa?"AAAA":"A"));
}
}
if(xfr->task_transfer->lookup_target->list &&
xfr->task_transfer->lookup_target == xfr_transfer_current_master(xfr))
xfr->task_transfer->scan_addr = xfr->task_transfer->lookup_target->list;
/* move to lookup AAAA after A lookup, move to next hostname lookup,
* or move to fetch the zone, or, if nothing to do, end task_transfer */
xfr_transfer_move_to_next_lookup(xfr, env);
xfr_transfer_nexttarget_or_end(xfr, env);
}
/** check if xfer (AXFR or IXFR) packet is OK.
* return false if we lost connection (SERVFAIL, or unreadable).
* return false if we need to move from IXFR to AXFR, with gonextonfail
* set to false, so the same master is tried again, but with AXFR.
* return true if fine to link into data.
* return true with transferdone=true when the transfer has ended.
*/
static int
check_xfer_packet(sldns_buffer* pkt, struct auth_xfer* xfr,
int* gonextonfail, int* transferdone)
{
uint8_t* wire = sldns_buffer_begin(pkt);
int i;
if(sldns_buffer_limit(pkt) < LDNS_HEADER_SIZE) {
verbose(VERB_ALGO, "xfr to %s failed, packet too small",
xfr->task_transfer->master->host);
return 0;
}
if(!LDNS_QR_WIRE(wire)) {
verbose(VERB_ALGO, "xfr to %s failed, packet has no QR flag",
xfr->task_transfer->master->host);
return 0;
}
if(LDNS_TC_WIRE(wire)) {
verbose(VERB_ALGO, "xfr to %s failed, packet has TC flag",
xfr->task_transfer->master->host);
return 0;
}
/* check ID */
if(LDNS_ID_WIRE(wire) != xfr->task_transfer->id) {
verbose(VERB_ALGO, "xfr to %s failed, packet wrong ID",
xfr->task_transfer->master->host);
return 0;
}
if(LDNS_RCODE_WIRE(wire) != LDNS_RCODE_NOERROR) {
char rcode[32];
sldns_wire2str_rcode_buf((int)LDNS_RCODE_WIRE(wire), rcode,
sizeof(rcode));
/* if we are doing IXFR, check for fallback */
if(xfr->task_transfer->on_ixfr) {
if(LDNS_RCODE_WIRE(wire) == LDNS_RCODE_NOTIMPL ||
LDNS_RCODE_WIRE(wire) == LDNS_RCODE_SERVFAIL ||
LDNS_RCODE_WIRE(wire) == LDNS_RCODE_REFUSED ||
LDNS_RCODE_WIRE(wire) == LDNS_RCODE_FORMERR) {
verbose(VERB_ALGO, "xfr to %s, fallback "
"from IXFR to AXFR (with rcode %s)",
xfr->task_transfer->master->host,
rcode);
xfr->task_transfer->ixfr_fail = 1;
*gonextonfail = 0;
return 0;
}
}
verbose(VERB_ALGO, "xfr to %s failed, packet with rcode %s",
xfr->task_transfer->master->host, rcode);
return 0;
}
if(LDNS_OPCODE_WIRE(wire) != LDNS_PACKET_QUERY) {
verbose(VERB_ALGO, "xfr to %s failed, packet with bad opcode",
xfr->task_transfer->master->host);
return 0;
}
if(LDNS_QDCOUNT(wire) > 1) {
verbose(VERB_ALGO, "xfr to %s failed, packet has qdcount %d",
xfr->task_transfer->master->host,
(int)LDNS_QDCOUNT(wire));
return 0;
}
/* check qname */
sldns_buffer_set_position(pkt, LDNS_HEADER_SIZE);
for(i=0; i<(int)LDNS_QDCOUNT(wire); i++) {
size_t pos = sldns_buffer_position(pkt);
uint16_t qtype, qclass;
if(pkt_dname_len(pkt) == 0) {
verbose(VERB_ALGO, "xfr to %s failed, packet with "
"malformed dname",
xfr->task_transfer->master->host);
return 0;
}
if(dname_pkt_compare(pkt, sldns_buffer_at(pkt, pos),
xfr->name) != 0) {
verbose(VERB_ALGO, "xfr to %s failed, packet with "
"wrong qname",
xfr->task_transfer->master->host);
return 0;
}
if(sldns_buffer_remaining(pkt) < 4) {
verbose(VERB_ALGO, "xfr to %s failed, packet with "
"truncated query RR",
xfr->task_transfer->master->host);
return 0;
}
qtype = sldns_buffer_read_u16(pkt);
qclass = sldns_buffer_read_u16(pkt);
if(qclass != xfr->dclass) {
verbose(VERB_ALGO, "xfr to %s failed, packet with "
"wrong qclass",
xfr->task_transfer->master->host);
return 0;
}
if(xfr->task_transfer->on_ixfr) {
if(qtype != LDNS_RR_TYPE_IXFR) {
verbose(VERB_ALGO, "xfr to %s failed, packet "
"with wrong qtype, expected IXFR",
xfr->task_transfer->master->host);
return 0;
}
} else {
if(qtype != LDNS_RR_TYPE_AXFR) {
verbose(VERB_ALGO, "xfr to %s failed, packet "
"with wrong qtype, expected AXFR",
xfr->task_transfer->master->host);
return 0;
}
}
}
/* check parse of RRs in packet, store first SOA serial
* to be able to detect last SOA (with that serial) to see if done */
/* also check for IXFR 'zone up to date' reply */
for(i=0; i<(int)LDNS_ANCOUNT(wire); i++) {
size_t pos = sldns_buffer_position(pkt);
uint16_t tp, rdlen;
if(pkt_dname_len(pkt) == 0) {
verbose(VERB_ALGO, "xfr to %s failed, packet with "
"malformed dname in answer section",
xfr->task_transfer->master->host);
return 0;
}
if(sldns_buffer_remaining(pkt) < 10) {
verbose(VERB_ALGO, "xfr to %s failed, packet with "
"truncated RR",
xfr->task_transfer->master->host);
return 0;
}
tp = sldns_buffer_read_u16(pkt);
(void)sldns_buffer_read_u16(pkt); /* class */
(void)sldns_buffer_read_u32(pkt); /* ttl */
rdlen = sldns_buffer_read_u16(pkt);
if(sldns_buffer_remaining(pkt) < rdlen) {
verbose(VERB_ALGO, "xfr to %s failed, packet with "
"truncated RR rdata",
xfr->task_transfer->master->host);
return 0;
}
/* RR parses (haven't checked rdata itself), now look at
* SOA records to see serial number */
if(xfr->task_transfer->rr_scan_num == 0 &&
tp != LDNS_RR_TYPE_SOA) {
verbose(VERB_ALGO, "xfr to %s failed, packet with "
"malformed zone transfer, no start SOA",
xfr->task_transfer->master->host);
return 0;
}
if(xfr->task_transfer->rr_scan_num == 1 &&
tp != LDNS_RR_TYPE_SOA) {
/* second RR is not a SOA record, this is not an IXFR
* the master is replying with an AXFR */
xfr->task_transfer->on_ixfr_is_axfr = 1;
}
if(tp == LDNS_RR_TYPE_SOA) {
uint32_t serial;
if(rdlen < 22) {
verbose(VERB_ALGO, "xfr to %s failed, packet "
"with SOA with malformed rdata",
xfr->task_transfer->master->host);
return 0;
}
if(dname_pkt_compare(pkt, sldns_buffer_at(pkt, pos),
xfr->name) != 0) {
verbose(VERB_ALGO, "xfr to %s failed, packet "
"with SOA with wrong dname",
xfr->task_transfer->master->host);
return 0;
}
/* read serial number of SOA */
serial = sldns_buffer_read_u32_at(pkt,
sldns_buffer_position(pkt)+rdlen-20);
/* check for IXFR 'zone has SOA x' reply */
if(xfr->task_transfer->on_ixfr &&
xfr->task_transfer->rr_scan_num == 0 &&
LDNS_ANCOUNT(wire)==1) {
verbose(VERB_ALGO, "xfr to %s ended, "
"IXFR reply that zone has serial %u,"
" fallback from IXFR to AXFR",
xfr->task_transfer->master->host,
(unsigned)serial);
xfr->task_transfer->ixfr_fail = 1;
*gonextonfail = 0;
return 0;
}
/* if first SOA, store serial number */
if(xfr->task_transfer->got_xfr_serial == 0) {
xfr->task_transfer->got_xfr_serial = 1;
xfr->task_transfer->incoming_xfr_serial =
serial;
verbose(VERB_ALGO, "xfr %s: contains "
"SOA serial %u",
xfr->task_transfer->master->host,
(unsigned)serial);
/* see if end of AXFR */
} else if(!xfr->task_transfer->on_ixfr ||
xfr->task_transfer->on_ixfr_is_axfr) {
/* second SOA with serial is the end
* for AXFR */
*transferdone = 1;
verbose(VERB_ALGO, "xfr %s: last AXFR packet",
xfr->task_transfer->master->host);
/* for IXFR, count SOA records with that serial */
} else if(xfr->task_transfer->incoming_xfr_serial ==
serial && xfr->task_transfer->got_xfr_serial
== 1) {
xfr->task_transfer->got_xfr_serial++;
/* if not first soa, if serial==firstserial, the
* third time we are at the end, for IXFR */
} else if(xfr->task_transfer->incoming_xfr_serial ==
serial && xfr->task_transfer->got_xfr_serial
== 2) {
verbose(VERB_ALGO, "xfr %s: last IXFR packet",
xfr->task_transfer->master->host);
*transferdone = 1;
/* continue parse check, if that succeeds,
* transfer is done */
}
}
xfr->task_transfer->rr_scan_num++;
/* skip over RR rdata to go to the next RR */
sldns_buffer_skip(pkt, (ssize_t)rdlen);
}
/* check authority section */
/* we skip over the RRs checking packet format */
for(i=0; i<(int)LDNS_NSCOUNT(wire); i++) {
uint16_t rdlen;
if(pkt_dname_len(pkt) == 0) {
verbose(VERB_ALGO, "xfr to %s failed, packet with "
"malformed dname in authority section",
xfr->task_transfer->master->host);
return 0;
}
if(sldns_buffer_remaining(pkt) < 10) {
verbose(VERB_ALGO, "xfr to %s failed, packet with "
"truncated RR",
xfr->task_transfer->master->host);
return 0;
}
(void)sldns_buffer_read_u16(pkt); /* type */
(void)sldns_buffer_read_u16(pkt); /* class */
(void)sldns_buffer_read_u32(pkt); /* ttl */
rdlen = sldns_buffer_read_u16(pkt);
if(sldns_buffer_remaining(pkt) < rdlen) {
verbose(VERB_ALGO, "xfr to %s failed, packet with "
"truncated RR rdata",
xfr->task_transfer->master->host);
return 0;
}
/* skip over RR rdata to go to the next RR */
sldns_buffer_skip(pkt, (ssize_t)rdlen);
}
/* check additional section */
for(i=0; i<(int)LDNS_ARCOUNT(wire); i++) {
uint16_t rdlen;
if(pkt_dname_len(pkt) == 0) {
verbose(VERB_ALGO, "xfr to %s failed, packet with "
"malformed dname in additional section",
xfr->task_transfer->master->host);
return 0;
}
if(sldns_buffer_remaining(pkt) < 10) {
verbose(VERB_ALGO, "xfr to %s failed, packet with "
"truncated RR",
xfr->task_transfer->master->host);
return 0;
}
(void)sldns_buffer_read_u16(pkt); /* type */
(void)sldns_buffer_read_u16(pkt); /* class */
(void)sldns_buffer_read_u32(pkt); /* ttl */
rdlen = sldns_buffer_read_u16(pkt);
if(sldns_buffer_remaining(pkt) < rdlen) {
verbose(VERB_ALGO, "xfr to %s failed, packet with "
"truncated RR rdata",
xfr->task_transfer->master->host);
return 0;
}
/* skip over RR rdata to go to the next RR */
sldns_buffer_skip(pkt, (ssize_t)rdlen);
}
return 1;
}
/** Link the data from this packet into the worklist of transferred data */
static int
xfer_link_data(sldns_buffer* pkt, struct auth_xfer* xfr)
{
/* alloc it */
struct auth_chunk* e;
e = (struct auth_chunk*)calloc(1, sizeof(*e));
if(!e) return 0;
e->next = NULL;
e->len = sldns_buffer_limit(pkt);
e->data = memdup(sldns_buffer_begin(pkt), e->len);
if(!e->data) {
free(e);
return 0;
}
/* alloc succeeded, link into list */
if(!xfr->task_transfer->chunks_first)
xfr->task_transfer->chunks_first = e;
if(xfr->task_transfer->chunks_last)
xfr->task_transfer->chunks_last->next = e;
xfr->task_transfer->chunks_last = e;
return 1;
}
/** task transfer. the list of data is complete. process it and if failed
* move to next master, if succeeded, end the task transfer */
static void
process_list_end_transfer(struct auth_xfer* xfr, struct module_env* env)
{
int ixfr_fail = 0;
if(xfr_process_chunk_list(xfr, env, &ixfr_fail)) {
/* it worked! */
auth_chunks_delete(xfr->task_transfer);
/* we fetched the zone, move to wait task */
xfr_transfer_disown(xfr);
if(xfr->notify_received && (!xfr->notify_has_serial ||
(xfr->notify_has_serial &&
xfr_serial_means_update(xfr, xfr->notify_serial)))) {
uint32_t sr = xfr->notify_serial;
int has_sr = xfr->notify_has_serial;
/* we received a notify while probe/transfer was
* in progress. start a new probe and transfer */
xfr->notify_received = 0;
xfr->notify_has_serial = 0;
xfr->notify_serial = 0;
if(!xfr_start_probe(xfr, env, NULL)) {
/* if we couldn't start it, already in
* progress; restore notify serial,
* while xfr still locked */
xfr->notify_received = 1;
xfr->notify_has_serial = has_sr;
xfr->notify_serial = sr;
lock_basic_unlock(&xfr->lock);
}
return;
} else {
/* pick up the nextprobe task and wait (normail wait time) */
if(xfr->task_nextprobe->worker == NULL)
xfr_set_timeout(xfr, env, 0, 0);
}
lock_basic_unlock(&xfr->lock);
return;
}
/* processing failed */
/* when done, delete data from list */
auth_chunks_delete(xfr->task_transfer);
if(ixfr_fail) {
xfr->task_transfer->ixfr_fail = 1;
} else {
xfr_transfer_nextmaster(xfr);
}
xfr_transfer_nexttarget_or_end(xfr, env);
}
/** callback for the task_transfer timer */
void
auth_xfer_transfer_timer_callback(void* arg)
{
struct auth_xfer* xfr = (struct auth_xfer*)arg;
struct module_env* env;
int gonextonfail = 1;
log_assert(xfr->task_transfer);
lock_basic_lock(&xfr->lock);
env = xfr->task_transfer->env;
if(!env || env->outnet->want_to_quit) {
lock_basic_unlock(&xfr->lock);
return; /* stop on quit */
}
verbose(VERB_ALGO, "xfr stopped, connection timeout to %s",
xfr->task_transfer->master->host);
/* see if IXFR caused the failure, if so, try AXFR */
if(xfr->task_transfer->on_ixfr) {
xfr->task_transfer->ixfr_possible_timeout_count++;
if(xfr->task_transfer->ixfr_possible_timeout_count >=
NUM_TIMEOUTS_FALLBACK_IXFR) {
verbose(VERB_ALGO, "xfr to %s, fallback "
"from IXFR to AXFR (because of timeouts)",
xfr->task_transfer->master->host);
xfr->task_transfer->ixfr_fail = 1;
gonextonfail = 0;
}
}
/* delete transferred data from list */
auth_chunks_delete(xfr->task_transfer);
comm_point_delete(xfr->task_transfer->cp);
xfr->task_transfer->cp = NULL;
if(gonextonfail)
xfr_transfer_nextmaster(xfr);
xfr_transfer_nexttarget_or_end(xfr, env);
}
/** callback for task_transfer tcp connections */
int
auth_xfer_transfer_tcp_callback(struct comm_point* c, void* arg, int err,
struct comm_reply* ATTR_UNUSED(repinfo))
{
struct auth_xfer* xfr = (struct auth_xfer*)arg;
struct module_env* env;
int gonextonfail = 1;
int transferdone = 0;
log_assert(xfr->task_transfer);
lock_basic_lock(&xfr->lock);
env = xfr->task_transfer->env;
if(!env || env->outnet->want_to_quit) {
lock_basic_unlock(&xfr->lock);
return 0; /* stop on quit */
}
/* stop the timer */
comm_timer_disable(xfr->task_transfer->timer);
if(err != NETEVENT_NOERROR) {
/* connection failed, closed, or timeout */
/* stop this transfer, cleanup
* and continue task_transfer*/
verbose(VERB_ALGO, "xfr stopped, connection lost to %s",
xfr->task_transfer->master->host);
/* see if IXFR caused the failure, if so, try AXFR */
if(xfr->task_transfer->on_ixfr) {
xfr->task_transfer->ixfr_possible_timeout_count++;
if(xfr->task_transfer->ixfr_possible_timeout_count >=
NUM_TIMEOUTS_FALLBACK_IXFR) {
verbose(VERB_ALGO, "xfr to %s, fallback "
"from IXFR to AXFR (because of timeouts)",
xfr->task_transfer->master->host);
xfr->task_transfer->ixfr_fail = 1;
gonextonfail = 0;
}
}
failed:
/* delete transferred data from list */
auth_chunks_delete(xfr->task_transfer);
comm_point_delete(xfr->task_transfer->cp);
xfr->task_transfer->cp = NULL;
if(gonextonfail)
xfr_transfer_nextmaster(xfr);
xfr_transfer_nexttarget_or_end(xfr, env);
return 0;
}
/* note that IXFR worked without timeout */
if(xfr->task_transfer->on_ixfr)
xfr->task_transfer->ixfr_possible_timeout_count = 0;
/* handle returned packet */
/* if it fails, cleanup and end this transfer */
/* if it needs to fallback from IXFR to AXFR, do that */
if(!check_xfer_packet(c->buffer, xfr, &gonextonfail, &transferdone)) {
goto failed;
}
/* if it is good, link it into the list of data */
/* if the link into list of data fails (malloc fail) cleanup and end */
if(!xfer_link_data(c->buffer, xfr)) {
verbose(VERB_ALGO, "xfr stopped to %s, malloc failed",
xfr->task_transfer->master->host);
goto failed;
}
/* if the transfer is done now, disconnect and process the list */
if(transferdone) {
comm_point_delete(xfr->task_transfer->cp);
xfr->task_transfer->cp = NULL;
process_list_end_transfer(xfr, env);
return 0;
}
/* if we want to read more messages, setup the commpoint to read
* a DNS packet, and the timeout */
lock_basic_unlock(&xfr->lock);
c->tcp_is_reading = 1;
sldns_buffer_clear(c->buffer);
comm_point_start_listening(c, -1, AUTH_TRANSFER_TIMEOUT);
return 0;
}
/** callback for task_transfer http connections */
int
auth_xfer_transfer_http_callback(struct comm_point* c, void* arg, int err,
struct comm_reply* repinfo)
{
struct auth_xfer* xfr = (struct auth_xfer*)arg;
struct module_env* env;
log_assert(xfr->task_transfer);
lock_basic_lock(&xfr->lock);
env = xfr->task_transfer->env;
if(!env || env->outnet->want_to_quit) {
lock_basic_unlock(&xfr->lock);
return 0; /* stop on quit */
}
verbose(VERB_ALGO, "auth zone transfer http callback");
/* stop the timer */
comm_timer_disable(xfr->task_transfer->timer);
if(err != NETEVENT_NOERROR && err != NETEVENT_DONE) {
/* connection failed, closed, or timeout */
/* stop this transfer, cleanup
* and continue task_transfer*/
verbose(VERB_ALGO, "http stopped, connection lost to %s",
xfr->task_transfer->master->host);
failed:
/* delete transferred data from list */
auth_chunks_delete(xfr->task_transfer);
if(repinfo) repinfo->c = NULL; /* signal cp deleted to
the routine calling this callback */
comm_point_delete(xfr->task_transfer->cp);
xfr->task_transfer->cp = NULL;
xfr_transfer_nextmaster(xfr);
xfr_transfer_nexttarget_or_end(xfr, env);
return 0;
}
/* if it is good, link it into the list of data */
/* if the link into list of data fails (malloc fail) cleanup and end */
if(sldns_buffer_limit(c->buffer) > 0) {
verbose(VERB_ALGO, "auth zone http queued up %d bytes",
(int)sldns_buffer_limit(c->buffer));
if(!xfer_link_data(c->buffer, xfr)) {
verbose(VERB_ALGO, "http stopped to %s, malloc failed",
xfr->task_transfer->master->host);
goto failed;
}
}
/* if the transfer is done now, disconnect and process the list */
if(err == NETEVENT_DONE) {
if(repinfo) repinfo->c = NULL; /* signal cp deleted to
the routine calling this callback */
comm_point_delete(xfr->task_transfer->cp);
xfr->task_transfer->cp = NULL;
process_list_end_transfer(xfr, env);
return 0;
}
/* if we want to read more messages, setup the commpoint to read
* a DNS packet, and the timeout */
lock_basic_unlock(&xfr->lock);
c->tcp_is_reading = 1;
sldns_buffer_clear(c->buffer);
comm_point_start_listening(c, -1, AUTH_TRANSFER_TIMEOUT);
return 0;
}
/** start transfer task by this worker , xfr is locked. */
static void
xfr_start_transfer(struct auth_xfer* xfr, struct module_env* env,
struct auth_master* master)
{
log_assert(xfr->task_transfer != NULL);
log_assert(xfr->task_transfer->worker == NULL);
log_assert(xfr->task_transfer->chunks_first == NULL);
log_assert(xfr->task_transfer->chunks_last == NULL);
xfr->task_transfer->worker = env->worker;
xfr->task_transfer->env = env;
/* init transfer process */
/* find that master in the transfer's list of masters? */
xfr_transfer_start_list(xfr, master);
/* start lookup for hostnames in transfer master list */
xfr_transfer_start_lookups(xfr);
/* initiate TCP, and set timeout on it */
xfr_transfer_nexttarget_or_end(xfr, env);
}
/** disown task_probe. caller must hold xfr.lock */
static void
xfr_probe_disown(struct auth_xfer* xfr)
{
/* remove timer (from this worker's event base) */
comm_timer_delete(xfr->task_probe->timer);
xfr->task_probe->timer = NULL;
/* remove the commpoint */
comm_point_delete(xfr->task_probe->cp);
xfr->task_probe->cp = NULL;
/* we don't own this item anymore */
xfr->task_probe->worker = NULL;
xfr->task_probe->env = NULL;
}
/** send the UDP probe to the master, this is part of task_probe */
static int
xfr_probe_send_probe(struct auth_xfer* xfr, struct module_env* env,
int timeout)
{
struct sockaddr_storage addr;
socklen_t addrlen = 0;
struct timeval t;
/* pick master */
struct auth_master* master = xfr_probe_current_master(xfr);
char *auth_name = NULL;
if(!master) return 0;
if(master->allow_notify) return 0; /* only for notify */
if(master->http) return 0; /* only masters get SOA UDP probe,
not urls, if those are in this list */
/* get master addr */
if(xfr->task_probe->scan_addr) {
addrlen = xfr->task_probe->scan_addr->addrlen;
memmove(&addr, &xfr->task_probe->scan_addr->addr, addrlen);
} else {
if(!authextstrtoaddr(master->host, &addr, &addrlen, &auth_name)) {
/* the ones that are not in addr format are supposed
* to be looked up. The lookup has failed however,
* so skip them */
char zname[255+1];
dname_str(xfr->name, zname);
log_err("%s: failed lookup, cannot probe to master %s",
zname, master->host);
return 0;
}
if (auth_name != NULL) {
if (addr.ss_family == AF_INET
&& (int)ntohs(((struct sockaddr_in *)&addr)->sin_port)
== env->cfg->ssl_port)
((struct sockaddr_in *)&addr)->sin_port
= htons((uint16_t)env->cfg->port);
else if (addr.ss_family == AF_INET6
&& (int)ntohs(((struct sockaddr_in6 *)&addr)->sin6_port)
== env->cfg->ssl_port)
((struct sockaddr_in6 *)&addr)->sin6_port
= htons((uint16_t)env->cfg->port);
}
}
/* create packet */
/* create new ID for new probes, but not on timeout retries,
* this means we'll accept replies to previous retries to same ip */
if(timeout == AUTH_PROBE_TIMEOUT)
xfr->task_probe->id = GET_RANDOM_ID(env->rnd);
xfr_create_soa_probe_packet(xfr, env->scratch_buffer,
xfr->task_probe->id);
/* we need to remove the cp if we have a different ip4/ip6 type now */
if(xfr->task_probe->cp &&
((xfr->task_probe->cp_is_ip6 && !addr_is_ip6(&addr, addrlen)) ||
(!xfr->task_probe->cp_is_ip6 && addr_is_ip6(&addr, addrlen)))
) {
comm_point_delete(xfr->task_probe->cp);
xfr->task_probe->cp = NULL;
}
if(!xfr->task_probe->cp) {
if(addr_is_ip6(&addr, addrlen))
xfr->task_probe->cp_is_ip6 = 1;
else xfr->task_probe->cp_is_ip6 = 0;
xfr->task_probe->cp = outnet_comm_point_for_udp(env->outnet,
auth_xfer_probe_udp_callback, xfr, &addr, addrlen);
if(!xfr->task_probe->cp) {
char zname[255+1], as[256];
dname_str(xfr->name, zname);
addr_to_str(&addr, addrlen, as, sizeof(as));
verbose(VERB_ALGO, "cannot create udp cp for "
"probe %s to %s", zname, as);
return 0;
}
}
if(!xfr->task_probe->timer) {
xfr->task_probe->timer = comm_timer_create(env->worker_base,
auth_xfer_probe_timer_callback, xfr);
if(!xfr->task_probe->timer) {
log_err("malloc failure");
return 0;
}
}
/* send udp packet */
if(!comm_point_send_udp_msg(xfr->task_probe->cp, env->scratch_buffer,
(struct sockaddr*)&addr, addrlen, 0)) {
char zname[255+1], as[256];
dname_str(xfr->name, zname);
addr_to_str(&addr, addrlen, as, sizeof(as));
verbose(VERB_ALGO, "failed to send soa probe for %s to %s",
zname, as);
return 0;
}
if(verbosity >= VERB_ALGO) {
char zname[255+1], as[256];
dname_str(xfr->name, zname);
addr_to_str(&addr, addrlen, as, sizeof(as));
verbose(VERB_ALGO, "auth zone %s soa probe sent to %s", zname,
as);
}
xfr->task_probe->timeout = timeout;
#ifndef S_SPLINT_S
t.tv_sec = timeout/1000;
t.tv_usec = (timeout%1000)*1000;
#endif
comm_timer_set(xfr->task_probe->timer, &t);
return 1;
}
/** callback for task_probe timer */
void
auth_xfer_probe_timer_callback(void* arg)
{
struct auth_xfer* xfr = (struct auth_xfer*)arg;
struct module_env* env;
log_assert(xfr->task_probe);
lock_basic_lock(&xfr->lock);
env = xfr->task_probe->env;
if(!env || env->outnet->want_to_quit) {
lock_basic_unlock(&xfr->lock);
return; /* stop on quit */
}
if(verbosity >= VERB_ALGO) {
char zname[255+1];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s soa probe timeout", zname);
}
if(xfr->task_probe->timeout <= AUTH_PROBE_TIMEOUT_STOP) {
/* try again with bigger timeout */
if(xfr_probe_send_probe(xfr, env, xfr->task_probe->timeout*2)) {
lock_basic_unlock(&xfr->lock);
return;
}
}
/* delete commpoint so a new one is created, with a fresh port nr */
comm_point_delete(xfr->task_probe->cp);
xfr->task_probe->cp = NULL;
/* too many timeouts (or fail to send), move to next or end */
xfr_probe_nextmaster(xfr);
xfr_probe_send_or_end(xfr, env);
}
/** callback for task_probe udp packets */
int
auth_xfer_probe_udp_callback(struct comm_point* c, void* arg, int err,
struct comm_reply* repinfo)
{
struct auth_xfer* xfr = (struct auth_xfer*)arg;
struct module_env* env;
log_assert(xfr->task_probe);
lock_basic_lock(&xfr->lock);
env = xfr->task_probe->env;
if(!env || env->outnet->want_to_quit) {
lock_basic_unlock(&xfr->lock);
return 0; /* stop on quit */
}
/* the comm_point_udp_callback is in a for loop for NUM_UDP_PER_SELECT
* and we set rep.c=NULL to stop if from looking inside the commpoint*/
repinfo->c = NULL;
/* stop the timer */
comm_timer_disable(xfr->task_probe->timer);
/* see if we got a packet and what that means */
if(err == NETEVENT_NOERROR) {
uint32_t serial = 0;
if(check_packet_ok(c->buffer, LDNS_RR_TYPE_SOA, xfr,
&serial)) {
/* successful lookup */
if(verbosity >= VERB_ALGO) {
char buf[256];
dname_str(xfr->name, buf);
verbose(VERB_ALGO, "auth zone %s: soa probe "
"serial is %u", buf, (unsigned)serial);
}
/* see if this serial indicates that the zone has
* to be updated */
if(xfr_serial_means_update(xfr, serial)) {
/* if updated, start the transfer task, if needed */
verbose(VERB_ALGO, "auth_zone updated, start transfer");
if(xfr->task_transfer->worker == NULL) {
struct auth_master* master =
xfr_probe_current_master(xfr);
/* if we have download URLs use them
* in preference to this master we
* just probed the SOA from */
if(xfr->task_transfer->masters &&
xfr->task_transfer->masters->http)
master = NULL;
xfr_probe_disown(xfr);
xfr_start_transfer(xfr, env, master);
return 0;
}
/* other tasks are running, we don't do this anymore */
xfr_probe_disown(xfr);
lock_basic_unlock(&xfr->lock);
/* return, we don't sent a reply to this udp packet,
* and we setup the tasks to do next */
return 0;
} else {
verbose(VERB_ALGO, "auth_zone master reports unchanged soa serial");
/* we if cannot find updates amongst the
* masters, this means we then have a new lease
* on the zone */
xfr->task_probe->have_new_lease = 1;
}
} else {
if(verbosity >= VERB_ALGO) {
char buf[256];
dname_str(xfr->name, buf);
verbose(VERB_ALGO, "auth zone %s: bad reply to soa probe", buf);
}
}
} else {
if(verbosity >= VERB_ALGO) {
char buf[256];
dname_str(xfr->name, buf);
verbose(VERB_ALGO, "auth zone %s: soa probe failed", buf);
}
}
/* failed lookup or not an update */
/* delete commpoint so a new one is created, with a fresh port nr */
comm_point_delete(xfr->task_probe->cp);
xfr->task_probe->cp = NULL;
/* if the result was not a successful probe, we need
* to send the next one */
xfr_probe_nextmaster(xfr);
xfr_probe_send_or_end(xfr, env);
return 0;
}
/** lookup a host name for its addresses, if needed */
static int
xfr_probe_lookup_host(struct auth_xfer* xfr, struct module_env* env)
{
struct sockaddr_storage addr;
socklen_t addrlen = 0;
struct auth_master* master = xfr->task_probe->lookup_target;
struct query_info qinfo;
uint16_t qflags = BIT_RD;
uint8_t dname[LDNS_MAX_DOMAINLEN+1];
struct edns_data edns;
sldns_buffer* buf = env->scratch_buffer;
if(!master) return 0;
if(extstrtoaddr(master->host, &addr, &addrlen, UNBOUND_DNS_PORT)) {
/* not needed, host is in IP addr format */
return 0;
}
if(master->allow_notify && !master->http &&
strchr(master->host, '/') != NULL &&
strchr(master->host, '/') == strrchr(master->host, '/')) {
return 0; /* is IP/prefix format, not something to look up */
}
/* use mesh_new_callback to probe for non-addr hosts,
* and then wait for them to be looked up (in cache, or query) */
qinfo.qname_len = sizeof(dname);
if(sldns_str2wire_dname_buf(master->host, dname, &qinfo.qname_len)
!= 0) {
log_err("cannot parse host name of master %s", master->host);
return 0;
}
qinfo.qname = dname;
qinfo.qclass = xfr->dclass;
qinfo.qtype = LDNS_RR_TYPE_A;
if(xfr->task_probe->lookup_aaaa)
qinfo.qtype = LDNS_RR_TYPE_AAAA;
qinfo.local_alias = NULL;
if(verbosity >= VERB_ALGO) {
char buf1[512];
char buf2[LDNS_MAX_DOMAINLEN+1];
dname_str(xfr->name, buf2);
snprintf(buf1, sizeof(buf1), "auth zone %s: master lookup"
" for task_probe", buf2);
log_query_info(VERB_ALGO, buf1, &qinfo);
}
edns.edns_present = 1;
edns.ext_rcode = 0;
edns.edns_version = 0;
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;
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;
/* unlock xfr during mesh_new_callback() because the callback can be
* called straight away */
lock_basic_unlock(&xfr->lock);
if(!mesh_new_callback(env->mesh, &qinfo, qflags, &edns, buf, 0,
&auth_xfer_probe_lookup_callback, xfr, 0)) {
lock_basic_lock(&xfr->lock);
log_err("out of memory lookup up master %s", master->host);
return 0;
}
lock_basic_lock(&xfr->lock);
return 1;
}
/** move to sending the probe packets, next if fails. task_probe */
static void
xfr_probe_send_or_end(struct auth_xfer* xfr, struct module_env* env)
{
/* are we doing hostname lookups? */
while(xfr->task_probe->lookup_target) {
if(xfr_probe_lookup_host(xfr, env)) {
/* wait for lookup to finish,
* note that the hostname may be in unbound's cache
* and we may then get an instant cache response,
* and that calls the callback just like a full
* lookup and lookup failures also call callback */
if(verbosity >= VERB_ALGO) {
char zname[255+1];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s probe next target lookup", zname);
}
lock_basic_unlock(&xfr->lock);
return;
}
xfr_probe_move_to_next_lookup(xfr, env);
}
/* probe of list has ended. Create or refresh the list of of
* allow_notify addrs */
probe_copy_masters_for_allow_notify(xfr);
if(verbosity >= VERB_ALGO) {
char zname[255+1];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s probe: notify addrs updated", zname);
}
if(xfr->task_probe->only_lookup) {
/* only wanted lookups for copy, stop probe and start wait */
xfr->task_probe->only_lookup = 0;
if(verbosity >= VERB_ALGO) {
char zname[255+1];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s probe: finished only_lookup", zname);
}
xfr_probe_disown(xfr);
if(xfr->task_nextprobe->worker == NULL)
xfr_set_timeout(xfr, env, 0, 0);
lock_basic_unlock(&xfr->lock);
return;
}
/* send probe packets */
while(!xfr_probe_end_of_list(xfr)) {
if(xfr_probe_send_probe(xfr, env, AUTH_PROBE_TIMEOUT)) {
/* successfully sent probe, wait for callback */
lock_basic_unlock(&xfr->lock);
return;
}
/* failed to send probe, next master */
xfr_probe_nextmaster(xfr);
}
/* done with probe sequence, wait */
if(xfr->task_probe->have_new_lease) {
/* if zone not updated, start the wait timer again */
if(verbosity >= VERB_ALGO) {
char zname[255+1];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth_zone %s unchanged, new lease, wait", zname);
}
xfr_probe_disown(xfr);
if(xfr->have_zone)
xfr->lease_time = *env->now;
if(xfr->task_nextprobe->worker == NULL)
xfr_set_timeout(xfr, env, 0, 0);
} else {
if(verbosity >= VERB_ALGO) {
char zname[255+1];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s soa probe failed, wait to retry", zname);
}
/* we failed to send this as well, move to the wait task,
* use the shorter retry timeout */
xfr_probe_disown(xfr);
/* pick up the nextprobe task and wait */
if(xfr->task_nextprobe->worker == NULL)
xfr_set_timeout(xfr, env, 1, 0);
}
lock_basic_unlock(&xfr->lock);
}
/** callback for task_probe lookup of host name, of A or AAAA */
void auth_xfer_probe_lookup_callback(void* arg, int rcode, sldns_buffer* buf,
enum sec_status ATTR_UNUSED(sec), char* ATTR_UNUSED(why_bogus),
int ATTR_UNUSED(was_ratelimited))
{
struct auth_xfer* xfr = (struct auth_xfer*)arg;
struct module_env* env;
log_assert(xfr->task_probe);
lock_basic_lock(&xfr->lock);
env = xfr->task_probe->env;
if(!env || env->outnet->want_to_quit) {
lock_basic_unlock(&xfr->lock);
return; /* stop on quit */
}
/* process result */
if(rcode == LDNS_RCODE_NOERROR) {
uint16_t wanted_qtype = LDNS_RR_TYPE_A;
struct regional* temp = env->scratch;
struct query_info rq;
struct reply_info* rep;
if(xfr->task_probe->lookup_aaaa)
wanted_qtype = LDNS_RR_TYPE_AAAA;
memset(&rq, 0, sizeof(rq));
rep = parse_reply_in_temp_region(buf, temp, &rq);
if(rep && rq.qtype == wanted_qtype &&
FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NOERROR) {
/* parsed successfully */
struct ub_packed_rrset_key* answer =
reply_find_answer_rrset(&rq, rep);
if(answer) {
xfr_master_add_addrs(xfr->task_probe->
lookup_target, answer, wanted_qtype);
} else {
if(verbosity >= VERB_ALGO) {
char zname[255+1];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s host %s type %s probe lookup has nodata", zname, xfr->task_probe->lookup_target->host, (xfr->task_probe->lookup_aaaa?"AAAA":"A"));
}
}
} else {
if(verbosity >= VERB_ALGO) {
char zname[255+1];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s host %s type %s probe lookup has no address", zname, xfr->task_probe->lookup_target->host, (xfr->task_probe->lookup_aaaa?"AAAA":"A"));
}
}
regional_free_all(temp);
} else {
if(verbosity >= VERB_ALGO) {
char zname[255+1];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s host %s type %s probe lookup failed", zname, xfr->task_probe->lookup_target->host, (xfr->task_probe->lookup_aaaa?"AAAA":"A"));
}
}
if(xfr->task_probe->lookup_target->list &&
xfr->task_probe->lookup_target == xfr_probe_current_master(xfr))
xfr->task_probe->scan_addr = xfr->task_probe->lookup_target->list;
/* move to lookup AAAA after A lookup, move to next hostname lookup,
* or move to send the probes, or, if nothing to do, end task_probe */
xfr_probe_move_to_next_lookup(xfr, env);
xfr_probe_send_or_end(xfr, env);
}
/** disown task_nextprobe. caller must hold xfr.lock */
static void
xfr_nextprobe_disown(struct auth_xfer* xfr)
{
/* delete the timer, because the next worker to pick this up may
* not have the same event base */
comm_timer_delete(xfr->task_nextprobe->timer);
xfr->task_nextprobe->timer = NULL;
xfr->task_nextprobe->next_probe = 0;
/* we don't own this item anymore */
xfr->task_nextprobe->worker = NULL;
xfr->task_nextprobe->env = NULL;
}
/** xfer nextprobe timeout callback, this is part of task_nextprobe */
void
auth_xfer_timer(void* arg)
{
struct auth_xfer* xfr = (struct auth_xfer*)arg;
struct module_env* env;
log_assert(xfr->task_nextprobe);
lock_basic_lock(&xfr->lock);
env = xfr->task_nextprobe->env;
if(!env || env->outnet->want_to_quit) {
lock_basic_unlock(&xfr->lock);
return; /* stop on quit */
}
/* see if zone has expired, and if so, also set auth_zone expired */
if(xfr->have_zone && !xfr->zone_expired &&
*env->now >= xfr->lease_time + xfr->expiry) {
lock_basic_unlock(&xfr->lock);
auth_xfer_set_expired(xfr, env, 1);
lock_basic_lock(&xfr->lock);
}
xfr_nextprobe_disown(xfr);
if(!xfr_start_probe(xfr, env, NULL)) {
/* not started because already in progress */
lock_basic_unlock(&xfr->lock);
}
}
/** return true if there are probe (SOA UDP query) targets in the master list*/
static int
have_probe_targets(struct auth_master* list)
{
struct auth_master* p;
for(p=list; p; p = p->next) {
if(!p->allow_notify && p->host)
return 1;
}
return 0;
}
/** start task_probe if possible, if no masters for probe start task_transfer
* returns true if task has been started, and false if the task is already
* in progress. */
static int
xfr_start_probe(struct auth_xfer* xfr, struct module_env* env,
struct auth_master* spec)
{
/* see if we need to start a probe (or maybe it is already in
* progress (due to notify)) */
if(xfr->task_probe->worker == NULL) {
if(!have_probe_targets(xfr->task_probe->masters) &&
!(xfr->task_probe->only_lookup &&
xfr->task_probe->masters != NULL)) {
/* useless to pick up task_probe, no masters to
* probe. Instead attempt to pick up task transfer */
if(xfr->task_transfer->worker == NULL) {
xfr_start_transfer(xfr, env, spec);
return 1;
}
/* task transfer already in progress */
return 0;
}
/* pick up the probe task ourselves */
xfr->task_probe->worker = env->worker;
xfr->task_probe->env = env;
xfr->task_probe->cp = NULL;
/* start the task */
/* have not seen a new lease yet, this scan */
xfr->task_probe->have_new_lease = 0;
/* if this was a timeout, no specific first master to scan */
/* otherwise, spec is nonNULL the notified master, scan
* first and also transfer first from it */
xfr_probe_start_list(xfr, spec);
/* setup to start the lookup of hostnames of masters afresh */
xfr_probe_start_lookups(xfr);
/* send the probe packet or next send, or end task */
xfr_probe_send_or_end(xfr, env);
return 1;
}
return 0;
}
/** for task_nextprobe.
* determine next timeout for auth_xfer. Also (re)sets timer.
* @param xfr: task structure
* @param env: module environment, with worker and time.
* @param failure: set true if timer should be set for failure retry.
* @param lookup_only: only perform lookups when timer done, 0 sec timeout
*/
static void
xfr_set_timeout(struct auth_xfer* xfr, struct module_env* env,
int failure, int lookup_only)
{
struct timeval tv;
log_assert(xfr->task_nextprobe != NULL);
log_assert(xfr->task_nextprobe->worker == NULL ||
xfr->task_nextprobe->worker == env->worker);
/* normally, nextprobe = startoflease + refresh,
* but if expiry is sooner, use that one.
* after a failure, use the retry timer instead. */
xfr->task_nextprobe->next_probe = *env->now;
if(xfr->lease_time && !failure)
xfr->task_nextprobe->next_probe = xfr->lease_time;
if(!failure) {
xfr->task_nextprobe->backoff = 0;
} else {
if(xfr->task_nextprobe->backoff == 0)
xfr->task_nextprobe->backoff = 3;
else xfr->task_nextprobe->backoff *= 2;
if(xfr->task_nextprobe->backoff > AUTH_TRANSFER_MAX_BACKOFF)
xfr->task_nextprobe->backoff =
AUTH_TRANSFER_MAX_BACKOFF;
}
if(xfr->have_zone) {
time_t wait = xfr->refresh;
if(failure) wait = xfr->retry;
if(xfr->expiry < wait)
xfr->task_nextprobe->next_probe += xfr->expiry;
else xfr->task_nextprobe->next_probe += wait;
if(failure)
xfr->task_nextprobe->next_probe +=
xfr->task_nextprobe->backoff;
/* put the timer exactly on expiry, if possible */
if(xfr->lease_time && xfr->lease_time+xfr->expiry <
xfr->task_nextprobe->next_probe &&
xfr->lease_time+xfr->expiry > *env->now)
xfr->task_nextprobe->next_probe =
xfr->lease_time+xfr->expiry;
} else {
xfr->task_nextprobe->next_probe +=
xfr->task_nextprobe->backoff;
}
if(!xfr->task_nextprobe->timer) {
xfr->task_nextprobe->timer = comm_timer_create(
env->worker_base, auth_xfer_timer, xfr);
if(!xfr->task_nextprobe->timer) {
/* failed to malloc memory. likely zone transfer
* also fails for that. skip the timeout */
char zname[255+1];
dname_str(xfr->name, zname);
log_err("cannot allocate timer, no refresh for %s",
zname);
return;
}
}
xfr->task_nextprobe->worker = env->worker;
xfr->task_nextprobe->env = env;
if(*(xfr->task_nextprobe->env->now) <= xfr->task_nextprobe->next_probe)
tv.tv_sec = xfr->task_nextprobe->next_probe -
*(xfr->task_nextprobe->env->now);
else tv.tv_sec = 0;
if(tv.tv_sec != 0 && lookup_only && xfr->task_probe->masters) {
/* don't lookup_only, if lookup timeout is 0 anyway,
* or if we don't have masters to lookup */
tv.tv_sec = 0;
if(xfr->task_probe->worker == NULL)
xfr->task_probe->only_lookup = 1;
}
if(verbosity >= VERB_ALGO) {
char zname[255+1];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s timeout in %d seconds",
zname, (int)tv.tv_sec);
}
tv.tv_usec = 0;
comm_timer_set(xfr->task_nextprobe->timer, &tv);
}
/** initial pick up of worker timeouts, ties events to worker event loop */
void
auth_xfer_pickup_initial(struct auth_zones* az, struct module_env* env)
{
struct auth_xfer* x;
lock_rw_wrlock(&az->lock);
RBTREE_FOR(x, struct auth_xfer*, &az->xtree) {
lock_basic_lock(&x->lock);
/* set lease_time, because we now have timestamp in env,
* (not earlier during startup and apply_cfg), and this
* notes the start time when the data was acquired */
if(x->have_zone)
x->lease_time = *env->now;
if(x->task_nextprobe && x->task_nextprobe->worker == NULL) {
xfr_set_timeout(x, env, 0, 1);
}
lock_basic_unlock(&x->lock);
}
lock_rw_unlock(&az->lock);
}
void auth_zones_cleanup(struct auth_zones* az)
{
struct auth_xfer* x;
lock_rw_wrlock(&az->lock);
RBTREE_FOR(x, struct auth_xfer*, &az->xtree) {
lock_basic_lock(&x->lock);
if(x->task_nextprobe && x->task_nextprobe->worker != NULL) {
xfr_nextprobe_disown(x);
}
if(x->task_probe && x->task_probe->worker != NULL) {
xfr_probe_disown(x);
}
if(x->task_transfer && x->task_transfer->worker != NULL) {
auth_chunks_delete(x->task_transfer);
xfr_transfer_disown(x);
}
lock_basic_unlock(&x->lock);
}
lock_rw_unlock(&az->lock);
}
/**
* malloc the xfer and tasks
* @param z: auth_zone with name of zone.
*/
static struct auth_xfer*
auth_xfer_new(struct auth_zone* z)
{
struct auth_xfer* xfr;
xfr = (struct auth_xfer*)calloc(1, sizeof(*xfr));
if(!xfr) return NULL;
xfr->name = memdup(z->name, z->namelen);
if(!xfr->name) {
free(xfr);
return NULL;
}
xfr->node.key = xfr;
xfr->namelen = z->namelen;
xfr->namelabs = z->namelabs;
xfr->dclass = z->dclass;
xfr->task_nextprobe = (struct auth_nextprobe*)calloc(1,
sizeof(struct auth_nextprobe));
if(!xfr->task_nextprobe) {
free(xfr->name);
free(xfr);
return NULL;
}
xfr->task_probe = (struct auth_probe*)calloc(1,
sizeof(struct auth_probe));
if(!xfr->task_probe) {
free(xfr->task_nextprobe);
free(xfr->name);
free(xfr);
return NULL;
}
xfr->task_transfer = (struct auth_transfer*)calloc(1,
sizeof(struct auth_transfer));
if(!xfr->task_transfer) {
free(xfr->task_probe);
free(xfr->task_nextprobe);
free(xfr->name);
free(xfr);
return NULL;
}
lock_basic_init(&xfr->lock);
lock_protect(&xfr->lock, &xfr->name, sizeof(xfr->name));
lock_protect(&xfr->lock, &xfr->namelen, sizeof(xfr->namelen));
lock_protect(&xfr->lock, xfr->name, xfr->namelen);
lock_protect(&xfr->lock, &xfr->namelabs, sizeof(xfr->namelabs));
lock_protect(&xfr->lock, &xfr->dclass, sizeof(xfr->dclass));
lock_protect(&xfr->lock, &xfr->notify_received, sizeof(xfr->notify_received));
lock_protect(&xfr->lock, &xfr->notify_serial, sizeof(xfr->notify_serial));
lock_protect(&xfr->lock, &xfr->zone_expired, sizeof(xfr->zone_expired));
lock_protect(&xfr->lock, &xfr->have_zone, sizeof(xfr->have_zone));
lock_protect(&xfr->lock, &xfr->serial, sizeof(xfr->serial));
lock_protect(&xfr->lock, &xfr->retry, sizeof(xfr->retry));
lock_protect(&xfr->lock, &xfr->refresh, sizeof(xfr->refresh));
lock_protect(&xfr->lock, &xfr->expiry, sizeof(xfr->expiry));
lock_protect(&xfr->lock, &xfr->lease_time, sizeof(xfr->lease_time));
lock_protect(&xfr->lock, &xfr->task_nextprobe->worker,
sizeof(xfr->task_nextprobe->worker));
lock_protect(&xfr->lock, &xfr->task_probe->worker,
sizeof(xfr->task_probe->worker));
lock_protect(&xfr->lock, &xfr->task_transfer->worker,
sizeof(xfr->task_transfer->worker));
lock_basic_lock(&xfr->lock);
return xfr;
}
/** Create auth_xfer structure.
* This populates the have_zone, soa values, and so on times.
* and sets the timeout, if a zone transfer is needed a short timeout is set.
* For that the auth_zone itself must exist (and read in zonefile)
* returns false on alloc failure. */
struct auth_xfer*
auth_xfer_create(struct auth_zones* az, struct auth_zone* z)
{
struct auth_xfer* xfr;
/* malloc it */
xfr = auth_xfer_new(z);
if(!xfr) {
log_err("malloc failure");
return NULL;
}
/* insert in tree */
(void)rbtree_insert(&az->xtree, &xfr->node);
return xfr;
}
/** create new auth_master structure */
static struct auth_master*
auth_master_new(struct auth_master*** list)
{
struct auth_master *m;
m = (struct auth_master*)calloc(1, sizeof(*m));
if(!m) {
log_err("malloc failure");
return NULL;
}
/* set first pointer to m, or next pointer of previous element to m */
(**list) = m;
/* store m's next pointer as future point to store at */
(*list) = &(m->next);
return m;
}
/** dup_prefix : create string from initial part of other string, malloced */
static char*
dup_prefix(char* str, size_t num)
{
char* result;
size_t len = strlen(str);
if(len < num) num = len; /* not more than strlen */
result = (char*)malloc(num+1);
if(!result) {
log_err("malloc failure");
return result;
}
memmove(result, str, num);
result[num] = 0;
return result;
}
/** dup string and print error on error */
static char*
dup_all(char* str)
{
char* result = strdup(str);
if(!result) {
log_err("malloc failure");
return NULL;
}
return result;
}
/** find first of two characters */
static char*
str_find_first_of_chars(char* s, char a, char b)
{
char* ra = strchr(s, a);
char* rb = strchr(s, b);
if(!ra) return rb;
if(!rb) return ra;
if(ra < rb) return ra;
return rb;
}
/** parse URL into host and file parts, false on malloc or parse error */
static int
parse_url(char* url, char** host, char** file, int* port, int* ssl)
{
char* p = url;
/* parse http://www.example.com/file.htm
* or http://127.0.0.1 (index.html)
* or https://[::1@1234]/a/b/c/d */
*ssl = 1;
*port = AUTH_HTTPS_PORT;
/* parse http:// or https:// */
if(strncmp(p, "http://", 7) == 0) {
p += 7;
*ssl = 0;
*port = AUTH_HTTP_PORT;
} else if(strncmp(p, "https://", 8) == 0) {
p += 8;
} else if(strstr(p, "://") && strchr(p, '/') > strstr(p, "://") &&
strchr(p, ':') >= strstr(p, "://")) {
char* uri = dup_prefix(p, (size_t)(strstr(p, "://")-p));
log_err("protocol %s:// not supported (for url %s)",
uri?uri:"", p);
free(uri);
return 0;
}
/* parse hostname part */
if(p[0] == '[') {
char* end = strchr(p, ']');
p++; /* skip over [ */
if(end) {
*host = dup_prefix(p, (size_t)(end-p));
if(!*host) return 0;
p = end+1; /* skip over ] */
} else {
*host = dup_all(p);
if(!*host) return 0;
p = end;
}
} else {
char* end = str_find_first_of_chars(p, ':', '/');
if(end) {
*host = dup_prefix(p, (size_t)(end-p));
if(!*host) return 0;
} else {
*host = dup_all(p);
if(!*host) return 0;
}
p = end; /* at next : or / or NULL */
}
/* parse port number */
if(p && p[0] == ':') {
char* end = NULL;
*port = strtol(p+1, &end, 10);
p = end;
}
/* parse filename part */
while(p && *p == '/')
p++;
if(!p || p[0] == 0)
*file = strdup("/");
else *file = strdup(p);
if(!*file) {
log_err("malloc failure");
return 0;
}
return 1;
}
int
xfer_set_masters(struct auth_master** list, struct config_auth* c,
int with_http)
{
struct auth_master* m;
struct config_strlist* p;
/* list points to the first, or next pointer for the new element */
while(*list) {
list = &( (*list)->next );
}
if(with_http)
for(p = c->urls; p; p = p->next) {
m = auth_master_new(&list);
if(!m) return 0;
m->http = 1;
if(!parse_url(p->str, &m->host, &m->file, &m->port, &m->ssl))
return 0;
}
for(p = c->masters; p; p = p->next) {
m = auth_master_new(&list);
if(!m) return 0;
m->ixfr = 1; /* this flag is not configurable */
m->host = strdup(p->str);
if(!m->host) {
log_err("malloc failure");
return 0;
}
}
for(p = c->allow_notify; p; p = p->next) {
m = auth_master_new(&list);
if(!m) return 0;
m->allow_notify = 1;
m->host = strdup(p->str);
if(!m->host) {
log_err("malloc failure");
return 0;
}
}
return 1;
}
#define SERIAL_BITS 32
int
compare_serial(uint32_t a, uint32_t b)
{
const uint32_t cutoff = ((uint32_t) 1 << (SERIAL_BITS - 1));
if (a == b) {
return 0;
} else if ((a < b && b - a < cutoff) || (a > b && a - b > cutoff)) {
return -1;
} else {
return 1;
}
}
int zonemd_hashalgo_supported(int hashalgo)
{
if(hashalgo == ZONEMD_ALGO_SHA384) return 1;
if(hashalgo == ZONEMD_ALGO_SHA512) return 1;
return 0;
}
int zonemd_scheme_supported(int scheme)
{
if(scheme == ZONEMD_SCHEME_SIMPLE) return 1;
return 0;
}
/** initialize hash for hashing with zonemd hash algo */
static struct secalgo_hash* zonemd_digest_init(int hashalgo, char** reason)
{
struct secalgo_hash *h;
if(hashalgo == ZONEMD_ALGO_SHA384) {
/* sha384 */
h = secalgo_hash_create_sha384();
if(!h)
*reason = "digest sha384 could not be created";
return h;
} else if(hashalgo == ZONEMD_ALGO_SHA512) {
/* sha512 */
h = secalgo_hash_create_sha512();
if(!h)
*reason = "digest sha512 could not be created";
return h;
}
/* unknown hash algo */
*reason = "unsupported algorithm";
return NULL;
}
/** update the hash for zonemd */
static int zonemd_digest_update(int hashalgo, struct secalgo_hash* h,
uint8_t* data, size_t len, char** reason)
{
if(hashalgo == ZONEMD_ALGO_SHA384) {
if(!secalgo_hash_update(h, data, len)) {
*reason = "digest sha384 failed";
return 0;
}
return 1;
} else if(hashalgo == ZONEMD_ALGO_SHA512) {
if(!secalgo_hash_update(h, data, len)) {
*reason = "digest sha512 failed";
return 0;
}
return 1;
}
/* unknown hash algo */
*reason = "unsupported algorithm";
return 0;
}
/** finish the hash for zonemd */
static int zonemd_digest_finish(int hashalgo, struct secalgo_hash* h,
uint8_t* result, size_t hashlen, size_t* resultlen, char** reason)
{
if(hashalgo == ZONEMD_ALGO_SHA384) {
if(hashlen < 384/8) {
*reason = "digest buffer too small for sha384";
return 0;
}
if(!secalgo_hash_final(h, result, hashlen, resultlen)) {
*reason = "digest sha384 finish failed";
return 0;
}
return 1;
} else if(hashalgo == ZONEMD_ALGO_SHA512) {
if(hashlen < 512/8) {
*reason = "digest buffer too small for sha512";
return 0;
}
if(!secalgo_hash_final(h, result, hashlen, resultlen)) {
*reason = "digest sha512 finish failed";
return 0;
}
return 1;
}
/* unknown algo */
*reason = "unsupported algorithm";
return 0;
}
/** add rrsets from node to the list */
static size_t authdata_rrsets_to_list(struct auth_rrset** array,
size_t arraysize, struct auth_rrset* first)
{
struct auth_rrset* rrset = first;
size_t num = 0;
while(rrset) {
if(num >= arraysize)
return num;
array[num] = rrset;
num++;
rrset = rrset->next;
}
return num;
}
/** compare rr list entries */
static int rrlist_compare(const void* arg1, const void* arg2)
{
struct auth_rrset* r1 = *(struct auth_rrset**)arg1;
struct auth_rrset* r2 = *(struct auth_rrset**)arg2;
uint16_t t1, t2;
if(r1 == NULL) t1 = LDNS_RR_TYPE_RRSIG;
else t1 = r1->type;
if(r2 == NULL) t2 = LDNS_RR_TYPE_RRSIG;
else t2 = r2->type;
if(t1 < t2)
return -1;
if(t1 > t2)
return 1;
return 0;
}
/** add type RRSIG to rr list if not one there already,
* this is to perform RRSIG collate processing at that point. */
static void addrrsigtype_if_needed(struct auth_rrset** array,
size_t arraysize, size_t* rrnum, struct auth_data* node)
{
if(az_domain_rrset(node, LDNS_RR_TYPE_RRSIG))
return; /* already one there */
if((*rrnum) >= arraysize)
return; /* array too small? */
array[*rrnum] = NULL; /* nothing there, but need entry in list */
(*rrnum)++;
}
/** collate the RRs in an RRset using the simple scheme */
static int zonemd_simple_rrset(struct auth_zone* z, int hashalgo,
struct secalgo_hash* h, struct auth_data* node,
struct auth_rrset* rrset, struct regional* region,
struct sldns_buffer* buf, char** reason)
{
/* canonicalize */
struct ub_packed_rrset_key key;
memset(&key, 0, sizeof(key));
key.entry.key = &key;
key.entry.data = rrset->data;
key.rk.dname = node->name;
key.rk.dname_len = node->namelen;
key.rk.type = htons(rrset->type);
key.rk.rrset_class = htons(z->dclass);
if(!rrset_canonicalize_to_buffer(region, buf, &key)) {
*reason = "out of memory";
return 0;
}
regional_free_all(region);
/* hash */
if(!zonemd_digest_update(hashalgo, h, sldns_buffer_begin(buf),
sldns_buffer_limit(buf), reason)) {
return 0;
}
return 1;
}
/** count number of RRSIGs in a domain name rrset list */
static size_t zonemd_simple_count_rrsig(struct auth_rrset* rrset,
struct auth_rrset** rrlist, size_t rrnum,
struct auth_zone* z, struct auth_data* node)
{
size_t i, count = 0;
if(rrset) {
size_t j;
for(j = 0; j<rrset->data->count; j++) {
if(rrsig_rdata_get_type_covered(rrset->data->
rr_data[j], rrset->data->rr_len[j]) ==
LDNS_RR_TYPE_ZONEMD &&
query_dname_compare(z->name, node->name)==0) {
/* omit RRSIGs over type ZONEMD at apex */
continue;
}
count++;
}
}
for(i=0; i<rrnum; i++) {
if(rrlist[i] && rrlist[i]->type == LDNS_RR_TYPE_ZONEMD &&
query_dname_compare(z->name, node->name)==0) {
/* omit RRSIGs over type ZONEMD at apex */
continue;
}
count += (rrlist[i]?rrlist[i]->data->rrsig_count:0);
}
return count;
}
/** allocate sparse rrset data for the number of entries in tepm region */
static int zonemd_simple_rrsig_allocs(struct regional* region,
struct packed_rrset_data* data, size_t count)
{
data->rr_len = regional_alloc(region, sizeof(*data->rr_len) * count);
if(!data->rr_len) {
return 0;
}
data->rr_ttl = regional_alloc(region, sizeof(*data->rr_ttl) * count);
if(!data->rr_ttl) {
return 0;
}
data->rr_data = regional_alloc(region, sizeof(*data->rr_data) * count);
if(!data->rr_data) {
return 0;
}
return 1;
}
/** add the RRSIGs from the rrs in the domain into the data */
static void add_rrlist_rrsigs_into_data(struct packed_rrset_data* data,
size_t* done, struct auth_rrset** rrlist, size_t rrnum,
struct auth_zone* z, struct auth_data* node)
{
size_t i;
for(i=0; i<rrnum; i++) {
size_t j;
if(!rrlist[i])
continue;
if(rrlist[i]->type == LDNS_RR_TYPE_ZONEMD &&
query_dname_compare(z->name, node->name)==0) {
/* omit RRSIGs over type ZONEMD at apex */
continue;
}
for(j = 0; j<rrlist[i]->data->rrsig_count; j++) {
data->rr_len[*done] = rrlist[i]->data->rr_len[rrlist[i]->data->count + j];
data->rr_ttl[*done] = rrlist[i]->data->rr_ttl[rrlist[i]->data->count + j];
/* reference the rdata in the rrset, no need to
* copy it, it is no longer needed at the end of
* the routine */
data->rr_data[*done] = rrlist[i]->data->rr_data[rrlist[i]->data->count + j];
(*done)++;
}
}
}
static void add_rrset_into_data(struct packed_rrset_data* data,
size_t* done, struct auth_rrset* rrset,
struct auth_zone* z, struct auth_data* node)
{
if(rrset) {
size_t j;
for(j = 0; j<rrset->data->count; j++) {
if(rrsig_rdata_get_type_covered(rrset->data->
rr_data[j], rrset->data->rr_len[j]) ==
LDNS_RR_TYPE_ZONEMD &&
query_dname_compare(z->name, node->name)==0) {
/* omit RRSIGs over type ZONEMD at apex */
continue;
}
data->rr_len[*done] = rrset->data->rr_len[j];
data->rr_ttl[*done] = rrset->data->rr_ttl[j];
/* reference the rdata in the rrset, no need to
* copy it, it is no longer need at the end of
* the routine */
data->rr_data[*done] = rrset->data->rr_data[j];
(*done)++;
}
}
}
/** collate the RRSIGs using the simple scheme */
static int zonemd_simple_rrsig(struct auth_zone* z, int hashalgo,
struct secalgo_hash* h, struct auth_data* node,
struct auth_rrset* rrset, struct auth_rrset** rrlist, size_t rrnum,
struct regional* region, struct sldns_buffer* buf, char** reason)
{
/* the rrset pointer can be NULL, this means it is type RRSIG and
* there is no ordinary type RRSIG there. The RRSIGs are stored
* with the RRsets in their data.
*
* The RRset pointer can be nonNULL. This happens if there is
* no RR that is covered by the RRSIG for the domain. Then this
* RRSIG RR is stored in an rrset of type RRSIG. The other RRSIGs
* are stored in the rrset entries for the RRs in the rr list for
* the domain node. We need to collate the rrset's data, if any, and
* the rrlist's rrsigs */
/* if this is the apex, omit RRSIGs that cover type ZONEMD */
/* build rrsig rrset */
size_t done = 0;
struct ub_packed_rrset_key key;
struct packed_rrset_data data;
memset(&key, 0, sizeof(key));
memset(&data, 0, sizeof(data));
key.entry.key = &key;
key.entry.data = &data;
key.rk.dname = node->name;
key.rk.dname_len = node->namelen;
key.rk.type = htons(LDNS_RR_TYPE_RRSIG);
key.rk.rrset_class = htons(z->dclass);
data.count = zonemd_simple_count_rrsig(rrset, rrlist, rrnum, z, node);
if(!zonemd_simple_rrsig_allocs(region, &data, data.count)) {
*reason = "out of memory";
regional_free_all(region);
return 0;
}
/* all the RRSIGs stored in the other rrsets for this domain node */
add_rrlist_rrsigs_into_data(&data, &done, rrlist, rrnum, z, node);
/* plus the RRSIGs stored in an rrset of type RRSIG for this node */
add_rrset_into_data(&data, &done, rrset, z, node);
/* canonicalize */
if(!rrset_canonicalize_to_buffer(region, buf, &key)) {
*reason = "out of memory";
regional_free_all(region);
return 0;
}
regional_free_all(region);
/* hash */
if(!zonemd_digest_update(hashalgo, h, sldns_buffer_begin(buf),
sldns_buffer_limit(buf), reason)) {
return 0;
}
return 1;
}
/** collate a domain's rrsets using the simple scheme */
static int zonemd_simple_domain(struct auth_zone* z, int hashalgo,
struct secalgo_hash* h, struct auth_data* node,
struct regional* region, struct sldns_buffer* buf, char** reason)
{
const size_t rrlistsize = 65536;
struct auth_rrset* rrlist[rrlistsize];
size_t i, rrnum = 0;
/* see if the domain is out of scope, the zone origin,
* that would be omitted */
if(!dname_subdomain_c(node->name, z->name))
return 1; /* continue */
/* loop over the rrsets in ascending order. */
rrnum = authdata_rrsets_to_list(rrlist, rrlistsize, node->rrsets);
addrrsigtype_if_needed(rrlist, rrlistsize, &rrnum, node);
qsort(rrlist, rrnum, sizeof(*rrlist), rrlist_compare);
for(i=0; i<rrnum; i++) {
if(rrlist[i] && rrlist[i]->type == LDNS_RR_TYPE_ZONEMD &&
query_dname_compare(z->name, node->name) == 0) {
/* omit type ZONEMD at apex */
continue;
}
if(rrlist[i] == NULL || rrlist[i]->type ==
LDNS_RR_TYPE_RRSIG) {
if(!zonemd_simple_rrsig(z, hashalgo, h, node,
rrlist[i], rrlist, rrnum, region, buf, reason))
return 0;
} else if(!zonemd_simple_rrset(z, hashalgo, h, node,
rrlist[i], region, buf, reason)) {
return 0;
}
}
return 1;
}
/** collate the zone using the simple scheme */
static int zonemd_simple_collate(struct auth_zone* z, int hashalgo,
struct secalgo_hash* h, struct regional* region,
struct sldns_buffer* buf, char** reason)
{
/* our tree is sorted in canonical order, so we can just loop over
* the tree */
struct auth_data* n;
RBTREE_FOR(n, struct auth_data*, &z->data) {
if(!zonemd_simple_domain(z, hashalgo, h, n, region, buf,
reason))
return 0;
}
return 1;
}
int auth_zone_generate_zonemd_hash(struct auth_zone* z, int scheme,
int hashalgo, uint8_t* hash, size_t hashlen, size_t* resultlen,
struct regional* region, struct sldns_buffer* buf, char** reason)
{
struct secalgo_hash* h = zonemd_digest_init(hashalgo, reason);
if(!h) {
if(!*reason)
*reason = "digest init fail";
return 0;
}
if(scheme == ZONEMD_SCHEME_SIMPLE) {
if(!zonemd_simple_collate(z, hashalgo, h, region, buf, reason)) {
if(!*reason) *reason = "scheme simple collate fail";
secalgo_hash_delete(h);
return 0;
}
}
if(!zonemd_digest_finish(hashalgo, h, hash, hashlen, resultlen,
reason)) {
secalgo_hash_delete(h);
*reason = "digest finish fail";
return 0;
}
secalgo_hash_delete(h);
return 1;
}
int auth_zone_generate_zonemd_check(struct auth_zone* z, int scheme,
int hashalgo, uint8_t* hash, size_t hashlen, struct regional* region,
struct sldns_buffer* buf, char** reason)
{
uint8_t gen[512];
size_t genlen = 0;
*reason = NULL;
if(!zonemd_hashalgo_supported(hashalgo)) {
/* allow it */
*reason = "unsupported algorithm";
return 1;
}
if(!zonemd_scheme_supported(scheme)) {
/* allow it */
*reason = "unsupported scheme";
return 1;
}
if(hashlen < 12) {
/* the ZONEMD draft requires digests to fail if too small */
*reason = "digest length too small, less than 12";
return 0;
}
/* generate digest */
if(!auth_zone_generate_zonemd_hash(z, scheme, hashalgo, gen,
sizeof(gen), &genlen, region, buf, reason)) {
/* reason filled in by zonemd hash routine */
return 0;
}
/* check digest length */
if(hashlen != genlen) {
*reason = "incorrect digest length";
if(verbosity >= VERB_ALGO) {
verbose(VERB_ALGO, "zonemd scheme=%d hashalgo=%d",
scheme, hashalgo);
log_hex("ZONEMD should be ", gen, genlen);
log_hex("ZONEMD to check is", hash, hashlen);
}
return 0;
}
/* check digest */
if(memcmp(hash, gen, genlen) != 0) {
*reason = "incorrect digest";
if(verbosity >= VERB_ALGO) {
verbose(VERB_ALGO, "zonemd scheme=%d hashalgo=%d",
scheme, hashalgo);
log_hex("ZONEMD should be ", gen, genlen);
log_hex("ZONEMD to check is", hash, hashlen);
}
return 0;
}
return 1;
}
/** log auth zone message with zone name in front. */
static void auth_zone_log(uint8_t* name, enum verbosity_value level,
const char* format, ...) ATTR_FORMAT(printf, 3, 4);
static void auth_zone_log(uint8_t* name, enum verbosity_value level,
const char* format, ...)
{
va_list args;
va_start(args, format);
if(verbosity >= level) {
char str[255+1];
char msg[MAXSYSLOGMSGLEN];
dname_str(name, str);
vsnprintf(msg, sizeof(msg), format, args);
verbose(level, "auth zone %s %s", str, msg);
}
va_end(args);
}
/** ZONEMD, dnssec verify the rrset with the dnskey */
static int zonemd_dnssec_verify_rrset(struct auth_zone* z,
struct module_env* env, struct module_stack* mods,
struct ub_packed_rrset_key* dnskey, struct auth_data* node,
struct auth_rrset* rrset, char** why_bogus, uint8_t* sigalg)
{
struct ub_packed_rrset_key pk;
enum sec_status sec;
struct val_env* ve;
int m;
m = modstack_find(mods, "validator");
if(m == -1) {
auth_zone_log(z->name, VERB_ALGO, "zonemd dnssec verify: have "
"DNSKEY chain of trust, but no validator module");
return 0;
}
ve = (struct val_env*)env->modinfo[m];
memset(&pk, 0, sizeof(pk));
pk.entry.key = &pk;
pk.entry.data = rrset->data;
pk.rk.dname = node->name;
pk.rk.dname_len = node->namelen;
pk.rk.type = htons(rrset->type);
pk.rk.rrset_class = htons(z->dclass);
if(verbosity >= VERB_ALGO) {
char typestr[32];
typestr[0]=0;
sldns_wire2str_type_buf(rrset->type, typestr, sizeof(typestr));
auth_zone_log(z->name, VERB_ALGO,
"zonemd: verify %s RRset with DNSKEY", typestr);
}
sec = dnskeyset_verify_rrset(env, ve, &pk, dnskey, sigalg, why_bogus, NULL,
LDNS_SECTION_ANSWER, NULL);
if(sec == sec_status_secure) {
return 1;
}
if(why_bogus)
auth_zone_log(z->name, VERB_ALGO, "DNSSEC verify was bogus: %s", *why_bogus);
return 0;
}
/** check for nsec3, the RR with params equal, if bitmap has the type */
static int nsec3_of_param_has_type(struct auth_rrset* nsec3, int algo,
size_t iter, uint8_t* salt, size_t saltlen, uint16_t rrtype)
{
int i, count = (int)nsec3->data->count;
struct ub_packed_rrset_key pk;
memset(&pk, 0, sizeof(pk));
pk.entry.data = nsec3->data;
for(i=0; i<count; i++) {
int rralgo;
size_t rriter, rrsaltlen;
uint8_t* rrsalt;
if(!nsec3_get_params(&pk, i, &rralgo, &rriter, &rrsalt,
&rrsaltlen))
continue; /* no parameters, malformed */
if(rralgo != algo || rriter != iter || rrsaltlen != saltlen)
continue; /* different parameters */
if(saltlen != 0) {
if(rrsalt == NULL || salt == NULL)
continue;
if(memcmp(rrsalt, salt, saltlen) != 0)
continue; /* different salt parameters */
}
if(nsec3_has_type(&pk, i, rrtype))
return 1;
}
return 0;
}
/** Verify the absence of ZONEMD with DNSSEC by checking NSEC, NSEC3 type flag.
* return false on failure, reason contains description of failure. */
static int zonemd_check_dnssec_absence(struct auth_zone* z,
struct module_env* env, struct module_stack* mods,
struct ub_packed_rrset_key* dnskey, struct auth_data* apex,
char** reason, char** why_bogus, uint8_t* sigalg)
{
struct auth_rrset* nsec = NULL;
if(!apex) {
*reason = "zone has no apex domain but ZONEMD missing";
return 0;
}
nsec = az_domain_rrset(apex, LDNS_RR_TYPE_NSEC);
if(nsec) {
struct ub_packed_rrset_key pk;
/* dnssec verify the NSEC */
if(!zonemd_dnssec_verify_rrset(z, env, mods, dnskey, apex,
nsec, why_bogus, sigalg)) {
*reason = "DNSSEC verify failed for NSEC RRset";
return 0;
}
/* check type bitmap */
memset(&pk, 0, sizeof(pk));
pk.entry.data = nsec->data;
if(nsec_has_type(&pk, LDNS_RR_TYPE_ZONEMD)) {
*reason = "DNSSEC NSEC bitmap says type ZONEMD exists";
return 0;
}
auth_zone_log(z->name, VERB_ALGO, "zonemd DNSSEC NSEC verification of absence of ZONEMD secure");
} else {
/* NSEC3 perhaps ? */
int algo;
size_t iter, saltlen;
uint8_t* salt;
struct auth_rrset* nsec3param = az_domain_rrset(apex,
LDNS_RR_TYPE_NSEC3PARAM);
struct auth_data* match;
struct auth_rrset* nsec3;
if(!nsec3param) {
*reason = "zone has no NSEC information but ZONEMD missing";
return 0;
}
if(!az_nsec3_param(z, &algo, &iter, &salt, &saltlen)) {
*reason = "zone has no NSEC information but ZONEMD missing";
return 0;
}
/* find the NSEC3 record */
match = az_nsec3_find_exact(z, z->name, z->namelen, algo,
iter, salt, saltlen);
if(!match) {
*reason = "zone has no NSEC3 domain for the apex but ZONEMD missing";
return 0;
}
nsec3 = az_domain_rrset(match, LDNS_RR_TYPE_NSEC3);
if(!nsec3) {
*reason = "zone has no NSEC3 RRset for the apex but ZONEMD missing";
return 0;
}
/* dnssec verify the NSEC3 */
if(!zonemd_dnssec_verify_rrset(z, env, mods, dnskey, match,
nsec3, why_bogus, sigalg)) {
*reason = "DNSSEC verify failed for NSEC3 RRset";
return 0;
}
/* check type bitmap */
if(nsec3_of_param_has_type(nsec3, algo, iter, salt, saltlen,
LDNS_RR_TYPE_ZONEMD)) {
*reason = "DNSSEC NSEC3 bitmap says type ZONEMD exists";
return 0;
}
auth_zone_log(z->name, VERB_ALGO, "zonemd DNSSEC NSEC3 verification of absence of ZONEMD secure");
}
return 1;
}
/** Verify the SOA and ZONEMD DNSSEC signatures.
* return false on failure, reason contains description of failure. */
static int zonemd_check_dnssec_soazonemd(struct auth_zone* z,
struct module_env* env, struct module_stack* mods,
struct ub_packed_rrset_key* dnskey, struct auth_data* apex,
struct auth_rrset* zonemd_rrset, char** reason, char** why_bogus,
uint8_t* sigalg)
{
struct auth_rrset* soa;
if(!apex) {
*reason = "zone has no apex domain";
return 0;
}
soa = az_domain_rrset(apex, LDNS_RR_TYPE_SOA);
if(!soa) {
*reason = "zone has no SOA RRset";
return 0;
}
if(!zonemd_dnssec_verify_rrset(z, env, mods, dnskey, apex, soa,
why_bogus, sigalg)) {
*reason = "DNSSEC verify failed for SOA RRset";
return 0;
}
if(!zonemd_dnssec_verify_rrset(z, env, mods, dnskey, apex,
zonemd_rrset, why_bogus, sigalg)) {
*reason = "DNSSEC verify failed for ZONEMD RRset";
return 0;
}
auth_zone_log(z->name, VERB_ALGO, "zonemd DNSSEC verification of SOA and ZONEMD RRsets secure");
return 1;
}
/**
* Fail the ZONEMD verification.
* @param z: auth zone that fails.
* @param env: environment with config, to ignore failure or not.
* @param reason: failure string description.
* @param why_bogus: failure string for DNSSEC verification failure.
* @param result: strdup result in here if not NULL.
*/
static void auth_zone_zonemd_fail(struct auth_zone* z, struct module_env* env,
char* reason, char* why_bogus, char** result)
{
char zstr[255+1];
/* if fail: log reason, and depending on config also take action
* and drop the zone, eg. it is gone from memory, set zone_expired */
dname_str(z->name, zstr);
if(!reason) reason = "verification failed";
if(result) {
if(why_bogus) {
char res[1024];
snprintf(res, sizeof(res), "%s: %s", reason,
why_bogus);
*result = strdup(res);
} else {
*result = strdup(reason);
}
if(!*result) log_err("out of memory");
} else {
log_warn("auth zone %s: ZONEMD verification failed: %s", zstr, reason);
}
if(env->cfg->zonemd_permissive_mode) {
verbose(VERB_ALGO, "zonemd-permissive-mode enabled, "
"not blocking zone %s", zstr);
return;
}
/* expired means the zone gives servfail and is not used by
* lookup if fallback_enabled*/
z->zone_expired = 1;
}
/**
* Verify the zonemd with DNSSEC and hash check, with given key.
* @param z: auth zone.
* @param env: environment with config and temp buffers.
* @param mods: module stack with validator env for verification.
* @param dnskey: dnskey that we can use, or NULL. If nonnull, the key
* has been verified and is the start of the chain of trust.
* @param is_insecure: if true, the dnskey is not used, the zone is insecure.
* And dnssec is not used. It is DNSSEC secure insecure or not under
* a trust anchor.
* @param sigalg: if nonNULL provide algorithm downgrade protection.
* Otherwise one algorithm is enough. Must have space of ALGO_NEEDS_MAX+1.
* @param result: if not NULL result reason copied here.
*/
static void
auth_zone_verify_zonemd_with_key(struct auth_zone* z, struct module_env* env,
struct module_stack* mods, struct ub_packed_rrset_key* dnskey,
int is_insecure, char** result, uint8_t* sigalg)
{
char* reason = NULL, *why_bogus = NULL;
struct auth_data* apex = NULL;
struct auth_rrset* zonemd_rrset = NULL;
int zonemd_absent = 0, zonemd_absence_dnssecok = 0;
/* see if ZONEMD is present or absent. */
apex = az_find_name(z, z->name, z->namelen);
if(!apex) {
zonemd_absent = 1;
} else {
zonemd_rrset = az_domain_rrset(apex, LDNS_RR_TYPE_ZONEMD);
if(!zonemd_rrset || zonemd_rrset->data->count==0) {
zonemd_absent = 1;
zonemd_rrset = NULL;
}
}
/* if no DNSSEC, done. */
/* if no ZONEMD, and DNSSEC, use DNSKEY to verify NSEC or NSEC3 for
* zone apex. Check ZONEMD bit is turned off or else fail */
/* if ZONEMD, and DNSSEC, check DNSSEC signature on SOA and ZONEMD,
* or else fail */
if(!dnskey && !is_insecure) {
auth_zone_zonemd_fail(z, env, "DNSKEY missing", NULL, result);
return;
} else if(!zonemd_rrset && dnskey && !is_insecure) {
/* fetch, DNSSEC verify, and check NSEC/NSEC3 */
if(!zonemd_check_dnssec_absence(z, env, mods, dnskey, apex,
&reason, &why_bogus, sigalg)) {
auth_zone_zonemd_fail(z, env, reason, why_bogus, result);
return;
}
zonemd_absence_dnssecok = 1;
} else if(zonemd_rrset && dnskey && !is_insecure) {
/* check DNSSEC verify of SOA and ZONEMD */
if(!zonemd_check_dnssec_soazonemd(z, env, mods, dnskey, apex,
zonemd_rrset, &reason, &why_bogus, sigalg)) {
auth_zone_zonemd_fail(z, env, reason, why_bogus, result);
return;
}
}
if(zonemd_absent && z->zonemd_reject_absence) {
auth_zone_zonemd_fail(z, env, "ZONEMD absent and that is not allowed by config", NULL, result);
return;
}
if(zonemd_absent && zonemd_absence_dnssecok) {
auth_zone_log(z->name, VERB_ALGO, "DNSSEC verified nonexistence of ZONEMD");
if(result) {
*result = strdup("DNSSEC verified nonexistence of ZONEMD");
if(!*result) log_err("out of memory");
}
return;
}
if(zonemd_absent) {
auth_zone_log(z->name, VERB_ALGO, "no ZONEMD present");
if(result) {
*result = strdup("no ZONEMD present");
if(!*result) log_err("out of memory");
}
return;
}
/* check ZONEMD checksum and report or else fail. */
if(!auth_zone_zonemd_check_hash(z, env, &reason)) {
auth_zone_zonemd_fail(z, env, reason, NULL, result);
return;
}
/* success! log the success */
if(reason)
auth_zone_log(z->name, VERB_ALGO, "ZONEMD %s", reason);
else auth_zone_log(z->name, VERB_ALGO, "ZONEMD verification successful");
if(result) {
if(reason)
*result = strdup(reason);
else *result = strdup("ZONEMD verification successful");
if(!*result) log_err("out of memory");
}
}
/**
* verify the zone DNSKEY rrset from the trust anchor
* This is possible because the anchor is for the zone itself, and can
* thus apply straight to the zone DNSKEY set.
* @param z: the auth zone.
* @param env: environment with time and temp buffers.
* @param mods: module stack for validator environment for dnssec validation.
* @param anchor: trust anchor to use
* @param is_insecure: returned, true if the zone is securely insecure.
* @param why_bogus: if the routine fails, returns the failure reason.
* @param keystorage: where to store the ub_packed_rrset_key that is created
* on success. A pointer to it is returned on success.
* @return the dnskey RRset, reference to zone data and keystorage, or
* NULL on failure.
*/
static struct ub_packed_rrset_key*
zonemd_get_dnskey_from_anchor(struct auth_zone* z, struct module_env* env,
struct module_stack* mods, struct trust_anchor* anchor,
int* is_insecure, char** why_bogus,
struct ub_packed_rrset_key* keystorage)
{
struct auth_data* apex;
struct auth_rrset* dnskey_rrset;
enum sec_status sec;
struct val_env* ve;
int m;
apex = az_find_name(z, z->name, z->namelen);
if(!apex) {
*why_bogus = "have trust anchor, but zone has no apex domain for DNSKEY";
return 0;
}
dnskey_rrset = az_domain_rrset(apex, LDNS_RR_TYPE_DNSKEY);
if(!dnskey_rrset || dnskey_rrset->data->count==0) {
*why_bogus = "have trust anchor, but zone has no DNSKEY";
return 0;
}
m = modstack_find(mods, "validator");
if(m == -1) {
*why_bogus = "have trust anchor, but no validator module";
return 0;
}
ve = (struct val_env*)env->modinfo[m];
memset(keystorage, 0, sizeof(*keystorage));
keystorage->entry.key = keystorage;
keystorage->entry.data = dnskey_rrset->data;
keystorage->rk.dname = apex->name;
keystorage->rk.dname_len = apex->namelen;
keystorage->rk.type = htons(LDNS_RR_TYPE_DNSKEY);
keystorage->rk.rrset_class = htons(z->dclass);
auth_zone_log(z->name, VERB_QUERY,
"zonemd: verify DNSKEY RRset with trust anchor");
sec = val_verify_DNSKEY_with_TA(env, ve, keystorage, anchor->ds_rrset,
anchor->dnskey_rrset, NULL, why_bogus, NULL, NULL);
regional_free_all(env->scratch);
if(sec == sec_status_secure) {
/* success */
*is_insecure = 0;
return keystorage;
} else if(sec == sec_status_insecure) {
/* insecure */
*is_insecure = 1;
} else {
/* bogus */
*is_insecure = 0;
auth_zone_log(z->name, VERB_ALGO,
"zonemd: verify DNSKEY RRset with trust anchor failed: %s", *why_bogus);
}
return NULL;
}
/** verify the DNSKEY from the zone with looked up DS record */
static struct ub_packed_rrset_key*
auth_zone_verify_zonemd_key_with_ds(struct auth_zone* z,
struct module_env* env, struct module_stack* mods,
struct ub_packed_rrset_key* ds, int* is_insecure, char** why_bogus,
struct ub_packed_rrset_key* keystorage, uint8_t* sigalg)
{
struct auth_data* apex;
struct auth_rrset* dnskey_rrset;
enum sec_status sec;
struct val_env* ve;
int m;
/* fetch DNSKEY from zone data */
apex = az_find_name(z, z->name, z->namelen);
if(!apex) {
*why_bogus = "in verifywithDS, zone has no apex";
return NULL;
}
dnskey_rrset = az_domain_rrset(apex, LDNS_RR_TYPE_DNSKEY);
if(!dnskey_rrset || dnskey_rrset->data->count==0) {
*why_bogus = "in verifywithDS, zone has no DNSKEY";
return NULL;
}
m = modstack_find(mods, "validator");
if(m == -1) {
*why_bogus = "in verifywithDS, have no validator module";
return NULL;
}
ve = (struct val_env*)env->modinfo[m];
memset(keystorage, 0, sizeof(*keystorage));
keystorage->entry.key = keystorage;
keystorage->entry.data = dnskey_rrset->data;
keystorage->rk.dname = apex->name;
keystorage->rk.dname_len = apex->namelen;
keystorage->rk.type = htons(LDNS_RR_TYPE_DNSKEY);
keystorage->rk.rrset_class = htons(z->dclass);
auth_zone_log(z->name, VERB_QUERY, "zonemd: verify zone DNSKEY with DS");
sec = val_verify_DNSKEY_with_DS(env, ve, keystorage, ds, sigalg,
why_bogus, NULL, NULL);
regional_free_all(env->scratch);
if(sec == sec_status_secure) {
/* success */
return keystorage;
} else if(sec == sec_status_insecure) {
/* insecure */
*is_insecure = 1;
} else {
/* bogus */
*is_insecure = 0;
if(*why_bogus == NULL)
*why_bogus = "verify failed";
auth_zone_log(z->name, VERB_ALGO,
"zonemd: verify DNSKEY RRset with DS failed: %s",
*why_bogus);
}
return NULL;
}
/** callback for ZONEMD lookup of DNSKEY */
void auth_zonemd_dnskey_lookup_callback(void* arg, int rcode, sldns_buffer* buf,
enum sec_status sec, char* why_bogus, int ATTR_UNUSED(was_ratelimited))
{
struct auth_zone* z = (struct auth_zone*)arg;
struct module_env* env;
char* reason = NULL, *ds_bogus = NULL, *typestr="DNSKEY";
struct ub_packed_rrset_key* dnskey = NULL, *ds = NULL;
int is_insecure = 0, downprot;
struct ub_packed_rrset_key keystorage;
uint8_t sigalg[ALGO_NEEDS_MAX+1];
lock_rw_wrlock(&z->lock);
env = z->zonemd_callback_env;
/* release the env variable so another worker can pick up the
* ZONEMD verification task if it wants to */
z->zonemd_callback_env = NULL;
if(!env || env->outnet->want_to_quit || z->zone_deleted) {
lock_rw_unlock(&z->lock);
return; /* stop on quit */
}
if(z->zonemd_callback_qtype == LDNS_RR_TYPE_DS)
typestr = "DS";
downprot = env->cfg->harden_algo_downgrade;
/* process result */
if(sec == sec_status_bogus) {
reason = why_bogus;
if(!reason) {
if(z->zonemd_callback_qtype == LDNS_RR_TYPE_DNSKEY)
reason = "lookup of DNSKEY was bogus";
else reason = "lookup of DS was bogus";
}
auth_zone_log(z->name, VERB_ALGO,
"zonemd lookup of %s was bogus: %s", typestr, reason);
} else if(rcode == LDNS_RCODE_NOERROR) {
uint16_t wanted_qtype = z->zonemd_callback_qtype;
struct regional* temp = env->scratch;
struct query_info rq;
struct reply_info* rep;
memset(&rq, 0, sizeof(rq));
rep = parse_reply_in_temp_region(buf, temp, &rq);
if(rep && rq.qtype == wanted_qtype &&
query_dname_compare(z->name, rq.qname) == 0 &&
FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NOERROR) {
/* parsed successfully */
struct ub_packed_rrset_key* answer =
reply_find_answer_rrset(&rq, rep);
if(answer && sec == sec_status_secure) {
if(z->zonemd_callback_qtype == LDNS_RR_TYPE_DNSKEY)
dnskey = answer;
else ds = answer;
auth_zone_log(z->name, VERB_ALGO,
"zonemd lookup of %s was secure", typestr);
} else if(sec == sec_status_secure && !answer) {
is_insecure = 1;
auth_zone_log(z->name, VERB_ALGO,
"zonemd lookup of %s has no content, but is secure, treat as insecure", typestr);
} else if(sec == sec_status_insecure) {
is_insecure = 1;
auth_zone_log(z->name, VERB_ALGO,
"zonemd lookup of %s was insecure", typestr);
} else if(sec == sec_status_indeterminate) {
is_insecure = 1;
auth_zone_log(z->name, VERB_ALGO,
"zonemd lookup of %s was indeterminate, treat as insecure", typestr);
} else {
auth_zone_log(z->name, VERB_ALGO,
"zonemd lookup of %s has nodata", typestr);
if(z->zonemd_callback_qtype == LDNS_RR_TYPE_DNSKEY)
reason = "lookup of DNSKEY has nodata";
else reason = "lookup of DS has nodata";
}
} else if(rep && rq.qtype == wanted_qtype &&
query_dname_compare(z->name, rq.qname) == 0 &&
FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NXDOMAIN &&
sec == sec_status_secure) {
/* secure nxdomain, so the zone is like some RPZ zone
* that does not exist in the wider internet, with
* a secure nxdomain answer outside of it. So we
* treat the zonemd zone without a dnssec chain of
* trust, as insecure. */
is_insecure = 1;
auth_zone_log(z->name, VERB_ALGO,
"zonemd lookup of %s was secure NXDOMAIN, treat as insecure", typestr);
} else if(rep && rq.qtype == wanted_qtype &&
query_dname_compare(z->name, rq.qname) == 0 &&
FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NXDOMAIN &&
sec == sec_status_insecure) {
is_insecure = 1;
auth_zone_log(z->name, VERB_ALGO,
"zonemd lookup of %s was insecure NXDOMAIN, treat as insecure", typestr);
} else if(rep && rq.qtype == wanted_qtype &&
query_dname_compare(z->name, rq.qname) == 0 &&
FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NXDOMAIN &&
sec == sec_status_indeterminate) {
is_insecure = 1;
auth_zone_log(z->name, VERB_ALGO,
"zonemd lookup of %s was indeterminate NXDOMAIN, treat as insecure", typestr);
} else {
auth_zone_log(z->name, VERB_ALGO,
"zonemd lookup of %s has no answer", typestr);
if(z->zonemd_callback_qtype == LDNS_RR_TYPE_DNSKEY)
reason = "lookup of DNSKEY has no answer";
else reason = "lookup of DS has no answer";
}
} else {
auth_zone_log(z->name, VERB_ALGO,
"zonemd lookup of %s failed", typestr);
if(z->zonemd_callback_qtype == LDNS_RR_TYPE_DNSKEY)
reason = "lookup of DNSKEY failed";
else reason = "lookup of DS failed";
}
if(!reason && !is_insecure && !dnskey && ds) {
dnskey = auth_zone_verify_zonemd_key_with_ds(z, env,
&env->mesh->mods, ds, &is_insecure, &ds_bogus,
&keystorage, downprot?sigalg:NULL);
if(!dnskey && !is_insecure && !reason)
reason = "DNSKEY verify with DS failed";
}
if(reason) {
auth_zone_zonemd_fail(z, env, reason, ds_bogus, NULL);
lock_rw_unlock(&z->lock);
return;
}
auth_zone_verify_zonemd_with_key(z, env, &env->mesh->mods, dnskey,
is_insecure, NULL, downprot?sigalg:NULL);
regional_free_all(env->scratch);
lock_rw_unlock(&z->lock);
}
/** lookup DNSKEY for ZONEMD verification */
static int
zonemd_lookup_dnskey(struct auth_zone* z, struct module_env* env)
{
struct query_info qinfo;
uint16_t qflags = BIT_RD;
struct edns_data edns;
sldns_buffer* buf = env->scratch_buffer;
int fetch_ds = 0;
if(!z->fallback_enabled) {
/* we cannot actually get the DNSKEY, because it is in the
* zone we have ourselves, and it is not served yet
* (possibly), so fetch type DS */
fetch_ds = 1;
}
if(z->zonemd_callback_env) {
/* another worker is already working on the callback
* for the DNSKEY lookup for ZONEMD verification.
* We do not also have to do ZONEMD verification, let that
* worker do it */
auth_zone_log(z->name, VERB_ALGO,
"zonemd needs lookup of %s and that already is worked on by another worker", (fetch_ds?"DS":"DNSKEY"));
return 1;
}
/* use mesh_new_callback to lookup the DNSKEY,
* and then wait for them to be looked up (in cache, or query) */
qinfo.qname_len = z->namelen;
qinfo.qname = z->name;
qinfo.qclass = z->dclass;
if(fetch_ds)
qinfo.qtype = LDNS_RR_TYPE_DS;
else qinfo.qtype = LDNS_RR_TYPE_DNSKEY;
qinfo.local_alias = NULL;
if(verbosity >= VERB_ALGO) {
char buf1[512];
char buf2[LDNS_MAX_DOMAINLEN+1];
dname_str(z->name, buf2);
snprintf(buf1, sizeof(buf1), "auth zone %s: lookup %s "
"for zonemd verification", buf2,
(fetch_ds?"DS":"DNSKEY"));
log_query_info(VERB_ALGO, buf1, &qinfo);
}
edns.edns_present = 1;
edns.ext_rcode = 0;
edns.edns_version = 0;
edns.bits = EDNS_DO;
edns.opt_list_in = NULL;
edns.opt_list_out = NULL;
edns.opt_list_inplace_cb_out = NULL;
if(sldns_buffer_capacity(buf) < 65535)
edns.udp_size = (uint16_t)sldns_buffer_capacity(buf);
else edns.udp_size = 65535;
/* store the worker-specific module env for the callback.
* We can then reference this when the callback executes */
z->zonemd_callback_env = env;
z->zonemd_callback_qtype = qinfo.qtype;
/* the callback can be called straight away */
lock_rw_unlock(&z->lock);
if(!mesh_new_callback(env->mesh, &qinfo, qflags, &edns, buf, 0,
&auth_zonemd_dnskey_lookup_callback, z, 0)) {
lock_rw_wrlock(&z->lock);
log_err("out of memory lookup of %s for zonemd",
(fetch_ds?"DS":"DNSKEY"));
return 0;
}
lock_rw_wrlock(&z->lock);
return 1;
}
void auth_zone_verify_zonemd(struct auth_zone* z, struct module_env* env,
struct module_stack* mods, char** result, int offline, int only_online)
{
char* reason = NULL, *why_bogus = NULL;
struct trust_anchor* anchor = NULL;
struct ub_packed_rrset_key* dnskey = NULL;
struct ub_packed_rrset_key keystorage;
int is_insecure = 0;
/* verify the ZONEMD if present.
* If not present check if absence is allowed by DNSSEC */
if(!z->zonemd_check)
return;
if(z->data.count == 0)
return; /* no data */
/* if zone is under a trustanchor */
/* is it equal to trustanchor - get dnskey's verified */
/* else, find chain of trust by fetching DNSKEYs lookup for zone */
/* result if that, if insecure, means no DNSSEC for the ZONEMD,
* otherwise we have the zone DNSKEY for the DNSSEC verification. */
if(env->anchors)
anchor = anchors_lookup(env->anchors, z->name, z->namelen,
z->dclass);
if(anchor && anchor->numDS == 0 && anchor->numDNSKEY == 0) {
/* domain-insecure trust anchor for unsigned zones */
lock_basic_unlock(&anchor->lock);
if(only_online)
return;
dnskey = NULL;
is_insecure = 1;
} else if(anchor && query_dname_compare(z->name, anchor->name) == 0) {
if(only_online) {
lock_basic_unlock(&anchor->lock);
return;
}
/* equal to trustanchor, no need for online lookups */
dnskey = zonemd_get_dnskey_from_anchor(z, env, mods, anchor,
&is_insecure, &why_bogus, &keystorage);
lock_basic_unlock(&anchor->lock);
if(!dnskey && !reason && !is_insecure) {
reason = "verify DNSKEY RRset with trust anchor failed";
}
} else if(anchor) {
lock_basic_unlock(&anchor->lock);
/* perform online lookups */
if(offline)
return;
/* setup online lookups, and wait for them */
if(zonemd_lookup_dnskey(z, env)) {
/* wait for the lookup */
return;
}
reason = "could not lookup DNSKEY for chain of trust";
} else {
/* the zone is not under a trust anchor */
if(only_online)
return;
dnskey = NULL;
is_insecure = 1;
}
if(reason) {
auth_zone_zonemd_fail(z, env, reason, why_bogus, result);
return;
}
auth_zone_verify_zonemd_with_key(z, env, mods, dnskey, is_insecure,
result, NULL);
regional_free_all(env->scratch);
}
void auth_zones_pickup_zonemd_verify(struct auth_zones* az,
struct module_env* env)
{
struct auth_zone key;
uint8_t savezname[255+1];
size_t savezname_len;
struct auth_zone* z;
key.node.key = &key;
lock_rw_rdlock(&az->lock);
RBTREE_FOR(z, struct auth_zone*, &az->ztree) {
lock_rw_wrlock(&z->lock);
if(!z->zonemd_check) {
lock_rw_unlock(&z->lock);
continue;
}
key.dclass = z->dclass;
key.namelabs = z->namelabs;
if(z->namelen > sizeof(savezname)) {
lock_rw_unlock(&z->lock);
log_err("auth_zones_pickup_zonemd_verify: zone name too long");
continue;
}
savezname_len = z->namelen;
memmove(savezname, z->name, z->namelen);
lock_rw_unlock(&az->lock);
auth_zone_verify_zonemd(z, env, &env->mesh->mods, NULL, 0, 1);
lock_rw_unlock(&z->lock);
lock_rw_rdlock(&az->lock);
/* find the zone we had before, it is not deleted,
* because we have a flag for that that is processed at
* apply_cfg time */
key.namelen = savezname_len;
key.name = savezname;
z = (struct auth_zone*)rbtree_search(&az->ztree, &key);
if(!z)
break;
}
lock_rw_unlock(&az->lock);
}
diff --git a/contrib/unbound/services/listen_dnsport.c b/contrib/unbound/services/listen_dnsport.c
index 60f9b41e5f6c..753550978a07 100644
--- a/contrib/unbound/services/listen_dnsport.c
+++ b/contrib/unbound/services/listen_dnsport.c
@@ -1,3139 +1,3145 @@
/*
* services/listen_dnsport.c - listen on port 53 for incoming DNS queries.
*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file has functions to get queries from clients.
*/
#include "config.h"
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#include <sys/time.h>
#include <limits.h>
#ifdef USE_TCP_FASTOPEN
#include <netinet/tcp.h>
#endif
#include <ctype.h>
#include "services/listen_dnsport.h"
#include "services/outside_network.h"
#include "util/netevent.h"
#include "util/log.h"
#include "util/config_file.h"
#include "util/net_help.h"
#include "sldns/sbuffer.h"
#include "sldns/parseutil.h"
#include "services/mesh.h"
#include "util/fptr_wlist.h"
#include "util/locks.h"
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#include <fcntl.h>
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif
#ifdef HAVE_SYSTEMD
#include <systemd/sd-daemon.h>
#endif
#ifdef HAVE_IFADDRS_H
#include <ifaddrs.h>
#endif
#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
#ifndef THREADS_DISABLED
/** lock on the counter of stream buffer memory */
static lock_basic_type stream_wait_count_lock;
/** lock on the counter of HTTP2 query buffer memory */
static lock_basic_type http2_query_buffer_count_lock;
/** lock on the counter of HTTP2 response buffer memory */
static lock_basic_type http2_response_buffer_count_lock;
#endif
/** size (in bytes) of stream wait buffers */
static size_t stream_wait_count = 0;
/** is the lock initialised for stream wait buffers */
static int stream_wait_lock_inited = 0;
/** size (in bytes) of HTTP2 query buffers */
static size_t http2_query_buffer_count = 0;
/** is the lock initialised for HTTP2 query buffers */
static int http2_query_buffer_lock_inited = 0;
/** size (in bytes) of HTTP2 response buffers */
static size_t http2_response_buffer_count = 0;
/** is the lock initialised for HTTP2 response buffers */
static int http2_response_buffer_lock_inited = 0;
/**
* Debug print of the getaddrinfo returned address.
* @param addr: the address returned.
*/
static void
verbose_print_addr(struct addrinfo *addr)
{
if(verbosity >= VERB_ALGO) {
char buf[100];
void* sinaddr = &((struct sockaddr_in*)addr->ai_addr)->sin_addr;
#ifdef INET6
if(addr->ai_family == AF_INET6)
sinaddr = &((struct sockaddr_in6*)addr->ai_addr)->
sin6_addr;
#endif /* INET6 */
if(inet_ntop(addr->ai_family, sinaddr, buf,
(socklen_t)sizeof(buf)) == 0) {
(void)strlcpy(buf, "(null)", sizeof(buf));
}
buf[sizeof(buf)-1] = 0;
verbose(VERB_ALGO, "creating %s%s socket %s %d",
addr->ai_socktype==SOCK_DGRAM?"udp":
addr->ai_socktype==SOCK_STREAM?"tcp":"otherproto",
addr->ai_family==AF_INET?"4":
addr->ai_family==AF_INET6?"6":
"_otherfam", buf,
ntohs(((struct sockaddr_in*)addr->ai_addr)->sin_port));
}
}
void
verbose_print_unbound_socket(struct unbound_socket* ub_sock)
{
if(verbosity >= VERB_ALGO) {
log_info("listing of unbound_socket structure:");
verbose_print_addr(ub_sock->addr);
log_info("s is: %d, fam is: %s, acl: %s", ub_sock->s,
ub_sock->fam == AF_INET?"AF_INET":"AF_INET6",
ub_sock->acl?"yes":"no");
}
}
#ifdef HAVE_SYSTEMD
static int
systemd_get_activated(int family, int socktype, int listen,
struct sockaddr *addr, socklen_t addrlen,
const char *path)
{
int i = 0;
int r = 0;
int s = -1;
const char* listen_pid, *listen_fds;
/* We should use "listen" option only for stream protocols. For UDP it should be -1 */
if((r = sd_booted()) < 1) {
if(r == 0)
log_warn("systemd is not running");
else
log_err("systemd sd_booted(): %s", strerror(-r));
return -1;
}
listen_pid = getenv("LISTEN_PID");
listen_fds = getenv("LISTEN_FDS");
if (!listen_pid) {
log_warn("Systemd mandatory ENV variable is not defined: LISTEN_PID");
return -1;
}
if (!listen_fds) {
log_warn("Systemd mandatory ENV variable is not defined: LISTEN_FDS");
return -1;
}
if((r = sd_listen_fds(0)) < 1) {
if(r == 0)
log_warn("systemd: did not return socket, check unit configuration");
else
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;
break;
}
}
if (s == -1) {
if (addr)
log_err_addr("systemd sd_listen_fds()",
"no such socket",
(struct sockaddr_storage *)addr, addrlen);
else
log_err("systemd sd_listen_fds(): %s", path);
}
return s;
}
#endif
int
create_udp_sock(int family, int socktype, struct sockaddr* addr,
socklen_t addrlen, int v6only, int* inuse, int* noproto,
int rcv, int snd, int listen, int* reuseport, int transparent,
int freebind, int use_systemd, int dscp)
{
int s;
char* err;
#if defined(SO_REUSEADDR) || defined(SO_REUSEPORT) || defined(IPV6_USE_MIN_MTU) || defined(IP_TRANSPARENT) || defined(IP_BINDANY) || defined(IP_FREEBIND) || defined (SO_BINDANY)
int on=1;
#endif
#ifdef IPV6_MTU
int mtu = IPV6_MIN_MTU;
#endif
#if !defined(SO_RCVBUFFORCE) && !defined(SO_RCVBUF)
(void)rcv;
#endif
#if !defined(SO_SNDBUFFORCE) && !defined(SO_SNDBUF)
(void)snd;
#endif
#ifndef IPV6_V6ONLY
(void)v6only;
#endif
#if !defined(IP_TRANSPARENT) && !defined(IP_BINDANY) && !defined(SO_BINDANY)
(void)transparent;
#endif
#if !defined(IP_FREEBIND)
(void)freebind;
#endif
#ifdef HAVE_SYSTEMD
int got_fd_from_systemd = 0;
if (!use_systemd
|| (use_systemd
&& (s = systemd_get_activated(family, socktype, -1, addr,
addrlen, NULL)) == -1)) {
#else
(void)use_systemd;
#endif
if((s = socket(family, socktype, 0)) == -1) {
*inuse = 0;
#ifndef USE_WINSOCK
if(errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) {
*noproto = 1;
return -1;
}
#else
if(WSAGetLastError() == WSAEAFNOSUPPORT ||
WSAGetLastError() == WSAEPROTONOSUPPORT) {
*noproto = 1;
return -1;
}
#endif
log_err("can't create socket: %s", sock_strerror(errno));
*noproto = 0;
return -1;
}
#ifdef HAVE_SYSTEMD
} else {
got_fd_from_systemd = 1;
}
#endif
if(listen) {
#ifdef SO_REUSEADDR
if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
log_err("setsockopt(.. SO_REUSEADDR ..) failed: %s",
sock_strerror(errno));
#ifndef USE_WINSOCK
if(errno != ENOSYS) {
close(s);
*noproto = 0;
*inuse = 0;
return -1;
}
#else
closesocket(s);
*noproto = 0;
*inuse = 0;
return -1;
#endif
}
#endif /* SO_REUSEADDR */
#ifdef SO_REUSEPORT
# ifdef SO_REUSEPORT_LB
/* on FreeBSD 12 we have SO_REUSEPORT_LB that does loadbalance
* like SO_REUSEPORT on Linux. This is what the users want
* with the config option in unbound.conf; if we actually
* need local address and port reuse they'll also need to
* have SO_REUSEPORT set for them, assume it was _LB they want.
*/
if (reuseport && *reuseport &&
setsockopt(s, SOL_SOCKET, SO_REUSEPORT_LB, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
#ifdef ENOPROTOOPT
if(errno != ENOPROTOOPT || verbosity >= 3)
log_warn("setsockopt(.. SO_REUSEPORT_LB ..) failed: %s",
strerror(errno));
#endif
/* this option is not essential, we can continue */
*reuseport = 0;
}
# else /* no SO_REUSEPORT_LB */
/* try to set SO_REUSEPORT so that incoming
* queries are distributed evenly among the receiving threads.
* Each thread must have its own socket bound to the same port,
* with SO_REUSEPORT set on each socket.
*/
if (reuseport && *reuseport &&
setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
#ifdef ENOPROTOOPT
if(errno != ENOPROTOOPT || verbosity >= 3)
log_warn("setsockopt(.. SO_REUSEPORT ..) failed: %s",
strerror(errno));
#endif
/* this option is not essential, we can continue */
*reuseport = 0;
}
# endif /* SO_REUSEPORT_LB */
#else
(void)reuseport;
#endif /* defined(SO_REUSEPORT) */
#ifdef IP_TRANSPARENT
if (transparent &&
setsockopt(s, IPPROTO_IP, IP_TRANSPARENT, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
log_warn("setsockopt(.. IP_TRANSPARENT ..) failed: %s",
strerror(errno));
}
#elif defined(IP_BINDANY)
if (transparent &&
setsockopt(s, (family==AF_INET6? IPPROTO_IPV6:IPPROTO_IP),
(family == AF_INET6? IPV6_BINDANY:IP_BINDANY),
(void*)&on, (socklen_t)sizeof(on)) < 0) {
log_warn("setsockopt(.. IP%s_BINDANY ..) failed: %s",
(family==AF_INET6?"V6":""), strerror(errno));
}
#elif defined(SO_BINDANY)
if (transparent &&
setsockopt(s, SOL_SOCKET, SO_BINDANY, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
log_warn("setsockopt(.. SO_BINDANY ..) failed: %s",
strerror(errno));
}
#endif /* IP_TRANSPARENT || IP_BINDANY || SO_BINDANY */
}
#ifdef IP_FREEBIND
if(freebind &&
setsockopt(s, IPPROTO_IP, IP_FREEBIND, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
log_warn("setsockopt(.. IP_FREEBIND ..) failed: %s",
strerror(errno));
}
#endif /* IP_FREEBIND */
if(rcv) {
#ifdef SO_RCVBUF
int got;
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
* /proc/sys/net/core/rmem_max or sysctl net.core.rmem_max */
if(setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, (void*)&rcv,
(socklen_t)sizeof(rcv)) < 0) {
if(errno != EPERM) {
log_err("setsockopt(..., SO_RCVBUFFORCE, "
"...) failed: %s", sock_strerror(errno));
sock_close(s);
*noproto = 0;
*inuse = 0;
return -1;
}
# endif /* SO_RCVBUFFORCE */
if(setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*)&rcv,
(socklen_t)sizeof(rcv)) < 0) {
log_err("setsockopt(..., SO_RCVBUF, "
"...) failed: %s", sock_strerror(errno));
sock_close(s);
*noproto = 0;
*inuse = 0;
return -1;
}
/* 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,
&slen) >= 0 && got < rcv/2) {
log_warn("so-rcvbuf %u was not granted. "
"Got %u. To fix: start with "
"root permissions(linux) or sysctl "
"bigger net.core.rmem_max(linux) or "
"kern.ipc.maxsockbuf(bsd) values.",
(unsigned)rcv, (unsigned)got);
}
# ifdef SO_RCVBUFFORCE
}
# endif
#endif /* SO_RCVBUF */
}
/* first do RCVBUF as the receive buffer is more important */
if(snd) {
#ifdef SO_SNDBUF
int got;
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
* /proc/sys/net/core/wmem_max or sysctl net.core.wmem_max */
if(setsockopt(s, SOL_SOCKET, SO_SNDBUFFORCE, (void*)&snd,
(socklen_t)sizeof(snd)) < 0) {
if(errno != EPERM) {
log_err("setsockopt(..., SO_SNDBUFFORCE, "
"...) failed: %s", sock_strerror(errno));
sock_close(s);
*noproto = 0;
*inuse = 0;
return -1;
}
# endif /* SO_SNDBUFFORCE */
if(setsockopt(s, SOL_SOCKET, SO_SNDBUF, (void*)&snd,
(socklen_t)sizeof(snd)) < 0) {
log_err("setsockopt(..., SO_SNDBUF, "
"...) failed: %s", sock_strerror(errno));
sock_close(s);
*noproto = 0;
*inuse = 0;
return -1;
}
/* 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,
&slen) >= 0 && got < snd/2) {
log_warn("so-sndbuf %u was not granted. "
"Got %u. To fix: start with "
"root permissions(linux) or sysctl "
"bigger net.core.wmem_max(linux) or "
"kern.ipc.maxsockbuf(bsd) values.",
(unsigned)snd, (unsigned)got);
}
# ifdef SO_SNDBUFFORCE
}
# endif
#endif /* SO_SNDBUF */
}
err = set_ip_dscp(s, family, dscp);
if(err != NULL)
log_warn("error setting IP DiffServ codepoint %d on UDP socket: %s", dscp, err);
if(family == AF_INET6) {
# if defined(IPV6_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
int omit6_set = 0;
int action;
# endif
# if defined(IPV6_V6ONLY)
if(v6only
# ifdef HAVE_SYSTEMD
/* Systemd wants to control if the socket is v6 only
* or both, with BindIPv6Only=default, ipv6-only or
* both in systemd.socket, so it is not set here. */
&& !got_fd_from_systemd
# endif
) {
int val=(v6only==2)?0:1;
if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
(void*)&val, (socklen_t)sizeof(val)) < 0) {
log_err("setsockopt(..., IPV6_V6ONLY"
", ...) failed: %s", sock_strerror(errno));
sock_close(s);
*noproto = 0;
*inuse = 0;
return -1;
}
}
# endif
# if defined(IPV6_USE_MIN_MTU)
/*
* There is no fragmentation of IPv6 datagrams
* during forwarding in the network. Therefore
* we do not send UDP datagrams larger than
* the minimum IPv6 MTU of 1280 octets. The
* EDNS0 message length can be larger if the
* network stack supports IPV6_USE_MIN_MTU.
*/
if (setsockopt(s, IPPROTO_IPV6, IPV6_USE_MIN_MTU,
(void*)&on, (socklen_t)sizeof(on)) < 0) {
log_err("setsockopt(..., IPV6_USE_MIN_MTU, "
"...) failed: %s", sock_strerror(errno));
sock_close(s);
*noproto = 0;
*inuse = 0;
return -1;
}
# elif defined(IPV6_MTU)
# ifndef USE_WINSOCK
/*
* On Linux, to send no larger than 1280, the PMTUD is
* disabled by default for datagrams anyway, so we set
* the MTU to use.
*/
if (setsockopt(s, IPPROTO_IPV6, IPV6_MTU,
(void*)&mtu, (socklen_t)sizeof(mtu)) < 0) {
log_err("setsockopt(..., IPV6_MTU, ...) failed: %s",
sock_strerror(errno));
sock_close(s);
*noproto = 0;
*inuse = 0;
return -1;
}
# elif defined(IPV6_USER_MTU)
/* As later versions of the mingw crosscompiler define
* IPV6_MTU, do the same for windows but use IPV6_USER_MTU
* instead which is writable; IPV6_MTU is readonly there. */
if (setsockopt(s, IPPROTO_IPV6, IPV6_USER_MTU,
(void*)&mtu, (socklen_t)sizeof(mtu)) < 0) {
if (WSAGetLastError() != WSAENOPROTOOPT) {
log_err("setsockopt(..., IPV6_USER_MTU, ...) failed: %s",
wsa_strerror(WSAGetLastError()));
sock_close(s);
*noproto = 0;
*inuse = 0;
return -1;
}
}
# endif /* USE_WINSOCK */
# endif /* IPv6 MTU */
# if defined(IPV6_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
# if defined(IP_PMTUDISC_OMIT)
action = IP_PMTUDISC_OMIT;
if (setsockopt(s, IPPROTO_IPV6, IPV6_MTU_DISCOVER,
&action, (socklen_t)sizeof(action)) < 0) {
if (errno != EINVAL) {
log_err("setsockopt(..., IPV6_MTU_DISCOVER, IP_PMTUDISC_OMIT...) failed: %s",
strerror(errno));
sock_close(s);
*noproto = 0;
*inuse = 0;
return -1;
}
}
else
{
omit6_set = 1;
}
# endif
if (omit6_set == 0) {
action = IP_PMTUDISC_DONT;
if (setsockopt(s, IPPROTO_IPV6, IPV6_MTU_DISCOVER,
&action, (socklen_t)sizeof(action)) < 0) {
log_err("setsockopt(..., IPV6_MTU_DISCOVER, IP_PMTUDISC_DONT...) failed: %s",
strerror(errno));
sock_close(s);
*noproto = 0;
*inuse = 0;
return -1;
}
}
# endif /* IPV6_MTU_DISCOVER */
} else if(family == AF_INET) {
# if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
/* linux 3.15 has IP_PMTUDISC_OMIT, Hannes Frederic Sowa made it so that
* PMTU information is not accepted, but fragmentation is allowed
* if and only if the packet size exceeds the outgoing interface MTU
* (and also uses the interface mtu to determine the size of the packets).
* So there won't be any EMSGSIZE error. Against DNS fragmentation attacks.
* FreeBSD already has same semantics without setting the option. */
int omit_set = 0;
int action;
# if defined(IP_PMTUDISC_OMIT)
action = IP_PMTUDISC_OMIT;
if (setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER,
&action, (socklen_t)sizeof(action)) < 0) {
if (errno != EINVAL) {
log_err("setsockopt(..., IP_MTU_DISCOVER, IP_PMTUDISC_OMIT...) failed: %s",
strerror(errno));
sock_close(s);
*noproto = 0;
*inuse = 0;
return -1;
}
}
else
{
omit_set = 1;
}
# endif
if (omit_set == 0) {
action = IP_PMTUDISC_DONT;
if (setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER,
&action, (socklen_t)sizeof(action)) < 0) {
log_err("setsockopt(..., IP_MTU_DISCOVER, IP_PMTUDISC_DONT...) failed: %s",
strerror(errno));
sock_close(s);
*noproto = 0;
*inuse = 0;
return -1;
}
}
# elif defined(IP_DONTFRAG) && !defined(__APPLE__)
/* 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,
&off, (socklen_t)sizeof(off)) < 0) {
log_err("setsockopt(..., IP_DONTFRAG, ...) failed: %s",
strerror(errno));
sock_close(s);
*noproto = 0;
*inuse = 0;
return -1;
}
# endif /* IPv4 MTU */
}
if(
#ifdef HAVE_SYSTEMD
!got_fd_from_systemd &&
#endif
bind(s, (struct sockaddr*)addr, addrlen) != 0) {
*noproto = 0;
*inuse = 0;
#ifndef USE_WINSOCK
#ifdef EADDRINUSE
*inuse = (errno == EADDRINUSE);
/* detect freebsd jail with no ipv6 permission */
if(family==AF_INET6 && errno==EINVAL)
*noproto = 1;
else if(errno != EADDRINUSE &&
!(errno == EACCES && verbosity < 4 && !listen)
#ifdef EADDRNOTAVAIL
&& !(errno == EADDRNOTAVAIL && verbosity < 4 && !listen)
#endif
) {
log_err_addr("can't bind socket", strerror(errno),
(struct sockaddr_storage*)addr, addrlen);
}
#endif /* EADDRINUSE */
#else /* USE_WINSOCK */
if(WSAGetLastError() != WSAEADDRINUSE &&
WSAGetLastError() != WSAEADDRNOTAVAIL &&
!(WSAGetLastError() == WSAEACCES && verbosity < 4 && !listen)) {
log_err_addr("can't bind socket",
wsa_strerror(WSAGetLastError()),
(struct sockaddr_storage*)addr, addrlen);
}
#endif /* USE_WINSOCK */
sock_close(s);
return -1;
}
if(!fd_set_nonblock(s)) {
*noproto = 0;
*inuse = 0;
sock_close(s);
return -1;
}
return s;
}
int
create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto,
int* reuseport, int transparent, int mss, int nodelay, int freebind,
int use_systemd, int dscp)
{
int s;
char* err;
#if defined(SO_REUSEADDR) || defined(SO_REUSEPORT) || defined(IPV6_V6ONLY) || defined(IP_TRANSPARENT) || defined(IP_BINDANY) || defined(IP_FREEBIND) || defined(SO_BINDANY)
int on = 1;
#endif
#ifdef HAVE_SYSTEMD
int got_fd_from_systemd = 0;
#endif
#ifdef USE_TCP_FASTOPEN
int qlen;
#endif
#if !defined(IP_TRANSPARENT) && !defined(IP_BINDANY) && !defined(SO_BINDANY)
(void)transparent;
#endif
#if !defined(IP_FREEBIND)
(void)freebind;
#endif
verbose_print_addr(addr);
*noproto = 0;
#ifdef HAVE_SYSTEMD
if (!use_systemd ||
(use_systemd
&& (s = systemd_get_activated(addr->ai_family, addr->ai_socktype, 1,
addr->ai_addr, addr->ai_addrlen,
NULL)) == -1)) {
#else
(void)use_systemd;
#endif
if((s = socket(addr->ai_family, addr->ai_socktype, 0)) == -1) {
#ifndef USE_WINSOCK
if(errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) {
*noproto = 1;
return -1;
}
#else
if(WSAGetLastError() == WSAEAFNOSUPPORT ||
WSAGetLastError() == WSAEPROTONOSUPPORT) {
*noproto = 1;
return -1;
}
#endif
log_err("can't create socket: %s", sock_strerror(errno));
return -1;
}
if(nodelay) {
#if defined(IPPROTO_TCP) && defined(TCP_NODELAY)
if(setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
#ifndef USE_WINSOCK
log_err(" setsockopt(.. TCP_NODELAY ..) failed: %s",
strerror(errno));
#else
log_err(" setsockopt(.. TCP_NODELAY ..) failed: %s",
wsa_strerror(WSAGetLastError()));
#endif
}
#else
log_warn(" setsockopt(TCP_NODELAY) unsupported");
#endif /* defined(IPPROTO_TCP) && defined(TCP_NODELAY) */
}
if (mss > 0) {
#if defined(IPPROTO_TCP) && defined(TCP_MAXSEG)
if(setsockopt(s, IPPROTO_TCP, TCP_MAXSEG, (void*)&mss,
(socklen_t)sizeof(mss)) < 0) {
log_err(" setsockopt(.. TCP_MAXSEG ..) failed: %s",
sock_strerror(errno));
} else {
verbose(VERB_ALGO,
" tcp socket mss set to %d", mss);
}
#else
log_warn(" setsockopt(TCP_MAXSEG) unsupported");
#endif /* defined(IPPROTO_TCP) && defined(TCP_MAXSEG) */
}
#ifdef HAVE_SYSTEMD
} else {
got_fd_from_systemd = 1;
}
#endif
#ifdef SO_REUSEADDR
if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
log_err("setsockopt(.. SO_REUSEADDR ..) failed: %s",
sock_strerror(errno));
sock_close(s);
return -1;
}
#endif /* SO_REUSEADDR */
#ifdef IP_FREEBIND
if (freebind && setsockopt(s, IPPROTO_IP, IP_FREEBIND, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
log_warn("setsockopt(.. IP_FREEBIND ..) failed: %s",
strerror(errno));
}
#endif /* IP_FREEBIND */
#ifdef SO_REUSEPORT
/* try to set SO_REUSEPORT so that incoming
* connections are distributed evenly among the receiving threads.
* Each thread must have its own socket bound to the same port,
* with SO_REUSEPORT set on each socket.
*/
if (reuseport && *reuseport &&
setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
#ifdef ENOPROTOOPT
if(errno != ENOPROTOOPT || verbosity >= 3)
log_warn("setsockopt(.. SO_REUSEPORT ..) failed: %s",
strerror(errno));
#endif
/* this option is not essential, we can continue */
*reuseport = 0;
}
#else
(void)reuseport;
#endif /* defined(SO_REUSEPORT) */
#if defined(IPV6_V6ONLY)
if(addr->ai_family == AF_INET6 && v6only
# ifdef HAVE_SYSTEMD
/* Systemd wants to control if the socket is v6 only
* or both, with BindIPv6Only=default, ipv6-only or
* both in systemd.socket, so it is not set here. */
&& !got_fd_from_systemd
# endif
) {
if(setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
(void*)&on, (socklen_t)sizeof(on)) < 0) {
log_err("setsockopt(..., IPV6_V6ONLY, ...) failed: %s",
sock_strerror(errno));
sock_close(s);
return -1;
}
}
#else
(void)v6only;
#endif /* IPV6_V6ONLY */
#ifdef IP_TRANSPARENT
if (transparent &&
setsockopt(s, IPPROTO_IP, IP_TRANSPARENT, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
log_warn("setsockopt(.. IP_TRANSPARENT ..) failed: %s",
strerror(errno));
}
#elif defined(IP_BINDANY)
if (transparent &&
setsockopt(s, (addr->ai_family==AF_INET6? IPPROTO_IPV6:IPPROTO_IP),
(addr->ai_family == AF_INET6? IPV6_BINDANY:IP_BINDANY),
(void*)&on, (socklen_t)sizeof(on)) < 0) {
log_warn("setsockopt(.. IP%s_BINDANY ..) failed: %s",
(addr->ai_family==AF_INET6?"V6":""), strerror(errno));
}
#elif defined(SO_BINDANY)
if (transparent &&
setsockopt(s, SOL_SOCKET, SO_BINDANY, (void*)&on, (socklen_t)
sizeof(on)) < 0) {
log_warn("setsockopt(.. SO_BINDANY ..) failed: %s",
strerror(errno));
}
#endif /* IP_TRANSPARENT || IP_BINDANY || SO_BINDANY */
err = set_ip_dscp(s, addr->ai_family, dscp);
if(err != NULL)
log_warn("error setting IP DiffServ codepoint %d on TCP socket: %s", dscp, err);
if(
#ifdef HAVE_SYSTEMD
!got_fd_from_systemd &&
#endif
bind(s, addr->ai_addr, addr->ai_addrlen) != 0) {
#ifndef USE_WINSOCK
/* detect freebsd jail with no ipv6 permission */
if(addr->ai_family==AF_INET6 && errno==EINVAL)
*noproto = 1;
else {
log_err_addr("can't bind socket", strerror(errno),
(struct sockaddr_storage*)addr->ai_addr,
addr->ai_addrlen);
}
#else
log_err_addr("can't bind socket",
wsa_strerror(WSAGetLastError()),
(struct sockaddr_storage*)addr->ai_addr,
addr->ai_addrlen);
#endif
sock_close(s);
return -1;
}
if(!fd_set_nonblock(s)) {
sock_close(s);
return -1;
}
if(listen(s, TCP_BACKLOG) == -1) {
log_err("can't listen: %s", sock_strerror(errno));
sock_close(s);
return -1;
}
#ifdef USE_TCP_FASTOPEN
/* qlen specifies how many outstanding TFO requests to allow. Limit is a defense
against IP spoofing attacks as suggested in RFC7413 */
#ifdef __APPLE__
/* OS X implementation only supports qlen of 1 via this call. Actual
value is configured by the net.inet.tcp.fastopen_backlog kernel parm. */
qlen = 1;
#else
/* 5 is recommended on linux */
qlen = 5;
#endif
if ((setsockopt(s, IPPROTO_TCP, TCP_FASTOPEN, &qlen,
sizeof(qlen))) == -1 ) {
#ifdef ENOPROTOOPT
/* squelch ENOPROTOOPT: freebsd server mode with kernel support
disabled, except when verbosity enabled for debugging */
if(errno != ENOPROTOOPT || verbosity >= 3) {
#endif
if(errno == EPERM) {
log_warn("Setting TCP Fast Open as server failed: %s ; this could likely be because sysctl net.inet.tcp.fastopen.enabled, net.inet.tcp.fastopen.server_enable, or net.ipv4.tcp_fastopen is disabled", strerror(errno));
} else {
log_err("Setting TCP Fast Open as server failed: %s", strerror(errno));
}
#ifdef ENOPROTOOPT
}
#endif
}
#endif
return s;
}
char*
set_ip_dscp(int socket, int addrfamily, int dscp)
{
int ds;
if(dscp == 0)
return NULL;
ds = dscp << 2;
switch(addrfamily) {
case AF_INET6:
#ifdef IPV6_TCLASS
if(setsockopt(socket, IPPROTO_IPV6, IPV6_TCLASS, (void*)&ds,
sizeof(ds)) < 0)
return sock_strerror(errno);
break;
#else
return "IPV6_TCLASS not defined on this system";
#endif
default:
if(setsockopt(socket, IPPROTO_IP, IP_TOS, (void*)&ds, sizeof(ds)) < 0)
return sock_strerror(errno);
break;
}
return NULL;
}
int
create_local_accept_sock(const char *path, int* noproto, int use_systemd)
{
#ifdef HAVE_SYSTEMD
int ret;
if (use_systemd && (ret = systemd_get_activated(AF_LOCAL, SOCK_STREAM, 1, NULL, 0, path)) != -1)
return ret;
else {
#endif
#ifdef HAVE_SYS_UN_H
int s;
struct sockaddr_un usock;
#ifndef HAVE_SYSTEMD
(void)use_systemd;
#endif
verbose(VERB_ALGO, "creating unix socket %s", path);
#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
/* this member exists on BSDs, not Linux */
usock.sun_len = (unsigned)sizeof(usock);
#endif
usock.sun_family = AF_LOCAL;
/* length is 92-108, 104 on FreeBSD */
(void)strlcpy(usock.sun_path, path, sizeof(usock.sun_path));
if ((s = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1) {
log_err("Cannot create local socket %s (%s)",
path, strerror(errno));
return -1;
}
if (unlink(path) && errno != ENOENT) {
/* The socket already exists and cannot be removed */
log_err("Cannot remove old local socket %s (%s)",
path, strerror(errno));
goto err;
}
if (bind(s, (struct sockaddr *)&usock,
(socklen_t)sizeof(struct sockaddr_un)) == -1) {
log_err("Cannot bind local socket %s (%s)",
path, strerror(errno));
goto err;
}
if (!fd_set_nonblock(s)) {
log_err("Cannot set non-blocking mode");
goto err;
}
if (listen(s, TCP_BACKLOG) == -1) {
log_err("can't listen: %s", strerror(errno));
goto err;
}
(void)noproto; /*unused*/
return s;
err:
sock_close(s);
return -1;
#ifdef HAVE_SYSTEMD
}
#endif
#else
(void)use_systemd;
(void)path;
log_err("Local sockets are not supported");
*noproto = 1;
return -1;
#endif
}
/**
* Create socket from getaddrinfo results
*/
static int
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)
{
struct addrinfo *res = NULL;
int r, s, inuse, noproto;
hints->ai_socktype = stype;
*noip6 = 0;
if((r=getaddrinfo(ifname, port, hints, &res)) != 0 || !res) {
#ifdef USE_WINSOCK
if(r == EAI_NONAME && hints->ai_family == AF_INET6){
*noip6 = 1; /* 'Host not found' for IP6 on winXP */
return -1;
}
#endif
log_err("node %s:%s getaddrinfo: %s %s",
ifname?ifname:"default", port, gai_strerror(r),
#ifdef EAI_SYSTEM
(r==EAI_SYSTEM?(char*)strerror(errno):"")
#else
""
#endif
);
return -1;
}
if(stype == SOCK_DGRAM) {
verbose_print_addr(res);
s = create_udp_sock(res->ai_family, res->ai_socktype,
(struct sockaddr*)res->ai_addr, res->ai_addrlen,
v6only, &inuse, &noproto, (int)rcv, (int)snd, 1,
reuseport, transparent, freebind, use_systemd, dscp);
if(s == -1 && inuse) {
log_err("bind: address already in use");
} else if(s == -1 && noproto && hints->ai_family == AF_INET6){
*noip6 = 1;
}
} else {
s = create_tcp_accept_sock(res, v6only, &noproto, reuseport,
transparent, tcp_mss, nodelay, freebind, use_systemd,
dscp);
if(s == -1 && noproto && hints->ai_family == AF_INET6){
*noip6 = 1;
}
}
ub_sock->addr = res;
ub_sock->s = s;
ub_sock->fam = hints->ai_family;
ub_sock->acl = NULL;
return s;
}
/** make socket and first see if ifname contains port override info */
static int
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)
{
char* s = strchr(ifname, '@');
if(s) {
/* override port with ifspec@port */
char p[16];
char newif[128];
if((size_t)(s-ifname) >= sizeof(newif)) {
log_err("ifname too long: %s", ifname);
*noip6 = 0;
return -1;
}
if(strlen(s+1) >= sizeof(p)) {
log_err("portnumber too long: %s", ifname);
*noip6 = 0;
return -1;
}
(void)strlcpy(newif, ifname, sizeof(newif));
newif[s-ifname] = 0;
(void)strlcpy(p, s+1, sizeof(p));
p[strlen(s+1)]=0;
return make_sock(stype, newif, p, hints, v6only, noip6, rcv,
snd, reuseport, transparent, tcp_mss, nodelay, freebind,
use_systemd, dscp, ub_sock);
}
return make_sock(stype, ifname, port, hints, v6only, noip6, rcv, snd,
reuseport, transparent, tcp_mss, nodelay, freebind, use_systemd,
dscp, ub_sock);
}
/**
* Add port to open ports list.
* @param list: list head. changed.
* @param s: fd.
* @param ftype: if fd is UDP.
* @param pp2_enabled: if PROXYv2 is enabled for this port.
* @param ub_sock: socket with address.
* @return false on failure. list in unchanged then.
*/
static int
port_insert(struct listen_port** list, int s, enum listen_type ftype,
int pp2_enabled, struct unbound_socket* ub_sock)
{
struct listen_port* item = (struct listen_port*)malloc(
sizeof(struct listen_port));
if(!item)
return 0;
item->next = *list;
item->fd = s;
item->ftype = ftype;
item->pp2_enabled = pp2_enabled;
item->socket = ub_sock;
*list = item;
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)
{
#if defined(IPV6_RECVPKTINFO) || defined(IPV6_PKTINFO) || (defined(IP_RECVDSTADDR) && defined(IP_SENDSRCADDR)) || defined(IP_PKTINFO)
int on = 1;
#else
(void)s;
#endif
if(family == AF_INET6) {
# ifdef IPV6_RECVPKTINFO
if(setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO,
(void*)&on, (socklen_t)sizeof(on)) < 0) {
log_err("setsockopt(..., IPV6_RECVPKTINFO, ...) failed: %s",
strerror(errno));
return 0;
}
# elif defined(IPV6_PKTINFO)
if(setsockopt(s, IPPROTO_IPV6, IPV6_PKTINFO,
(void*)&on, (socklen_t)sizeof(on)) < 0) {
log_err("setsockopt(..., IPV6_PKTINFO, ...) failed: %s",
strerror(errno));
return 0;
}
# else
log_err("no IPV6_RECVPKTINFO and IPV6_PKTINFO options, please "
"disable interface-automatic or do-ip6 in config");
return 0;
# endif /* defined IPV6_RECVPKTINFO */
} else if(family == AF_INET) {
# ifdef IP_PKTINFO
if(setsockopt(s, IPPROTO_IP, IP_PKTINFO,
(void*)&on, (socklen_t)sizeof(on)) < 0) {
log_err("setsockopt(..., IP_PKTINFO, ...) failed: %s",
strerror(errno));
return 0;
}
# elif defined(IP_RECVDSTADDR) && defined(IP_SENDSRCADDR)
if(setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR,
(void*)&on, (socklen_t)sizeof(on)) < 0) {
log_err("setsockopt(..., IP_RECVDSTADDR, ...) failed: %s",
strerror(errno));
return 0;
}
# else
log_err("no IP_SENDSRCADDR or IP_PKTINFO option, please disable "
"interface-automatic or do-ip4 in config");
return 0;
# endif /* IP_PKTINFO */
}
return 1;
}
/** see if interface is ssl, its port number == the ssl port number */
static int
if_is_ssl(const char* ifname, const char* port, int ssl_port,
struct config_strlist* tls_additional_port)
{
struct config_strlist* s;
char* p = strchr(ifname, '@');
if(!p && atoi(port) == ssl_port)
return 1;
if(p && atoi(p+1) == ssl_port)
return 1;
for(s = tls_additional_port; s; s = s->next) {
if(p && atoi(p+1) == atoi(s->str))
return 1;
if(!p && atoi(port) == atoi(s->str))
return 1;
}
return 0;
}
/**
* Helper for ports_open. Creates one interface (or NULL for default).
* @param ifname: The interface ip address.
* @param do_auto: use automatic interface detection.
* If enabled, then ifname must be the wildcard name.
* @param do_udp: if udp should be used.
* @param do_tcp: if tcp should be used.
* @param hints: for getaddrinfo. family and flags have to be set by caller.
* @param port: Port number to use (as string).
* @param list: list of open ports, appended to, changed to point to list head.
* @param rcv: receive buffer size for UDP
* @param snd: send buffer size for UDP
* @param ssl_port: ssl service port number
* @param tls_additional_port: list of additional ssl service port numbers.
* @param https_port: DoH service port number
* @param proxy_protocol_port: list of PROXYv2 port numbers.
* @param reuseport: try to set SO_REUSEPORT if nonNULL and true.
* set to false on exit if reuseport failed due to no kernel support.
* @param transparent: set IP_TRANSPARENT socket option.
* @param tcp_mss: maximum segment size of tcp socket. default if zero.
* @param freebind: set IP_FREEBIND socket option.
* @param http2_nodelay: set TCP_NODELAY on HTTP/2 connection
* @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
ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
struct addrinfo *hints, const char* port, struct listen_port** list,
size_t rcv, size_t snd, int ssl_port,
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 sock_queue_timeout)
{
int s, noip6=0;
int is_https = if_is_https(ifname, port, https_port);
int is_dnscrypt = if_is_dnscrypt(ifname, port, dnscrypt_port);
int is_pp2 = if_is_pp2(ifname, port, proxy_protocol_port);
int nodelay = is_https && http2_nodelay;
struct unbound_socket* ub_sock;
if(!do_udp && !do_tcp)
return 0;
if(is_pp2) {
if(is_dnscrypt) {
fatal_exit("PROXYv2 and DNSCrypt combination not "
"supported!");
} else if(is_https) {
fatal_exit("PROXYv2 and DoH combination not "
"supported!");
}
}
if(do_auto) {
ub_sock = calloc(1, sizeof(struct unbound_socket));
if(!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) {
if(ub_sock->addr)
freeaddrinfo(ub_sock->addr);
free(ub_sock);
if(noip6) {
log_warn("IPv6 protocol not available");
return 1;
}
return 0;
}
/* getting source addr packet info is highly non-portable */
if(!set_recvpktinfo(s, hints->ai_family)) {
sock_close(s);
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);
if(ub_sock->addr)
freeaddrinfo(ub_sock->addr);
free(ub_sock);
return 0;
}
} else if(do_udp) {
ub_sock = calloc(1, sizeof(struct unbound_socket));
if(!ub_sock)
return 0;
/* regular udp socket */
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) {
if(ub_sock->addr)
freeaddrinfo(ub_sock->addr);
free(ub_sock);
if(noip6) {
log_warn("IPv6 protocol not available");
return 1;
}
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,
+ ?listen_type_udp_dnscrypt :
+ (sock_queue_timeout ?
+ listen_type_udpancil:listen_type_udp),
is_pp2, ub_sock)) {
sock_close(s);
if(ub_sock->addr)
freeaddrinfo(ub_sock->addr);
free(ub_sock);
return 0;
}
}
if(do_tcp) {
int is_ssl = if_is_ssl(ifname, port, ssl_port,
tls_additional_port);
enum listen_type port_type;
ub_sock = calloc(1, sizeof(struct unbound_socket));
if(!ub_sock)
return 0;
if(is_ssl)
port_type = listen_type_ssl;
else if(is_https)
port_type = listen_type_http;
else if(is_dnscrypt)
port_type = listen_type_tcp_dnscrypt;
else
port_type = listen_type_tcp;
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) {
if(ub_sock->addr)
freeaddrinfo(ub_sock->addr);
free(ub_sock);
if(noip6) {
/*log_warn("IPv6 protocol not available");*/
return 1;
}
return 0;
}
if(is_ssl)
verbose(VERB_ALGO, "setup TCP for SSL service");
if(!port_insert(list, s, port_type, is_pp2, ub_sock)) {
sock_close(s);
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.
* @return: false on failure.
*/
static int
listen_cp_insert(struct comm_point* c, struct listen_dnsport* front)
{
struct listen_list* item = (struct listen_list*)malloc(
sizeof(struct listen_list));
if(!item)
return 0;
item->com = c;
item->next = front->cps;
front->cps = item;
return 1;
}
void listen_setup_locks(void)
{
if(!stream_wait_lock_inited) {
lock_basic_init(&stream_wait_count_lock);
stream_wait_lock_inited = 1;
}
if(!http2_query_buffer_lock_inited) {
lock_basic_init(&http2_query_buffer_count_lock);
http2_query_buffer_lock_inited = 1;
}
if(!http2_response_buffer_lock_inited) {
lock_basic_init(&http2_response_buffer_count_lock);
http2_response_buffer_lock_inited = 1;
}
}
void listen_desetup_locks(void)
{
if(stream_wait_lock_inited) {
stream_wait_lock_inited = 0;
lock_basic_destroy(&stream_wait_count_lock);
}
if(http2_query_buffer_lock_inited) {
http2_query_buffer_lock_inited = 0;
lock_basic_destroy(&http2_query_buffer_count_lock);
}
if(http2_response_buffer_lock_inited) {
http2_response_buffer_lock_inited = 0;
lock_basic_destroy(&http2_response_buffer_count_lock);
}
}
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,
char* http_endpoint, int http_notls, struct tcl_list* tcp_conn_limit,
void* sslctx, struct dt_env* dtenv, comm_point_callback_type* cb,
void *cb_arg)
{
struct listen_dnsport* front = (struct listen_dnsport*)
malloc(sizeof(struct listen_dnsport));
if(!front)
return NULL;
front->cps = NULL;
front->udp_buff = sldns_buffer_new(bufsize);
#ifdef USE_DNSCRYPT
front->dnscrypt_udp_buff = NULL;
#endif
if(!front->udp_buff) {
free(front);
return NULL;
}
/* create comm points as needed */
while(ports) {
struct comm_point* cp = NULL;
if(ports->ftype == listen_type_udp ||
ports->ftype == listen_type_udp_dnscrypt) {
cp = comm_point_create_udp(base, ports->fd,
front->udp_buff, ports->pp2_enabled, cb,
cb_arg, ports->socket);
} else if(ports->ftype == listen_type_tcp ||
ports->ftype == listen_type_tcp_dnscrypt) {
cp = comm_point_create_tcp(base, ports->fd,
tcp_accept_count, tcp_idle_timeout,
harden_large_queries, 0, NULL,
tcp_conn_limit, bufsize, front->udp_buff,
ports->ftype, ports->pp2_enabled, cb, cb_arg,
ports->socket);
} else if(ports->ftype == listen_type_ssl ||
ports->ftype == listen_type_http) {
cp = comm_point_create_tcp(base, ports->fd,
tcp_accept_count, tcp_idle_timeout,
harden_large_queries,
http_max_streams, http_endpoint,
tcp_conn_limit, bufsize, front->udp_buff,
ports->ftype, ports->pp2_enabled, cb, cb_arg,
ports->socket);
if(ports->ftype == listen_type_http) {
if(!sslctx && !http_notls) {
log_warn("HTTPS port configured, but "
"no TLS tls-service-key or "
"tls-service-pem set");
}
#ifndef HAVE_SSL_CTX_SET_ALPN_SELECT_CB
if(!http_notls) {
log_warn("Unbound is not compiled "
"with an OpenSSL version "
"supporting ALPN "
"(OpenSSL >= 1.0.2). This "
"is required to use "
"DNS-over-HTTPS");
}
#endif
#ifndef HAVE_NGHTTP2_NGHTTP2_H
log_warn("Unbound is not compiled with "
"nghttp2. This is required to use "
"DNS-over-HTTPS.");
#endif
}
} else if(ports->ftype == listen_type_udpancil ||
ports->ftype == listen_type_udpancil_dnscrypt) {
+#if defined(AF_INET6) && defined(IPV6_PKTINFO) && defined(HAVE_RECVMSG)
cp = comm_point_create_udp_ancil(base, ports->fd,
front->udp_buff, ports->pp2_enabled, cb,
cb_arg, ports->socket);
+#else
+ log_warn("This system does not support UDP ancilliary data.");
+#endif
}
if(!cp) {
log_err("can't create commpoint");
listen_delete(front);
return NULL;
}
if((http_notls && ports->ftype == listen_type_http) ||
(ports->ftype == listen_type_tcp) ||
(ports->ftype == listen_type_udp) ||
(ports->ftype == listen_type_udpancil) ||
(ports->ftype == listen_type_tcp_dnscrypt) ||
(ports->ftype == listen_type_udp_dnscrypt) ||
(ports->ftype == listen_type_udpancil_dnscrypt))
cp->ssl = NULL;
else
cp->ssl = sslctx;
cp->dtenv = dtenv;
cp->do_not_close = 1;
#ifdef USE_DNSCRYPT
if (ports->ftype == listen_type_udp_dnscrypt ||
ports->ftype == listen_type_tcp_dnscrypt ||
ports->ftype == listen_type_udpancil_dnscrypt) {
cp->dnscrypt = 1;
cp->dnscrypt_buffer = sldns_buffer_new(bufsize);
if(!cp->dnscrypt_buffer) {
log_err("can't alloc dnscrypt_buffer");
comm_point_delete(cp);
listen_delete(front);
return NULL;
}
front->dnscrypt_udp_buff = cp->dnscrypt_buffer;
}
#endif
if(!listen_cp_insert(cp, front)) {
log_err("malloc failed");
comm_point_delete(cp);
listen_delete(front);
return NULL;
}
ports = ports->next;
}
if(!front->cps) {
log_err("Could not open sockets to accept queries.");
listen_delete(front);
return NULL;
}
return front;
}
void
listen_list_delete(struct listen_list* list)
{
struct listen_list *p = list, *pn;
while(p) {
pn = p->next;
comm_point_delete(p->com);
free(p);
p = pn;
}
}
void
listen_delete(struct listen_dnsport* front)
{
if(!front)
return;
listen_list_delete(front->cps);
#ifdef USE_DNSCRYPT
if(front->dnscrypt_udp_buff &&
front->udp_buff != front->dnscrypt_udp_buff) {
sldns_buffer_free(front->dnscrypt_udp_buff);
}
#endif
sldns_buffer_free(front->udp_buff);
free(front);
}
#ifdef HAVE_GETIFADDRS
static int
resolve_ifa_name(struct ifaddrs *ifas, const char *search_ifa, char ***ip_addresses, int *ip_addresses_size)
{
struct ifaddrs *ifa;
void *tmpbuf;
int last_ip_addresses_size = *ip_addresses_size;
for(ifa = ifas; ifa != NULL; ifa = ifa->ifa_next) {
sa_family_t family;
const char* atsign;
#ifdef INET6 /* | address ip | % | ifa name | @ | port | nul */
char addr_buf[INET6_ADDRSTRLEN + 1 + IF_NAMESIZE + 1 + 16 + 1];
#else
char addr_buf[INET_ADDRSTRLEN + 1 + 16 + 1];
#endif
if((atsign=strrchr(search_ifa, '@')) != NULL) {
if(strlen(ifa->ifa_name) != (size_t)(atsign-search_ifa)
|| strncmp(ifa->ifa_name, search_ifa,
atsign-search_ifa) != 0)
continue;
} else {
if(strcmp(ifa->ifa_name, search_ifa) != 0)
continue;
atsign = "";
}
if(ifa->ifa_addr == NULL)
continue;
family = ifa->ifa_addr->sa_family;
if(family == AF_INET) {
char a4[INET_ADDRSTRLEN + 1];
struct sockaddr_in *in4 = (struct sockaddr_in *)
ifa->ifa_addr;
if(!inet_ntop(family, &in4->sin_addr, a4, sizeof(a4))) {
log_err("inet_ntop failed");
return 0;
}
snprintf(addr_buf, sizeof(addr_buf), "%s%s",
a4, atsign);
}
#ifdef INET6
else if(family == AF_INET6) {
struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)
ifa->ifa_addr;
char a6[INET6_ADDRSTRLEN + 1];
char if_index_name[IF_NAMESIZE + 1];
if_index_name[0] = 0;
if(!inet_ntop(family, &in6->sin6_addr, a6, sizeof(a6))) {
log_err("inet_ntop failed");
return 0;
}
(void)if_indextoname(in6->sin6_scope_id,
(char *)if_index_name);
if (strlen(if_index_name) != 0) {
snprintf(addr_buf, sizeof(addr_buf),
"%s%%%s%s", a6, if_index_name, atsign);
} else {
snprintf(addr_buf, sizeof(addr_buf), "%s%s",
a6, atsign);
}
}
#endif
else {
continue;
}
verbose(4, "interface %s has address %s", search_ifa, addr_buf);
tmpbuf = realloc(*ip_addresses, sizeof(char *) * (*ip_addresses_size + 1));
if(!tmpbuf) {
log_err("realloc failed: out of memory");
return 0;
} else {
*ip_addresses = tmpbuf;
}
(*ip_addresses)[*ip_addresses_size] = strdup(addr_buf);
if(!(*ip_addresses)[*ip_addresses_size]) {
log_err("strdup failed: out of memory");
return 0;
}
(*ip_addresses_size)++;
}
if (*ip_addresses_size == last_ip_addresses_size) {
tmpbuf = realloc(*ip_addresses, sizeof(char *) * (*ip_addresses_size + 1));
if(!tmpbuf) {
log_err("realloc failed: out of memory");
return 0;
} else {
*ip_addresses = tmpbuf;
}
(*ip_addresses)[*ip_addresses_size] = strdup(search_ifa);
if(!(*ip_addresses)[*ip_addresses_size]) {
log_err("strdup failed: out of memory");
return 0;
}
(*ip_addresses_size)++;
}
return 1;
}
#endif /* HAVE_GETIFADDRS */
int resolve_interface_names(char** ifs, int num_ifs,
struct config_strlist* list, char*** resif, int* num_resif)
{
#ifdef HAVE_GETIFADDRS
struct ifaddrs *addrs = NULL;
if(num_ifs == 0 && list == NULL) {
*resif = NULL;
*num_resif = 0;
return 1;
}
if(getifaddrs(&addrs) == -1) {
log_err("failed to list interfaces: getifaddrs: %s",
strerror(errno));
freeifaddrs(addrs);
return 0;
}
if(ifs) {
int i;
for(i=0; i<num_ifs; i++) {
if(!resolve_ifa_name(addrs, ifs[i], resif, num_resif)) {
freeifaddrs(addrs);
config_del_strarray(*resif, *num_resif);
*resif = NULL;
*num_resif = 0;
return 0;
}
}
}
if(list) {
struct config_strlist* p;
for(p = list; p; p = p->next) {
if(!resolve_ifa_name(addrs, p->str, resif, num_resif)) {
freeifaddrs(addrs);
config_del_strarray(*resif, *num_resif);
*resif = NULL;
*num_resif = 0;
return 0;
}
}
}
freeifaddrs(addrs);
return 1;
#else
struct config_strlist* p;
if(num_ifs == 0 && list == NULL) {
*resif = NULL;
*num_resif = 0;
return 1;
}
*num_resif = num_ifs;
for(p = list; p; p = p->next) {
(*num_resif)++;
}
*resif = calloc(*num_resif, sizeof(**resif));
if(!*resif) {
log_err("out of memory");
return 0;
}
if(ifs) {
int i;
for(i=0; i<num_ifs; i++) {
(*resif)[i] = strdup(ifs[i]);
if(!((*resif)[i])) {
log_err("out of memory");
config_del_strarray(*resif, *num_resif);
*resif = NULL;
*num_resif = 0;
return 0;
}
}
}
if(list) {
int idx = num_ifs;
for(p = list; p; p = p->next) {
(*resif)[idx] = strdup(p->str);
if(!((*resif)[idx])) {
log_err("out of memory");
config_del_strarray(*resif, *num_resif);
*resif = NULL;
*num_resif = 0;
return 0;
}
idx++;
}
}
return 1;
#endif /* HAVE_GETIFADDRS */
}
struct listen_port*
listening_ports_open(struct config_file* cfg, char** ifs, int num_ifs,
int* reuseport)
{
struct listen_port* list = NULL;
struct addrinfo hints;
int i, do_ip4, do_ip6;
int do_tcp, do_auto;
char portbuf[32];
snprintf(portbuf, sizeof(portbuf), "%d", cfg->port);
do_ip4 = cfg->do_ip4;
do_ip6 = cfg->do_ip6;
do_tcp = cfg->do_tcp;
do_auto = cfg->if_automatic && cfg->do_udp;
if(cfg->incoming_num_tcp == 0)
do_tcp = 0;
/* getaddrinfo */
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_PASSIVE;
/* no name lookups on our listening ports */
if(num_ifs > 0)
hints.ai_flags |= AI_NUMERICHOST;
hints.ai_family = AF_UNSPEC;
#ifndef INET6
do_ip6 = 0;
#endif
if(!do_ip4 && !do_ip6) {
return NULL;
}
/* create ip4 and ip6 ports so that return addresses are nice. */
if(do_auto || num_ifs == 0) {
if(do_auto && cfg->if_automatic_ports &&
cfg->if_automatic_ports[0]!=0) {
char* now = cfg->if_automatic_ports;
while(now && *now) {
char* after;
int extraport;
while(isspace((unsigned char)*now))
now++;
if(!*now)
break;
after = now;
extraport = (int)strtol(now, &after, 10);
if(extraport < 0 || extraport > 65535) {
log_err("interface-automatic-ports port number out of range, at position %d of '%s'", (int)(now-cfg->if_automatic_ports)+1, cfg->if_automatic_ports);
listening_ports_free(list);
return NULL;
}
if(extraport == 0 && now == after) {
log_err("interface-automatic-ports could not be parsed, at position %d of '%s'", (int)(now-cfg->if_automatic_ports)+1, cfg->if_automatic_ports);
listening_ports_free(list);
return NULL;
}
now = after;
snprintf(portbuf, sizeof(portbuf), "%d", extraport);
if(do_ip6) {
hints.ai_family = AF_INET6;
if(!ports_create_if("::0",
do_auto, cfg->do_udp, do_tcp,
&hints, portbuf, &list,
cfg->so_rcvbuf, cfg->so_sndbuf,
cfg->ssl_port, cfg->tls_additional_port,
cfg->https_port,
cfg->proxy_protocol_port,
reuseport, cfg->ip_transparent,
cfg->tcp_mss, cfg->ip_freebind,
cfg->http_nodelay, cfg->use_systemd,
cfg->dnscrypt_port, cfg->ip_dscp, cfg->sock_queue_timeout)) {
listening_ports_free(list);
return NULL;
}
}
if(do_ip4) {
hints.ai_family = AF_INET;
if(!ports_create_if("0.0.0.0",
do_auto, cfg->do_udp, do_tcp,
&hints, portbuf, &list,
cfg->so_rcvbuf, cfg->so_sndbuf,
cfg->ssl_port, cfg->tls_additional_port,
cfg->https_port,
cfg->proxy_protocol_port,
reuseport, cfg->ip_transparent,
cfg->tcp_mss, cfg->ip_freebind,
cfg->http_nodelay, cfg->use_systemd,
cfg->dnscrypt_port, cfg->ip_dscp, cfg->sock_queue_timeout)) {
listening_ports_free(list);
return NULL;
}
}
}
return list;
}
if(do_ip6) {
hints.ai_family = AF_INET6;
if(!ports_create_if(do_auto?"::0":"::1",
do_auto, cfg->do_udp, do_tcp,
&hints, portbuf, &list,
cfg->so_rcvbuf, cfg->so_sndbuf,
cfg->ssl_port, cfg->tls_additional_port,
cfg->https_port, cfg->proxy_protocol_port,
reuseport, cfg->ip_transparent,
cfg->tcp_mss, cfg->ip_freebind,
cfg->http_nodelay, cfg->use_systemd,
cfg->dnscrypt_port, cfg->ip_dscp, cfg->sock_queue_timeout)) {
listening_ports_free(list);
return NULL;
}
}
if(do_ip4) {
hints.ai_family = AF_INET;
if(!ports_create_if(do_auto?"0.0.0.0":"127.0.0.1",
do_auto, cfg->do_udp, do_tcp,
&hints, portbuf, &list,
cfg->so_rcvbuf, cfg->so_sndbuf,
cfg->ssl_port, cfg->tls_additional_port,
cfg->https_port, cfg->proxy_protocol_port,
reuseport, cfg->ip_transparent,
cfg->tcp_mss, cfg->ip_freebind,
cfg->http_nodelay, cfg->use_systemd,
cfg->dnscrypt_port, cfg->ip_dscp, cfg->sock_queue_timeout)) {
listening_ports_free(list);
return NULL;
}
}
} else for(i = 0; i<num_ifs; i++) {
if(str_is_ip6(ifs[i])) {
if(!do_ip6)
continue;
hints.ai_family = AF_INET6;
if(!ports_create_if(ifs[i], 0, cfg->do_udp,
do_tcp, &hints, portbuf, &list,
cfg->so_rcvbuf, cfg->so_sndbuf,
cfg->ssl_port, cfg->tls_additional_port,
cfg->https_port, cfg->proxy_protocol_port,
reuseport, cfg->ip_transparent,
cfg->tcp_mss, cfg->ip_freebind,
cfg->http_nodelay, cfg->use_systemd,
cfg->dnscrypt_port, cfg->ip_dscp, cfg->sock_queue_timeout)) {
listening_ports_free(list);
return NULL;
}
} else {
if(!do_ip4)
continue;
hints.ai_family = AF_INET;
if(!ports_create_if(ifs[i], 0, cfg->do_udp,
do_tcp, &hints, portbuf, &list,
cfg->so_rcvbuf, cfg->so_sndbuf,
cfg->ssl_port, cfg->tls_additional_port,
cfg->https_port, cfg->proxy_protocol_port,
reuseport, cfg->ip_transparent,
cfg->tcp_mss, cfg->ip_freebind,
cfg->http_nodelay, cfg->use_systemd,
cfg->dnscrypt_port, cfg->ip_dscp, cfg->sock_queue_timeout)) {
listening_ports_free(list);
return NULL;
}
}
}
return list;
}
void listening_ports_free(struct listen_port* list)
{
struct listen_port* nx;
while(list) {
nx = list->next;
if(list->fd != -1) {
sock_close(list->fd);
}
/* rc_ports don't have ub_socket */
if(list->socket) {
if(list->socket->addr)
freeaddrinfo(list->socket->addr);
free(list->socket);
}
free(list);
list = nx;
}
}
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) +
sldns_buffer_capacity(listen->udp_buff);
#ifdef USE_DNSCRYPT
s += sizeof(*listen->dnscrypt_udp_buff);
if(listen->udp_buff != listen->dnscrypt_udp_buff){
s += sldns_buffer_capacity(listen->dnscrypt_udp_buff);
}
#endif
for(p = listen->cps; p; p = p->next) {
s += sizeof(*p);
s += comm_point_get_mem(p->com);
}
return s;
}
void listen_stop_accept(struct listen_dnsport* listen)
{
/* do not stop the ones that have no tcp_free list
* (they have already stopped listening) */
struct listen_list* p;
for(p=listen->cps; p; p=p->next) {
if(p->com->type == comm_tcp_accept &&
p->com->tcp_free != NULL) {
comm_point_stop_listening(p->com);
}
}
}
void listen_start_accept(struct listen_dnsport* listen)
{
/* do not start the ones that have no tcp_free list, it is no
* use to listen to them because they have no free tcp handlers */
struct listen_list* p;
for(p=listen->cps; p; p=p->next) {
if(p->com->type == comm_tcp_accept &&
p->com->tcp_free != NULL) {
comm_point_start_listening(p->com, -1, -1);
}
}
}
struct tcp_req_info*
tcp_req_info_create(struct sldns_buffer* spoolbuf)
{
struct tcp_req_info* req = (struct tcp_req_info*)malloc(sizeof(*req));
if(!req) {
log_err("malloc failure for new stream outoforder processing structure");
return NULL;
}
memset(req, 0, sizeof(*req));
req->spool_buffer = spoolbuf;
return req;
}
void
tcp_req_info_delete(struct tcp_req_info* req)
{
if(!req) return;
tcp_req_info_clear(req);
/* cp is pointer back to commpoint that owns this struct and
* called delete on us */
/* spool_buffer is shared udp buffer, not deleted here */
free(req);
}
void tcp_req_info_clear(struct tcp_req_info* req)
{
struct tcp_req_open_item* open, *nopen;
struct tcp_req_done_item* item, *nitem;
if(!req) return;
/* free outstanding request mesh reply entries */
open = req->open_req_list;
while(open) {
nopen = open->next;
mesh_state_remove_reply(open->mesh, open->mesh_state, req->cp);
free(open);
open = nopen;
}
req->open_req_list = NULL;
req->num_open_req = 0;
/* free pending writable result packets */
item = req->done_req_list;
while(item) {
nitem = item->next;
lock_basic_lock(&stream_wait_count_lock);
stream_wait_count -= (sizeof(struct tcp_req_done_item)
+item->len);
lock_basic_unlock(&stream_wait_count_lock);
free(item->buf);
free(item);
item = nitem;
}
req->done_req_list = NULL;
req->num_done_req = 0;
req->read_is_closed = 0;
}
void
tcp_req_info_remove_mesh_state(struct tcp_req_info* req, struct mesh_state* m)
{
struct tcp_req_open_item* open, *prev = NULL;
if(!req || !m) return;
open = req->open_req_list;
while(open) {
if(open->mesh_state == m) {
struct tcp_req_open_item* next;
if(prev) prev->next = open->next;
else req->open_req_list = open->next;
/* caller has to manage the mesh state reply entry */
next = open->next;
free(open);
req->num_open_req --;
/* prev = prev; */
open = next;
continue;
}
prev = open;
open = open->next;
}
}
/** setup listening for read or write */
static void
tcp_req_info_setup_listen(struct tcp_req_info* req)
{
int wr = 0;
int rd = 0;
if(req->cp->tcp_byte_count != 0) {
/* cannot change, halfway through */
return;
}
if(!req->cp->tcp_is_reading)
wr = 1;
if(!req->read_is_closed)
rd = 1;
if(wr) {
req->cp->tcp_is_reading = 0;
comm_point_stop_listening(req->cp);
comm_point_start_listening(req->cp, -1,
adjusted_tcp_timeout(req->cp));
} else if(rd) {
req->cp->tcp_is_reading = 1;
comm_point_stop_listening(req->cp);
comm_point_start_listening(req->cp, -1,
adjusted_tcp_timeout(req->cp));
/* and also read it (from SSL stack buffers), so
* no event read event is expected since the remainder of
* the TLS frame is sitting in the buffers. */
req->read_again = 1;
} else {
comm_point_stop_listening(req->cp);
comm_point_start_listening(req->cp, -1,
adjusted_tcp_timeout(req->cp));
comm_point_listen_for_rw(req->cp, 0, 0);
}
}
/** remove first item from list of pending results */
static struct tcp_req_done_item*
tcp_req_info_pop_done(struct tcp_req_info* req)
{
struct tcp_req_done_item* item;
log_assert(req->num_done_req > 0 && req->done_req_list);
item = req->done_req_list;
lock_basic_lock(&stream_wait_count_lock);
stream_wait_count -= (sizeof(struct tcp_req_done_item)+item->len);
lock_basic_unlock(&stream_wait_count_lock);
req->done_req_list = req->done_req_list->next;
req->num_done_req --;
return item;
}
/** Send given buffer and setup to write */
static void
tcp_req_info_start_write_buf(struct tcp_req_info* req, uint8_t* buf,
size_t len)
{
sldns_buffer_clear(req->cp->buffer);
sldns_buffer_write(req->cp->buffer, buf, len);
sldns_buffer_flip(req->cp->buffer);
req->cp->tcp_is_reading = 0; /* we are now writing */
}
/** pick up the next result and start writing it to the channel */
static void
tcp_req_pickup_next_result(struct tcp_req_info* req)
{
if(req->num_done_req > 0) {
/* unlist the done item from the list of pending results */
struct tcp_req_done_item* item = tcp_req_info_pop_done(req);
tcp_req_info_start_write_buf(req, item->buf, item->len);
free(item->buf);
free(item);
}
}
/** the read channel has closed */
int
tcp_req_info_handle_read_close(struct tcp_req_info* req)
{
verbose(VERB_ALGO, "tcp channel read side closed %d", req->cp->fd);
/* reset byte count for (potential) partial read */
req->cp->tcp_byte_count = 0;
/* if we still have results to write, pick up next and write it */
if(req->num_done_req != 0) {
tcp_req_pickup_next_result(req);
tcp_req_info_setup_listen(req);
return 1;
}
/* if nothing to do, this closes the connection */
if(req->num_open_req == 0 && req->num_done_req == 0)
return 0;
/* otherwise, we must be waiting for dns resolve, wait with timeout */
req->read_is_closed = 1;
tcp_req_info_setup_listen(req);
return 1;
}
void
tcp_req_info_handle_writedone(struct tcp_req_info* req)
{
/* back to reading state, we finished this write event */
sldns_buffer_clear(req->cp->buffer);
if(req->num_done_req == 0 && req->read_is_closed) {
/* no more to write and nothing to read, close it */
comm_point_drop_reply(&req->cp->repinfo);
return;
}
req->cp->tcp_is_reading = 1;
/* see if another result needs writing */
tcp_req_pickup_next_result(req);
/* see if there is more to write, if not stop_listening for writing */
/* see if new requests are allowed, if so, start_listening
* for reading */
tcp_req_info_setup_listen(req);
}
void
tcp_req_info_handle_readdone(struct tcp_req_info* req)
{
struct comm_point* c = req->cp;
/* we want to read up several requests, unless there are
* pending answers */
req->is_drop = 0;
req->is_reply = 0;
req->in_worker_handle = 1;
sldns_buffer_set_limit(req->spool_buffer, 0);
/* handle the current request */
/* this calls the worker handle request routine that could give
* a cache response, or localdata response, or drop the reply,
* or schedule a mesh entry for later */
fptr_ok(fptr_whitelist_comm_point(c->callback));
if( (*c->callback)(c, c->cb_arg, NETEVENT_NOERROR, &c->repinfo) ) {
req->in_worker_handle = 0;
/* there is an answer, put it up. It is already in the
* c->buffer, just send it. */
/* since we were just reading a query, the channel is
* clear to write to */
send_it:
c->tcp_is_reading = 0;
comm_point_stop_listening(c);
comm_point_start_listening(c, -1, adjusted_tcp_timeout(c));
return;
}
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.
* Then the mesh state has been cleared. */
if(req->is_drop) {
/* the reply has been dropped, stream has been closed. */
return;
}
/* If mesh failed(mallocfail) and called commpoint_send_reply with
* something like servfail then we pick up that reply below. */
if(req->is_reply) {
goto send_it;
}
sldns_buffer_clear(c->buffer);
/* if pending answers, pick up an answer and start sending it */
tcp_req_pickup_next_result(req);
/* if answers pending, start sending answers */
/* read more requests if we can have more requests */
tcp_req_info_setup_listen(req);
}
int
tcp_req_info_add_meshstate(struct tcp_req_info* req,
struct mesh_area* mesh, struct mesh_state* m)
{
struct tcp_req_open_item* item;
log_assert(req && mesh && m);
item = (struct tcp_req_open_item*)malloc(sizeof(*item));
if(!item) return 0;
item->next = req->open_req_list;
item->mesh = mesh;
item->mesh_state = m;
req->open_req_list = item;
req->num_open_req++;
return 1;
}
/** Add a result to the result list. At the end. */
static int
tcp_req_info_add_result(struct tcp_req_info* req, uint8_t* buf, size_t len)
{
struct tcp_req_done_item* last = NULL;
struct tcp_req_done_item* item;
size_t space;
/* see if we have space */
space = sizeof(struct tcp_req_done_item) + len;
lock_basic_lock(&stream_wait_count_lock);
if(stream_wait_count + space > stream_wait_max) {
lock_basic_unlock(&stream_wait_count_lock);
verbose(VERB_ALGO, "drop stream reply, no space left, in stream-wait-size");
return 0;
}
stream_wait_count += space;
lock_basic_unlock(&stream_wait_count_lock);
/* find last element */
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) {
log_err("malloc failure, for stream result list");
return 0;
}
item->next = NULL;
item->len = len;
item->buf = memdup(buf, len);
if(!item->buf) {
free(item);
log_err("malloc failure, adding reply to stream result list");
return 0;
}
/* link in */
if(last) last->next = item;
else req->done_req_list = item;
req->num_done_req++;
return 1;
}
void
tcp_req_info_send_reply(struct tcp_req_info* req)
{
if(req->in_worker_handle) {
/* reply from mesh is in the spool_buffer */
/* copy now, so that the spool buffer is free for other tasks
* before the callback is done */
sldns_buffer_clear(req->cp->buffer);
sldns_buffer_write(req->cp->buffer,
sldns_buffer_begin(req->spool_buffer),
sldns_buffer_limit(req->spool_buffer));
sldns_buffer_flip(req->cp->buffer);
req->is_reply = 1;
return;
}
/* now that the query has been handled, that mesh_reply entry
* should be removed, from the tcp_req_info list,
* the mesh state cleanup removes then with region_cleanup and
* replies_sent true. */
/* see if we can send it straight away (we are not doing
* anything else). If so, copy to buffer and start */
if(req->cp->tcp_is_reading && req->cp->tcp_byte_count == 0) {
/* buffer is free, and was ready to read new query into,
* but we are now going to use it to send this answer */
tcp_req_info_start_write_buf(req,
sldns_buffer_begin(req->spool_buffer),
sldns_buffer_limit(req->spool_buffer));
/* switch to listen to write events */
comm_point_stop_listening(req->cp);
comm_point_start_listening(req->cp, -1,
adjusted_tcp_timeout(req->cp));
return;
}
/* queue up the answer behind the others already pending */
if(!tcp_req_info_add_result(req, sldns_buffer_begin(req->spool_buffer),
sldns_buffer_limit(req->spool_buffer))) {
/* drop the connection, we are out of resources */
comm_point_drop_reply(&req->cp->repinfo);
}
}
size_t tcp_req_info_get_stream_buffer_size(void)
{
size_t s;
if(!stream_wait_lock_inited)
return stream_wait_count;
lock_basic_lock(&stream_wait_count_lock);
s = stream_wait_count;
lock_basic_unlock(&stream_wait_count_lock);
return s;
}
size_t http2_get_query_buffer_size(void)
{
size_t s;
if(!http2_query_buffer_lock_inited)
return http2_query_buffer_count;
lock_basic_lock(&http2_query_buffer_count_lock);
s = http2_query_buffer_count;
lock_basic_unlock(&http2_query_buffer_count_lock);
return s;
}
size_t http2_get_response_buffer_size(void)
{
size_t s;
if(!http2_response_buffer_lock_inited)
return http2_response_buffer_count;
lock_basic_lock(&http2_response_buffer_count_lock);
s = http2_response_buffer_count;
lock_basic_unlock(&http2_response_buffer_count_lock);
return s;
}
#ifdef HAVE_NGHTTP2
/** nghttp2 callback. Used to copy response from rbuffer to nghttp2 session */
static ssize_t http2_submit_response_read_callback(
nghttp2_session* ATTR_UNUSED(session),
int32_t stream_id, uint8_t* buf, size_t length, uint32_t* data_flags,
nghttp2_data_source* source, void* ATTR_UNUSED(cb_arg))
{
struct http2_stream* h2_stream;
struct http2_session* h2_session = source->ptr;
size_t copylen = length;
if(!(h2_stream = nghttp2_session_get_stream_user_data(
h2_session->session, stream_id))) {
verbose(VERB_QUERY, "http2: cannot get stream data, closing "
"stream");
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
if(!h2_stream->rbuffer ||
sldns_buffer_remaining(h2_stream->rbuffer) == 0) {
verbose(VERB_QUERY, "http2: cannot submit buffer. No data "
"available in rbuffer");
/* rbuffer will be free'd in frame close cb */
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
if(copylen > sldns_buffer_remaining(h2_stream->rbuffer))
copylen = sldns_buffer_remaining(h2_stream->rbuffer);
if(copylen > SSIZE_MAX)
copylen = SSIZE_MAX; /* will probably never happen */
memcpy(buf, sldns_buffer_current(h2_stream->rbuffer), copylen);
sldns_buffer_skip(h2_stream->rbuffer, copylen);
if(sldns_buffer_remaining(h2_stream->rbuffer) == 0) {
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
lock_basic_lock(&http2_response_buffer_count_lock);
http2_response_buffer_count -=
sldns_buffer_capacity(h2_stream->rbuffer);
lock_basic_unlock(&http2_response_buffer_count_lock);
sldns_buffer_free(h2_stream->rbuffer);
h2_stream->rbuffer = NULL;
}
return copylen;
}
/**
* Send RST_STREAM frame for stream.
* @param h2_session: http2 session to submit frame to
* @param h2_stream: http2 stream containing frame ID to use in RST_STREAM
* @return 0 on error, 1 otherwise
*/
static int http2_submit_rst_stream(struct http2_session* h2_session,
struct http2_stream* h2_stream)
{
int ret = nghttp2_submit_rst_stream(h2_session->session,
NGHTTP2_FLAG_NONE, h2_stream->stream_id,
NGHTTP2_INTERNAL_ERROR);
if(ret) {
verbose(VERB_QUERY, "http2: nghttp2_submit_rst_stream failed, "
"error: %s", nghttp2_strerror(ret));
return 0;
}
return 1;
}
/**
* DNS response ready to be submitted to nghttp2, to be prepared for sending
* out. Response is stored in c->buffer. Copy to rbuffer because the c->buffer
* might be used before this will be sent out.
* @param h2_session: http2 session, containing c->buffer which contains answer
* @return 0 on error, 1 otherwise
*/
int http2_submit_dns_response(struct http2_session* h2_session)
{
int ret;
nghttp2_data_provider data_prd;
char status[4];
nghttp2_nv headers[3];
struct http2_stream* h2_stream = h2_session->c->h2_stream;
size_t rlen;
char rlen_str[32];
if(h2_stream->rbuffer) {
log_err("http2 submit response error: rbuffer already "
"exists");
return 0;
}
if(sldns_buffer_remaining(h2_session->c->buffer) == 0) {
log_err("http2 submit response error: c->buffer not complete");
return 0;
}
if(snprintf(status, 4, "%d", h2_stream->status) != 3) {
verbose(VERB_QUERY, "http2: submit response error: "
"invalid status");
return 0;
}
rlen = sldns_buffer_remaining(h2_session->c->buffer);
snprintf(rlen_str, sizeof(rlen_str), "%u", (unsigned)rlen);
lock_basic_lock(&http2_response_buffer_count_lock);
if(http2_response_buffer_count + rlen > http2_response_buffer_max) {
lock_basic_unlock(&http2_response_buffer_count_lock);
verbose(VERB_ALGO, "reset HTTP2 stream, no space left, "
"in https-response-buffer-size");
return http2_submit_rst_stream(h2_session, h2_stream);
}
http2_response_buffer_count += rlen;
lock_basic_unlock(&http2_response_buffer_count_lock);
if(!(h2_stream->rbuffer = sldns_buffer_new(rlen))) {
lock_basic_lock(&http2_response_buffer_count_lock);
http2_response_buffer_count -= rlen;
lock_basic_unlock(&http2_response_buffer_count_lock);
log_err("http2 submit response error: malloc failure");
return 0;
}
headers[0].name = (uint8_t*)":status";
headers[0].namelen = 7;
headers[0].value = (uint8_t*)status;
headers[0].valuelen = 3;
headers[0].flags = NGHTTP2_NV_FLAG_NONE;
headers[1].name = (uint8_t*)"content-type";
headers[1].namelen = 12;
headers[1].value = (uint8_t*)"application/dns-message";
headers[1].valuelen = 23;
headers[1].flags = NGHTTP2_NV_FLAG_NONE;
headers[2].name = (uint8_t*)"content-length";
headers[2].namelen = 14;
headers[2].value = (uint8_t*)rlen_str;
headers[2].valuelen = strlen(rlen_str);
headers[2].flags = NGHTTP2_NV_FLAG_NONE;
sldns_buffer_write(h2_stream->rbuffer,
sldns_buffer_current(h2_session->c->buffer),
sldns_buffer_remaining(h2_session->c->buffer));
sldns_buffer_flip(h2_stream->rbuffer);
data_prd.source.ptr = h2_session;
data_prd.read_callback = http2_submit_response_read_callback;
ret = nghttp2_submit_response(h2_session->session, h2_stream->stream_id,
headers, 3, &data_prd);
if(ret) {
verbose(VERB_QUERY, "http2: set_stream_user_data failed, "
"error: %s", nghttp2_strerror(ret));
return 0;
}
return 1;
}
#else
int http2_submit_dns_response(void* ATTR_UNUSED(v))
{
return 0;
}
#endif
#ifdef HAVE_NGHTTP2
/** HTTP status to descriptive string */
static char* http_status_to_str(enum http_status s)
{
switch(s) {
case HTTP_STATUS_OK:
return "OK";
case HTTP_STATUS_BAD_REQUEST:
return "Bad Request";
case HTTP_STATUS_NOT_FOUND:
return "Not Found";
case HTTP_STATUS_PAYLOAD_TOO_LARGE:
return "Payload Too Large";
case HTTP_STATUS_URI_TOO_LONG:
return "URI Too Long";
case HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE:
return "Unsupported Media Type";
case HTTP_STATUS_NOT_IMPLEMENTED:
return "Not Implemented";
}
return "Status Unknown";
}
/** nghttp2 callback. Used to copy error message to nghttp2 session */
static ssize_t http2_submit_error_read_callback(
nghttp2_session* ATTR_UNUSED(session),
int32_t stream_id, uint8_t* buf, size_t length, uint32_t* data_flags,
nghttp2_data_source* source, void* ATTR_UNUSED(cb_arg))
{
struct http2_stream* h2_stream;
struct http2_session* h2_session = source->ptr;
char* msg;
if(!(h2_stream = nghttp2_session_get_stream_user_data(
h2_session->session, stream_id))) {
verbose(VERB_QUERY, "http2: cannot get stream data, closing "
"stream");
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
msg = http_status_to_str(h2_stream->status);
if(length < strlen(msg))
return 0; /* not worth trying over multiple frames */
memcpy(buf, msg, strlen(msg));
return strlen(msg);
}
/**
* HTTP error response ready to be submitted to nghttp2, to be prepared for
* sending out. Message body will contain descriptive string for HTTP status.
* @param h2_session: http2 session to submit to
* @param h2_stream: http2 stream containing HTTP status to use for error
* @return 0 on error, 1 otherwise
*/
static int http2_submit_error(struct http2_session* h2_session,
struct http2_stream* h2_stream)
{
int ret;
char status[4];
nghttp2_data_provider data_prd;
nghttp2_nv headers[1]; /* will be copied by nghttp */
if(snprintf(status, 4, "%d", h2_stream->status) != 3) {
verbose(VERB_QUERY, "http2: submit error failed, "
"invalid status");
return 0;
}
headers[0].name = (uint8_t*)":status";
headers[0].namelen = 7;
headers[0].value = (uint8_t*)status;
headers[0].valuelen = 3;
headers[0].flags = NGHTTP2_NV_FLAG_NONE;
data_prd.source.ptr = h2_session;
data_prd.read_callback = http2_submit_error_read_callback;
ret = nghttp2_submit_response(h2_session->session, h2_stream->stream_id,
headers, 1, &data_prd);
if(ret) {
verbose(VERB_QUERY, "http2: submit error failed, "
"error: %s", nghttp2_strerror(ret));
return 0;
}
return 1;
}
/**
* Start query handling. Query is stored in the stream, and will be free'd here.
* @param h2_session: http2 session, containing comm point
* @param h2_stream: stream containing buffered query
* @return: -1 on error, 1 if answer is stored in c->buffer, 0 if there is no
* reply available (yet).
*/
static int http2_query_read_done(struct http2_session* h2_session,
struct http2_stream* h2_stream)
{
log_assert(h2_stream->qbuffer);
if(h2_session->c->h2_stream) {
verbose(VERB_ALGO, "http2_query_read_done failure: shared "
"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);
if(sldns_buffer_remaining(h2_session->c->buffer) <
sldns_buffer_remaining(h2_stream->qbuffer)) {
/* qbuffer will be free'd in frame close cb */
sldns_buffer_clear(h2_session->c->buffer);
verbose(VERB_ALGO, "http2_query_read_done failure: can't fit "
"qbuffer in c->buffer");
return -1;
}
sldns_buffer_write(h2_session->c->buffer,
sldns_buffer_current(h2_stream->qbuffer),
sldns_buffer_remaining(h2_stream->qbuffer));
lock_basic_lock(&http2_query_buffer_count_lock);
http2_query_buffer_count -= sldns_buffer_capacity(h2_stream->qbuffer);
lock_basic_unlock(&http2_query_buffer_count_lock);
sldns_buffer_free(h2_stream->qbuffer);
h2_stream->qbuffer = NULL;
sldns_buffer_flip(h2_session->c->buffer);
h2_session->c->h2_stream = h2_stream;
fptr_ok(fptr_whitelist_comm_point(h2_session->c->callback));
if((*h2_session->c->callback)(h2_session->c, h2_session->c->cb_arg,
NETEVENT_NOERROR, &h2_session->c->repinfo)) {
return 1; /* answer in c->buffer */
}
sldns_buffer_clear(h2_session->c->buffer);
h2_session->c->h2_stream = NULL;
return 0; /* mesh state added, or dropped */
}
/** nghttp2 callback. Used to check if the received frame indicates the end of a
* stream. Gather collected request data and start query handling. */
static int http2_req_frame_recv_cb(nghttp2_session* session,
const nghttp2_frame* frame, void* cb_arg)
{
struct http2_session* h2_session = (struct http2_session*)cb_arg;
struct http2_stream* h2_stream;
int query_read_done;
if((frame->hd.type != NGHTTP2_DATA &&
frame->hd.type != NGHTTP2_HEADERS) ||
!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) {
return 0;
}
if(!(h2_stream = nghttp2_session_get_stream_user_data(
session, frame->hd.stream_id)))
return 0;
if(h2_stream->invalid_endpoint) {
h2_stream->status = HTTP_STATUS_NOT_FOUND;
goto submit_http_error;
}
if(h2_stream->invalid_content_type) {
h2_stream->status = HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE;
goto submit_http_error;
}
if(h2_stream->http_method != HTTP_METHOD_GET &&
h2_stream->http_method != HTTP_METHOD_POST) {
h2_stream->status = HTTP_STATUS_NOT_IMPLEMENTED;
goto submit_http_error;
}
if(h2_stream->query_too_large) {
if(h2_stream->http_method == HTTP_METHOD_POST)
h2_stream->status = HTTP_STATUS_PAYLOAD_TOO_LARGE;
else
h2_stream->status = HTTP_STATUS_URI_TOO_LONG;
goto submit_http_error;
}
if(!h2_stream->qbuffer) {
h2_stream->status = HTTP_STATUS_BAD_REQUEST;
goto submit_http_error;
}
if(h2_stream->status) {
submit_http_error:
verbose(VERB_QUERY, "http2 request invalid, returning :status="
"%d", h2_stream->status);
if(!http2_submit_error(h2_session, h2_stream)) {
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
return 0;
}
h2_stream->status = HTTP_STATUS_OK;
sldns_buffer_flip(h2_stream->qbuffer);
h2_session->postpone_drop = 1;
query_read_done = http2_query_read_done(h2_session, h2_stream);
if(query_read_done < 0)
return NGHTTP2_ERR_CALLBACK_FAILURE;
else if(!query_read_done) {
if(h2_session->is_drop) {
/* connection needs to be closed. Return failure to make
* sure no other action are taken anymore on comm point.
* failure will result in reclaiming (and closing)
* of comm point. */
verbose(VERB_QUERY, "http2 query dropped in worker cb");
h2_session->postpone_drop = 0;
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
/* nothing to submit right now, query added to mesh. */
h2_session->postpone_drop = 0;
return 0;
}
if(!http2_submit_dns_response(h2_session)) {
sldns_buffer_clear(h2_session->c->buffer);
h2_session->c->h2_stream = NULL;
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
verbose(VERB_QUERY, "http2 query submitted to session");
sldns_buffer_clear(h2_session->c->buffer);
h2_session->c->h2_stream = NULL;
return 0;
}
/** nghttp2 callback. Used to detect start of new streams. */
static int http2_req_begin_headers_cb(nghttp2_session* session,
const nghttp2_frame* frame, void* cb_arg)
{
struct http2_session* h2_session = (struct http2_session*)cb_arg;
struct http2_stream* h2_stream;
int ret;
if(frame->hd.type != NGHTTP2_HEADERS ||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
/* only interested in request headers */
return 0;
}
if(!(h2_stream = http2_stream_create(frame->hd.stream_id))) {
log_err("malloc failure while creating http2 stream");
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
http2_session_add_stream(h2_session, h2_stream);
ret = nghttp2_session_set_stream_user_data(session,
frame->hd.stream_id, h2_stream);
if(ret) {
/* stream does not exist */
verbose(VERB_QUERY, "http2: set_stream_user_data failed, "
"error: %s", nghttp2_strerror(ret));
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
return 0;
}
/**
* base64url decode, store in qbuffer
* @param h2_session: http2 session
* @param h2_stream: http2 stream
* @param start: start of the base64 string
* @param length: length of the base64 string
* @return: 0 on error, 1 otherwise. query will be stored in h2_stream->qbuffer,
* buffer will be NULL is unparseble.
*/
static int http2_buffer_uri_query(struct http2_session* h2_session,
struct http2_stream* h2_stream, const uint8_t* start, size_t length)
{
size_t expectb64len;
int b64len;
if(h2_stream->http_method == HTTP_METHOD_POST)
return 1;
if(length == 0)
return 1;
if(h2_stream->qbuffer) {
verbose(VERB_ALGO, "http2_req_header fail, "
"qbuffer already set");
return 0;
}
/* calculate size, might be a bit bigger than the real
* decoded buffer size */
expectb64len = sldns_b64_pton_calculate_size(length);
log_assert(expectb64len > 0);
if(expectb64len >
h2_session->c->http2_stream_max_qbuffer_size) {
h2_stream->query_too_large = 1;
return 1;
}
lock_basic_lock(&http2_query_buffer_count_lock);
if(http2_query_buffer_count + expectb64len > http2_query_buffer_max) {
lock_basic_unlock(&http2_query_buffer_count_lock);
verbose(VERB_ALGO, "reset HTTP2 stream, no space left, "
"in http2-query-buffer-size");
return http2_submit_rst_stream(h2_session, h2_stream);
}
http2_query_buffer_count += expectb64len;
lock_basic_unlock(&http2_query_buffer_count_lock);
if(!(h2_stream->qbuffer = sldns_buffer_new(expectb64len))) {
lock_basic_lock(&http2_query_buffer_count_lock);
http2_query_buffer_count -= expectb64len;
lock_basic_unlock(&http2_query_buffer_count_lock);
log_err("http2_req_header fail, qbuffer "
"malloc failure");
return 0;
}
if(sldns_b64_contains_nonurl((char const*)start, length)) {
char buf[65536+4];
verbose(VERB_ALGO, "HTTP2 stream contains wrong b64 encoding");
/* copy to the scratch buffer temporarily to terminate the
* string with a zero */
if(length+1 > sizeof(buf)) {
/* too long */
lock_basic_lock(&http2_query_buffer_count_lock);
http2_query_buffer_count -= expectb64len;
lock_basic_unlock(&http2_query_buffer_count_lock);
sldns_buffer_free(h2_stream->qbuffer);
h2_stream->qbuffer = NULL;
return 1;
}
memmove(buf, start, length);
buf[length] = 0;
if(!(b64len = sldns_b64_pton(buf, sldns_buffer_current(
h2_stream->qbuffer), expectb64len)) || b64len < 0) {
lock_basic_lock(&http2_query_buffer_count_lock);
http2_query_buffer_count -= expectb64len;
lock_basic_unlock(&http2_query_buffer_count_lock);
sldns_buffer_free(h2_stream->qbuffer);
h2_stream->qbuffer = NULL;
return 1;
}
} else {
if(!(b64len = sldns_b64url_pton(
(char const *)start, length,
sldns_buffer_current(h2_stream->qbuffer),
expectb64len)) || b64len < 0) {
lock_basic_lock(&http2_query_buffer_count_lock);
http2_query_buffer_count -= expectb64len;
lock_basic_unlock(&http2_query_buffer_count_lock);
sldns_buffer_free(h2_stream->qbuffer);
h2_stream->qbuffer = NULL;
/* return without error, method can be an
* unknown POST */
return 1;
}
}
sldns_buffer_skip(h2_stream->qbuffer, (size_t)b64len);
return 1;
}
/** nghttp2 callback. Used to parse headers from HEADER frames. */
static int http2_req_header_cb(nghttp2_session* session,
const nghttp2_frame* frame, const uint8_t* name, size_t namelen,
const uint8_t* value, size_t valuelen, uint8_t ATTR_UNUSED(flags),
void* cb_arg)
{
struct http2_stream* h2_stream = NULL;
struct http2_session* h2_session = (struct http2_session*)cb_arg;
/* nghttp2 deals with CONTINUATION frames and provides them as part of
* the HEADER */
if(frame->hd.type != NGHTTP2_HEADERS ||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
/* only interested in request headers */
return 0;
}
if(!(h2_stream = nghttp2_session_get_stream_user_data(session,
frame->hd.stream_id)))
return 0;
/* earlier checks already indicate we can stop handling this query */
if(h2_stream->http_method == HTTP_METHOD_UNSUPPORTED ||
h2_stream->invalid_content_type ||
h2_stream->invalid_endpoint)
return 0;
/* nghttp2 performs some sanity checks in the headers, including:
* name and value are guaranteed to be null terminated
* name is guaranteed to be lowercase
* content-length value is guaranteed to contain digits
*/
if(!h2_stream->http_method && namelen == 7 &&
memcmp(":method", name, namelen) == 0) {
/* Case insensitive check on :method value to be on the safe
* side. I failed to find text about case sensitivity in specs.
*/
if(valuelen == 3 && strcasecmp("GET", (const char*)value) == 0)
h2_stream->http_method = HTTP_METHOD_GET;
else if(valuelen == 4 &&
strcasecmp("POST", (const char*)value) == 0) {
h2_stream->http_method = HTTP_METHOD_POST;
if(h2_stream->qbuffer) {
/* POST method uses query from DATA frames */
lock_basic_lock(&http2_query_buffer_count_lock);
http2_query_buffer_count -=
sldns_buffer_capacity(h2_stream->qbuffer);
lock_basic_unlock(&http2_query_buffer_count_lock);
sldns_buffer_free(h2_stream->qbuffer);
h2_stream->qbuffer = NULL;
}
} else
h2_stream->http_method = HTTP_METHOD_UNSUPPORTED;
return 0;
}
if(namelen == 5 && memcmp(":path", name, namelen) == 0) {
/* :path may contain DNS query, depending on method. Method might
* not be known yet here, so check after finishing receiving
* stream. */
#define HTTP_QUERY_PARAM "?dns="
size_t el = strlen(h2_session->c->http_endpoint);
size_t qpl = strlen(HTTP_QUERY_PARAM);
if(valuelen < el || memcmp(h2_session->c->http_endpoint,
value, el) != 0) {
h2_stream->invalid_endpoint = 1;
return 0;
}
/* larger than endpoint only allowed if it is for the query
* parameter */
if(valuelen <= el+qpl ||
memcmp(HTTP_QUERY_PARAM, value+el, qpl) != 0) {
if(valuelen != el)
h2_stream->invalid_endpoint = 1;
return 0;
}
if(!http2_buffer_uri_query(h2_session, h2_stream,
value+(el+qpl), valuelen-(el+qpl))) {
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
return 0;
}
/* Content type is a SHOULD (rfc7231#section-3.1.1.5) when using POST,
* and not needed when using GET. Don't enfore.
* If set only allow lowercase "application/dns-message".
*
* Clients SHOULD (rfc8484#section-4.1) set an accept header, but MUST
* be able to handle "application/dns-message". Since that is the only
* content-type supported we can ignore the accept header.
*/
if((namelen == 12 && memcmp("content-type", name, namelen) == 0)) {
if(valuelen != 23 || memcmp("application/dns-message", value,
valuelen) != 0) {
h2_stream->invalid_content_type = 1;
}
}
/* Only interested in content-lentg for POST (on not yet known) method.
*/
if((!h2_stream->http_method ||
h2_stream->http_method == HTTP_METHOD_POST) &&
!h2_stream->content_length && namelen == 14 &&
memcmp("content-length", name, namelen) == 0) {
if(valuelen > 5) {
h2_stream->query_too_large = 1;
return 0;
}
/* guaranteed to only contain digits and be null terminated */
h2_stream->content_length = atoi((const char*)value);
if(h2_stream->content_length >
h2_session->c->http2_stream_max_qbuffer_size) {
h2_stream->query_too_large = 1;
return 0;
}
}
return 0;
}
/** nghttp2 callback. Used to get data from DATA frames, which can contain
* queries in POST requests. */
static int http2_req_data_chunk_recv_cb(nghttp2_session* ATTR_UNUSED(session),
uint8_t ATTR_UNUSED(flags), int32_t stream_id, const uint8_t* data,
size_t len, void* cb_arg)
{
struct http2_session* h2_session = (struct http2_session*)cb_arg;
struct http2_stream* h2_stream;
size_t qlen = 0;
if(!(h2_stream = nghttp2_session_get_stream_user_data(
h2_session->session, stream_id))) {
return 0;
}
if(h2_stream->query_too_large)
return 0;
if(!h2_stream->qbuffer) {
if(h2_stream->content_length) {
if(h2_stream->content_length < len)
/* getting more data in DATA frame than
* advertised in content-length header. */
return NGHTTP2_ERR_CALLBACK_FAILURE;
qlen = h2_stream->content_length;
} else if(len <= h2_session->c->http2_stream_max_qbuffer_size) {
/* setting this to msg-buffer-size can result in a lot
* of memory consuption. Most queries should fit in a
* single DATA frame, and most POST queries will
* contain content-length which does not impose this
* limit. */
qlen = len;
}
}
if(!h2_stream->qbuffer && qlen) {
lock_basic_lock(&http2_query_buffer_count_lock);
if(http2_query_buffer_count + qlen > http2_query_buffer_max) {
lock_basic_unlock(&http2_query_buffer_count_lock);
verbose(VERB_ALGO, "reset HTTP2 stream, no space left, "
"in http2-query-buffer-size");
return http2_submit_rst_stream(h2_session, h2_stream);
}
http2_query_buffer_count += qlen;
lock_basic_unlock(&http2_query_buffer_count_lock);
if(!(h2_stream->qbuffer = sldns_buffer_new(qlen))) {
lock_basic_lock(&http2_query_buffer_count_lock);
http2_query_buffer_count -= qlen;
lock_basic_unlock(&http2_query_buffer_count_lock);
}
}
if(!h2_stream->qbuffer ||
sldns_buffer_remaining(h2_stream->qbuffer) < len) {
verbose(VERB_ALGO, "http2 data_chunck_recv failed. Not enough "
"buffer space for POST query. Can happen on multi "
"frame requests without content-length header");
h2_stream->query_too_large = 1;
return 0;
}
sldns_buffer_write(h2_stream->qbuffer, data, len);
return 0;
}
void http2_req_stream_clear(struct http2_stream* h2_stream)
{
if(h2_stream->qbuffer) {
lock_basic_lock(&http2_query_buffer_count_lock);
http2_query_buffer_count -=
sldns_buffer_capacity(h2_stream->qbuffer);
lock_basic_unlock(&http2_query_buffer_count_lock);
sldns_buffer_free(h2_stream->qbuffer);
h2_stream->qbuffer = NULL;
}
if(h2_stream->rbuffer) {
lock_basic_lock(&http2_response_buffer_count_lock);
http2_response_buffer_count -=
sldns_buffer_capacity(h2_stream->rbuffer);
lock_basic_unlock(&http2_response_buffer_count_lock);
sldns_buffer_free(h2_stream->rbuffer);
h2_stream->rbuffer = NULL;
}
}
nghttp2_session_callbacks* http2_req_callbacks_create(void)
{
nghttp2_session_callbacks *callbacks;
if(nghttp2_session_callbacks_new(&callbacks) == NGHTTP2_ERR_NOMEM) {
log_err("failed to initialize nghttp2 callback");
return NULL;
}
/* reception of header block started, used to create h2_stream */
nghttp2_session_callbacks_set_on_begin_headers_callback(callbacks,
http2_req_begin_headers_cb);
/* complete frame received, used to get data from stream if frame
* has end stream flag, and start processing query */
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
http2_req_frame_recv_cb);
/* get request info from headers */
nghttp2_session_callbacks_set_on_header_callback(callbacks,
http2_req_header_cb);
/* get data from DATA frames, containing POST query */
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(callbacks,
http2_req_data_chunk_recv_cb);
/* generic HTTP2 callbacks */
nghttp2_session_callbacks_set_recv_callback(callbacks, http2_recv_cb);
nghttp2_session_callbacks_set_send_callback(callbacks, http2_send_cb);
nghttp2_session_callbacks_set_on_stream_close_callback(callbacks,
http2_stream_close_cb);
return callbacks;
}
#endif /* HAVE_NGHTTP2 */
diff --git a/contrib/unbound/services/mesh.c b/contrib/unbound/services/mesh.c
index 52d14a2d1f54..509bee36a2c4 100644
--- a/contrib/unbound/services/mesh.c
+++ b/contrib/unbound/services/mesh.c
@@ -1,2233 +1,2238 @@
/*
* services/mesh.c - deal with mesh of query states and handle events for that.
*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file contains functions to assist in dealing with a mesh of
* query states. This mesh is supposed to be thread-specific.
* It consists of query states (per qname, qtype, qclass) and connections
* between query states and the super and subquery states, and replies to
* send back to clients.
*/
#include "config.h"
#include "services/mesh.h"
#include "services/outbound_list.h"
#include "services/cache/dns.h"
#include "services/cache/rrset.h"
#include "util/log.h"
#include "util/net_help.h"
#include "util/module.h"
#include "util/regional.h"
#include "util/data/msgencode.h"
#include "util/timehist.h"
#include "util/fptr_wlist.h"
#include "util/alloc.h"
#include "util/config_file.h"
#include "util/edns.h"
#include "sldns/sbuffer.h"
#include "sldns/wire2str.h"
#include "services/localzone.h"
#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
/**
* 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
* 1 or -1 (they mean 'ci_a is larger/smaller than ci_b', respectively, but
* in practice it should be only used to mean they are different).
* We cannot share the mesh state for two queries if different response-ip
* actions can apply in the end, even if those queries are otherwise identical.
* For this purpose we compare tag lists and tag action lists; they should be
* identical to share the same state.
* For tag data, we don't look into the data content, as it can be
* expensive; unless tag data are not defined for both or they point to the
* exact same data in memory (i.e., they come from the same ACL entry), we
* consider these data different.
* Likewise, if the client info is associated with views, we don't look into
* the views. They are considered different unless they are exactly the same
* even if the views only differ in the names.
*/
static int
client_info_compare(const struct respip_client_info* ci_a,
const struct respip_client_info* ci_b)
{
int cmp;
if(!ci_a && !ci_b)
return 0;
if(ci_a && !ci_b)
return -1;
if(!ci_a && ci_b)
return 1;
if(ci_a->taglen != ci_b->taglen)
return (ci_a->taglen < ci_b->taglen) ? -1 : 1;
if(ci_a->taglist && !ci_b->taglist)
return -1;
if(!ci_a->taglist && ci_b->taglist)
return 1;
if(ci_a->taglist && ci_b->taglist) {
cmp = memcmp(ci_a->taglist, ci_b->taglist, ci_a->taglen);
if(cmp != 0)
return cmp;
}
if(ci_a->tag_actions_size != ci_b->tag_actions_size)
return (ci_a->tag_actions_size < ci_b->tag_actions_size) ?
-1 : 1;
if(ci_a->tag_actions && !ci_b->tag_actions)
return -1;
if(!ci_a->tag_actions && ci_b->tag_actions)
return 1;
if(ci_a->tag_actions && ci_b->tag_actions) {
cmp = memcmp(ci_a->tag_actions, ci_b->tag_actions,
ci_a->tag_actions_size);
if(cmp != 0)
return cmp;
}
if(ci_a->tag_datas != ci_b->tag_datas)
return ci_a->tag_datas < ci_b->tag_datas ? -1 : 1;
if(ci_a->view != ci_b->view)
return ci_a->view < ci_b->view ? -1 : 1;
/* For the unbound daemon these should be non-NULL and identical,
* but we check that just in case. */
if(ci_a->respip_set != ci_b->respip_set)
return ci_a->respip_set < ci_b->respip_set ? -1 : 1;
return 0;
}
int
mesh_state_compare(const void* ap, const void* bp)
{
struct mesh_state* a = (struct mesh_state*)ap;
struct mesh_state* b = (struct mesh_state*)bp;
int cmp;
if(a->unique < b->unique)
return -1;
if(a->unique > b->unique)
return 1;
if(a->s.is_priming && !b->s.is_priming)
return -1;
if(!a->s.is_priming && b->s.is_priming)
return 1;
if(a->s.is_valrec && !b->s.is_valrec)
return -1;
if(!a->s.is_valrec && b->s.is_valrec)
return 1;
if((a->s.query_flags&BIT_RD) && !(b->s.query_flags&BIT_RD))
return -1;
if(!(a->s.query_flags&BIT_RD) && (b->s.query_flags&BIT_RD))
return 1;
if((a->s.query_flags&BIT_CD) && !(b->s.query_flags&BIT_CD))
return -1;
if(!(a->s.query_flags&BIT_CD) && (b->s.query_flags&BIT_CD))
return 1;
cmp = query_info_compare(&a->s.qinfo, &b->s.qinfo);
if(cmp != 0)
return cmp;
return client_info_compare(a->s.client_info, b->s.client_info);
}
int
mesh_state_ref_compare(const void* ap, const void* bp)
{
struct mesh_state_ref* a = (struct mesh_state_ref*)ap;
struct mesh_state_ref* b = (struct mesh_state_ref*)bp;
return mesh_state_compare(a->s, b->s);
}
struct mesh_area*
mesh_create(struct module_stack* stack, struct module_env* env)
{
struct mesh_area* mesh = calloc(1, sizeof(struct mesh_area));
if(!mesh) {
log_err("mesh area alloc: out of memory");
return NULL;
}
mesh->histogram = timehist_setup();
mesh->qbuf_bak = sldns_buffer_new(env->cfg->msg_buffer_size);
if(!mesh->histogram || !mesh->qbuf_bak) {
free(mesh);
log_err("mesh area alloc: out of memory");
return NULL;
}
mesh->mods = *stack;
mesh->env = env;
rbtree_init(&mesh->run, &mesh_state_compare);
rbtree_init(&mesh->all, &mesh_state_compare);
mesh->num_reply_addrs = 0;
mesh->num_reply_states = 0;
mesh->num_detached_states = 0;
mesh->num_forever_states = 0;
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
mesh->jostle_max.tv_sec = (time_t)(env->cfg->jostle_time / 1000);
mesh->jostle_max.tv_usec = (time_t)((env->cfg->jostle_time % 1000)
*1000);
#endif
return mesh;
}
/** help mesh delete delete mesh states */
static void
mesh_delete_helper(rbnode_type* n)
{
struct mesh_state* mstate = (struct mesh_state*)n->key;
/* perform a full delete, not only 'cleanup' routine,
* because other callbacks expect a clean state in the mesh.
* For 're-entrant' calls */
mesh_state_delete(&mstate->s);
/* but because these delete the items from the tree, postorder
* traversal and rbtree rebalancing do not work together */
}
void
mesh_delete(struct mesh_area* mesh)
{
if(!mesh)
return;
/* free all query states */
while(mesh->all.count)
mesh_delete_helper(mesh->all.root);
timehist_delete(mesh->histogram);
sldns_buffer_free(mesh->qbuf_bak);
free(mesh);
}
void
mesh_delete_all(struct mesh_area* mesh)
{
/* free all query states */
while(mesh->all.count)
mesh_delete_helper(mesh->all.root);
mesh->stats_dropped += mesh->num_reply_addrs;
/* clear mesh area references */
rbtree_init(&mesh->run, &mesh_state_compare);
rbtree_init(&mesh->all, &mesh_state_compare);
mesh->num_reply_addrs = 0;
mesh->num_reply_states = 0;
mesh->num_detached_states = 0;
mesh->num_forever_states = 0;
mesh->forever_first = NULL;
mesh->forever_last = NULL;
mesh->jostle_first = NULL;
mesh->jostle_last = NULL;
}
int mesh_make_new_space(struct mesh_area* mesh, sldns_buffer* qbuf)
{
struct mesh_state* m = mesh->jostle_first;
/* free space is available */
if(mesh->num_reply_states < mesh->max_reply_states)
return 1;
/* try to kick out a jostle-list item */
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,
&m->reply_list->start_time);
if(timeval_smaller(&mesh->jostle_max, &age)) {
/* its a goner */
log_nametypeclass(VERB_ALGO, "query jostled out to "
"make space for a new one",
m->s.qinfo.qname, m->s.qinfo.qtype,
m->s.qinfo.qclass);
/* backup the query */
if(qbuf) sldns_buffer_copy(mesh->qbuf_bak, qbuf);
/* notify supers */
if(m->super_set.count > 0) {
verbose(VERB_ALGO, "notify supers of failure");
m->s.return_msg = NULL;
m->s.return_rcode = LDNS_RCODE_SERVFAIL;
mesh_walk_supers(mesh, m);
}
mesh->stats_jostled ++;
mesh_state_delete(&m->s);
/* restore the query - note that the qinfo ptr to
* the querybuffer is then correct again. */
if(qbuf) sldns_buffer_copy(qbuf, mesh->qbuf_bak);
return 1;
}
}
/* no space for new item */
return 0;
}
struct dns_msg*
mesh_serve_expired_lookup(struct module_qstate* qstate,
struct query_info* lookup_qinfo)
{
hashvalue_type h;
struct lruhash_entry* e;
struct dns_msg* msg;
struct reply_info* data;
struct msgreply_entry* key;
time_t timenow = *qstate->env->now;
int must_validate = (!(qstate->query_flags&BIT_CD)
|| qstate->env->cfg->ignore_cd) && qstate->env->need_to_validate;
/* Lookup cache */
h = query_info_hash(lookup_qinfo, qstate->query_flags);
e = slabhash_lookup(qstate->env->msg_cache, h, lookup_qinfo, 0);
if(!e) return NULL;
key = (struct msgreply_entry*)e->key;
data = (struct reply_info*)e->data;
msg = tomsg(qstate->env, &key->key, data, qstate->region, timenow,
qstate->env->cfg->serve_expired, qstate->env->scratch);
if(!msg)
goto bail_out;
/* Check CNAME chain (if any)
* This is part of tomsg above; no need to check now. */
/* Check security status of the cached answer.
* tomsg above has a subset of these checks, so we are leaving
* these as is.
* In case of bogus or revalidation we don't care to reply here. */
if(must_validate && (msg->rep->security == sec_status_bogus ||
msg->rep->security == sec_status_secure_sentinel_fail)) {
verbose(VERB_ALGO, "Serve expired: bogus answer found in cache");
goto bail_out;
} else if(msg->rep->security == sec_status_unchecked && must_validate) {
verbose(VERB_ALGO, "Serve expired: unchecked entry needs "
"validation");
goto bail_out; /* need to validate cache entry first */
} else if(msg->rep->security == sec_status_secure &&
!reply_all_rrsets_secure(msg->rep) && must_validate) {
verbose(VERB_ALGO, "Serve expired: secure entry"
" changed status");
goto bail_out; /* rrset changed, re-verify */
}
lock_rw_unlock(&e->lock);
return msg;
bail_out:
lock_rw_unlock(&e->lock);
return NULL;
}
/** Init the serve expired data structure */
static int
mesh_serve_expired_init(struct mesh_state* mstate, int timeout)
{
struct timeval t;
/* Create serve_expired_data if not there yet */
if(!mstate->s.serve_expired_data) {
mstate->s.serve_expired_data = (struct serve_expired_data*)
regional_alloc_zero(
mstate->s.region, sizeof(struct serve_expired_data));
if(!mstate->s.serve_expired_data)
return 0;
}
/* Don't overwrite the function if already set */
mstate->s.serve_expired_data->get_cached_answer =
mstate->s.serve_expired_data->get_cached_answer?
mstate->s.serve_expired_data->get_cached_answer:
&mesh_serve_expired_lookup;
/* In case this timer already popped, start it again */
if(!mstate->s.serve_expired_data->timer) {
mstate->s.serve_expired_data->timer = comm_timer_create(
mstate->s.env->worker_base, mesh_serve_expired_callback, mstate);
if(!mstate->s.serve_expired_data->timer)
return 0;
#ifndef S_SPLINT_S
t.tv_sec = timeout/1000;
t.tv_usec = (timeout%1000)*1000;
#endif
comm_timer_set(mstate->s.serve_expired_data->timer, &t);
}
return 1;
}
void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
struct respip_client_info* cinfo, uint16_t qflags,
struct edns_data* edns, struct comm_reply* rep, uint16_t qid,
int rpz_passthru)
{
struct mesh_state* s = NULL;
int unique = unique_mesh_state(edns->opt_list_in, mesh->env);
int was_detached = 0;
int was_noreply = 0;
int added = 0;
int timeout = mesh->env->cfg->serve_expired?
mesh->env->cfg->serve_expired_client_timeout:0;
struct sldns_buffer* r_buffer = rep->c->buffer;
if(rep->c->tcp_req_info) {
r_buffer = rep->c->tcp_req_info->spool_buffer;
}
if(!unique)
s = mesh_area_find(mesh, cinfo, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0);
/* does this create a new reply state? */
if(!s || s->list_select == mesh_no_list) {
if(!mesh_make_new_space(mesh, rep->c->buffer)) {
verbose(VERB_ALGO, "Too many queries. dropping "
"incoming query.");
comm_point_drop_reply(rep);
mesh->stats_dropped++;
return;
}
/* for this new reply state, the reply address is free,
* so the limit of reply addresses does not stop reply states*/
} else {
/* protect our memory usage from storing reply addresses */
if(mesh->num_reply_addrs > mesh->max_reply_states*16) {
verbose(VERB_ALGO, "Too many requests queued. "
"dropping incoming query.");
comm_point_drop_reply(rep);
mesh->stats_dropped++;
return;
}
}
/* see if it already exists, if not, create one */
if(!s) {
#ifdef UNBOUND_DEBUG
struct rbnode_type* n;
#endif
s = mesh_state_create(mesh->env, qinfo, cinfo,
qflags&(BIT_RD|BIT_CD), 0, 0);
if(!s) {
log_err("mesh_state_create: 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);
return;
}
/* set detached (it is now) */
mesh->num_detached_states++;
if(unique)
mesh_state_make_unique(s);
s->s.rpz_passthru = rpz_passthru;
/* copy the edns options we got from the front */
if(edns->opt_list_in) {
s->s.edns_opts_front_in = edns_opt_copy_region(edns->opt_list_in,
s->s.region);
if(!s->s.edns_opts_front_in) {
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;
}
}
#ifdef UNBOUND_DEBUG
n =
#else
(void)
#endif
rbtree_insert(&mesh->all, &s->node);
log_assert(n != NULL);
added = 1;
}
if(!s->reply_list && !s->cb_list) {
was_noreply = 1;
if(s->super_set.count == 0) {
was_detached = 1;
}
}
/* add reply to s */
if(!mesh_state_add_reply(s, edns, rep, qid, qflags, qinfo)) {
log_err("mesh_new_client: out of memory; SERVFAIL");
goto servfail_mem;
}
if(rep->c->tcp_req_info) {
if(!tcp_req_info_add_meshstate(rep->c->tcp_req_info, mesh, s)) {
log_err("mesh_new_client: out of memory add tcpreqinfo");
goto servfail_mem;
}
}
if(rep->c->use_h2) {
http2_stream_add_meshstate(rep->c->h2_stream, mesh, s);
}
/* add serve expired timer if required and not already there */
if(timeout && !mesh_serve_expired_init(s, timeout)) {
log_err("mesh_new_client: out of memory initializing serve expired");
goto servfail_mem;
}
/* update statistics */
if(was_detached) {
log_assert(mesh->num_detached_states > 0);
mesh->num_detached_states--;
}
if(was_noreply) {
mesh->num_reply_states ++;
}
mesh->num_reply_addrs++;
if(s->list_select == mesh_no_list) {
/* move to either the forever or the jostle_list */
if(mesh->num_forever_states < mesh->max_forever_states) {
mesh->num_forever_states ++;
mesh_list_insert(s, &mesh->forever_first,
&mesh->forever_last);
s->list_select = mesh_forever_list;
} else {
mesh_list_insert(s, &mesh->jostle_first,
&mesh->jostle_last);
s->list_select = mesh_jostle_list;
}
}
if(added)
mesh_run(mesh, s, module_event_new, NULL);
return;
servfail_mem:
if(!inplace_cb_reply_servfail_call(mesh->env, qinfo, &s->s,
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);
if(added)
mesh_state_delete(&s->s);
return;
}
int
mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
uint16_t qflags, struct edns_data* edns, sldns_buffer* buf,
uint16_t qid, mesh_cb_func_type cb, void* cb_arg, int rpz_passthru)
{
struct mesh_state* s = NULL;
int unique = unique_mesh_state(edns->opt_list_in, mesh->env);
int timeout = mesh->env->cfg->serve_expired?
mesh->env->cfg->serve_expired_client_timeout:0;
int was_detached = 0;
int was_noreply = 0;
int added = 0;
if(!unique)
s = mesh_area_find(mesh, NULL, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0);
/* there are no limits on the number of callbacks */
/* see if it already exists, if not, create one */
if(!s) {
#ifdef UNBOUND_DEBUG
struct rbnode_type* n;
#endif
s = mesh_state_create(mesh->env, qinfo, NULL,
qflags&(BIT_RD|BIT_CD), 0, 0);
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;
if(edns->opt_list_in) {
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;
}
}
#ifdef UNBOUND_DEBUG
n =
#else
(void)
#endif
rbtree_insert(&mesh->all, &s->node);
log_assert(n != NULL);
added = 1;
}
if(!s->reply_list && !s->cb_list) {
was_noreply = 1;
if(s->super_set.count == 0) {
was_detached = 1;
}
}
/* add reply to s */
if(!mesh_state_add_cb(s, edns, buf, cb, cb_arg, qid, qflags)) {
if(added)
mesh_state_delete(&s->s);
return 0;
}
/* 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 */
if(was_detached) {
log_assert(mesh->num_detached_states > 0);
mesh->num_detached_states--;
}
if(was_noreply) {
mesh->num_reply_states ++;
}
mesh->num_reply_addrs++;
if(added)
mesh_run(mesh, s, module_event_new, NULL);
return 1;
}
/* Internal backend routine of mesh_new_prefetch(). It takes one additional
* parameter, 'run', which controls whether to run the prefetch state
* immediately. When this function is called internally 'run' could be
* 0 (false), in which case the new state is only made runnable so it
* will not be run recursively on top of the current state. */
static void mesh_schedule_prefetch(struct mesh_area* mesh,
struct query_info* qinfo, uint16_t qflags, time_t leeway, int run,
int rpz_passthru)
{
struct mesh_state* s = mesh_area_find(mesh, NULL, qinfo,
qflags&(BIT_RD|BIT_CD), 0, 0);
#ifdef UNBOUND_DEBUG
struct rbnode_type* n;
#endif
/* already exists, and for a different purpose perhaps.
* if mesh_no_list, keep it that way. */
if(s) {
/* make it ignore the cache from now on */
if(!s->s.blacklist)
sock_list_insert(&s->s.blacklist, NULL, 0, s->s.region);
if(s->s.prefetch_leeway < leeway)
s->s.prefetch_leeway = leeway;
return;
}
if(!mesh_make_new_space(mesh, NULL)) {
verbose(VERB_ALGO, "Too many queries. dropped prefetch.");
mesh->stats_dropped ++;
return;
}
s = mesh_state_create(mesh->env, qinfo, NULL,
qflags&(BIT_RD|BIT_CD), 0, 0);
if(!s) {
log_err("prefetch mesh_state_create: out of memory");
return;
}
#ifdef UNBOUND_DEBUG
n =
#else
(void)
#endif
rbtree_insert(&mesh->all, &s->node);
log_assert(n != NULL);
/* set detached (it is now) */
mesh->num_detached_states++;
/* make it ignore the cache */
sock_list_insert(&s->s.blacklist, NULL, 0, s->s.region);
s->s.prefetch_leeway = leeway;
if(s->list_select == mesh_no_list) {
/* move to either the forever or the jostle_list */
if(mesh->num_forever_states < mesh->max_forever_states) {
mesh->num_forever_states ++;
mesh_list_insert(s, &mesh->forever_first,
&mesh->forever_last);
s->list_select = mesh_forever_list;
} else {
mesh_list_insert(s, &mesh->jostle_first,
&mesh->jostle_last);
s->list_select = mesh_jostle_list;
}
}
s->s.rpz_passthru = rpz_passthru;
if(!run) {
#ifdef UNBOUND_DEBUG
n =
#else
(void)
#endif
rbtree_insert(&mesh->run, &s->run_node);
log_assert(n != NULL);
return;
}
mesh_run(mesh, s, module_event_new, NULL);
}
#ifdef CLIENT_SUBNET
/* Same logic as mesh_schedule_prefetch but tailored to the subnet module logic
* like passing along the comm_reply info. This will be faked into an EDNS
* option for processing by the subnet module if the client has not already
* attached its own ECS data. */
static void mesh_schedule_prefetch_subnet(struct mesh_area* mesh,
struct query_info* qinfo, uint16_t qflags, time_t leeway, int run,
int rpz_passthru, struct sockaddr_storage* addr, struct edns_option* edns_list)
{
struct mesh_state* s = NULL;
struct edns_option* opt = NULL;
#ifdef UNBOUND_DEBUG
struct rbnode_type* n;
#endif
if(!mesh_make_new_space(mesh, NULL)) {
verbose(VERB_ALGO, "Too many queries. dropped prefetch.");
mesh->stats_dropped ++;
return;
}
s = mesh_state_create(mesh->env, qinfo, NULL,
qflags&(BIT_RD|BIT_CD), 0, 0);
if(!s) {
log_err("prefetch_subnet mesh_state_create: out of memory");
return;
}
mesh_state_make_unique(s);
opt = edns_opt_list_find(edns_list, mesh->env->cfg->client_subnet_opcode);
if(opt) {
/* Use the client's ECS data */
if(!edns_opt_list_append(&s->s.edns_opts_front_in, opt->opt_code,
opt->opt_len, opt->opt_data, s->s.region)) {
log_err("prefetch_subnet edns_opt_list_append: out of memory");
return;
}
} else {
/* 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 =
#else
(void)
#endif
rbtree_insert(&mesh->all, &s->node);
log_assert(n != NULL);
/* set detached (it is now) */
mesh->num_detached_states++;
/* make it ignore the cache */
sock_list_insert(&s->s.blacklist, NULL, 0, s->s.region);
s->s.prefetch_leeway = leeway;
if(s->list_select == mesh_no_list) {
/* move to either the forever or the jostle_list */
if(mesh->num_forever_states < mesh->max_forever_states) {
mesh->num_forever_states ++;
mesh_list_insert(s, &mesh->forever_first,
&mesh->forever_last);
s->list_select = mesh_forever_list;
} else {
mesh_list_insert(s, &mesh->jostle_first,
&mesh->jostle_last);
s->list_select = mesh_jostle_list;
}
}
s->s.rpz_passthru = rpz_passthru;
if(!run) {
#ifdef UNBOUND_DEBUG
n =
#else
(void)
#endif
rbtree_insert(&mesh->run, &s->run_node);
log_assert(n != NULL);
return;
}
mesh_run(mesh, s, module_event_new, NULL);
}
#endif /* CLIENT_SUBNET */
void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo,
uint16_t qflags, time_t leeway, int rpz_passthru,
struct sockaddr_storage* addr, struct edns_option* opt_list)
{
(void)addr;
(void)opt_list;
#ifdef CLIENT_SUBNET
if(addr)
mesh_schedule_prefetch_subnet(mesh, qinfo, qflags, leeway, 1,
rpz_passthru, addr, opt_list);
else
#endif
mesh_schedule_prefetch(mesh, qinfo, qflags, leeway, 1,
rpz_passthru);
}
void mesh_report_reply(struct mesh_area* mesh, struct outbound_entry* e,
struct comm_reply* reply, int what)
{
enum module_ev event = module_event_reply;
e->qstate->reply = reply;
if(what != NETEVENT_NOERROR) {
event = module_event_noreply;
if(what == NETEVENT_CAPSFAIL)
event = module_event_capsfail;
}
mesh_run(mesh, e->qstate->mesh_info, event, e);
}
struct mesh_state*
mesh_state_create(struct module_env* env, struct query_info* qinfo,
struct respip_client_info* cinfo, uint16_t qflags, int prime,
int valrec)
{
struct regional* region = alloc_reg_obtain(env->alloc);
struct mesh_state* mstate;
int i;
if(!region)
return NULL;
mstate = (struct mesh_state*)regional_alloc(region,
sizeof(struct mesh_state));
if(!mstate) {
alloc_reg_release(env->alloc, region);
return NULL;
}
memset(mstate, 0, sizeof(*mstate));
mstate->node = *RBTREE_NULL;
mstate->run_node = *RBTREE_NULL;
mstate->node.key = mstate;
mstate->run_node.key = mstate;
mstate->reply_list = NULL;
mstate->list_select = mesh_no_list;
mstate->replies_sent = 0;
rbtree_init(&mstate->super_set, &mesh_state_ref_compare);
rbtree_init(&mstate->sub_set, &mesh_state_ref_compare);
mstate->num_activated = 0;
mstate->unique = NULL;
/* init module qstate */
mstate->s.qinfo.qtype = qinfo->qtype;
mstate->s.qinfo.qclass = qinfo->qclass;
mstate->s.qinfo.local_alias = NULL;
mstate->s.qinfo.qname_len = qinfo->qname_len;
mstate->s.qinfo.qname = regional_alloc_init(region, qinfo->qname,
qinfo->qname_len);
if(!mstate->s.qinfo.qname) {
alloc_reg_release(env->alloc, region);
return NULL;
}
if(cinfo) {
mstate->s.client_info = regional_alloc_init(region, cinfo,
sizeof(*cinfo));
if(!mstate->s.client_info) {
alloc_reg_release(env->alloc, region);
return NULL;
}
}
/* remove all weird bits from qflags */
mstate->s.query_flags = (qflags & (BIT_RD|BIT_CD));
mstate->s.is_priming = prime;
mstate->s.is_valrec = valrec;
mstate->s.reply = NULL;
mstate->s.region = region;
mstate->s.curmod = 0;
mstate->s.return_msg = 0;
mstate->s.return_rcode = LDNS_RCODE_NOERROR;
mstate->s.env = env;
mstate->s.mesh_info = mstate;
mstate->s.prefetch_leeway = 0;
mstate->s.serve_expired_data = NULL;
mstate->s.no_cache_lookup = 0;
mstate->s.no_cache_store = 0;
mstate->s.need_refetch = 0;
mstate->s.was_ratelimited = 0;
mstate->s.qstarttime = *env->now;
/* init modules */
for(i=0; i<env->mesh->mods.num; i++) {
mstate->s.minfo[i] = NULL;
mstate->s.ext_state[i] = module_state_initial;
}
/* init edns option lists */
mstate->s.edns_opts_front_in = NULL;
mstate->s.edns_opts_back_out = NULL;
mstate->s.edns_opts_back_in = NULL;
mstate->s.edns_opts_front_out = NULL;
return mstate;
}
void
mesh_state_make_unique(struct mesh_state* mstate)
{
mstate->unique = mstate;
}
void
mesh_state_cleanup(struct mesh_state* mstate)
{
struct mesh_area* mesh;
int i;
if(!mstate)
return;
mesh = mstate->s.env->mesh;
/* Stop and delete the serve expired timer */
if(mstate->s.serve_expired_data && mstate->s.serve_expired_data->timer) {
comm_timer_delete(mstate->s.serve_expired_data->timer);
mstate->s.serve_expired_data->timer = NULL;
}
/* drop unsent replies */
if(!mstate->replies_sent) {
struct mesh_reply* rep = mstate->reply_list;
struct mesh_cb* cb;
/* in tcp_req_info, the mstates linked are removed, but
* the reply_list is now NULL, so the remove-from-empty-list
* takes no time and also it does not do the mesh accounting */
mstate->reply_list = NULL;
for(; rep; rep=rep->next) {
comm_point_drop_reply(&rep->query_reply);
log_assert(mesh->num_reply_addrs > 0);
mesh->num_reply_addrs--;
}
while((cb = mstate->cb_list)!=NULL) {
mstate->cb_list = cb->next;
fptr_ok(fptr_whitelist_mesh_cb(cb->cb));
(*cb->cb)(cb->cb_arg, LDNS_RCODE_SERVFAIL, NULL,
sec_status_unchecked, NULL, 0);
log_assert(mesh->num_reply_addrs > 0);
mesh->num_reply_addrs--;
}
}
/* de-init modules */
for(i=0; i<mesh->mods.num; i++) {
fptr_ok(fptr_whitelist_mod_clear(mesh->mods.mod[i]->clear));
(*mesh->mods.mod[i]->clear)(&mstate->s, i);
mstate->s.minfo[i] = NULL;
mstate->s.ext_state[i] = module_finished;
}
alloc_reg_release(mstate->s.env->alloc, mstate->s.region);
}
void
mesh_state_delete(struct module_qstate* qstate)
{
struct mesh_area* mesh;
struct mesh_state_ref* super, ref;
struct mesh_state* mstate;
if(!qstate)
return;
mstate = qstate->mesh_info;
mesh = mstate->s.env->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->forever_last);
} else if(mstate->list_select == mesh_jostle_list) {
mesh_list_remove(mstate, &mesh->jostle_first,
&mesh->jostle_last);
}
if(!mstate->reply_list && !mstate->cb_list
&& mstate->super_set.count == 0) {
log_assert(mesh->num_detached_states > 0);
mesh->num_detached_states--;
}
if(mstate->reply_list || mstate->cb_list) {
log_assert(mesh->num_reply_states > 0);
mesh->num_reply_states--;
}
ref.node.key = &ref;
ref.s = mstate;
RBTREE_FOR(super, struct mesh_state_ref*, &mstate->super_set) {
(void)rbtree_delete(&super->s->sub_set, &ref);
}
(void)rbtree_delete(&mesh->run, mstate);
(void)rbtree_delete(&mesh->all, mstate);
mesh_state_cleanup(mstate);
}
/** helper recursive rbtree find routine */
static int
find_in_subsub(struct mesh_state* m, struct mesh_state* tofind, size_t *c)
{
struct mesh_state_ref* r;
if((*c)++ > MESH_MAX_SUBSUB)
return 1;
RBTREE_FOR(r, struct mesh_state_ref*, &m->sub_set) {
if(r->s == tofind || find_in_subsub(r->s, tofind, c))
return 1;
}
return 0;
}
/** find cycle for already looked up mesh_state */
static int
mesh_detect_cycle_found(struct module_qstate* qstate, struct mesh_state* dep_m)
{
struct mesh_state* cyc_m = qstate->mesh_info;
size_t counter = 0;
if(!dep_m)
return 0;
if(dep_m == cyc_m || find_in_subsub(dep_m, cyc_m, &counter)) {
if(counter > MESH_MAX_SUBSUB)
return 2;
return 1;
}
return 0;
}
void mesh_detach_subs(struct module_qstate* qstate)
{
struct mesh_area* mesh = qstate->env->mesh;
struct mesh_state_ref* ref, lookup;
#ifdef UNBOUND_DEBUG
struct rbnode_type* n;
#endif
lookup.node.key = &lookup;
lookup.s = qstate->mesh_info;
RBTREE_FOR(ref, struct mesh_state_ref*, &qstate->mesh_info->sub_set) {
#ifdef UNBOUND_DEBUG
n =
#else
(void)
#endif
rbtree_delete(&ref->s->super_set, &lookup);
log_assert(n != NULL); /* must have been present */
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 +
mesh->num_reply_states <= mesh->all.count);
}
}
rbtree_init(&qstate->mesh_info->sub_set, &mesh_state_ref_compare);
}
int mesh_add_sub(struct module_qstate* qstate, struct query_info* qinfo,
uint16_t qflags, int prime, int valrec, struct module_qstate** newq,
struct mesh_state** sub)
{
/* find it, if not, create it */
struct mesh_area* mesh = qstate->env->mesh;
*sub = mesh_area_find(mesh, NULL, qinfo, qflags,
prime, valrec);
if(mesh_detect_cycle_found(qstate, *sub)) {
verbose(VERB_ALGO, "attach failed, cycle detected");
return 0;
}
if(!*sub) {
#ifdef UNBOUND_DEBUG
struct rbnode_type* n;
#endif
/* create a new one */
*sub = mesh_state_create(qstate->env, qinfo, NULL, qflags, prime,
valrec);
if(!*sub) {
log_err("mesh_attach_sub: out of memory");
return 0;
}
#ifdef UNBOUND_DEBUG
n =
#else
(void)
#endif
rbtree_insert(&mesh->all, &(*sub)->node);
log_assert(n != NULL);
/* set detached (it is now) */
mesh->num_detached_states++;
/* set new query state to run */
#ifdef UNBOUND_DEBUG
n =
#else
(void)
#endif
rbtree_insert(&mesh->run, &(*sub)->run_node);
log_assert(n != NULL);
*newq = &(*sub)->s;
} else
*newq = NULL;
return 1;
}
int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo,
uint16_t qflags, int prime, int valrec, struct module_qstate** newq)
{
struct mesh_area* mesh = qstate->env->mesh;
struct mesh_state* sub = NULL;
int was_detached;
if(!mesh_add_sub(qstate, qinfo, qflags, prime, valrec, newq, &sub))
return 0;
was_detached = (sub->super_set.count == 0);
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 &&
sub->super_set.count == 1) {
/* it used to be detached, before this one got added */
log_assert(mesh->num_detached_states > 0);
mesh->num_detached_states--;
}
/* *newq will be run when inited after the current module stops */
return 1;
}
int mesh_state_attachment(struct mesh_state* super, struct mesh_state* sub)
{
#ifdef UNBOUND_DEBUG
struct rbnode_type* n;
#endif
struct mesh_state_ref* subref; /* points to sub, inserted in super */
struct mesh_state_ref* superref; /* points to super, inserted in sub */
if( !(subref = regional_alloc(super->s.region,
sizeof(struct mesh_state_ref))) ||
!(superref = regional_alloc(sub->s.region,
sizeof(struct mesh_state_ref))) ) {
log_err("mesh_state_attachment: out of memory");
return 0;
}
superref->node.key = superref;
superref->s = super;
subref->node.key = subref;
subref->s = sub;
if(!rbtree_insert(&sub->super_set, &superref->node)) {
/* this should not happen, iterator and validator do not
* attach subqueries that are identical. */
/* already attached, we are done, nothing todo.
* since superref and subref already allocated in region,
* we cannot free them */
return 1;
}
#ifdef UNBOUND_DEBUG
n =
#else
(void)
#endif
rbtree_insert(&super->sub_set, &subref->node);
log_assert(n != NULL); /* we checked above if statement, the reverse
administration should not fail now, unless they are out of sync */
return 1;
}
/**
* callback results to mesh cb entry
* @param m: mesh state to send it for.
* @param rcode: if not 0, error code.
* @param rep: reply to send (or NULL if rcode is set).
* @param r: callback entry
* @param start_time: the time to pass to callback functions, it is 0 or
* a value from one of the packets if the mesh state had packets.
*/
static void
mesh_do_callback(struct mesh_state* m, int rcode, struct reply_info* rep,
struct mesh_cb* r, struct timeval* start_time)
{
int secure;
char* reason = NULL;
int was_ratelimited = m->s.was_ratelimited;
/* bogus messages are not made into servfail, sec_status passed
* to the callback function */
if(rep && rep->security == sec_status_secure)
secure = 1;
else secure = 0;
if(!rep && rcode == LDNS_RCODE_NOERROR)
rcode = LDNS_RCODE_SERVFAIL;
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;
}
/* send the reply */
if(rcode) {
if(rcode == LDNS_RCODE_SERVFAIL) {
if(!inplace_cb_reply_servfail_call(m->s.env, &m->s.qinfo, &m->s,
rep, rcode, &r->edns, NULL, m->s.region, start_time))
r->edns.opt_list_inplace_cb_out = NULL;
} else {
if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep, rcode,
&r->edns, NULL, m->s.region, start_time))
r->edns.opt_list_inplace_cb_out = NULL;
}
fptr_ok(fptr_whitelist_mesh_cb(r->cb));
(*r->cb)(r->cb_arg, rcode, r->buf, sec_status_unchecked, NULL,
was_ratelimited);
} else {
size_t udp_size = r->edns.udp_size;
sldns_buffer_clear(r->buf);
r->edns.edns_version = EDNS_ADVERTISED_VERSION;
r->edns.udp_size = EDNS_ADVERTISED_SIZE;
r->edns.ext_rcode = 0;
r->edns.bits &= EDNS_DO;
+ if(m->s.env->cfg->disable_edns_do && (r->edns.bits&EDNS_DO))
+ r->edns.edns_present = 0;
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))
{
fptr_ok(fptr_whitelist_mesh_cb(r->cb));
(*r->cb)(r->cb_arg, LDNS_RCODE_SERVFAIL, r->buf,
sec_status_unchecked, NULL, 0);
} else {
fptr_ok(fptr_whitelist_mesh_cb(r->cb));
(*r->cb)(r->cb_arg, LDNS_RCODE_NOERROR, r->buf,
(rep?rep->security:sec_status_unchecked),
reason, was_ratelimited);
}
}
free(reason);
log_assert(m->s.env->mesh->num_reply_addrs > 0);
m->s.env->mesh->num_reply_addrs--;
}
static inline int
mesh_is_rpz_respip_tcponly_action(struct mesh_state const* m)
{
struct respip_action_info const* respip_info = m->s.respip_action_info;
- return respip_info == NULL
+ return (respip_info == NULL
? 0
: (respip_info->rpz_used
&& !respip_info->rpz_disabled
- && respip_info->action == respip_truncate);
+ && respip_info->action == respip_truncate))
+ || m->s.tcp_required;
}
static inline int
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.
* @param rcode: if not 0, error code.
* @param rep: reply to send (or NULL if rcode is set).
* @param r: reply entry
* @param r_buffer: buffer to use for reply entry.
* @param prev: previous reply, already has its answer encoded in buffer.
* @param prev_buffer: buffer for previous reply.
*/
static void
mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
struct mesh_reply* r, struct sldns_buffer* r_buffer,
struct mesh_reply* prev, struct sldns_buffer* prev_buffer)
{
struct timeval end_time;
struct timeval duration;
int secure;
/* briefly set the replylist to null in case the
* meshsendreply calls tcpreqinfo sendreply that
* comm_point_drops because of size, and then the
* null stops the mesh state remove and thus
* reply_list modification and accounting */
struct mesh_reply* rlist = m->reply_list;
/* rpz: apply actions */
rcode = mesh_is_udp(r) && mesh_is_rpz_respip_tcponly_action(m)
? (rcode|BIT_TC) : rcode;
/* examine security status */
if(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)) {
rcode = LDNS_RCODE_SERVFAIL;
if(m->s.env->cfg->stat_extended)
m->s.env->mesh->ans_bogus++;
}
if(rep && rep->security == sec_status_secure)
secure = 1;
else secure = 0;
if(!rep && rcode == LDNS_RCODE_NOERROR)
rcode = LDNS_RCODE_SERVFAIL;
if(r->query_reply.c->use_h2) {
r->query_reply.c->h2_stream = r->h2_stream;
/* Mesh reply won't exist for long anymore. Make it impossible
* for HTTP/2 stream to refer to mesh state, in case
* connection gets cleanup before HTTP/2 stream close. */
r->h2_stream->mesh_state = NULL;
}
/* send the reply */
/* We don't reuse the encoded answer if:
* - either the previous or current response has a local alias. We could
* compare the alias records and still reuse the previous answer if they
* are the same, but that would be complicated and error prone for the
* relatively minor case. So we err on the side of safety.
* - there are registered callback functions for the given rcode, as these
* need to be called for each reply. */
if(((rcode != LDNS_RCODE_SERVFAIL &&
!m->s.env->inplace_cb_lists[inplace_cb_reply]) ||
(rcode == LDNS_RCODE_SERVFAIL &&
!m->s.env->inplace_cb_lists[inplace_cb_reply_servfail])) &&
prev && prev_buffer && prev->qflags == r->qflags &&
!prev->local_alias && !r->local_alias &&
prev->edns.edns_present == r->edns.edns_present &&
prev->edns.bits == r->edns.bits &&
prev->edns.udp_size == r->edns.udp_size &&
edns_opt_list_compare(prev->edns.opt_list_out, r->edns.opt_list_out) == 0 &&
edns_opt_list_compare(prev->edns.opt_list_inplace_cb_out, r->edns.opt_list_inplace_cb_out) == 0
) {
/* if the previous reply is identical to this one, fix ID */
if(prev_buffer != r_buffer)
sldns_buffer_copy(r_buffer, prev_buffer);
sldns_buffer_write_at(r_buffer, 0, &r->qid, sizeof(uint16_t));
sldns_buffer_write_at(r_buffer, 12, r->qname,
m->s.qinfo.qname_len);
m->reply_list = NULL;
comm_point_send_reply(&r->query_reply);
m->reply_list = rlist;
} else if(rcode) {
m->s.qinfo.qname = r->qname;
m->s.qinfo.local_alias = r->local_alias;
if(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 {
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 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);
m->reply_list = NULL;
comm_point_send_reply(&r->query_reply);
m->reply_list = rlist;
} else {
size_t udp_size = r->edns.udp_size;
r->edns.edns_version = EDNS_ADVERTISED_VERSION;
r->edns.udp_size = EDNS_ADVERTISED_SIZE;
r->edns.ext_rcode = 0;
r->edns.bits &= EDNS_DO;
+ if(m->s.env->cfg->disable_edns_do && (r->edns.bits&EDNS_DO))
+ r->edns.edns_present = 0;
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,
r->qflags, r_buffer, 0, 1, m->s.env->scratch,
udp_size, &r->edns, (int)(r->edns.bits & EDNS_DO),
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))
r->edns.opt_list_inplace_cb_out = NULL;
/* internal server error (probably malloc failure) so no
* EDE (RFC8914) needed */
error_encode(r_buffer, LDNS_RCODE_SERVFAIL,
&m->s.qinfo, r->qid, r->qflags, &r->edns);
}
m->reply_list = NULL;
comm_point_send_reply(&r->query_reply);
m->reply_list = rlist;
}
/* account */
log_assert(m->s.env->mesh->num_reply_addrs > 0);
m->s.env->mesh->num_reply_addrs--;
end_time = *m->s.env->now_tv;
timeval_subtract(&duration, &end_time, &r->start_time);
verbose(VERB_ALGO, "query took " ARG_LL "d.%6.6d sec",
(long long)duration.tv_sec, (int)duration.tv_usec);
m->s.env->mesh->replies_sent++;
timeval_add(&m->s.env->mesh->replies_sum_wait, &duration);
timehist_insert(m->s.env->mesh->histogram, &duration);
if(m->s.env->cfg->stat_extended) {
uint16_t rc = FLAGS_GET_RCODE(sldns_buffer_read_u16_at(
r_buffer, 2));
if(secure) m->s.env->mesh->ans_secure++;
m->s.env->mesh->ans_rcode[ rc ] ++;
if(rc == 0 && LDNS_ANCOUNT(sldns_buffer_begin(r_buffer)) == 0)
m->s.env->mesh->ans_nodata++;
}
/* Log reply sent */
if(m->s.env->cfg->log_replies) {
log_reply_info(NO_VERBOSE, &m->s.qinfo,
&r->query_reply.client_addr,
r->query_reply.client_addrlen, duration, 0, r_buffer);
}
}
void mesh_query_done(struct mesh_state* mstate)
{
struct mesh_reply* r;
struct mesh_reply* prev = NULL;
struct sldns_buffer* prev_buffer = NULL;
struct mesh_cb* c;
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);
mstate->s.serve_expired_data->timer = NULL;
}
if(mstate->s.return_rcode == LDNS_RCODE_SERVFAIL ||
(rep && FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_SERVFAIL)) {
/* we are SERVFAILing; check for expired answer here */
mesh_serve_expired_callback(mstate);
if((mstate->reply_list || mstate->cb_list)
&& mstate->s.env->cfg->log_servfail
&& !mstate->s.env->cfg->val_log_squelch) {
char* err = errinf_to_str_servfail(&mstate->s);
if(err)
log_err("%s", err);
free(err);
}
}
for(r = mstate->reply_list; r; r = r->next) {
i++;
tv = r->start_time;
/* if a response-ip address block has been stored the
* information should be logged for each client. */
if(mstate->s.respip_action_info &&
mstate->s.respip_action_info->addrinfo) {
respip_inform_print(mstate->s.respip_action_info,
r->qname, mstate->s.qinfo.qtype,
mstate->s.qinfo.qclass, r->local_alias,
&r->query_reply.client_addr,
r->query_reply.client_addrlen);
}
/* if this query is determined to be dropped during the
* mesh processing, this is the point to take that action. */
if(mstate->s.is_drop) {
/* briefly set the reply_list to NULL, so that the
* tcp req info cleanup routine that calls the mesh
* to deregister the meshstate for it is not done
* because the list is NULL and also accounting is not
* done there, but instead we do that here. */
struct mesh_reply* reply_list = mstate->reply_list;
mstate->reply_list = NULL;
comm_point_drop_reply(&r->query_reply);
mstate->reply_list = reply_list;
} else {
struct sldns_buffer* r_buffer = r->query_reply.c->buffer;
if(r->query_reply.c->tcp_req_info) {
r_buffer = r->query_reply.c->tcp_req_info->spool_buffer;
prev_buffer = NULL;
}
mesh_send_reply(mstate, mstate->s.return_rcode, rep,
r, r_buffer, prev, prev_buffer);
if(r->query_reply.c->tcp_req_info) {
tcp_req_info_remove_mesh_state(r->query_reply.c->tcp_req_info, mstate);
r_buffer = NULL;
}
prev = r;
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) {
/* was a reply state, not anymore */
log_assert(mstate->s.env->mesh->num_reply_states > 0);
mstate->s.env->mesh->num_reply_states--;
}
if(!mstate->reply_list && !mstate->cb_list &&
mstate->super_set.count == 0)
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 */
if(!mstate->reply_list && mstate->cb_list && !c->next) {
/* was a reply state, not anymore */
log_assert(mstate->s.env->mesh->num_reply_states > 0);
mstate->s.env->mesh->num_reply_states--;
}
mstate->cb_list = c->next;
if(!mstate->reply_list && !mstate->cb_list &&
mstate->super_set.count == 0)
mstate->s.env->mesh->num_detached_states++;
mesh_do_callback(mstate, mstate->s.return_rcode, rep, c, &tv);
}
}
void mesh_walk_supers(struct mesh_area* mesh, struct mesh_state* mstate)
{
struct mesh_state_ref* ref;
RBTREE_FOR(ref, struct mesh_state_ref*, &mstate->super_set)
{
/* make super runnable */
(void)rbtree_insert(&mesh->run, &ref->s->run_node);
/* 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,
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);
}
}
struct mesh_state* mesh_area_find(struct mesh_area* mesh,
struct respip_client_info* cinfo, struct query_info* qinfo,
uint16_t qflags, int prime, int valrec)
{
struct mesh_state key;
struct mesh_state* result;
key.node.key = &key;
key.s.is_priming = prime;
key.s.is_valrec = valrec;
key.s.qinfo = *qinfo;
key.s.query_flags = qflags;
/* We are searching for a similar mesh state when we DO want to
* aggregate the state. Thus unique is set to NULL. (default when we
* desire aggregation).*/
key.unique = NULL;
key.s.client_info = cinfo;
result = (struct mesh_state*)rbtree_search(&mesh->all, &key);
return result;
}
int mesh_state_add_cb(struct mesh_state* s, struct edns_data* edns,
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,
sizeof(struct mesh_cb));
if(!r)
return 0;
r->buf = buf;
log_assert(fptr_whitelist_mesh_cb(cb)); /* early failure ifmissing*/
r->cb = cb;
r->cb_arg = cb_arg;
r->edns = *edns;
if(edns->opt_list_in && !(r->edns.opt_list_in =
edns_opt_copy_region(edns->opt_list_in, s->s.region)))
return 0;
if(edns->opt_list_out && !(r->edns.opt_list_out =
edns_opt_copy_region(edns->opt_list_out, s->s.region)))
return 0;
if(edns->opt_list_inplace_cb_out && !(r->edns.opt_list_inplace_cb_out =
edns_opt_copy_region(edns->opt_list_inplace_cb_out, s->s.region)))
return 0;
r->qid = qid;
r->qflags = qflags;
r->next = s->cb_list;
s->cb_list = r;
return 1;
}
int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns,
struct comm_reply* rep, uint16_t qid, uint16_t qflags,
const struct query_info* qinfo)
{
struct mesh_reply* r = regional_alloc(s->s.region,
sizeof(struct mesh_reply));
if(!r)
return 0;
r->query_reply = *rep;
r->edns = *edns;
if(edns->opt_list_in && !(r->edns.opt_list_in =
edns_opt_copy_region(edns->opt_list_in, s->s.region)))
return 0;
if(edns->opt_list_out && !(r->edns.opt_list_out =
edns_opt_copy_region(edns->opt_list_out, s->s.region)))
return 0;
if(edns->opt_list_inplace_cb_out && !(r->edns.opt_list_inplace_cb_out =
edns_opt_copy_region(edns->opt_list_inplace_cb_out, s->s.region)))
return 0;
r->qid = qid;
r->qflags = qflags;
r->start_time = *s->s.env->now_tv;
r->next = s->reply_list;
r->qname = regional_alloc_init(s->s.region, qinfo->qname,
s->s.qinfo.qname_len);
if(!r->qname)
return 0;
if(rep->c->use_h2)
r->h2_stream = rep->c->h2_stream;
/* Data related to local alias stored in 'qinfo' (if any) is ephemeral
* and can be different for different original queries (even if the
* replaced query name is the same). So we need to make a deep copy
* and store the copy for each reply info. */
if(qinfo->local_alias) {
struct packed_rrset_data* d;
struct packed_rrset_data* dsrc;
r->local_alias = regional_alloc_zero(s->s.region,
sizeof(*qinfo->local_alias));
if(!r->local_alias)
return 0;
r->local_alias->rrset = regional_alloc_init(s->s.region,
qinfo->local_alias->rrset,
sizeof(*qinfo->local_alias->rrset));
if(!r->local_alias->rrset)
return 0;
dsrc = qinfo->local_alias->rrset->entry.data;
/* In the current implementation, a local alias must be
* a single CNAME RR (see worker_handle_request()). */
log_assert(!qinfo->local_alias->next && dsrc->count == 1 &&
qinfo->local_alias->rrset->rk.type ==
htons(LDNS_RR_TYPE_CNAME));
/* we should make a local copy for the owner name of
* the RRset */
r->local_alias->rrset->rk.dname_len =
qinfo->local_alias->rrset->rk.dname_len;
r->local_alias->rrset->rk.dname = regional_alloc_init(
s->s.region, qinfo->local_alias->rrset->rk.dname,
qinfo->local_alias->rrset->rk.dname_len);
if(!r->local_alias->rrset->rk.dname)
return 0;
/* the rrset is not packed, like in the cache, but it is
* individually allocated with an allocator from localzone. */
d = regional_alloc_zero(s->s.region, sizeof(*d));
if(!d)
return 0;
r->local_alias->rrset->entry.data = d;
if(!rrset_insert_rr(s->s.region, d, dsrc->rr_data[0],
dsrc->rr_len[0], dsrc->rr_ttl[0], "CNAME local alias"))
return 0;
} else
r->local_alias = NULL;
s->reply_list = r;
return 1;
}
/* Extract the query info and flags from 'mstate' into '*qinfop' and '*qflags'.
* Since this is only used for internal refetch of otherwise-expired answer,
* we simply ignore the rare failure mode when memory allocation fails. */
static void
mesh_copy_qinfo(struct mesh_state* mstate, struct query_info** qinfop,
uint16_t* qflags)
{
struct regional* region = mstate->s.env->scratch;
struct query_info* qinfo;
qinfo = regional_alloc_init(region, &mstate->s.qinfo, sizeof(*qinfo));
if(!qinfo)
return;
qinfo->qname = regional_alloc_init(region, qinfo->qname,
qinfo->qname_len);
if(!qinfo->qname)
return;
*qinfop = qinfo;
*qflags = mstate->s.query_flags;
}
/**
* Continue processing the mesh state at another module.
* Handles module to modules transfer of control.
* Handles module finished.
* @param mesh: the mesh area.
* @param mstate: currently active mesh state.
* 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.
* @param ev: the event sent to the module.
* returned is the event to send to the next module.
* @return true if continue processing at the new module.
* false if not continued processing is needed.
*/
static int
mesh_continue(struct mesh_area* mesh, struct mesh_state* mstate,
enum module_ext_state s, enum module_ev* ev)
{
mstate->num_activated++;
if(mstate->num_activated > MESH_MAX_ACTIVATION) {
/* module is looping. Stop it. */
log_err("internal error: looping module (%s) stopped",
mesh->mods.mod[mstate->s.curmod]->name);
log_query_info(NO_VERBOSE, "pass error for qstate",
&mstate->s.qinfo);
s = module_error;
}
if(s == module_wait_module || s == module_restart_next) {
/* start next module */
mstate->s.curmod++;
if(mesh->mods.num == mstate->s.curmod) {
log_err("Cannot pass to next module; at last module");
log_query_info(VERB_QUERY, "pass error for qstate",
&mstate->s.qinfo);
mstate->s.curmod--;
return mesh_continue(mesh, mstate, module_error, ev);
}
if(s == module_restart_next) {
int curmod = mstate->s.curmod;
for(; mstate->s.curmod < mesh->mods.num;
mstate->s.curmod++) {
fptr_ok(fptr_whitelist_mod_clear(
mesh->mods.mod[mstate->s.curmod]->clear));
(*mesh->mods.mod[mstate->s.curmod]->clear)
(&mstate->s, mstate->s.curmod);
mstate->s.minfo[mstate->s.curmod] = NULL;
}
mstate->s.curmod = curmod;
}
*ev = module_event_pass;
return 1;
}
if(s == module_wait_subquery && mstate->sub_set.count == 0) {
log_err("module cannot wait for subquery, subquery list empty");
log_query_info(VERB_QUERY, "pass error for qstate",
&mstate->s.qinfo);
s = module_error;
}
if(s == module_error && mstate->s.return_rcode == LDNS_RCODE_NOERROR) {
/* error is bad, handle pass back up below */
mstate->s.return_rcode = LDNS_RCODE_SERVFAIL;
}
if(s == module_error) {
mesh_query_done(mstate);
mesh_walk_supers(mesh, mstate);
mesh_state_delete(&mstate->s);
return 0;
}
if(s == module_finished) {
if(mstate->s.curmod == 0) {
struct query_info* qinfo = NULL;
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);
/* If the answer to the query needs to be refetched
* from an external DNS server, we'll need to schedule
* a prefetch after removing the current state, so
* we need to make a copy of the query info here. */
if(mstate->s.need_refetch) {
mesh_copy_qinfo(mstate, &qinfo, &qflags);
#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;
}
if(qinfo) {
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;
}
/* pass along the locus of control */
mstate->s.curmod --;
*ev = module_event_moddone;
return 1;
}
return 0;
}
void mesh_run(struct mesh_area* mesh, struct mesh_state* mstate,
enum module_ev ev, struct outbound_entry* e)
{
enum module_ext_state s;
verbose(VERB_ALGO, "mesh_run: start");
while(mstate) {
/* run the module */
fptr_ok(fptr_whitelist_mod_operate(
mesh->mods.mod[mstate->s.curmod]->operate));
(*mesh->mods.mod[mstate->s.curmod]->operate)
(&mstate->s, ev, mstate->s.curmod, e);
/* examine results */
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",
mesh->mods.mod[mstate->s.curmod]->name, strextstate(s));
e = NULL;
if(mesh_continue(mesh, mstate, s, &ev))
continue;
/* run more modules */
ev = module_event_pass;
if(mesh->run.count > 0) {
/* pop random element off the runnable tree */
mstate = (struct mesh_state*)mesh->run.root->key;
(void)rbtree_delete(&mesh->run, mstate);
} else mstate = NULL;
}
if(verbosity >= VERB_ALGO) {
mesh_stats(mesh, "mesh_run: end");
mesh_log_list(mesh);
}
}
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",
num++, (m->s.is_priming)?"p":"", /* prime */
(m->s.is_valrec)?"v":"", /* prime */
(m->s.query_flags&BIT_RD)?"RD":"",
(m->s.query_flags&BIT_CD)?"CD":"",
(m->super_set.count==0)?"d":"", /* detached */
(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
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,
(unsigned)mesh->num_reply_states,
(unsigned)mesh->num_detached_states,
(unsigned)mesh->num_reply_addrs,
(unsigned)mesh->replies_sent,
(unsigned)mesh->stats_dropped,
(unsigned)mesh->stats_jostled);
if(mesh->replies_sent > 0) {
struct timeval avg;
timeval_divide(&avg, &mesh->replies_sum_wait,
mesh->replies_sent);
log_info("average recursion processing time "
ARG_LL "d.%6.6d sec",
(long long)avg.tv_sec, (int)avg.tv_usec);
log_info("histogram of recursion processing times");
timehist_log(mesh->histogram, "recursions");
}
}
void
mesh_stats_clear(struct mesh_area* mesh)
{
if(!mesh)
return;
mesh->replies_sent = 0;
mesh->replies_sum_wait.tv_sec = 0;
mesh->replies_sum_wait.tv_usec = 0;
mesh->stats_jostled = 0;
mesh->stats_dropped = 0;
timehist_clear(mesh->histogram);
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
mesh_get_mem(struct mesh_area* mesh)
{
struct mesh_state* m;
size_t s = sizeof(*mesh) + sizeof(struct timehist) +
sizeof(struct th_buck)*mesh->histogram->num +
sizeof(sldns_buffer) + sldns_buffer_capacity(mesh->qbuf_bak);
RBTREE_FOR(m, struct mesh_state*, &mesh->all) {
/* all, including m itself allocated in qstate region */
s += regional_get_mem(m->s.region);
}
return s;
}
int
mesh_detect_cycle(struct module_qstate* qstate, struct query_info* qinfo,
uint16_t flags, int prime, int valrec)
{
struct mesh_area* mesh = qstate->env->mesh;
struct mesh_state* dep_m = NULL;
dep_m = mesh_area_find(mesh, NULL, qinfo, flags, prime, valrec);
return mesh_detect_cycle_found(qstate, dep_m);
}
void mesh_list_insert(struct mesh_state* m, struct mesh_state** fp,
struct mesh_state** lp)
{
/* insert as last element */
m->prev = *lp;
m->next = NULL;
if(*lp)
(*lp)->next = m;
else *fp = m;
*lp = m;
}
void mesh_list_remove(struct mesh_state* m, struct mesh_state** fp,
struct mesh_state** lp)
{
if(m->next)
m->next->prev = m->prev;
else *lp = m->prev;
if(m->prev)
m->prev->next = m->next;
else *fp = m->next;
}
void mesh_state_remove_reply(struct mesh_area* mesh, struct mesh_state* m,
struct comm_point* cp)
{
struct mesh_reply* n, *prev = NULL;
n = m->reply_list;
/* when in mesh_cleanup, it sets the reply_list to NULL, so that
* there is no accounting twice */
if(!n) return; /* nothing to remove, also no accounting needed */
while(n) {
if(n->query_reply.c == cp) {
/* unlink it */
if(prev) prev->next = n->next;
else m->reply_list = n->next;
/* delete it, but allocated in m region */
log_assert(mesh->num_reply_addrs > 0);
mesh->num_reply_addrs--;
/* prev = prev; */
n = n->next;
continue;
}
prev = n;
n = n->next;
}
/* it was not detached (because it had a reply list), could be now */
if(!m->reply_list && !m->cb_list
&& m->super_set.count == 0) {
mesh->num_detached_states++;
}
/* if not replies any more in mstate, it is no longer a reply_state */
if(!m->reply_list && !m->cb_list) {
log_assert(mesh->num_reply_states > 0);
mesh->num_reply_states--;
}
}
static int
apply_respip_action(struct module_qstate* qstate,
const struct query_info* qinfo, struct respip_client_info* cinfo,
struct respip_action_info* actinfo, struct reply_info* rep,
struct ub_packed_rrset_key** alias_rrset,
struct reply_info** encode_repp, struct auth_zones* az)
{
if(qinfo->qtype != LDNS_RR_TYPE_A &&
qinfo->qtype != LDNS_RR_TYPE_AAAA &&
qinfo->qtype != LDNS_RR_TYPE_ANY)
return 1;
if(!respip_rewrite_reply(qinfo, cinfo, rep, encode_repp, actinfo,
alias_rrset, 0, qstate->region, az, NULL))
return 0;
/* xxx_deny actions mean dropping the reply, unless the original reply
* was redirected to response-ip data. */
if((actinfo->action == respip_deny ||
actinfo->action == respip_inform_deny) &&
*encode_repp == rep)
*encode_repp = NULL;
return 1;
}
void
mesh_serve_expired_callback(void* arg)
{
struct mesh_state* mstate = (struct mesh_state*) arg;
struct module_qstate* qstate = &mstate->s;
struct mesh_reply* r;
struct mesh_area* mesh = qstate->env->mesh;
struct dns_msg* msg;
struct mesh_cb* c;
struct mesh_reply* prev = NULL;
struct sldns_buffer* prev_buffer = NULL;
struct sldns_buffer* r_buffer = NULL;
struct reply_info* partial_rep = NULL;
struct ub_packed_rrset_key* alias_rrset = NULL;
struct reply_info* encode_rep = NULL;
struct respip_action_info actinfo;
struct query_info* lookup_qinfo = &qstate->qinfo;
struct query_info qinfo_tmp;
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);
qstate->serve_expired_data->timer = NULL;
/* If is_drop or no_cache_lookup (modules that handle their own cache e.g.,
* subnetmod) ignore stale data from the main cache. */
if(qstate->no_cache_lookup || qstate->is_drop) {
verbose(VERB_ALGO,
"Serve expired: Not allowed to look into cache for stale");
return;
}
/* The following while is used instead of the `goto lookup_cache`
* like in the worker. */
while(1) {
fptr_ok(fptr_whitelist_serve_expired_lookup(
qstate->serve_expired_data->get_cached_answer));
msg = (*qstate->serve_expired_data->get_cached_answer)(qstate,
lookup_qinfo);
if(!msg)
return;
/* Reset these in case we pass a second time from here. */
encode_rep = msg->rep;
memset(&actinfo, 0, sizeof(actinfo));
actinfo.action = respip_none;
alias_rrset = NULL;
if((mesh->use_response_ip || mesh->use_rpz) &&
!partial_rep && !apply_respip_action(qstate, &qstate->qinfo,
qstate->client_info, &actinfo, msg->rep, &alias_rrset, &encode_rep,
qstate->env->auth_zones)) {
return;
} else if(partial_rep &&
!respip_merge_cname(partial_rep, &qstate->qinfo, msg->rep,
qstate->client_info, must_validate, &encode_rep, qstate->region,
qstate->env->auth_zones)) {
return;
}
if(!encode_rep || alias_rrset) {
if(!encode_rep) {
/* Needs drop */
return;
} else {
/* A partial CNAME chain is found. */
partial_rep = encode_rep;
}
}
/* We've found a partial reply ending with an
* alias. Replace the lookup qinfo for the
* alias target and lookup the cache again to
* (possibly) complete the reply. As we're
* passing the "base" reply, there will be no
* more alias chasing. */
if(partial_rep) {
memset(&qinfo_tmp, 0, sizeof(qinfo_tmp));
get_cname_target(alias_rrset, &qinfo_tmp.qname,
&qinfo_tmp.qname_len);
if(!qinfo_tmp.qname) {
log_err("Serve expired: unexpected: invalid answer alias");
return;
}
qinfo_tmp.qtype = qstate->qinfo.qtype;
qinfo_tmp.qclass = qstate->qinfo.qclass;
lookup_qinfo = &qinfo_tmp;
continue;
}
break;
}
if(verbosity >= VERB_ALGO)
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
* 'inform' variant and the information should be logged. */
if(actinfo.addrinfo) {
respip_inform_print(&actinfo, r->qname,
qstate->qinfo.qtype, qstate->qinfo.qclass,
r->local_alias, &r->query_reply.client_addr,
r->query_reply.client_addrlen);
}
/* Add EDE Stale Answer (RCF8914). Ignore global ede as this is
* warning instead of an error */
if (r->edns.edns_present && qstate->env->cfg->ede_serve_expired &&
qstate->env->cfg->ede) {
edns_opt_list_append_ede(&r->edns.opt_list_out,
mstate->s.region, LDNS_EDE_STALE_ANSWER, NULL);
}
r_buffer = r->query_reply.c->buffer;
if(r->query_reply.c->tcp_req_info)
r_buffer = r->query_reply.c->tcp_req_info->spool_buffer;
mesh_send_reply(mstate, LDNS_RCODE_NOERROR, msg->rep,
r, r_buffer, prev, prev_buffer);
if(r->query_reply.c->tcp_req_info)
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. */
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) {
log_assert(mesh->num_reply_states > 0);
mesh->num_reply_states--;
if(mstate->super_set.count == 0) {
mesh->num_detached_states++;
}
}
}
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 */
if(!mstate->reply_list && mstate->cb_list && !c->next) {
/* was a reply state, not anymore */
log_assert(qstate->env->mesh->num_reply_states > 0);
qstate->env->mesh->num_reply_states--;
}
mstate->cb_list = c->next;
if(!mstate->reply_list && !mstate->cb_list &&
mstate->super_set.count == 0)
qstate->env->mesh->num_detached_states++;
mesh_do_callback(mstate, LDNS_RCODE_NOERROR, msg->rep, c, &tv);
}
}
int mesh_jostle_exceeded(struct mesh_area* mesh)
{
if(mesh->all.count < mesh->max_reply_states)
return 0;
return 1;
}
diff --git a/contrib/unbound/services/outside_network.c b/contrib/unbound/services/outside_network.c
index 2a219cbc6e92..12923f07d788 100644
--- a/contrib/unbound/services/outside_network.c
+++ b/contrib/unbound/services/outside_network.c
@@ -1,3923 +1,3923 @@
/*
* services/outside_network.c - implement sending of queries and wait answer.
*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file has functions to send queries to authoritative servers and
* wait for the pending answer events.
*/
#include "config.h"
#include <ctype.h>
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#include <sys/time.h>
#include "services/outside_network.h"
#include "services/listen_dnsport.h"
#include "services/cache/infra.h"
#include "iterator/iterator.h"
#include "util/data/msgparse.h"
#include "util/data/msgreply.h"
#include "util/data/msgencode.h"
#include "util/data/dname.h"
#include "util/netevent.h"
#include "util/log.h"
#include "util/net_help.h"
#include "util/random.h"
#include "util/fptr_wlist.h"
#include "util/edns.h"
#include "sldns/sbuffer.h"
#include "dnstap/dnstap.h"
#ifdef HAVE_OPENSSL_SSL_H
#include <openssl/ssl.h>
#endif
#ifdef HAVE_X509_VERIFY_PARAM_SET1_HOST
#include <openssl/x509v3.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#include <fcntl.h>
/** number of times to retry making a random ID that is unique. */
#define MAX_ID_RETRY 1000
/** number of times to retry finding interface, port that can be opened. */
#define MAX_PORT_RETRY 10000
/** number of retries on outgoing UDP queries */
#define OUTBOUND_UDP_RETRY 1
/** initiate TCP transaction for serviced query */
static void serviced_tcp_initiate(struct serviced_query* sq, sldns_buffer* buff);
/** with a fd available, randomize and send UDP */
static int randomize_and_send_udp(struct pending* pend, sldns_buffer* packet,
int timeout);
/** select a DNS ID for a TCP stream */
static uint16_t tcp_select_id(struct outside_network* outnet,
struct reuse_tcp* reuse);
/** Perform serviced query UDP sending operation */
static int serviced_udp_send(struct serviced_query* sq, sldns_buffer* buff);
/** Send serviced query over TCP return false on initial failure */
static int serviced_tcp_send(struct serviced_query* sq, sldns_buffer* buff);
/** call the callbacks for a serviced query */
static void serviced_callbacks(struct serviced_query* sq, int error,
struct comm_point* c, struct comm_reply* rep);
int
pending_cmp(const void* key1, const void* key2)
{
struct pending *p1 = (struct pending*)key1;
struct pending *p2 = (struct pending*)key2;
if(p1->id < p2->id)
return -1;
if(p1->id > p2->id)
return 1;
log_assert(p1->id == p2->id);
return sockaddr_cmp(&p1->addr, p1->addrlen, &p2->addr, p2->addrlen);
}
int
serviced_cmp(const void* key1, const void* key2)
{
struct serviced_query* q1 = (struct serviced_query*)key1;
struct serviced_query* q2 = (struct serviced_query*)key2;
int r;
if(q1->qbuflen < q2->qbuflen)
return -1;
if(q1->qbuflen > q2->qbuflen)
return 1;
log_assert(q1->qbuflen == q2->qbuflen);
log_assert(q1->qbuflen >= 15 /* 10 header, root, type, class */);
/* alternate casing of qname is still the same query */
if((r = memcmp(q1->qbuf, q2->qbuf, 10)) != 0)
return r;
if((r = memcmp(q1->qbuf+q1->qbuflen-4, q2->qbuf+q2->qbuflen-4, 4)) != 0)
return r;
if(q1->dnssec != q2->dnssec) {
if(q1->dnssec < q2->dnssec)
return -1;
return 1;
}
if((r = query_dname_compare(q1->qbuf+10, q2->qbuf+10)) != 0)
return r;
if((r = edns_opt_list_compare(q1->opt_list, q2->opt_list)) != 0)
return r;
return sockaddr_cmp(&q1->addr, q1->addrlen, &q2->addr, q2->addrlen);
}
/** compare if the reuse element has the same address, port and same ssl-is
* used-for-it characteristic */
static int
reuse_cmp_addrportssl(const void* key1, const void* key2)
{
struct reuse_tcp* r1 = (struct reuse_tcp*)key1;
struct reuse_tcp* r2 = (struct reuse_tcp*)key2;
int r;
/* compare address and port */
r = sockaddr_cmp(&r1->addr, r1->addrlen, &r2->addr, r2->addrlen);
if(r != 0)
return r;
/* compare if SSL-enabled */
if(r1->is_ssl && !r2->is_ssl)
return 1;
if(!r1->is_ssl && r2->is_ssl)
return -1;
return 0;
}
int
reuse_cmp(const void* key1, const void* key2)
{
int r;
r = reuse_cmp_addrportssl(key1, key2);
if(r != 0)
return r;
/* compare ptr value */
if(key1 < key2) return -1;
if(key1 > key2) return 1;
return 0;
}
int reuse_id_cmp(const void* key1, const void* key2)
{
struct waiting_tcp* w1 = (struct waiting_tcp*)key1;
struct waiting_tcp* w2 = (struct waiting_tcp*)key2;
if(w1->id < w2->id)
return -1;
if(w1->id > w2->id)
return 1;
return 0;
}
/** delete waiting_tcp entry. Does not unlink from waiting list.
* @param w: to delete.
*/
static void
waiting_tcp_delete(struct waiting_tcp* w)
{
if(!w) return;
if(w->timer)
comm_timer_delete(w->timer);
free(w);
}
/**
* Pick random outgoing-interface of that family, and bind it.
* port set to 0 so OS picks a port number for us.
* if it is the ANY address, do not bind.
* @param pend: pending tcp structure, for storing the local address choice.
* @param w: tcp structure with destination address.
* @param s: socket fd.
* @return false on error, socket closed.
*/
static int
pick_outgoing_tcp(struct pending_tcp* pend, struct waiting_tcp* w, int s)
{
struct port_if* pi = NULL;
int num;
pend->pi = NULL;
#ifdef INET6
if(addr_is_ip6(&w->addr, w->addrlen))
num = w->outnet->num_ip6;
else
#endif
num = w->outnet->num_ip4;
if(num == 0) {
log_err("no TCP outgoing interfaces of family");
log_addr(VERB_OPS, "for addr", &w->addr, w->addrlen);
sock_close(s);
return 0;
}
#ifdef INET6
if(addr_is_ip6(&w->addr, w->addrlen))
pi = &w->outnet->ip6_ifs[ub_random_max(w->outnet->rnd, num)];
else
#endif
pi = &w->outnet->ip4_ifs[ub_random_max(w->outnet->rnd, num)];
log_assert(pi);
pend->pi = pi;
if(addr_is_any(&pi->addr, pi->addrlen)) {
/* binding to the ANY interface is for listening sockets */
return 1;
}
/* set port to 0 */
if(addr_is_ip6(&pi->addr, pi->addrlen))
((struct sockaddr_in6*)&pi->addr)->sin6_port = 0;
else ((struct sockaddr_in*)&pi->addr)->sin_port = 0;
if(bind(s, (struct sockaddr*)&pi->addr, pi->addrlen) != 0) {
#ifndef USE_WINSOCK
#ifdef EADDRNOTAVAIL
if(!(verbosity < 4 && errno == EADDRNOTAVAIL))
#endif
#else /* USE_WINSOCK */
if(!(verbosity < 4 && WSAGetLastError() == WSAEADDRNOTAVAIL))
#endif
log_err("outgoing tcp: bind: %s", sock_strerror(errno));
sock_close(s);
return 0;
}
log_addr(VERB_ALGO, "tcp bound to src", &pi->addr, pi->addrlen);
return 1;
}
/** get TCP file descriptor for address, returns -1 on failure,
* tcp_mss is 0 or maxseg size to set for TCP packets. */
int
outnet_get_tcp_fd(struct sockaddr_storage* addr, socklen_t addrlen, int tcp_mss, int dscp)
{
int s;
int af;
char* err;
#if defined(SO_REUSEADDR) || defined(IP_BIND_ADDRESS_NO_PORT)
int on = 1;
#endif
#ifdef INET6
if(addr_is_ip6(addr, addrlen)){
s = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP);
af = AF_INET6;
} else {
#else
{
#endif
af = AF_INET;
s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
}
if(s == -1) {
log_err_addr("outgoing tcp: socket", sock_strerror(errno),
addr, addrlen);
return -1;
}
#ifdef SO_REUSEADDR
if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
verbose(VERB_ALGO, "outgoing tcp:"
" setsockopt(.. SO_REUSEADDR ..) failed");
}
#endif
err = set_ip_dscp(s, af, dscp);
if(err != NULL) {
verbose(VERB_ALGO, "outgoing tcp:"
"error setting IP DiffServ codepoint on socket");
}
if(tcp_mss > 0) {
#if defined(IPPROTO_TCP) && defined(TCP_MAXSEG)
if(setsockopt(s, IPPROTO_TCP, TCP_MAXSEG,
(void*)&tcp_mss, (socklen_t)sizeof(tcp_mss)) < 0) {
verbose(VERB_ALGO, "outgoing tcp:"
" setsockopt(.. TCP_MAXSEG ..) failed");
}
#else
verbose(VERB_ALGO, "outgoing tcp:"
" setsockopt(TCP_MAXSEG) unsupported");
#endif /* defined(IPPROTO_TCP) && defined(TCP_MAXSEG) */
}
#ifdef IP_BIND_ADDRESS_NO_PORT
if(setsockopt(s, IPPROTO_IP, IP_BIND_ADDRESS_NO_PORT, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
verbose(VERB_ALGO, "outgoing tcp:"
" setsockopt(.. IP_BIND_ADDRESS_NO_PORT ..) failed");
}
#endif /* IP_BIND_ADDRESS_NO_PORT */
return s;
}
/** connect tcp connection to addr, 0 on failure */
int
outnet_tcp_connect(int s, struct sockaddr_storage* addr, socklen_t addrlen)
{
if(connect(s, (struct sockaddr*)addr, addrlen) == -1) {
#ifndef USE_WINSOCK
#ifdef EINPROGRESS
if(errno != EINPROGRESS) {
#endif
if(tcp_connect_errno_needs_log(
(struct sockaddr*)addr, addrlen))
log_err_addr("outgoing tcp: connect",
strerror(errno), addr, addrlen);
close(s);
return 0;
#ifdef EINPROGRESS
}
#endif
#else /* USE_WINSOCK */
if(WSAGetLastError() != WSAEINPROGRESS &&
WSAGetLastError() != WSAEWOULDBLOCK) {
closesocket(s);
return 0;
}
#endif
}
return 1;
}
/** log reuse item addr and ptr with message */
static void
log_reuse_tcp(enum verbosity_value v, const char* msg, struct reuse_tcp* reuse)
{
uint16_t port;
char addrbuf[128];
if(verbosity < v) return;
if(!reuse || !reuse->pending || !reuse->pending->c)
return;
addr_to_str(&reuse->addr, reuse->addrlen, addrbuf, sizeof(addrbuf));
port = ntohs(((struct sockaddr_in*)&reuse->addr)->sin_port);
verbose(v, "%s %s#%u fd %d", msg, addrbuf, (unsigned)port,
reuse->pending->c->fd);
}
/** pop the first element from the writewait list */
struct waiting_tcp*
reuse_write_wait_pop(struct reuse_tcp* reuse)
{
struct waiting_tcp* w = reuse->write_wait_first;
if(!w)
return NULL;
log_assert(w->write_wait_queued);
log_assert(!w->write_wait_prev);
reuse->write_wait_first = w->write_wait_next;
if(w->write_wait_next)
w->write_wait_next->write_wait_prev = NULL;
else reuse->write_wait_last = NULL;
w->write_wait_queued = 0;
w->write_wait_next = NULL;
w->write_wait_prev = NULL;
return w;
}
/** remove the element from the writewait list */
void
reuse_write_wait_remove(struct reuse_tcp* reuse, struct waiting_tcp* w)
{
log_assert(w);
log_assert(w->write_wait_queued);
if(!w)
return;
if(!w->write_wait_queued)
return;
if(w->write_wait_prev)
w->write_wait_prev->write_wait_next = w->write_wait_next;
else reuse->write_wait_first = w->write_wait_next;
log_assert(!w->write_wait_prev ||
w->write_wait_prev->write_wait_next != w->write_wait_prev);
if(w->write_wait_next)
w->write_wait_next->write_wait_prev = w->write_wait_prev;
else reuse->write_wait_last = w->write_wait_prev;
log_assert(!w->write_wait_next
|| w->write_wait_next->write_wait_prev != w->write_wait_next);
w->write_wait_queued = 0;
w->write_wait_next = NULL;
w->write_wait_prev = NULL;
}
/** push the element after the last on the writewait list */
void
reuse_write_wait_push_back(struct reuse_tcp* reuse, struct waiting_tcp* w)
{
if(!w) return;
log_assert(!w->write_wait_queued);
if(reuse->write_wait_last) {
reuse->write_wait_last->write_wait_next = w;
log_assert(reuse->write_wait_last->write_wait_next !=
reuse->write_wait_last);
w->write_wait_prev = reuse->write_wait_last;
} else {
reuse->write_wait_first = w;
w->write_wait_prev = NULL;
}
w->write_wait_next = NULL;
reuse->write_wait_last = w;
w->write_wait_queued = 1;
}
/** insert element in tree by id */
void
reuse_tree_by_id_insert(struct reuse_tcp* reuse, struct waiting_tcp* w)
{
#ifdef UNBOUND_DEBUG
rbnode_type* added;
#endif
log_assert(w->id_node.key == NULL);
w->id_node.key = w;
#ifdef UNBOUND_DEBUG
added =
#else
(void)
#endif
rbtree_insert(&reuse->tree_by_id, &w->id_node);
log_assert(added); /* should have been added */
}
/** find element in tree by id */
struct waiting_tcp*
reuse_tcp_by_id_find(struct reuse_tcp* reuse, uint16_t id)
{
struct waiting_tcp key_w;
rbnode_type* n;
memset(&key_w, 0, sizeof(key_w));
key_w.id_node.key = &key_w;
key_w.id = id;
n = rbtree_search(&reuse->tree_by_id, &key_w);
if(!n) return NULL;
return (struct waiting_tcp*)n->key;
}
/** return ID value of rbnode in tree_by_id */
static uint16_t
tree_by_id_get_id(rbnode_type* node)
{
struct waiting_tcp* w = (struct waiting_tcp*)node->key;
return w->id;
}
/** insert into reuse tcp tree and LRU, false on failure (duplicate) */
int
reuse_tcp_insert(struct outside_network* outnet, struct pending_tcp* pend_tcp)
{
log_reuse_tcp(VERB_CLIENT, "reuse_tcp_insert", &pend_tcp->reuse);
if(pend_tcp->reuse.item_on_lru_list) {
if(!pend_tcp->reuse.node.key)
log_err("internal error: reuse_tcp_insert: "
"in lru list without key");
return 1;
}
pend_tcp->reuse.node.key = &pend_tcp->reuse;
pend_tcp->reuse.pending = pend_tcp;
if(!rbtree_insert(&outnet->tcp_reuse, &pend_tcp->reuse.node)) {
/* We are not in the LRU list but we are already in the
* tcp_reuse tree, strange.
* Continue to add ourselves to the LRU list. */
log_err("internal error: reuse_tcp_insert: in lru list but "
"not in the tree");
}
/* insert into LRU, first is newest */
pend_tcp->reuse.lru_prev = NULL;
if(outnet->tcp_reuse_first) {
pend_tcp->reuse.lru_next = outnet->tcp_reuse_first;
log_assert(pend_tcp->reuse.lru_next != &pend_tcp->reuse);
outnet->tcp_reuse_first->lru_prev = &pend_tcp->reuse;
log_assert(outnet->tcp_reuse_first->lru_prev !=
outnet->tcp_reuse_first);
} else {
pend_tcp->reuse.lru_next = NULL;
outnet->tcp_reuse_last = &pend_tcp->reuse;
}
outnet->tcp_reuse_first = &pend_tcp->reuse;
pend_tcp->reuse.item_on_lru_list = 1;
log_assert((!outnet->tcp_reuse_first && !outnet->tcp_reuse_last) ||
(outnet->tcp_reuse_first && outnet->tcp_reuse_last));
log_assert(outnet->tcp_reuse_first != outnet->tcp_reuse_first->lru_next &&
outnet->tcp_reuse_first != outnet->tcp_reuse_first->lru_prev);
log_assert(outnet->tcp_reuse_last != outnet->tcp_reuse_last->lru_next &&
outnet->tcp_reuse_last != outnet->tcp_reuse_last->lru_prev);
return 1;
}
/** find reuse tcp stream to destination for query, or NULL if none */
static struct reuse_tcp*
reuse_tcp_find(struct outside_network* outnet, struct sockaddr_storage* addr,
socklen_t addrlen, int use_ssl)
{
struct waiting_tcp key_w;
struct pending_tcp key_p;
struct comm_point c;
rbnode_type* result = NULL, *prev;
verbose(VERB_CLIENT, "reuse_tcp_find");
memset(&key_w, 0, sizeof(key_w));
memset(&key_p, 0, sizeof(key_p));
memset(&c, 0, sizeof(c));
key_p.query = &key_w;
key_p.c = &c;
key_p.reuse.pending = &key_p;
key_p.reuse.node.key = &key_p.reuse;
if(use_ssl)
key_p.reuse.is_ssl = 1;
if(addrlen > (socklen_t)sizeof(key_p.reuse.addr))
return NULL;
memmove(&key_p.reuse.addr, addr, addrlen);
key_p.reuse.addrlen = addrlen;
verbose(VERB_CLIENT, "reuse_tcp_find: num reuse streams %u",
(unsigned)outnet->tcp_reuse.count);
if(outnet->tcp_reuse.root == NULL ||
outnet->tcp_reuse.root == RBTREE_NULL)
return NULL;
if(rbtree_find_less_equal(&outnet->tcp_reuse, &key_p.reuse,
&result)) {
/* exact match */
/* but the key is on stack, and ptr is compared, impossible */
log_assert(&key_p.reuse != (struct reuse_tcp*)result);
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);
}
+ /* not found, return null */
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
* also less than max number of open queries, or else, fail to open
* a new one */
/* rewind to start of sequence of same address,port,ssl */
prev = rbtree_previous(result);
while(prev && prev != RBTREE_NULL &&
reuse_cmp_addrportssl(prev->key, &key_p.reuse) == 0) {
result = prev;
prev = rbtree_previous(result);
}
/* loop to find first one that has correct characteristics */
while(result && result != RBTREE_NULL &&
reuse_cmp_addrportssl(result->key, &key_p.reuse) == 0) {
if(((struct reuse_tcp*)result)->tree_by_id.count <
outnet->max_reuse_tcp_queries) {
/* same address, port, ssl-yes-or-no, and has
* space for another query */
return (struct reuse_tcp*)result;
}
result = rbtree_next(result);
}
return NULL;
}
/** use the buffer to setup writing the query */
static void
outnet_tcp_take_query_setup(int s, struct pending_tcp* pend,
struct waiting_tcp* w)
{
struct timeval tv;
verbose(VERB_CLIENT, "outnet_tcp_take_query_setup: setup packet to write "
"len %d timeout %d msec",
(int)w->pkt_len, w->timeout);
pend->c->tcp_write_pkt = w->pkt;
pend->c->tcp_write_pkt_len = w->pkt_len;
pend->c->tcp_write_and_read = 1;
pend->c->tcp_write_byte_count = 0;
pend->c->tcp_is_reading = 0;
comm_point_start_listening(pend->c, s, -1);
/* set timer on the waiting_tcp entry, this is the write timeout
* for the written packet. The timer on pend->c is the timer
* for when there is no written packet and we have readtimeouts */
#ifndef S_SPLINT_S
tv.tv_sec = w->timeout/1000;
tv.tv_usec = (w->timeout%1000)*1000;
#endif
/* if the waiting_tcp was previously waiting for a buffer in the
* outside_network.tcpwaitlist, then the timer is reset now that
* we start writing it */
comm_timer_set(w->timer, &tv);
}
/** use next free buffer to service a tcp query */
static int
outnet_tcp_take_into_use(struct waiting_tcp* w)
{
struct pending_tcp* pend = w->outnet->tcp_free;
int s;
log_assert(pend);
log_assert(w->pkt);
log_assert(w->pkt_len > 0);
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);
if(s == -1)
return 0;
if(!pick_outgoing_tcp(pend, w, s))
return 0;
fd_set_nonblock(s);
#ifdef USE_OSX_MSG_FASTOPEN
/* API for fast open is different here. We use a connectx() function and
then writes can happen as normal even using SSL.*/
/* connectx requires that the len be set in the sockaddr struct*/
struct sockaddr_in *addr_in = (struct sockaddr_in *)&w->addr;
addr_in->sin_len = w->addrlen;
sa_endpoints_t endpoints;
endpoints.sae_srcif = 0;
endpoints.sae_srcaddr = NULL;
endpoints.sae_srcaddrlen = 0;
endpoints.sae_dstaddr = (struct sockaddr *)&w->addr;
endpoints.sae_dstaddrlen = w->addrlen;
if (connectx(s, &endpoints, SAE_ASSOCID_ANY,
CONNECT_DATA_IDEMPOTENT | CONNECT_RESUME_ON_READ_WRITE,
NULL, 0, NULL, NULL) == -1) {
/* if fails, failover to connect for OSX 10.10 */
#ifdef EINPROGRESS
if(errno != EINPROGRESS) {
#else
if(1) {
#endif
if(connect(s, (struct sockaddr*)&w->addr, w->addrlen) == -1) {
#else /* USE_OSX_MSG_FASTOPEN*/
#ifdef USE_MSG_FASTOPEN
pend->c->tcp_do_fastopen = 1;
/* Only do TFO for TCP in which case no connect() is required here.
Don't combine client TFO with SSL, since OpenSSL can't
currently support doing a handshake on fd that already isn't connected*/
if (w->outnet->sslctx && w->ssl_upstream) {
if(connect(s, (struct sockaddr*)&w->addr, w->addrlen) == -1) {
#else /* USE_MSG_FASTOPEN*/
if(connect(s, (struct sockaddr*)&w->addr, w->addrlen) == -1) {
#endif /* USE_MSG_FASTOPEN*/
#endif /* USE_OSX_MSG_FASTOPEN*/
#ifndef USE_WINSOCK
#ifdef EINPROGRESS
if(errno != EINPROGRESS) {
#else
if(1) {
#endif
if(tcp_connect_errno_needs_log(
(struct sockaddr*)&w->addr, w->addrlen))
log_err_addr("outgoing tcp: connect",
strerror(errno), &w->addr, w->addrlen);
close(s);
#else /* USE_WINSOCK */
if(WSAGetLastError() != WSAEINPROGRESS &&
WSAGetLastError() != WSAEWOULDBLOCK) {
closesocket(s);
#endif
return 0;
}
}
#ifdef USE_MSG_FASTOPEN
}
#endif /* USE_MSG_FASTOPEN */
#ifdef USE_OSX_MSG_FASTOPEN
}
}
#endif /* USE_OSX_MSG_FASTOPEN */
if(w->outnet->sslctx && w->ssl_upstream) {
pend->c->ssl = outgoing_ssl_fd(w->outnet->sslctx, s);
if(!pend->c->ssl) {
pend->c->fd = s;
comm_point_close(pend->c);
return 0;
}
verbose(VERB_ALGO, "the query is using TLS encryption, for %s",
(w->tls_auth_name?w->tls_auth_name:"an unauthenticated connection"));
#ifdef USE_WINSOCK
comm_point_tcp_win_bio_cb(pend->c, pend->c->ssl);
#endif
pend->c->ssl_shake_state = comm_ssl_shake_write;
if(!set_auth_name_on_ssl(pend->c->ssl, w->tls_auth_name,
w->outnet->tls_use_sni)) {
pend->c->fd = s;
#ifdef HAVE_SSL
SSL_free(pend->c->ssl);
#endif
pend->c->ssl = NULL;
comm_point_close(pend->c);
return 0;
}
}
w->next_waiting = (void*)pend;
w->outnet->num_tcp_outgoing++;
w->outnet->tcp_free = pend->next_free;
pend->next_free = NULL;
pend->query = w;
pend->reuse.outnet = w->outnet;
pend->c->repinfo.remote_addrlen = w->addrlen;
pend->c->tcp_more_read_again = &pend->reuse.cp_more_read_again;
pend->c->tcp_more_write_again = &pend->reuse.cp_more_write_again;
pend->reuse.cp_more_read_again = 0;
pend->reuse.cp_more_write_again = 0;
memcpy(&pend->c->repinfo.remote_addr, &w->addr, w->addrlen);
pend->reuse.pending = pend;
/* Remove from tree in case the is_ssl will be different and causes the
* identity of the reuse_tcp to change; could result in nodes not being
* deleted from the tree (because the new identity does not match the
* previous node) but their ->key would be changed to NULL. */
if(pend->reuse.node.key)
reuse_tcp_remove_tree_list(w->outnet, &pend->reuse);
if(pend->c->ssl)
pend->reuse.is_ssl = 1;
else pend->reuse.is_ssl = 0;
/* insert in reuse by address tree if not already inserted there */
(void)reuse_tcp_insert(w->outnet, pend);
reuse_tree_by_id_insert(&pend->reuse, w);
outnet_tcp_take_query_setup(s, pend, w);
return 1;
}
/** Touch the lru of a reuse_tcp element, it is in use.
* This moves it to the front of the list, where it is not likely to
* be closed. Items at the back of the list are closed to make space. */
void
reuse_tcp_lru_touch(struct outside_network* outnet, struct reuse_tcp* reuse)
{
if(!reuse->item_on_lru_list) {
log_err("internal error: we need to touch the lru_list but item not in list");
return; /* not on the list, no lru to modify */
}
log_assert(reuse->lru_prev ||
(!reuse->lru_prev && outnet->tcp_reuse_first == reuse));
if(!reuse->lru_prev)
return; /* already first in the list */
/* remove at current position */
/* since it is not first, there is a previous element */
reuse->lru_prev->lru_next = reuse->lru_next;
log_assert(reuse->lru_prev->lru_next != reuse->lru_prev);
if(reuse->lru_next)
reuse->lru_next->lru_prev = reuse->lru_prev;
else outnet->tcp_reuse_last = reuse->lru_prev;
log_assert(!reuse->lru_next || reuse->lru_next->lru_prev != reuse->lru_next);
log_assert(outnet->tcp_reuse_last != outnet->tcp_reuse_last->lru_next &&
outnet->tcp_reuse_last != outnet->tcp_reuse_last->lru_prev);
/* insert at the front */
reuse->lru_prev = NULL;
reuse->lru_next = outnet->tcp_reuse_first;
if(outnet->tcp_reuse_first) {
outnet->tcp_reuse_first->lru_prev = reuse;
}
log_assert(reuse->lru_next != reuse);
/* since it is not first, it is not the only element and
* lru_next is thus not NULL and thus reuse is now not the last in
* the list, so outnet->tcp_reuse_last does not need to be modified */
outnet->tcp_reuse_first = reuse;
log_assert(outnet->tcp_reuse_first != outnet->tcp_reuse_first->lru_next &&
outnet->tcp_reuse_first != outnet->tcp_reuse_first->lru_prev);
log_assert((!outnet->tcp_reuse_first && !outnet->tcp_reuse_last) ||
(outnet->tcp_reuse_first && outnet->tcp_reuse_last));
}
/** Snip the last reuse_tcp element off of the LRU list */
struct reuse_tcp*
reuse_tcp_lru_snip(struct outside_network* outnet)
{
struct reuse_tcp* reuse = outnet->tcp_reuse_last;
if(!reuse) return NULL;
/* snip off of LRU */
log_assert(reuse->lru_next == NULL);
if(reuse->lru_prev) {
outnet->tcp_reuse_last = reuse->lru_prev;
reuse->lru_prev->lru_next = NULL;
} else {
outnet->tcp_reuse_last = NULL;
outnet->tcp_reuse_first = NULL;
}
log_assert((!outnet->tcp_reuse_first && !outnet->tcp_reuse_last) ||
(outnet->tcp_reuse_first && outnet->tcp_reuse_last));
reuse->item_on_lru_list = 0;
reuse->lru_next = NULL;
reuse->lru_prev = NULL;
return reuse;
}
/** remove waiting tcp from the outnet waiting list */
void
outnet_waiting_tcp_list_remove(struct outside_network* outnet, struct waiting_tcp* w)
{
struct waiting_tcp* p = outnet->tcp_wait_first, *prev = NULL;
w->on_tcp_waiting_list = 0;
while(p) {
if(p == w) {
/* remove w */
if(prev)
prev->next_waiting = w->next_waiting;
else outnet->tcp_wait_first = w->next_waiting;
if(outnet->tcp_wait_last == w)
outnet->tcp_wait_last = prev;
w->next_waiting = NULL;
return;
}
prev = p;
p = p->next_waiting;
}
/* outnet_waiting_tcp_list_remove is currently called only with items
* that are already in the waiting list. */
log_assert(0);
}
/** pop the first waiting tcp from the outnet waiting list */
struct waiting_tcp*
outnet_waiting_tcp_list_pop(struct outside_network* outnet)
{
struct waiting_tcp* w = outnet->tcp_wait_first;
if(!outnet->tcp_wait_first) return NULL;
log_assert(w->on_tcp_waiting_list);
outnet->tcp_wait_first = w->next_waiting;
if(outnet->tcp_wait_last == w)
outnet->tcp_wait_last = NULL;
w->on_tcp_waiting_list = 0;
w->next_waiting = NULL;
return w;
}
/** add waiting_tcp element to the outnet tcp waiting list */
void
outnet_waiting_tcp_list_add(struct outside_network* outnet,
struct waiting_tcp* w, int set_timer)
{
struct timeval tv;
log_assert(!w->on_tcp_waiting_list);
if(w->on_tcp_waiting_list)
return;
w->next_waiting = NULL;
if(outnet->tcp_wait_last)
outnet->tcp_wait_last->next_waiting = w;
else outnet->tcp_wait_first = w;
outnet->tcp_wait_last = w;
w->on_tcp_waiting_list = 1;
if(set_timer) {
#ifndef S_SPLINT_S
tv.tv_sec = w->timeout/1000;
tv.tv_usec = (w->timeout%1000)*1000;
#endif
comm_timer_set(w->timer, &tv);
}
}
/** add waiting_tcp element as first to the outnet tcp waiting list */
void
outnet_waiting_tcp_list_add_first(struct outside_network* outnet,
struct waiting_tcp* w, int reset_timer)
{
struct timeval tv;
log_assert(!w->on_tcp_waiting_list);
if(w->on_tcp_waiting_list)
return;
w->next_waiting = outnet->tcp_wait_first;
log_assert(w->next_waiting != w);
if(!outnet->tcp_wait_last)
outnet->tcp_wait_last = w;
outnet->tcp_wait_first = w;
w->on_tcp_waiting_list = 1;
if(reset_timer) {
#ifndef S_SPLINT_S
tv.tv_sec = w->timeout/1000;
tv.tv_usec = (w->timeout%1000)*1000;
#endif
comm_timer_set(w->timer, &tv);
}
log_assert(
(!outnet->tcp_reuse_first && !outnet->tcp_reuse_last) ||
(outnet->tcp_reuse_first && outnet->tcp_reuse_last));
}
/** call callback on waiting_tcp, if not NULL */
static void
waiting_tcp_callback(struct waiting_tcp* w, struct comm_point* c, int error,
struct comm_reply* reply_info)
{
if(w && w->cb) {
fptr_ok(fptr_whitelist_pending_tcp(w->cb));
(void)(*w->cb)(c, w->cb_arg, error, reply_info);
}
}
/** see if buffers can be used to service TCP queries */
static void
use_free_buffer(struct outside_network* outnet)
{
struct waiting_tcp* w;
while(outnet->tcp_wait_first && !outnet->want_to_quit) {
#ifdef USE_DNSTAP
struct pending_tcp* pend_tcp = NULL;
#endif
struct reuse_tcp* reuse = NULL;
w = outnet_waiting_tcp_list_pop(outnet);
log_assert(
(!outnet->tcp_reuse_first && !outnet->tcp_reuse_last) ||
(outnet->tcp_reuse_first && outnet->tcp_reuse_last));
reuse = reuse_tcp_find(outnet, &w->addr, w->addrlen,
w->ssl_upstream);
/* re-select an ID when moving to a new TCP buffer */
w->id = tcp_select_id(outnet, reuse);
LDNS_ID_SET(w->pkt, w->id);
if(reuse) {
log_reuse_tcp(VERB_CLIENT, "use free buffer for waiting tcp: "
"found reuse", reuse);
#ifdef USE_DNSTAP
pend_tcp = reuse->pending;
#endif
reuse_tcp_lru_touch(outnet, reuse);
comm_timer_disable(w->timer);
w->next_waiting = (void*)reuse->pending;
reuse_tree_by_id_insert(reuse, w);
if(reuse->pending->query) {
/* on the write wait list */
reuse_write_wait_push_back(reuse, w);
} else {
/* write straight away */
/* stop the timer on read of the fd */
comm_point_stop_listening(reuse->pending->c);
reuse->pending->query = w;
outnet_tcp_take_query_setup(
reuse->pending->c->fd, reuse->pending,
w);
}
} else if(outnet->tcp_free) {
struct pending_tcp* pend = w->outnet->tcp_free;
rbtree_init(&pend->reuse.tree_by_id, reuse_id_cmp);
pend->reuse.pending = pend;
memcpy(&pend->reuse.addr, &w->addr, w->addrlen);
pend->reuse.addrlen = w->addrlen;
if(!outnet_tcp_take_into_use(w)) {
waiting_tcp_callback(w, NULL, NETEVENT_CLOSED,
NULL);
waiting_tcp_delete(w);
#ifdef USE_DNSTAP
w = NULL;
#endif
}
#ifdef USE_DNSTAP
pend_tcp = pend;
#endif
} else {
/* no reuse and no free buffer, put back at the start */
outnet_waiting_tcp_list_add_first(outnet, w, 0);
break;
}
#ifdef USE_DNSTAP
if(outnet->dtenv && pend_tcp && w && w->sq &&
(outnet->dtenv->log_resolver_query_messages ||
outnet->dtenv->log_forwarder_query_messages)) {
sldns_buffer tmp;
sldns_buffer_init_frm_data(&tmp, w->pkt, w->pkt_len);
dt_msg_send_outside_query(outnet->dtenv, &w->sq->addr,
&pend_tcp->pi->addr, comm_tcp, w->sq->zone,
w->sq->zonelen, &tmp);
}
#endif
}
}
/** delete element from tree by id */
static void
reuse_tree_by_id_delete(struct reuse_tcp* reuse, struct waiting_tcp* w)
{
#ifdef UNBOUND_DEBUG
rbnode_type* rem;
#endif
log_assert(w->id_node.key != NULL);
#ifdef UNBOUND_DEBUG
rem =
#else
(void)
#endif
rbtree_delete(&reuse->tree_by_id, w);
log_assert(rem); /* should have been there */
w->id_node.key = NULL;
}
/** move writewait list to go for another connection. */
static void
reuse_move_writewait_away(struct outside_network* outnet,
struct pending_tcp* pend)
{
/* the writewait list has not been written yet, so if the
* stream was closed, they have not actually been failed, only
* the queries written. Other queries can get written to another
* stream. For upstreams that do not support multiple queries
* and answers, the stream can get closed, and then the queries
* can get written on a new socket */
struct waiting_tcp* w;
if(pend->query && pend->query->error_count == 0 &&
pend->c->tcp_write_pkt == pend->query->pkt &&
pend->c->tcp_write_pkt_len == pend->query->pkt_len) {
/* since the current query is not written, it can also
* move to a free buffer */
if(verbosity >= VERB_CLIENT && pend->query->pkt_len > 12+2+2 &&
LDNS_QDCOUNT(pend->query->pkt) > 0 &&
dname_valid(pend->query->pkt+12, pend->query->pkt_len-12)) {
char buf[LDNS_MAX_DOMAINLEN+1];
dname_str(pend->query->pkt+12, buf);
verbose(VERB_CLIENT, "reuse_move_writewait_away current %s %d bytes were written",
buf, (int)pend->c->tcp_write_byte_count);
}
pend->c->tcp_write_pkt = NULL;
pend->c->tcp_write_pkt_len = 0;
pend->c->tcp_write_and_read = 0;
pend->reuse.cp_more_read_again = 0;
pend->reuse.cp_more_write_again = 0;
pend->c->tcp_is_reading = 1;
w = pend->query;
pend->query = NULL;
/* increase error count, so that if the next socket fails too
* the server selection is run again with this query failed
* and it can select a different server (if possible), or
* fail the query */
w->error_count ++;
reuse_tree_by_id_delete(&pend->reuse, w);
outnet_waiting_tcp_list_add(outnet, w, 1);
}
while((w = reuse_write_wait_pop(&pend->reuse)) != NULL) {
if(verbosity >= VERB_CLIENT && w->pkt_len > 12+2+2 &&
LDNS_QDCOUNT(w->pkt) > 0 &&
dname_valid(w->pkt+12, w->pkt_len-12)) {
char buf[LDNS_MAX_DOMAINLEN+1];
dname_str(w->pkt+12, buf);
verbose(VERB_CLIENT, "reuse_move_writewait_away item %s", buf);
}
reuse_tree_by_id_delete(&pend->reuse, w);
outnet_waiting_tcp_list_add(outnet, w, 1);
}
}
/** remove reused element from tree and lru list */
void
reuse_tcp_remove_tree_list(struct outside_network* outnet,
struct reuse_tcp* reuse)
{
verbose(VERB_CLIENT, "reuse_tcp_remove_tree_list");
if(reuse->node.key) {
/* delete it from reuse tree */
if(!rbtree_delete(&outnet->tcp_reuse, reuse)) {
/* should not be possible, it should be there */
char buf[256];
addr_to_str(&reuse->addr, reuse->addrlen, buf,
sizeof(buf));
log_err("reuse tcp delete: node not present, internal error, %s ssl %d lru %d", buf, reuse->is_ssl, reuse->item_on_lru_list);
}
reuse->node.key = NULL;
/* defend against loops on broken tree by zeroing the
* rbnode structure */
memset(&reuse->node, 0, sizeof(reuse->node));
}
/* delete from reuse list */
if(reuse->item_on_lru_list) {
if(reuse->lru_prev) {
/* assert that members of the lru list are waiting
* and thus have a pending pointer to the struct */
log_assert(reuse->lru_prev->pending);
reuse->lru_prev->lru_next = reuse->lru_next;
log_assert(reuse->lru_prev->lru_next != reuse->lru_prev);
} else {
log_assert(!reuse->lru_next || reuse->lru_next->pending);
outnet->tcp_reuse_first = reuse->lru_next;
log_assert(!outnet->tcp_reuse_first ||
(outnet->tcp_reuse_first !=
outnet->tcp_reuse_first->lru_next &&
outnet->tcp_reuse_first !=
outnet->tcp_reuse_first->lru_prev));
}
if(reuse->lru_next) {
/* assert that members of the lru list are waiting
* and thus have a pending pointer to the struct */
log_assert(reuse->lru_next->pending);
reuse->lru_next->lru_prev = reuse->lru_prev;
log_assert(reuse->lru_next->lru_prev != reuse->lru_next);
} else {
log_assert(!reuse->lru_prev || reuse->lru_prev->pending);
outnet->tcp_reuse_last = reuse->lru_prev;
log_assert(!outnet->tcp_reuse_last ||
(outnet->tcp_reuse_last !=
outnet->tcp_reuse_last->lru_next &&
outnet->tcp_reuse_last !=
outnet->tcp_reuse_last->lru_prev));
}
log_assert((!outnet->tcp_reuse_first && !outnet->tcp_reuse_last) ||
(outnet->tcp_reuse_first && outnet->tcp_reuse_last));
reuse->item_on_lru_list = 0;
reuse->lru_next = NULL;
reuse->lru_prev = NULL;
}
reuse->pending = NULL;
}
/** helper function that deletes an element from the tree of readwait
* elements in tcp reuse structure */
static void reuse_del_readwait_elem(rbnode_type* node, void* ATTR_UNUSED(arg))
{
struct waiting_tcp* w = (struct waiting_tcp*)node->key;
waiting_tcp_delete(w);
}
/** delete readwait waiting_tcp elements, deletes the elements in the list */
void reuse_del_readwait(rbtree_type* tree_by_id)
{
if(tree_by_id->root == NULL ||
tree_by_id->root == RBTREE_NULL)
return;
traverse_postorder(tree_by_id, &reuse_del_readwait_elem, NULL);
rbtree_init(tree_by_id, reuse_id_cmp);
}
/** decommission a tcp buffer, closes commpoint and frees waiting_tcp entry */
static void
decommission_pending_tcp(struct outside_network* outnet,
struct pending_tcp* pend)
{
verbose(VERB_CLIENT, "decommission_pending_tcp");
/* A certain code path can lead here twice for the same pending_tcp
* creating a loop in the free pending_tcp list. */
if(outnet->tcp_free != pend) {
pend->next_free = outnet->tcp_free;
outnet->tcp_free = pend;
}
if(pend->reuse.node.key) {
/* needs unlink from the reuse tree to get deleted */
reuse_tcp_remove_tree_list(outnet, &pend->reuse);
}
/* free SSL structure after remove from outnet tcp reuse tree,
* because the c->ssl null or not is used for sorting in the tree */
if(pend->c->ssl) {
#ifdef HAVE_SSL
SSL_shutdown(pend->c->ssl);
SSL_free(pend->c->ssl);
pend->c->ssl = NULL;
#endif
}
comm_point_close(pend->c);
pend->reuse.cp_more_read_again = 0;
pend->reuse.cp_more_write_again = 0;
/* unlink the query and writewait list, it is part of the tree
* nodes and is deleted */
pend->query = NULL;
pend->reuse.write_wait_first = NULL;
pend->reuse.write_wait_last = NULL;
reuse_del_readwait(&pend->reuse.tree_by_id);
}
/** perform failure callbacks for waiting queries in reuse read rbtree */
static void reuse_cb_readwait_for_failure(rbtree_type* tree_by_id, int err)
{
rbnode_type* node;
if(tree_by_id->root == NULL ||
tree_by_id->root == RBTREE_NULL)
return;
node = rbtree_first(tree_by_id);
while(node && node != RBTREE_NULL) {
struct waiting_tcp* w = (struct waiting_tcp*)node->key;
waiting_tcp_callback(w, NULL, err, NULL);
node = rbtree_next(node);
}
}
/** mark the entry for being in the cb_and_decommission stage */
static void mark_for_cb_and_decommission(rbnode_type* node,
void* ATTR_UNUSED(arg))
{
struct waiting_tcp* w = (struct waiting_tcp*)node->key;
/* Mark the waiting_tcp to signal later code (serviced_delete) that
* this item is part of the backed up tree_by_id and will be deleted
* later. */
w->in_cb_and_decommission = 1;
/* Mark the serviced_query for deletion so that later code through
* callbacks (iter_clear .. outnet_serviced_query_stop) won't
* prematurely delete it. */
if(w->cb)
((struct serviced_query*)w->cb_arg)->to_be_deleted = 1;
}
/** perform callbacks for failure and also decommission pending tcp.
* the callbacks remove references in sq->pending to the waiting_tcp
* members of the tree_by_id in the pending tcp. The pending_tcp is
* removed before the callbacks, so that the callbacks do not modify
* the pending_tcp due to its reference in the outside_network reuse tree */
static void reuse_cb_and_decommission(struct outside_network* outnet,
struct pending_tcp* pend, int error)
{
rbtree_type store;
store = pend->reuse.tree_by_id;
pend->query = NULL;
rbtree_init(&pend->reuse.tree_by_id, reuse_id_cmp);
pend->reuse.write_wait_first = NULL;
pend->reuse.write_wait_last = NULL;
decommission_pending_tcp(outnet, pend);
if(store.root != NULL && store.root != RBTREE_NULL) {
traverse_postorder(&store, &mark_for_cb_and_decommission, NULL);
}
reuse_cb_readwait_for_failure(&store, error);
reuse_del_readwait(&store);
}
/** set timeout on tcp fd and setup read event to catch incoming dns msgs */
static void
reuse_tcp_setup_timeout(struct pending_tcp* pend_tcp, int tcp_reuse_timeout)
{
log_reuse_tcp(VERB_CLIENT, "reuse_tcp_setup_timeout", &pend_tcp->reuse);
comm_point_start_listening(pend_tcp->c, -1, tcp_reuse_timeout);
}
/** set timeout on tcp fd and setup read event to catch incoming dns msgs */
static void
reuse_tcp_setup_read_and_timeout(struct pending_tcp* pend_tcp, int tcp_reuse_timeout)
{
log_reuse_tcp(VERB_CLIENT, "reuse_tcp_setup_readtimeout", &pend_tcp->reuse);
sldns_buffer_clear(pend_tcp->c->buffer);
pend_tcp->c->tcp_is_reading = 1;
pend_tcp->c->tcp_byte_count = 0;
comm_point_stop_listening(pend_tcp->c);
comm_point_start_listening(pend_tcp->c, -1, tcp_reuse_timeout);
}
int
outnet_tcp_cb(struct comm_point* c, void* arg, int error,
struct comm_reply *reply_info)
{
struct pending_tcp* pend = (struct pending_tcp*)arg;
struct outside_network* outnet = pend->reuse.outnet;
struct waiting_tcp* w = NULL;
log_assert(pend->reuse.item_on_lru_list && pend->reuse.node.key);
verbose(VERB_ALGO, "outnettcp cb");
if(error == NETEVENT_TIMEOUT) {
if(pend->c->tcp_write_and_read) {
verbose(VERB_QUERY, "outnettcp got tcp timeout "
"for read, ignored because write underway");
/* if we are writing, ignore readtimer, wait for write timer
* or write is done */
return 0;
} else {
verbose(VERB_QUERY, "outnettcp got tcp timeout %s",
(pend->reuse.tree_by_id.count?"for reading pkt":
"for keepalive for reuse"));
}
/* must be timeout for reading or keepalive reuse,
* close it. */
reuse_tcp_remove_tree_list(outnet, &pend->reuse);
} else if(error == NETEVENT_PKT_WRITTEN) {
/* the packet we want to write has been written. */
verbose(VERB_ALGO, "outnet tcp pkt was written event");
log_assert(c == pend->c);
log_assert(pend->query->pkt == pend->c->tcp_write_pkt);
log_assert(pend->query->pkt_len == pend->c->tcp_write_pkt_len);
pend->c->tcp_write_pkt = NULL;
pend->c->tcp_write_pkt_len = 0;
/* the pend.query is already in tree_by_id */
log_assert(pend->query->id_node.key);
pend->query = NULL;
/* setup to write next packet or setup read timeout */
if(pend->reuse.write_wait_first) {
verbose(VERB_ALGO, "outnet tcp setup next pkt");
/* we can write it straight away perhaps, set flag
* because this callback called after a tcp write
* succeeded and likely more buffer space is available
* and we can write some more. */
pend->reuse.cp_more_write_again = 1;
pend->query = reuse_write_wait_pop(&pend->reuse);
comm_point_stop_listening(pend->c);
outnet_tcp_take_query_setup(pend->c->fd, pend,
pend->query);
} else {
verbose(VERB_ALGO, "outnet tcp writes done, wait");
pend->c->tcp_write_and_read = 0;
pend->reuse.cp_more_read_again = 0;
pend->reuse.cp_more_write_again = 0;
pend->c->tcp_is_reading = 1;
comm_point_stop_listening(pend->c);
reuse_tcp_setup_timeout(pend, outnet->tcp_reuse_timeout);
}
return 0;
} else if(error != NETEVENT_NOERROR) {
verbose(VERB_QUERY, "outnettcp got tcp error %d", error);
reuse_move_writewait_away(outnet, pend);
/* pass error below and exit */
} else {
/* check ID */
if(sldns_buffer_limit(c->buffer) < sizeof(uint16_t)) {
log_addr(VERB_QUERY,
"outnettcp: bad ID in reply, too short, from:",
&pend->reuse.addr, pend->reuse.addrlen);
error = NETEVENT_CLOSED;
} else {
uint16_t id = LDNS_ID_WIRE(sldns_buffer_begin(
c->buffer));
/* find the query the reply is for */
w = reuse_tcp_by_id_find(&pend->reuse, id);
/* Make sure that the reply we got is at least for a
* sent query with the same ID; the waiting_tcp that
* gets a reply is assumed to not be waiting to be
* sent. */
if(w && (w->on_tcp_waiting_list || w->write_wait_queued))
w = NULL;
}
}
if(error == NETEVENT_NOERROR && !w) {
/* no struct waiting found in tree, no reply to call */
log_addr(VERB_QUERY, "outnettcp: bad ID in reply, from:",
&pend->reuse.addr, pend->reuse.addrlen);
error = NETEVENT_CLOSED;
}
if(error == NETEVENT_NOERROR) {
/* add to reuse tree so it can be reused, if not a failure.
* This is possible if the state machine wants to make a tcp
* query again to the same destination. */
if(outnet->tcp_reuse.count < outnet->tcp_reuse_max) {
(void)reuse_tcp_insert(outnet, pend);
}
}
if(w) {
log_assert(!w->on_tcp_waiting_list);
log_assert(!w->write_wait_queued);
reuse_tree_by_id_delete(&pend->reuse, w);
verbose(VERB_CLIENT, "outnet tcp callback query err %d buflen %d",
error, (int)sldns_buffer_limit(c->buffer));
waiting_tcp_callback(w, c, error, reply_info);
waiting_tcp_delete(w);
}
verbose(VERB_CLIENT, "outnet_tcp_cb reuse after cb");
if(error == NETEVENT_NOERROR && pend->reuse.node.key) {
verbose(VERB_CLIENT, "outnet_tcp_cb reuse after cb: keep it");
/* it is in the reuse_tcp tree, with other queries, or
* on the empty list. do not decommission it */
/* if there are more outstanding queries, we could try to
* read again, to see if it is on the input,
* because this callback called after a successful read
* and there could be more bytes to read on the input */
if(pend->reuse.tree_by_id.count != 0)
pend->reuse.cp_more_read_again = 1;
reuse_tcp_setup_read_and_timeout(pend, outnet->tcp_reuse_timeout);
return 0;
}
verbose(VERB_CLIENT, "outnet_tcp_cb reuse after cb: decommission it");
/* no queries on it, no space to keep it. or timeout or closed due
* to error. Close it */
reuse_cb_and_decommission(outnet, pend, (error==NETEVENT_TIMEOUT?
NETEVENT_TIMEOUT:NETEVENT_CLOSED));
use_free_buffer(outnet);
return 0;
}
/** lower use count on pc, see if it can be closed */
static void
portcomm_loweruse(struct outside_network* outnet, struct port_comm* pc)
{
struct port_if* pif;
pc->num_outstanding--;
if(pc->num_outstanding > 0) {
return;
}
/* close it and replace in unused list */
verbose(VERB_ALGO, "close of port %d", pc->number);
comm_point_close(pc->cp);
pif = pc->pif;
log_assert(pif->inuse > 0);
#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
pif->avail_ports[pif->avail_total - pif->inuse] = pc->number;
#endif
pif->inuse--;
pif->out[pc->index] = pif->out[pif->inuse];
pif->out[pc->index]->index = pc->index;
pc->next = outnet->unused_fds;
outnet->unused_fds = pc;
}
/** try to send waiting UDP queries */
static void
outnet_send_wait_udp(struct outside_network* outnet)
{
struct pending* pend;
/* process waiting queries */
while(outnet->udp_wait_first && outnet->unused_fds
&& !outnet->want_to_quit) {
pend = outnet->udp_wait_first;
outnet->udp_wait_first = pend->next_waiting;
if(!pend->next_waiting) outnet->udp_wait_last = NULL;
sldns_buffer_clear(outnet->udp_buff);
sldns_buffer_write(outnet->udp_buff, pend->pkt, pend->pkt_len);
sldns_buffer_flip(outnet->udp_buff);
free(pend->pkt); /* freeing now makes get_mem correct */
pend->pkt = NULL;
pend->pkt_len = 0;
log_assert(!pend->sq->busy);
pend->sq->busy = 1;
if(!randomize_and_send_udp(pend, outnet->udp_buff,
pend->timeout)) {
/* callback error on pending */
if(pend->cb) {
fptr_ok(fptr_whitelist_pending_udp(pend->cb));
(void)(*pend->cb)(outnet->unused_fds->cp, pend->cb_arg,
NETEVENT_CLOSED, NULL);
}
pending_delete(outnet, pend);
} else {
pend->sq->busy = 0;
}
}
}
int
outnet_udp_cb(struct comm_point* c, void* arg, int error,
struct comm_reply *reply_info)
{
struct outside_network* outnet = (struct outside_network*)arg;
struct pending key;
struct pending* p;
verbose(VERB_ALGO, "answer cb");
if(error != NETEVENT_NOERROR) {
verbose(VERB_QUERY, "outnetudp got udp error %d", error);
return 0;
}
if(sldns_buffer_limit(c->buffer) < LDNS_HEADER_SIZE) {
verbose(VERB_QUERY, "outnetudp udp too short");
return 0;
}
log_assert(reply_info);
/* setup lookup key */
key.id = (unsigned)LDNS_ID_WIRE(sldns_buffer_begin(c->buffer));
memcpy(&key.addr, &reply_info->remote_addr, reply_info->remote_addrlen);
key.addrlen = reply_info->remote_addrlen;
verbose(VERB_ALGO, "Incoming reply id = %4.4x", key.id);
log_addr(VERB_ALGO, "Incoming reply addr =",
&reply_info->remote_addr, reply_info->remote_addrlen);
/* find it, see if this thing is a valid query response */
verbose(VERB_ALGO, "lookup size is %d entries", (int)outnet->pending->count);
p = (struct pending*)rbtree_search(outnet->pending, &key);
if(!p) {
verbose(VERB_QUERY, "received unwanted or unsolicited udp reply dropped.");
log_buf(VERB_ALGO, "dropped message", c->buffer);
outnet->unwanted_replies++;
if(outnet->unwanted_threshold && ++outnet->unwanted_total
>= outnet->unwanted_threshold) {
log_warn("unwanted reply total reached threshold (%u)"
" you may be under attack."
" defensive action: clearing the cache",
(unsigned)outnet->unwanted_threshold);
fptr_ok(fptr_whitelist_alloc_cleanup(
outnet->unwanted_action));
(*outnet->unwanted_action)(outnet->unwanted_param);
outnet->unwanted_total = 0;
}
return 0;
}
verbose(VERB_ALGO, "received udp reply.");
log_buf(VERB_ALGO, "udp message", c->buffer);
if(p->pc->cp != c) {
verbose(VERB_QUERY, "received reply id,addr on wrong port. "
"dropped.");
outnet->unwanted_replies++;
if(outnet->unwanted_threshold && ++outnet->unwanted_total
>= outnet->unwanted_threshold) {
log_warn("unwanted reply total reached threshold (%u)"
" you may be under attack."
" defensive action: clearing the cache",
(unsigned)outnet->unwanted_threshold);
fptr_ok(fptr_whitelist_alloc_cleanup(
outnet->unwanted_action));
(*outnet->unwanted_action)(outnet->unwanted_param);
outnet->unwanted_total = 0;
}
return 0;
}
comm_timer_disable(p->timer);
verbose(VERB_ALGO, "outnet handle udp reply");
/* delete from tree first in case callback creates a retry */
(void)rbtree_delete(outnet->pending, p->node.key);
if(p->cb) {
fptr_ok(fptr_whitelist_pending_udp(p->cb));
(void)(*p->cb)(p->pc->cp, p->cb_arg, NETEVENT_NOERROR, reply_info);
}
portcomm_loweruse(outnet, p->pc);
pending_delete(NULL, p);
outnet_send_wait_udp(outnet);
return 0;
}
/** calculate number of ip4 and ip6 interfaces*/
static void
calc_num46(char** ifs, int num_ifs, int do_ip4, int do_ip6,
int* num_ip4, int* num_ip6)
{
int i;
*num_ip4 = 0;
*num_ip6 = 0;
if(num_ifs <= 0) {
if(do_ip4)
*num_ip4 = 1;
if(do_ip6)
*num_ip6 = 1;
return;
}
for(i=0; i<num_ifs; i++)
{
if(str_is_ip6(ifs[i])) {
if(do_ip6)
(*num_ip6)++;
} else {
if(do_ip4)
(*num_ip4)++;
}
}
}
void
pending_udp_timer_delay_cb(void* arg)
{
struct pending* p = (struct pending*)arg;
struct outside_network* outnet = p->outnet;
verbose(VERB_ALGO, "timeout udp with delay");
portcomm_loweruse(outnet, p->pc);
pending_delete(outnet, p);
outnet_send_wait_udp(outnet);
}
void
pending_udp_timer_cb(void *arg)
{
struct pending* p = (struct pending*)arg;
struct outside_network* outnet = p->outnet;
/* it timed out */
verbose(VERB_ALGO, "timeout udp");
if(p->cb) {
fptr_ok(fptr_whitelist_pending_udp(p->cb));
(void)(*p->cb)(p->pc->cp, p->cb_arg, NETEVENT_TIMEOUT, NULL);
}
/* if delayclose, keep port open for a longer time.
* But if the udpwaitlist exists, then we are struggling to
* keep up with demand for sockets, so do not wait, but service
* the customer (customer service more important than portICMPs) */
if(outnet->delayclose && !outnet->udp_wait_first) {
p->cb = NULL;
p->timer->callback = &pending_udp_timer_delay_cb;
comm_timer_set(p->timer, &outnet->delay_tv);
return;
}
portcomm_loweruse(outnet, p->pc);
pending_delete(outnet, p);
outnet_send_wait_udp(outnet);
}
/** create pending_tcp buffers */
static int
create_pending_tcp(struct outside_network* outnet, size_t bufsize)
{
size_t i;
if(outnet->num_tcp == 0)
return 1; /* no tcp needed, nothing to do */
if(!(outnet->tcp_conns = (struct pending_tcp **)calloc(
outnet->num_tcp, sizeof(struct pending_tcp*))))
return 0;
for(i=0; i<outnet->num_tcp; i++) {
if(!(outnet->tcp_conns[i] = (struct pending_tcp*)calloc(1,
sizeof(struct pending_tcp))))
return 0;
outnet->tcp_conns[i]->next_free = outnet->tcp_free;
outnet->tcp_free = outnet->tcp_conns[i];
outnet->tcp_conns[i]->c = comm_point_create_tcp_out(
outnet->base, bufsize, outnet_tcp_cb,
outnet->tcp_conns[i]);
if(!outnet->tcp_conns[i]->c)
return 0;
}
return 1;
}
/** setup an outgoing interface, ready address */
static int setup_if(struct port_if* pif, const char* addrstr,
int* avail, int numavail, size_t numfd)
{
#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
pif->avail_total = numavail;
pif->avail_ports = (int*)memdup(avail, (size_t)numavail*sizeof(int));
if(!pif->avail_ports)
return 0;
#endif
if(!ipstrtoaddr(addrstr, UNBOUND_DNS_PORT, &pif->addr, &pif->addrlen) &&
!netblockstrtoaddr(addrstr, UNBOUND_DNS_PORT,
&pif->addr, &pif->addrlen, &pif->pfxlen))
return 0;
pif->maxout = (int)numfd;
pif->inuse = 0;
pif->out = (struct port_comm**)calloc(numfd,
sizeof(struct port_comm*));
if(!pif->out)
return 0;
return 1;
}
struct outside_network*
outside_network_create(struct comm_base *base, size_t bufsize,
size_t num_ports, char** ifs, int num_ifs, int do_ip4,
int do_ip6, size_t num_tcp, int dscp, struct infra_cache* infra,
struct ub_randstate* rnd, int use_caps_for_id, int* availports,
int numavailports, size_t unwanted_threshold, int tcp_mss,
void (*unwanted_action)(void*), void* unwanted_param, int do_udp,
void* sslctx, int delayclose, int tls_use_sni, struct dt_env* dtenv,
int udp_connect, int max_reuse_tcp_queries, int tcp_reuse_timeout,
int tcp_auth_query_timeout)
{
struct outside_network* outnet = (struct outside_network*)
calloc(1, sizeof(struct outside_network));
size_t k;
if(!outnet) {
log_err("malloc failed");
return NULL;
}
comm_base_timept(base, &outnet->now_secs, &outnet->now_tv);
outnet->base = base;
outnet->num_tcp = num_tcp;
outnet->max_reuse_tcp_queries = max_reuse_tcp_queries;
outnet->tcp_reuse_timeout= tcp_reuse_timeout;
outnet->tcp_auth_query_timeout = tcp_auth_query_timeout;
outnet->num_tcp_outgoing = 0;
outnet->num_udp_outgoing = 0;
outnet->infra = infra;
outnet->rnd = rnd;
outnet->sslctx = sslctx;
outnet->tls_use_sni = tls_use_sni;
#ifdef USE_DNSTAP
outnet->dtenv = dtenv;
#else
(void)dtenv;
#endif
outnet->svcd_overhead = 0;
outnet->want_to_quit = 0;
outnet->unwanted_threshold = unwanted_threshold;
outnet->unwanted_action = unwanted_action;
outnet->unwanted_param = unwanted_param;
outnet->use_caps_for_id = use_caps_for_id;
outnet->do_udp = do_udp;
outnet->tcp_mss = tcp_mss;
outnet->ip_dscp = dscp;
#ifndef S_SPLINT_S
if(delayclose) {
outnet->delayclose = 1;
outnet->delay_tv.tv_sec = delayclose/1000;
outnet->delay_tv.tv_usec = (delayclose%1000)*1000;
}
#endif
if(udp_connect) {
outnet->udp_connect = 1;
}
if(numavailports == 0 || num_ports == 0) {
log_err("no outgoing ports available");
outside_network_delete(outnet);
return NULL;
}
#ifndef INET6
do_ip6 = 0;
#endif
calc_num46(ifs, num_ifs, do_ip4, do_ip6,
&outnet->num_ip4, &outnet->num_ip6);
if(outnet->num_ip4 != 0) {
if(!(outnet->ip4_ifs = (struct port_if*)calloc(
(size_t)outnet->num_ip4, sizeof(struct port_if)))) {
log_err("malloc failed");
outside_network_delete(outnet);
return NULL;
}
}
if(outnet->num_ip6 != 0) {
if(!(outnet->ip6_ifs = (struct port_if*)calloc(
(size_t)outnet->num_ip6, sizeof(struct port_if)))) {
log_err("malloc failed");
outside_network_delete(outnet);
return NULL;
}
}
if( !(outnet->udp_buff = sldns_buffer_new(bufsize)) ||
!(outnet->pending = rbtree_create(pending_cmp)) ||
!(outnet->serviced = rbtree_create(serviced_cmp)) ||
!create_pending_tcp(outnet, bufsize)) {
log_err("malloc failed");
outside_network_delete(outnet);
return NULL;
}
rbtree_init(&outnet->tcp_reuse, reuse_cmp);
outnet->tcp_reuse_max = num_tcp;
/* allocate commpoints */
for(k=0; k<num_ports; k++) {
struct port_comm* pc;
pc = (struct port_comm*)calloc(1, sizeof(*pc));
if(!pc) {
log_err("malloc failed");
outside_network_delete(outnet);
return NULL;
}
pc->cp = comm_point_create_udp(outnet->base, -1,
outnet->udp_buff, 0, outnet_udp_cb, outnet, NULL);
if(!pc->cp) {
log_err("malloc failed");
free(pc);
outside_network_delete(outnet);
return NULL;
}
pc->next = outnet->unused_fds;
outnet->unused_fds = pc;
}
/* allocate interfaces */
if(num_ifs == 0) {
if(do_ip4 && !setup_if(&outnet->ip4_ifs[0], "0.0.0.0",
availports, numavailports, num_ports)) {
log_err("malloc failed");
outside_network_delete(outnet);
return NULL;
}
if(do_ip6 && !setup_if(&outnet->ip6_ifs[0], "::",
availports, numavailports, num_ports)) {
log_err("malloc failed");
outside_network_delete(outnet);
return NULL;
}
} else {
size_t done_4 = 0, done_6 = 0;
int i;
for(i=0; i<num_ifs; i++) {
if(str_is_ip6(ifs[i]) && do_ip6) {
if(!setup_if(&outnet->ip6_ifs[done_6], ifs[i],
availports, numavailports, num_ports)){
log_err("malloc failed");
outside_network_delete(outnet);
return NULL;
}
done_6++;
}
if(!str_is_ip6(ifs[i]) && do_ip4) {
if(!setup_if(&outnet->ip4_ifs[done_4], ifs[i],
availports, numavailports, num_ports)){
log_err("malloc failed");
outside_network_delete(outnet);
return NULL;
}
done_4++;
}
}
}
return outnet;
}
/** helper pending delete */
static void
pending_node_del(rbnode_type* node, void* arg)
{
struct pending* pend = (struct pending*)node;
struct outside_network* outnet = (struct outside_network*)arg;
pending_delete(outnet, pend);
}
/** helper serviced delete */
static void
serviced_node_del(rbnode_type* node, void* ATTR_UNUSED(arg))
{
struct serviced_query* sq = (struct serviced_query*)node;
alloc_reg_release(sq->alloc, sq->region);
if(sq->timer)
comm_timer_delete(sq->timer);
free(sq);
}
void
outside_network_quit_prepare(struct outside_network* outnet)
{
if(!outnet)
return;
/* prevent queued items from being sent */
outnet->want_to_quit = 1;
}
void
outside_network_delete(struct outside_network* outnet)
{
if(!outnet)
return;
outnet->want_to_quit = 1;
/* check every element, since we can be called on malloc error */
if(outnet->pending) {
/* free pending elements, but do no unlink from tree. */
traverse_postorder(outnet->pending, pending_node_del, NULL);
free(outnet->pending);
}
if(outnet->serviced) {
traverse_postorder(outnet->serviced, serviced_node_del, NULL);
free(outnet->serviced);
}
if(outnet->udp_buff)
sldns_buffer_free(outnet->udp_buff);
if(outnet->unused_fds) {
struct port_comm* p = outnet->unused_fds, *np;
while(p) {
np = p->next;
comm_point_delete(p->cp);
free(p);
p = np;
}
outnet->unused_fds = NULL;
}
if(outnet->ip4_ifs) {
int i, k;
for(i=0; i<outnet->num_ip4; i++) {
for(k=0; k<outnet->ip4_ifs[i].inuse; k++) {
struct port_comm* pc = outnet->ip4_ifs[i].
out[k];
comm_point_delete(pc->cp);
free(pc);
}
#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
free(outnet->ip4_ifs[i].avail_ports);
#endif
free(outnet->ip4_ifs[i].out);
}
free(outnet->ip4_ifs);
}
if(outnet->ip6_ifs) {
int i, k;
for(i=0; i<outnet->num_ip6; i++) {
for(k=0; k<outnet->ip6_ifs[i].inuse; k++) {
struct port_comm* pc = outnet->ip6_ifs[i].
out[k];
comm_point_delete(pc->cp);
free(pc);
}
#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
free(outnet->ip6_ifs[i].avail_ports);
#endif
free(outnet->ip6_ifs[i].out);
}
free(outnet->ip6_ifs);
}
if(outnet->tcp_conns) {
size_t i;
for(i=0; i<outnet->num_tcp; i++)
if(outnet->tcp_conns[i]) {
struct pending_tcp* pend;
pend = outnet->tcp_conns[i];
if(pend->reuse.item_on_lru_list) {
/* delete waiting_tcp elements that
* the tcp conn is working on */
decommission_pending_tcp(outnet, pend);
}
comm_point_delete(outnet->tcp_conns[i]->c);
free(outnet->tcp_conns[i]);
outnet->tcp_conns[i] = NULL;
}
free(outnet->tcp_conns);
outnet->tcp_conns = NULL;
}
if(outnet->tcp_wait_first) {
struct waiting_tcp* p = outnet->tcp_wait_first, *np;
while(p) {
np = p->next_waiting;
waiting_tcp_delete(p);
p = np;
}
}
/* was allocated in struct pending that was deleted above */
rbtree_init(&outnet->tcp_reuse, reuse_cmp);
outnet->tcp_reuse_first = NULL;
outnet->tcp_reuse_last = NULL;
if(outnet->udp_wait_first) {
struct pending* p = outnet->udp_wait_first, *np;
while(p) {
np = p->next_waiting;
pending_delete(NULL, p);
p = np;
}
}
free(outnet);
}
void
pending_delete(struct outside_network* outnet, struct pending* p)
{
if(!p)
return;
if(outnet && outnet->udp_wait_first &&
(p->next_waiting || p == outnet->udp_wait_last) ) {
/* delete from waiting list, if it is in the waiting list */
struct pending* prev = NULL, *x = outnet->udp_wait_first;
while(x && x != p) {
prev = x;
x = x->next_waiting;
}
if(x) {
log_assert(x == p);
if(prev)
prev->next_waiting = p->next_waiting;
else outnet->udp_wait_first = p->next_waiting;
if(outnet->udp_wait_last == p)
outnet->udp_wait_last = prev;
}
}
if(outnet) {
(void)rbtree_delete(outnet->pending, p->node.key);
}
if(p->timer)
comm_timer_delete(p->timer);
free(p->pkt);
free(p);
}
static void
sai6_putrandom(struct sockaddr_in6 *sa, int pfxlen, struct ub_randstate *rnd)
{
int i, last;
if(!(pfxlen > 0 && pfxlen < 128))
return;
for(i = 0; i < (128 - pfxlen) / 8; i++) {
sa->sin6_addr.s6_addr[15-i] = (uint8_t)ub_random_max(rnd, 256);
}
last = pfxlen & 7;
if(last != 0) {
sa->sin6_addr.s6_addr[15-i] |=
((0xFF >> last) & ub_random_max(rnd, 256));
}
}
/**
* Try to open a UDP socket for outgoing communication.
* Sets sockets options as needed.
* @param addr: socket address.
* @param addrlen: length of address.
* @param pfxlen: length of network prefix (for address randomisation).
* @param port: port override for addr.
* @param inuse: if -1 is returned, this bool means the port was in use.
* @param rnd: random state (for address randomisation).
* @param dscp: DSCP to use.
* @return fd or -1
*/
static int
udp_sockport(struct sockaddr_storage* addr, socklen_t addrlen, int pfxlen,
int port, int* inuse, struct ub_randstate* rnd, int dscp)
{
int fd, noproto;
if(addr_is_ip6(addr, addrlen)) {
int freebind = 0;
struct sockaddr_in6 sa = *(struct sockaddr_in6*)addr;
sa.sin6_port = (in_port_t)htons((uint16_t)port);
sa.sin6_flowinfo = 0;
sa.sin6_scope_id = 0;
if(pfxlen != 0) {
freebind = 1;
sai6_putrandom(&sa, pfxlen, rnd);
}
fd = create_udp_sock(AF_INET6, SOCK_DGRAM,
(struct sockaddr*)&sa, addrlen, 1, inuse, &noproto,
0, 0, 0, NULL, 0, freebind, 0, dscp);
} else {
struct sockaddr_in* sa = (struct sockaddr_in*)addr;
sa->sin_port = (in_port_t)htons((uint16_t)port);
fd = create_udp_sock(AF_INET, SOCK_DGRAM,
(struct sockaddr*)addr, addrlen, 1, inuse, &noproto,
0, 0, 0, NULL, 0, 0, 0, dscp);
}
return fd;
}
/** Select random ID */
static int
select_id(struct outside_network* outnet, struct pending* pend,
sldns_buffer* packet)
{
int id_tries = 0;
pend->id = GET_RANDOM_ID(outnet->rnd);
LDNS_ID_SET(sldns_buffer_begin(packet), pend->id);
/* insert in tree */
pend->node.key = pend;
while(!rbtree_insert(outnet->pending, &pend->node)) {
/* change ID to avoid collision */
pend->id = GET_RANDOM_ID(outnet->rnd);
LDNS_ID_SET(sldns_buffer_begin(packet), pend->id);
id_tries++;
if(id_tries == MAX_ID_RETRY) {
pend->id=99999; /* non existent ID */
log_err("failed to generate unique ID, drop msg");
return 0;
}
}
verbose(VERB_ALGO, "inserted new pending reply id=%4.4x", pend->id);
return 1;
}
/** return true is UDP connect error needs to be logged */
static int udp_connect_needs_log(int err)
{
switch(err) {
case ECONNREFUSED:
# ifdef ENETUNREACH
case ENETUNREACH:
# endif
# ifdef EHOSTDOWN
case EHOSTDOWN:
# endif
# ifdef EHOSTUNREACH
case EHOSTUNREACH:
# endif
# ifdef ENETDOWN
case ENETDOWN:
# endif
# ifdef EADDRNOTAVAIL
case EADDRNOTAVAIL:
# endif
case EPERM:
case EACCES:
if(verbosity >= VERB_ALGO)
return 1;
return 0;
default:
break;
}
return 1;
}
/** Select random interface and port */
static int
select_ifport(struct outside_network* outnet, struct pending* pend,
int num_if, struct port_if* ifs)
{
int my_if, my_port, fd, portno, inuse, tries=0;
struct port_if* pif;
/* randomly select interface and port */
if(num_if == 0) {
verbose(VERB_QUERY, "Need to send query but have no "
"outgoing interfaces of that family");
return 0;
}
log_assert(outnet->unused_fds);
tries = 0;
while(1) {
my_if = ub_random_max(outnet->rnd, num_if);
pif = &ifs[my_if];
#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
if(outnet->udp_connect) {
/* if we connect() we cannot reuse fds for a port */
if(pif->inuse >= pif->avail_total) {
tries++;
if(tries < MAX_PORT_RETRY)
continue;
log_err("failed to find an open port, drop msg");
return 0;
}
my_port = pif->inuse + ub_random_max(outnet->rnd,
pif->avail_total - pif->inuse);
} else {
my_port = ub_random_max(outnet->rnd, pif->avail_total);
if(my_port < pif->inuse) {
/* port already open */
pend->pc = pif->out[my_port];
verbose(VERB_ALGO, "using UDP if=%d port=%d",
my_if, pend->pc->number);
break;
}
}
/* try to open new port, if fails, loop to try again */
log_assert(pif->inuse < pif->maxout);
portno = pif->avail_ports[my_port - pif->inuse];
#else
my_port = portno = 0;
#endif
fd = udp_sockport(&pif->addr, pif->addrlen, pif->pfxlen,
portno, &inuse, outnet->rnd, outnet->ip_dscp);
if(fd == -1 && !inuse) {
/* nonrecoverable error making socket */
return 0;
}
if(fd != -1) {
verbose(VERB_ALGO, "opened UDP if=%d port=%d",
my_if, portno);
if(outnet->udp_connect) {
/* connect() to the destination */
if(connect(fd, (struct sockaddr*)&pend->addr,
pend->addrlen) < 0) {
if(udp_connect_needs_log(errno)) {
log_err_addr("udp connect failed",
strerror(errno), &pend->addr,
pend->addrlen);
}
sock_close(fd);
return 0;
}
}
/* grab fd */
pend->pc = outnet->unused_fds;
outnet->unused_fds = pend->pc->next;
/* setup portcomm */
pend->pc->next = NULL;
pend->pc->number = portno;
pend->pc->pif = pif;
pend->pc->index = pif->inuse;
pend->pc->num_outstanding = 0;
comm_point_start_listening(pend->pc->cp, fd, -1);
/* grab port in interface */
pif->out[pif->inuse] = pend->pc;
#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
pif->avail_ports[my_port - pif->inuse] =
pif->avail_ports[pif->avail_total-pif->inuse-1];
#endif
pif->inuse++;
break;
}
/* failed, already in use */
verbose(VERB_QUERY, "port %d in use, trying another", portno);
tries++;
if(tries == MAX_PORT_RETRY) {
log_err("failed to find an open port, drop msg");
return 0;
}
}
log_assert(pend->pc);
pend->pc->num_outstanding++;
return 1;
}
static int
randomize_and_send_udp(struct pending* pend, sldns_buffer* packet, int timeout)
{
struct timeval tv;
struct outside_network* outnet = pend->sq->outnet;
/* select id */
if(!select_id(outnet, pend, packet)) {
return 0;
}
/* select src_if, port */
if(addr_is_ip6(&pend->addr, pend->addrlen)) {
if(!select_ifport(outnet, pend,
outnet->num_ip6, outnet->ip6_ifs))
return 0;
} else {
if(!select_ifport(outnet, pend,
outnet->num_ip4, outnet->ip4_ifs))
return 0;
}
log_assert(pend->pc && pend->pc->cp);
/* send it over the commlink */
if(!comm_point_send_udp_msg(pend->pc->cp, packet,
(struct sockaddr*)&pend->addr, pend->addrlen, outnet->udp_connect)) {
portcomm_loweruse(outnet, pend->pc);
return 0;
}
outnet->num_udp_outgoing++;
/* system calls to set timeout after sending UDP to make roundtrip
smaller. */
#ifndef S_SPLINT_S
tv.tv_sec = timeout/1000;
tv.tv_usec = (timeout%1000)*1000;
#endif
comm_timer_set(pend->timer, &tv);
#ifdef USE_DNSTAP
/*
* sending src (local service)/dst (upstream) addresses over DNSTAP
* There are no chances to get the src (local service) addr if unbound
* is not configured with specific outgoing IP-addresses. So we will
* pass 0.0.0.0 (::) to argument for
* dt_msg_send_outside_query()/dt_msg_send_outside_response() calls.
*/
if(outnet->dtenv &&
(outnet->dtenv->log_resolver_query_messages ||
outnet->dtenv->log_forwarder_query_messages)) {
log_addr(VERB_ALGO, "from local addr", &pend->pc->pif->addr, pend->pc->pif->addrlen);
log_addr(VERB_ALGO, "request to upstream", &pend->addr, pend->addrlen);
dt_msg_send_outside_query(outnet->dtenv, &pend->addr, &pend->pc->pif->addr, comm_udp,
pend->sq->zone, pend->sq->zonelen, packet);
}
#endif
return 1;
}
struct pending*
pending_udp_query(struct serviced_query* sq, struct sldns_buffer* packet,
int timeout, comm_point_callback_type* cb, void* cb_arg)
{
struct pending* pend = (struct pending*)calloc(1, sizeof(*pend));
if(!pend) return NULL;
pend->outnet = sq->outnet;
pend->sq = sq;
pend->addrlen = sq->addrlen;
memmove(&pend->addr, &sq->addr, sq->addrlen);
pend->cb = cb;
pend->cb_arg = cb_arg;
pend->node.key = pend;
pend->timer = comm_timer_create(sq->outnet->base, pending_udp_timer_cb,
pend);
if(!pend->timer) {
free(pend);
return NULL;
}
if(sq->outnet->unused_fds == NULL) {
/* no unused fd, cannot create a new port (randomly) */
verbose(VERB_ALGO, "no fds available, udp query waiting");
pend->timeout = timeout;
pend->pkt_len = sldns_buffer_limit(packet);
pend->pkt = (uint8_t*)memdup(sldns_buffer_begin(packet),
pend->pkt_len);
if(!pend->pkt) {
comm_timer_delete(pend->timer);
free(pend);
return NULL;
}
/* put at end of waiting list */
if(sq->outnet->udp_wait_last)
sq->outnet->udp_wait_last->next_waiting = pend;
else
sq->outnet->udp_wait_first = pend;
sq->outnet->udp_wait_last = pend;
return pend;
}
log_assert(!sq->busy);
sq->busy = 1;
if(!randomize_and_send_udp(pend, packet, timeout)) {
pending_delete(sq->outnet, pend);
return NULL;
}
sq->busy = 0;
return pend;
}
void
outnet_tcptimer(void* arg)
{
struct waiting_tcp* w = (struct waiting_tcp*)arg;
struct outside_network* outnet = w->outnet;
verbose(VERB_CLIENT, "outnet_tcptimer");
if(w->on_tcp_waiting_list) {
/* it is on the waiting list */
outnet_waiting_tcp_list_remove(outnet, w);
waiting_tcp_callback(w, NULL, NETEVENT_TIMEOUT, NULL);
waiting_tcp_delete(w);
} else {
/* it was in use */
struct pending_tcp* pend=(struct pending_tcp*)w->next_waiting;
reuse_cb_and_decommission(outnet, pend, NETEVENT_TIMEOUT);
}
use_free_buffer(outnet);
}
/** close the oldest reuse_tcp connection to make a fd and struct pend
* available for a new stream connection */
static void
reuse_tcp_close_oldest(struct outside_network* outnet)
{
struct reuse_tcp* reuse;
verbose(VERB_CLIENT, "reuse_tcp_close_oldest");
reuse = reuse_tcp_lru_snip(outnet);
if(!reuse) return;
/* free up */
reuse_cb_and_decommission(outnet, reuse->pending, NETEVENT_CLOSED);
}
static uint16_t
tcp_select_id(struct outside_network* outnet, struct reuse_tcp* reuse)
{
if(reuse)
return reuse_tcp_select_id(reuse, outnet);
return GET_RANDOM_ID(outnet->rnd);
}
/** find spare ID value for reuse tcp stream. That is random and also does
* not collide with an existing query ID that is in use or waiting */
uint16_t
reuse_tcp_select_id(struct reuse_tcp* reuse, struct outside_network* outnet)
{
uint16_t id = 0, curid, nextid;
const int try_random = 2000;
int i;
unsigned select, count, space;
rbnode_type* node;
/* make really sure the tree is not empty */
if(reuse->tree_by_id.count == 0) {
id = GET_RANDOM_ID(outnet->rnd);
return id;
}
/* try to find random empty spots by picking them */
for(i = 0; i<try_random; i++) {
id = GET_RANDOM_ID(outnet->rnd);
if(!reuse_tcp_by_id_find(reuse, id)) {
return id;
}
}
/* equally pick a random unused element from the tree that is
* not in use. Pick a the n-th index of an unused number,
* then loop over the empty spaces in the tree and find it */
log_assert(reuse->tree_by_id.count < 0xffff);
select = ub_random_max(outnet->rnd, 0xffff - reuse->tree_by_id.count);
/* select value now in 0 .. num free - 1 */
count = 0; /* number of free spaces passed by */
node = rbtree_first(&reuse->tree_by_id);
log_assert(node && node != RBTREE_NULL); /* tree not empty */
/* see if select is before first node */
if(select < (unsigned)tree_by_id_get_id(node))
return select;
count += tree_by_id_get_id(node);
/* perhaps select is between nodes */
while(node && node != RBTREE_NULL) {
rbnode_type* next = rbtree_next(node);
if(next && next != RBTREE_NULL) {
curid = tree_by_id_get_id(node);
nextid = tree_by_id_get_id(next);
log_assert(curid < nextid);
if(curid != 0xffff && curid + 1 < nextid) {
/* space between nodes */
space = nextid - curid - 1;
log_assert(select >= count);
if(select < count + space) {
/* here it is */
return curid + 1 + (select - count);
}
count += space;
}
}
node = next;
}
/* select is after the last node */
/* count is the number of free positions before the nodes in the
* tree */
node = rbtree_last(&reuse->tree_by_id);
log_assert(node && node != RBTREE_NULL); /* tree not empty */
curid = tree_by_id_get_id(node);
log_assert(count + (0xffff-curid) + reuse->tree_by_id.count == 0xffff);
return curid + 1 + (select - count);
}
struct waiting_tcp*
pending_tcp_query(struct serviced_query* sq, sldns_buffer* packet,
int timeout, comm_point_callback_type* callback, void* callback_arg)
{
struct pending_tcp* pend = sq->outnet->tcp_free;
struct reuse_tcp* reuse = NULL;
struct waiting_tcp* w;
verbose(VERB_CLIENT, "pending_tcp_query");
if(sldns_buffer_limit(packet) < sizeof(uint16_t)) {
verbose(VERB_ALGO, "pending tcp query with too short buffer < 2");
return NULL;
}
/* find out if a reused stream to the target exists */
/* if so, take it into use */
reuse = reuse_tcp_find(sq->outnet, &sq->addr, sq->addrlen,
sq->ssl_upstream);
if(reuse) {
log_reuse_tcp(VERB_CLIENT, "pending_tcp_query: found reuse", reuse);
log_assert(reuse->pending);
pend = reuse->pending;
reuse_tcp_lru_touch(sq->outnet, reuse);
}
log_assert(!reuse || (reuse && pend));
/* if !pend but we have reuse streams, close a reuse stream
* to be able to open a new one to this target, no use waiting
* to reuse a file descriptor while another query needs to use
* that buffer and file descriptor now. */
if(!pend) {
reuse_tcp_close_oldest(sq->outnet);
pend = sq->outnet->tcp_free;
log_assert(!reuse || (pend == reuse->pending));
}
/* allocate space to store query */
w = (struct waiting_tcp*)malloc(sizeof(struct waiting_tcp)
+ sldns_buffer_limit(packet));
if(!w) {
return NULL;
}
if(!(w->timer = comm_timer_create(sq->outnet->base, outnet_tcptimer, w))) {
free(w);
return NULL;
}
w->pkt = (uint8_t*)w + sizeof(struct waiting_tcp);
w->pkt_len = sldns_buffer_limit(packet);
memmove(w->pkt, sldns_buffer_begin(packet), w->pkt_len);
w->id = tcp_select_id(sq->outnet, reuse);
LDNS_ID_SET(w->pkt, w->id);
memcpy(&w->addr, &sq->addr, sq->addrlen);
w->addrlen = sq->addrlen;
w->outnet = sq->outnet;
w->on_tcp_waiting_list = 0;
w->next_waiting = NULL;
w->cb = callback;
w->cb_arg = callback_arg;
w->ssl_upstream = sq->ssl_upstream;
w->tls_auth_name = sq->tls_auth_name;
w->timeout = timeout;
w->id_node.key = NULL;
w->write_wait_prev = NULL;
w->write_wait_next = NULL;
w->write_wait_queued = 0;
w->error_count = 0;
#ifdef USE_DNSTAP
w->sq = NULL;
#endif
w->in_cb_and_decommission = 0;
if(pend) {
/* we have a buffer available right now */
if(reuse) {
log_assert(reuse == &pend->reuse);
/* reuse existing fd, write query and continue */
/* store query in tree by id */
verbose(VERB_CLIENT, "pending_tcp_query: reuse, store");
w->next_waiting = (void*)pend;
reuse_tree_by_id_insert(&pend->reuse, w);
/* can we write right now? */
if(pend->query == NULL) {
/* write straight away */
/* stop the timer on read of the fd */
comm_point_stop_listening(pend->c);
pend->query = w;
outnet_tcp_take_query_setup(pend->c->fd, pend,
w);
} else {
/* put it in the waiting list for
* this stream */
reuse_write_wait_push_back(&pend->reuse, w);
}
} else {
/* create new fd and connect to addr, setup to
* write query */
verbose(VERB_CLIENT, "pending_tcp_query: new fd, connect");
rbtree_init(&pend->reuse.tree_by_id, reuse_id_cmp);
pend->reuse.pending = pend;
memcpy(&pend->reuse.addr, &sq->addr, sq->addrlen);
pend->reuse.addrlen = sq->addrlen;
if(!outnet_tcp_take_into_use(w)) {
waiting_tcp_delete(w);
return NULL;
}
}
#ifdef USE_DNSTAP
if(sq->outnet->dtenv &&
(sq->outnet->dtenv->log_resolver_query_messages ||
sq->outnet->dtenv->log_forwarder_query_messages)) {
/* use w->pkt, because it has the ID value */
sldns_buffer tmp;
sldns_buffer_init_frm_data(&tmp, w->pkt, w->pkt_len);
dt_msg_send_outside_query(sq->outnet->dtenv, &sq->addr,
&pend->pi->addr, comm_tcp, sq->zone,
sq->zonelen, &tmp);
}
#endif
} else {
/* queue up */
/* waiting for a buffer on the outside network buffer wait
* list */
verbose(VERB_CLIENT, "pending_tcp_query: queue to wait");
#ifdef USE_DNSTAP
w->sq = sq;
#endif
outnet_waiting_tcp_list_add(sq->outnet, w, 1);
}
return w;
}
/** create query for serviced queries */
static void
serviced_gen_query(sldns_buffer* buff, uint8_t* qname, size_t qnamelen,
uint16_t qtype, uint16_t qclass, uint16_t flags)
{
sldns_buffer_clear(buff);
/* skip id */
sldns_buffer_write_u16(buff, flags);
sldns_buffer_write_u16(buff, 1); /* qdcount */
sldns_buffer_write_u16(buff, 0); /* ancount */
sldns_buffer_write_u16(buff, 0); /* nscount */
sldns_buffer_write_u16(buff, 0); /* arcount */
sldns_buffer_write(buff, qname, qnamelen);
sldns_buffer_write_u16(buff, qtype);
sldns_buffer_write_u16(buff, qclass);
sldns_buffer_flip(buff);
}
/** lookup serviced query in serviced query rbtree */
static struct serviced_query*
lookup_serviced(struct outside_network* outnet, sldns_buffer* buff, int dnssec,
struct sockaddr_storage* addr, socklen_t addrlen,
struct edns_option* opt_list)
{
struct serviced_query key;
key.node.key = &key;
key.qbuf = sldns_buffer_begin(buff);
key.qbuflen = sldns_buffer_limit(buff);
key.dnssec = dnssec;
memcpy(&key.addr, addr, addrlen);
key.addrlen = addrlen;
key.outnet = outnet;
key.opt_list = opt_list;
return (struct serviced_query*)rbtree_search(outnet->serviced, &key);
}
void
serviced_timer_cb(void* arg)
{
struct serviced_query* sq = (struct serviced_query*)arg;
struct outside_network* outnet = sq->outnet;
verbose(VERB_ALGO, "serviced send timer");
/* By the time this cb is called, if we don't have any registered
* callbacks for this serviced_query anymore; do not send. */
if(!sq->cblist)
goto delete;
/* perform first network action */
if(outnet->do_udp && !(sq->tcp_upstream || sq->ssl_upstream)) {
if(!serviced_udp_send(sq, outnet->udp_buff))
goto delete;
} else {
if(!serviced_tcp_send(sq, outnet->udp_buff))
goto delete;
}
/* Maybe by this time we don't have callbacks attached anymore. Don't
* proactively try to delete; let it run and maybe another callback
* will get attached by the time we get an answer. */
return;
delete:
serviced_callbacks(sq, NETEVENT_CLOSED, NULL, NULL);
}
/** Create new serviced entry */
static struct serviced_query*
serviced_create(struct outside_network* outnet, sldns_buffer* buff, int dnssec,
int want_dnssec, int nocaps, int tcp_upstream, int ssl_upstream,
char* tls_auth_name, struct sockaddr_storage* addr, socklen_t addrlen,
uint8_t* zone, size_t zonelen, int qtype, struct edns_option* opt_list,
size_t pad_queries_block_size, struct alloc_cache* alloc,
struct regional* region)
{
struct serviced_query* sq = (struct serviced_query*)malloc(sizeof(*sq));
struct timeval t;
#ifdef UNBOUND_DEBUG
rbnode_type* ins;
#endif
if(!sq) {
alloc_reg_release(alloc, region);
return NULL;
}
sq->node.key = sq;
sq->alloc = alloc;
sq->region = region;
sq->qbuf = regional_alloc_init(region, sldns_buffer_begin(buff),
sldns_buffer_limit(buff));
if(!sq->qbuf) {
alloc_reg_release(alloc, region);
free(sq);
return NULL;
}
sq->qbuflen = sldns_buffer_limit(buff);
sq->zone = regional_alloc_init(region, zone, zonelen);
if(!sq->zone) {
alloc_reg_release(alloc, region);
free(sq);
return NULL;
}
sq->zonelen = zonelen;
sq->qtype = qtype;
sq->dnssec = dnssec;
sq->want_dnssec = want_dnssec;
sq->nocaps = nocaps;
sq->tcp_upstream = tcp_upstream;
sq->ssl_upstream = ssl_upstream;
if(tls_auth_name) {
sq->tls_auth_name = regional_strdup(region, tls_auth_name);
if(!sq->tls_auth_name) {
alloc_reg_release(alloc, region);
free(sq);
return NULL;
}
} else {
sq->tls_auth_name = NULL;
}
memcpy(&sq->addr, addr, addrlen);
sq->addrlen = addrlen;
sq->opt_list = opt_list;
sq->busy = 0;
sq->timer = comm_timer_create(outnet->base, serviced_timer_cb, sq);
if(!sq->timer) {
alloc_reg_release(alloc, region);
free(sq);
return NULL;
}
memset(&t, 0, sizeof(t));
comm_timer_set(sq->timer, &t);
sq->outnet = outnet;
sq->cblist = NULL;
sq->pending = NULL;
sq->status = serviced_initial;
sq->retry = 0;
sq->to_be_deleted = 0;
sq->padding_block_size = pad_queries_block_size;
#ifdef UNBOUND_DEBUG
ins =
#else
(void)
#endif
rbtree_insert(outnet->serviced, &sq->node);
log_assert(ins != NULL); /* must not be already present */
return sq;
}
/** reuse tcp stream, remove serviced query from stream,
* return true if the stream is kept, false if it is to be closed */
static int
reuse_tcp_remove_serviced_keep(struct waiting_tcp* w,
struct serviced_query* sq)
{
struct pending_tcp* pend_tcp = (struct pending_tcp*)w->next_waiting;
verbose(VERB_CLIENT, "reuse_tcp_remove_serviced_keep");
/* remove the callback. let query continue to write to not cancel
* the stream itself. also keep it as an entry in the tree_by_id,
* in case the answer returns (that we no longer want), but we cannot
* pick the same ID number meanwhile */
w->cb = NULL;
/* see if can be entered in reuse tree
* for that the FD has to be non-1 */
if(pend_tcp->c->fd == -1) {
verbose(VERB_CLIENT, "reuse_tcp_remove_serviced_keep: -1 fd");
return 0;
}
/* if in tree and used by other queries */
if(pend_tcp->reuse.node.key) {
verbose(VERB_CLIENT, "reuse_tcp_remove_serviced_keep: in use by other queries");
/* do not reset the keepalive timer, for that
* we'd need traffic, and this is where the serviced is
* removed due to state machine internal reasons,
* eg. iterator no longer interested in this query */
return 1;
}
/* if still open and want to keep it open */
if(pend_tcp->c->fd != -1 && sq->outnet->tcp_reuse.count <
sq->outnet->tcp_reuse_max) {
verbose(VERB_CLIENT, "reuse_tcp_remove_serviced_keep: keep open");
/* set a keepalive timer on it */
if(!reuse_tcp_insert(sq->outnet, pend_tcp)) {
return 0;
}
reuse_tcp_setup_timeout(pend_tcp, sq->outnet->tcp_reuse_timeout);
return 1;
}
return 0;
}
/** cleanup serviced query entry */
static void
serviced_delete(struct serviced_query* sq)
{
verbose(VERB_CLIENT, "serviced_delete");
if(sq->pending) {
/* clear up the pending query */
if(sq->status == serviced_query_UDP_EDNS ||
sq->status == serviced_query_UDP ||
sq->status == serviced_query_UDP_EDNS_FRAG ||
sq->status == serviced_query_UDP_EDNS_fallback) {
struct pending* p = (struct pending*)sq->pending;
verbose(VERB_CLIENT, "serviced_delete: UDP");
if(p->pc)
portcomm_loweruse(sq->outnet, p->pc);
pending_delete(sq->outnet, p);
/* this call can cause reentrant calls back into the
* mesh */
outnet_send_wait_udp(sq->outnet);
} else {
struct waiting_tcp* w = (struct waiting_tcp*)
sq->pending;
verbose(VERB_CLIENT, "serviced_delete: TCP");
log_assert(!(w->write_wait_queued && w->on_tcp_waiting_list));
/* if on stream-write-waiting list then
* remove from waiting list and waiting_tcp_delete */
if(w->write_wait_queued) {
struct pending_tcp* pend =
(struct pending_tcp*)w->next_waiting;
verbose(VERB_CLIENT, "serviced_delete: writewait");
if(!w->in_cb_and_decommission)
reuse_tree_by_id_delete(&pend->reuse, w);
reuse_write_wait_remove(&pend->reuse, w);
if(!w->in_cb_and_decommission)
waiting_tcp_delete(w);
} else if(!w->on_tcp_waiting_list) {
struct pending_tcp* pend =
(struct pending_tcp*)w->next_waiting;
verbose(VERB_CLIENT, "serviced_delete: tcpreusekeep");
/* w needs to stay on tree_by_id to not assign
* the same ID; remove the callback since its
* serviced_query will be gone. */
w->cb = NULL;
if(!reuse_tcp_remove_serviced_keep(w, sq)) {
if(!w->in_cb_and_decommission)
reuse_cb_and_decommission(sq->outnet,
pend, NETEVENT_CLOSED);
use_free_buffer(sq->outnet);
}
sq->pending = NULL;
} else {
verbose(VERB_CLIENT, "serviced_delete: tcpwait");
outnet_waiting_tcp_list_remove(sq->outnet, w);
if(!w->in_cb_and_decommission)
waiting_tcp_delete(w);
}
}
}
/* does not delete from tree, caller has to do that */
serviced_node_del(&sq->node, NULL);
}
/** perturb a dname capitalization randomly */
static void
serviced_perturb_qname(struct ub_randstate* rnd, uint8_t* qbuf, size_t len)
{
uint8_t lablen;
uint8_t* d = qbuf + 10;
long int random = 0;
int bits = 0;
log_assert(len >= 10 + 5 /* offset qname, root, qtype, qclass */);
(void)len;
lablen = *d++;
while(lablen) {
while(lablen--) {
/* only perturb A-Z, a-z */
if(isalpha((unsigned char)*d)) {
/* get a random bit */
if(bits == 0) {
random = ub_random(rnd);
bits = 30;
}
if(random & 0x1) {
*d = (uint8_t)toupper((unsigned char)*d);
} else {
*d = (uint8_t)tolower((unsigned char)*d);
}
random >>= 1;
bits--;
}
d++;
}
lablen = *d++;
}
if(verbosity >= VERB_ALGO) {
char buf[LDNS_MAX_DOMAINLEN+1];
dname_str(qbuf+10, buf);
verbose(VERB_ALGO, "qname perturbed to %s", buf);
}
}
/** put serviced query into a buffer */
static void
serviced_encode(struct serviced_query* sq, sldns_buffer* buff, int with_edns)
{
/* if we are using 0x20 bits for ID randomness, perturb them */
if(sq->outnet->use_caps_for_id && !sq->nocaps) {
serviced_perturb_qname(sq->outnet->rnd, sq->qbuf, sq->qbuflen);
}
/* generate query */
sldns_buffer_clear(buff);
sldns_buffer_write_u16(buff, 0); /* id placeholder */
sldns_buffer_write(buff, sq->qbuf, sq->qbuflen);
sldns_buffer_flip(buff);
if(with_edns) {
/* add edns section */
struct edns_data edns;
struct edns_option padding_option;
edns.edns_present = 1;
edns.ext_rcode = 0;
edns.edns_version = EDNS_ADVERTISED_VERSION;
edns.opt_list_in = NULL;
edns.opt_list_out = sq->opt_list;
edns.opt_list_inplace_cb_out = NULL;
if(sq->status == serviced_query_UDP_EDNS_FRAG) {
if(addr_is_ip6(&sq->addr, sq->addrlen)) {
if(EDNS_FRAG_SIZE_IP6 < EDNS_ADVERTISED_SIZE)
edns.udp_size = EDNS_FRAG_SIZE_IP6;
else edns.udp_size = EDNS_ADVERTISED_SIZE;
} else {
if(EDNS_FRAG_SIZE_IP4 < EDNS_ADVERTISED_SIZE)
edns.udp_size = EDNS_FRAG_SIZE_IP4;
else edns.udp_size = EDNS_ADVERTISED_SIZE;
}
} else {
edns.udp_size = EDNS_ADVERTISED_SIZE;
}
edns.bits = 0;
if(sq->dnssec & EDNS_DO)
edns.bits = EDNS_DO;
if(sq->dnssec & BIT_CD)
LDNS_CD_SET(sldns_buffer_begin(buff));
if (sq->ssl_upstream && sq->padding_block_size) {
padding_option.opt_code = LDNS_EDNS_PADDING;
padding_option.opt_len = 0;
padding_option.opt_data = NULL;
padding_option.next = edns.opt_list_out;
edns.opt_list_out = &padding_option;
edns.padding_block_size = sq->padding_block_size;
}
attach_edns_record(buff, &edns);
}
}
/**
* Perform serviced query UDP sending operation.
* Sends UDP with EDNS, unless infra host marked non EDNS.
* @param sq: query to send.
* @param buff: buffer scratch space.
* @return 0 on error.
*/
static int
serviced_udp_send(struct serviced_query* sq, sldns_buffer* buff)
{
int rtt, vs;
uint8_t edns_lame_known;
time_t now = *sq->outnet->now_secs;
if(!infra_host(sq->outnet->infra, &sq->addr, sq->addrlen, sq->zone,
sq->zonelen, now, &vs, &edns_lame_known, &rtt))
return 0;
sq->last_rtt = rtt;
verbose(VERB_ALGO, "EDNS lookup known=%d vs=%d", edns_lame_known, vs);
if(sq->status == serviced_initial) {
if(vs != -1) {
sq->status = serviced_query_UDP_EDNS;
} else {
sq->status = serviced_query_UDP;
}
}
serviced_encode(sq, buff, (sq->status == serviced_query_UDP_EDNS) ||
(sq->status == serviced_query_UDP_EDNS_FRAG));
sq->last_sent_time = *sq->outnet->now_tv;
sq->edns_lame_known = (int)edns_lame_known;
verbose(VERB_ALGO, "serviced query UDP timeout=%d msec", rtt);
sq->pending = pending_udp_query(sq, buff, rtt,
serviced_udp_callback, sq);
if(!sq->pending)
return 0;
return 1;
}
/** check that perturbed qname is identical */
static int
serviced_check_qname(sldns_buffer* pkt, uint8_t* qbuf, size_t qbuflen)
{
uint8_t* d1 = sldns_buffer_begin(pkt)+12;
uint8_t* d2 = qbuf+10;
uint8_t len1, len2;
int count = 0;
if(sldns_buffer_limit(pkt) < 12+1+4) /* packet too small for qname */
return 0;
log_assert(qbuflen >= 15 /* 10 header, root, type, class */);
len1 = *d1++;
len2 = *d2++;
while(len1 != 0 || len2 != 0) {
if(LABEL_IS_PTR(len1)) {
/* check if we can read *d1 with compression ptr rest */
if(d1 >= sldns_buffer_at(pkt, sldns_buffer_limit(pkt)))
return 0;
d1 = sldns_buffer_begin(pkt)+PTR_OFFSET(len1, *d1);
/* check if we can read the destination *d1 */
if(d1 >= sldns_buffer_at(pkt, sldns_buffer_limit(pkt)))
return 0;
len1 = *d1++;
if(count++ > MAX_COMPRESS_PTRS)
return 0;
continue;
}
if(d2 > qbuf+qbuflen)
return 0;
if(len1 != len2)
return 0;
if(len1 > LDNS_MAX_LABELLEN)
return 0;
/* check len1 + 1(next length) are okay to read */
if(d1+len1 >= sldns_buffer_at(pkt, sldns_buffer_limit(pkt)))
return 0;
log_assert(len1 <= LDNS_MAX_LABELLEN);
log_assert(len2 <= LDNS_MAX_LABELLEN);
log_assert(len1 == len2 && len1 != 0);
/* compare the labels - bitwise identical */
if(memcmp(d1, d2, len1) != 0)
return 0;
d1 += len1;
d2 += len2;
len1 = *d1++;
len2 = *d2++;
}
return 1;
}
/** call the callbacks for a serviced query */
static void
serviced_callbacks(struct serviced_query* sq, int error, struct comm_point* c,
struct comm_reply* rep)
{
struct service_callback* p;
int dobackup = (sq->cblist && sq->cblist->next); /* >1 cb*/
uint8_t *backup_p = NULL;
size_t backlen = 0;
#ifdef UNBOUND_DEBUG
rbnode_type* rem =
#else
(void)
#endif
/* remove from tree, and schedule for deletion, so that callbacks
* can safely deregister themselves and even create new serviced
* queries that are identical to this one. */
rbtree_delete(sq->outnet->serviced, sq);
log_assert(rem); /* should have been present */
sq->to_be_deleted = 1;
verbose(VERB_ALGO, "svcd callbacks start");
if(sq->outnet->use_caps_for_id && error == NETEVENT_NOERROR && c &&
!sq->nocaps && sq->qtype != LDNS_RR_TYPE_PTR) {
/* for type PTR do not check perturbed name in answer,
* compatibility with cisco dns guard boxes that mess up
* reverse queries 0x20 contents */
/* noerror and nxdomain must have a qname in reply */
if(sldns_buffer_read_u16_at(c->buffer, 4) == 0 &&
(LDNS_RCODE_WIRE(sldns_buffer_begin(c->buffer))
== LDNS_RCODE_NOERROR ||
LDNS_RCODE_WIRE(sldns_buffer_begin(c->buffer))
== LDNS_RCODE_NXDOMAIN)) {
verbose(VERB_DETAIL, "no qname in reply to check 0x20ID");
log_addr(VERB_DETAIL, "from server",
&sq->addr, sq->addrlen);
log_buf(VERB_DETAIL, "for packet", c->buffer);
error = NETEVENT_CLOSED;
c = NULL;
} else if(sldns_buffer_read_u16_at(c->buffer, 4) > 0 &&
!serviced_check_qname(c->buffer, sq->qbuf,
sq->qbuflen)) {
verbose(VERB_DETAIL, "wrong 0x20-ID in reply qname");
log_addr(VERB_DETAIL, "from server",
&sq->addr, sq->addrlen);
log_buf(VERB_DETAIL, "for packet", c->buffer);
error = NETEVENT_CAPSFAIL;
/* and cleanup too */
pkt_dname_tolower(c->buffer,
sldns_buffer_at(c->buffer, 12));
} else {
verbose(VERB_ALGO, "good 0x20-ID in reply qname");
/* cleanup caps, prettier cache contents. */
pkt_dname_tolower(c->buffer,
sldns_buffer_at(c->buffer, 12));
}
}
if(dobackup && c) {
/* make a backup of the query, since the querystate processing
* may send outgoing queries that overwrite the buffer.
* use secondary buffer to store the query.
* This is a data copy, but faster than packet to server */
backlen = sldns_buffer_limit(c->buffer);
backup_p = regional_alloc_init(sq->region,
sldns_buffer_begin(c->buffer), backlen);
if(!backup_p) {
log_err("malloc failure in serviced query callbacks");
error = NETEVENT_CLOSED;
c = NULL;
}
sq->outnet->svcd_overhead = backlen;
}
/* test the actual sq->cblist, because the next elem could be deleted*/
while((p=sq->cblist) != NULL) {
sq->cblist = p->next; /* remove this element */
if(dobackup && c) {
sldns_buffer_clear(c->buffer);
sldns_buffer_write(c->buffer, backup_p, backlen);
sldns_buffer_flip(c->buffer);
}
fptr_ok(fptr_whitelist_serviced_query(p->cb));
(void)(*p->cb)(c, p->cb_arg, error, rep);
}
if(backup_p) {
sq->outnet->svcd_overhead = 0;
}
verbose(VERB_ALGO, "svcd callbacks end");
log_assert(sq->cblist == NULL);
serviced_delete(sq);
}
int
serviced_tcp_callback(struct comm_point* c, void* arg, int error,
struct comm_reply* rep)
{
struct serviced_query* sq = (struct serviced_query*)arg;
struct comm_reply r2;
#ifdef USE_DNSTAP
struct waiting_tcp* w = (struct waiting_tcp*)sq->pending;
struct pending_tcp* pend_tcp = NULL;
struct port_if* pi = NULL;
if(w && !w->on_tcp_waiting_list && w->next_waiting) {
pend_tcp = (struct pending_tcp*)w->next_waiting;
pi = pend_tcp->pi;
}
#endif
sq->pending = NULL; /* removed after this callback */
if(error != NETEVENT_NOERROR)
log_addr(VERB_QUERY, "tcp error for address",
&sq->addr, sq->addrlen);
if(error==NETEVENT_NOERROR)
infra_update_tcp_works(sq->outnet->infra, &sq->addr,
sq->addrlen, sq->zone, sq->zonelen);
#ifdef USE_DNSTAP
/*
* sending src (local service)/dst (upstream) addresses over DNSTAP
*/
if(error==NETEVENT_NOERROR && pi && sq->outnet->dtenv &&
(sq->outnet->dtenv->log_resolver_response_messages ||
sq->outnet->dtenv->log_forwarder_response_messages)) {
log_addr(VERB_ALGO, "response from upstream", &sq->addr, sq->addrlen);
log_addr(VERB_ALGO, "to local addr", &pi->addr, pi->addrlen);
dt_msg_send_outside_response(sq->outnet->dtenv, &sq->addr,
&pi->addr, c->type, sq->zone, sq->zonelen, sq->qbuf,
sq->qbuflen, &sq->last_sent_time, sq->outnet->now_tv,
c->buffer);
}
#endif
if(error==NETEVENT_NOERROR && sq->status == serviced_query_TCP_EDNS &&
(LDNS_RCODE_WIRE(sldns_buffer_begin(c->buffer)) ==
LDNS_RCODE_FORMERR || LDNS_RCODE_WIRE(sldns_buffer_begin(
c->buffer)) == LDNS_RCODE_NOTIMPL) ) {
/* attempt to fallback to nonEDNS */
sq->status = serviced_query_TCP_EDNS_fallback;
serviced_tcp_initiate(sq, c->buffer);
return 0;
} else if(error==NETEVENT_NOERROR &&
sq->status == serviced_query_TCP_EDNS_fallback &&
(LDNS_RCODE_WIRE(sldns_buffer_begin(c->buffer)) ==
LDNS_RCODE_NOERROR || LDNS_RCODE_WIRE(
sldns_buffer_begin(c->buffer)) == LDNS_RCODE_NXDOMAIN
|| LDNS_RCODE_WIRE(sldns_buffer_begin(c->buffer))
== LDNS_RCODE_YXDOMAIN)) {
/* the fallback produced a result that looks promising, note
* that this server should be approached without EDNS */
/* only store noEDNS in cache if domain is noDNSSEC */
if(!sq->want_dnssec)
if(!infra_edns_update(sq->outnet->infra, &sq->addr,
sq->addrlen, sq->zone, sq->zonelen, -1,
*sq->outnet->now_secs))
log_err("Out of memory caching no edns for host");
sq->status = serviced_query_TCP;
}
if(sq->tcp_upstream || sq->ssl_upstream) {
struct timeval now = *sq->outnet->now_tv;
if(error!=NETEVENT_NOERROR) {
if(!infra_rtt_update(sq->outnet->infra, &sq->addr,
sq->addrlen, sq->zone, sq->zonelen, sq->qtype,
-1, sq->last_rtt, (time_t)now.tv_sec))
log_err("out of memory in TCP exponential backoff.");
} else if(now.tv_sec > sq->last_sent_time.tv_sec ||
(now.tv_sec == sq->last_sent_time.tv_sec &&
now.tv_usec > sq->last_sent_time.tv_usec)) {
/* convert from microseconds to milliseconds */
int roundtime = ((int)(now.tv_sec - sq->last_sent_time.tv_sec))*1000
+ ((int)now.tv_usec - (int)sq->last_sent_time.tv_usec)/1000;
verbose(VERB_ALGO, "measured TCP-time at %d msec", roundtime);
log_assert(roundtime >= 0);
/* only store if less then AUTH_TIMEOUT seconds, it could be
* huge due to system-hibernated and we woke up */
if(roundtime < 60000) {
if(!infra_rtt_update(sq->outnet->infra, &sq->addr,
sq->addrlen, sq->zone, sq->zonelen, sq->qtype,
roundtime, sq->last_rtt, (time_t)now.tv_sec))
log_err("out of memory noting rtt.");
}
}
}
/* insert address into reply info */
if(!rep) {
/* create one if there isn't (on errors) */
rep = &r2;
r2.c = c;
}
memcpy(&rep->remote_addr, &sq->addr, sq->addrlen);
rep->remote_addrlen = sq->addrlen;
serviced_callbacks(sq, error, c, rep);
return 0;
}
static void
serviced_tcp_initiate(struct serviced_query* sq, sldns_buffer* buff)
{
verbose(VERB_ALGO, "initiate TCP query %s",
sq->status==serviced_query_TCP_EDNS?"EDNS":"");
serviced_encode(sq, buff, sq->status == serviced_query_TCP_EDNS);
sq->last_sent_time = *sq->outnet->now_tv;
log_assert(!sq->busy);
sq->busy = 1;
sq->pending = pending_tcp_query(sq, buff, sq->outnet->tcp_auth_query_timeout,
serviced_tcp_callback, sq);
sq->busy = 0;
if(!sq->pending) {
/* delete from tree so that a retry by above layer does not
* clash with this entry */
verbose(VERB_ALGO, "serviced_tcp_initiate: failed to send tcp query");
serviced_callbacks(sq, NETEVENT_CLOSED, NULL, NULL);
}
}
/** Send serviced query over TCP return false on initial failure */
static int
serviced_tcp_send(struct serviced_query* sq, sldns_buffer* buff)
{
int vs, rtt, timeout;
uint8_t edns_lame_known;
if(!infra_host(sq->outnet->infra, &sq->addr, sq->addrlen, sq->zone,
sq->zonelen, *sq->outnet->now_secs, &vs, &edns_lame_known,
&rtt))
return 0;
sq->last_rtt = rtt;
if(vs != -1)
sq->status = serviced_query_TCP_EDNS;
else sq->status = serviced_query_TCP;
serviced_encode(sq, buff, sq->status == serviced_query_TCP_EDNS);
sq->last_sent_time = *sq->outnet->now_tv;
if(sq->tcp_upstream || sq->ssl_upstream) {
timeout = rtt;
if(rtt >= UNKNOWN_SERVER_NICENESS && rtt < sq->outnet->tcp_auth_query_timeout)
timeout = sq->outnet->tcp_auth_query_timeout;
} else {
timeout = sq->outnet->tcp_auth_query_timeout;
}
log_assert(!sq->busy);
sq->busy = 1;
sq->pending = pending_tcp_query(sq, buff, timeout,
serviced_tcp_callback, sq);
sq->busy = 0;
return sq->pending != NULL;
}
/* see if packet is edns malformed; got zeroes at start.
* This is from servers that return malformed packets to EDNS0 queries,
* but they return good packets for nonEDNS0 queries.
* We try to detect their output; without resorting to a full parse or
* check for too many bytes after the end of the packet. */
static int
packet_edns_malformed(struct sldns_buffer* buf, int qtype)
{
size_t len;
if(sldns_buffer_limit(buf) < LDNS_HEADER_SIZE)
return 1; /* malformed */
/* they have NOERROR rcode, 1 answer. */
if(LDNS_RCODE_WIRE(sldns_buffer_begin(buf)) != LDNS_RCODE_NOERROR)
return 0;
/* one query (to skip) and answer records */
if(LDNS_QDCOUNT(sldns_buffer_begin(buf)) != 1 ||
LDNS_ANCOUNT(sldns_buffer_begin(buf)) == 0)
return 0;
/* skip qname */
len = dname_valid(sldns_buffer_at(buf, LDNS_HEADER_SIZE),
sldns_buffer_limit(buf)-LDNS_HEADER_SIZE);
if(len == 0)
return 0;
if(len == 1 && qtype == 0)
return 0; /* we asked for '.' and type 0 */
/* and then 4 bytes (type and class of query) */
if(sldns_buffer_limit(buf) < LDNS_HEADER_SIZE + len + 4 + 3)
return 0;
/* and start with 11 zeroes as the answer RR */
/* so check the qtype of the answer record, qname=0, type=0 */
if(sldns_buffer_at(buf, LDNS_HEADER_SIZE+len+4)[0] == 0 &&
sldns_buffer_at(buf, LDNS_HEADER_SIZE+len+4)[1] == 0 &&
sldns_buffer_at(buf, LDNS_HEADER_SIZE+len+4)[2] == 0)
return 1;
return 0;
}
int
serviced_udp_callback(struct comm_point* c, void* arg, int error,
struct comm_reply* rep)
{
struct serviced_query* sq = (struct serviced_query*)arg;
struct outside_network* outnet = sq->outnet;
struct timeval now = *sq->outnet->now_tv;
#ifdef USE_DNSTAP
struct pending* p = (struct pending*)sq->pending;
#endif
sq->pending = NULL; /* removed after callback */
if(error == NETEVENT_TIMEOUT) {
if(sq->status == serviced_query_UDP_EDNS && sq->last_rtt < 5000) {
/* fallback to 1480/1280 */
sq->status = serviced_query_UDP_EDNS_FRAG;
log_name_addr(VERB_ALGO, "try edns1xx0", sq->qbuf+10,
&sq->addr, sq->addrlen);
if(!serviced_udp_send(sq, c->buffer)) {
serviced_callbacks(sq, NETEVENT_CLOSED, c, rep);
}
return 0;
}
if(sq->status == serviced_query_UDP_EDNS_FRAG) {
/* fragmentation size did not fix it */
sq->status = serviced_query_UDP_EDNS;
}
sq->retry++;
if(!infra_rtt_update(outnet->infra, &sq->addr, sq->addrlen,
sq->zone, sq->zonelen, sq->qtype, -1, sq->last_rtt,
(time_t)now.tv_sec))
log_err("out of memory in UDP exponential backoff");
if(sq->retry < OUTBOUND_UDP_RETRY) {
log_name_addr(VERB_ALGO, "retry query", sq->qbuf+10,
&sq->addr, sq->addrlen);
if(!serviced_udp_send(sq, c->buffer)) {
serviced_callbacks(sq, NETEVENT_CLOSED, c, rep);
}
return 0;
}
}
if(error != NETEVENT_NOERROR) {
/* udp returns error (due to no ID or interface available) */
serviced_callbacks(sq, error, c, rep);
return 0;
}
#ifdef USE_DNSTAP
/*
* sending src (local service)/dst (upstream) addresses over DNSTAP
*/
if(error == NETEVENT_NOERROR && outnet->dtenv && p->pc &&
(outnet->dtenv->log_resolver_response_messages ||
outnet->dtenv->log_forwarder_response_messages)) {
log_addr(VERB_ALGO, "response from upstream", &sq->addr, sq->addrlen);
log_addr(VERB_ALGO, "to local addr", &p->pc->pif->addr,
p->pc->pif->addrlen);
dt_msg_send_outside_response(outnet->dtenv, &sq->addr,
&p->pc->pif->addr, c->type, sq->zone, sq->zonelen,
sq->qbuf, sq->qbuflen, &sq->last_sent_time,
sq->outnet->now_tv, c->buffer);
}
#endif
if( (sq->status == serviced_query_UDP_EDNS
||sq->status == serviced_query_UDP_EDNS_FRAG)
&& (LDNS_RCODE_WIRE(sldns_buffer_begin(c->buffer))
== LDNS_RCODE_FORMERR || LDNS_RCODE_WIRE(
sldns_buffer_begin(c->buffer)) == LDNS_RCODE_NOTIMPL
|| packet_edns_malformed(c->buffer, sq->qtype)
)) {
/* try to get an answer by falling back without EDNS */
verbose(VERB_ALGO, "serviced query: attempt without EDNS");
sq->status = serviced_query_UDP_EDNS_fallback;
sq->retry = 0;
if(!serviced_udp_send(sq, c->buffer)) {
serviced_callbacks(sq, NETEVENT_CLOSED, c, rep);
}
return 0;
} else if(sq->status == serviced_query_UDP_EDNS &&
!sq->edns_lame_known) {
/* now we know that edns queries received answers store that */
log_addr(VERB_ALGO, "serviced query: EDNS works for",
&sq->addr, sq->addrlen);
if(!infra_edns_update(outnet->infra, &sq->addr, sq->addrlen,
sq->zone, sq->zonelen, 0, (time_t)now.tv_sec)) {
log_err("Out of memory caching edns works");
}
sq->edns_lame_known = 1;
} else if(sq->status == serviced_query_UDP_EDNS_fallback &&
!sq->edns_lame_known && (LDNS_RCODE_WIRE(
sldns_buffer_begin(c->buffer)) == LDNS_RCODE_NOERROR ||
LDNS_RCODE_WIRE(sldns_buffer_begin(c->buffer)) ==
LDNS_RCODE_NXDOMAIN || LDNS_RCODE_WIRE(sldns_buffer_begin(
c->buffer)) == LDNS_RCODE_YXDOMAIN)) {
/* the fallback produced a result that looks promising, note
* that this server should be approached without EDNS */
/* only store noEDNS in cache if domain is noDNSSEC */
if(!sq->want_dnssec) {
log_addr(VERB_ALGO, "serviced query: EDNS fails for",
&sq->addr, sq->addrlen);
if(!infra_edns_update(outnet->infra, &sq->addr, sq->addrlen,
sq->zone, sq->zonelen, -1, (time_t)now.tv_sec)) {
log_err("Out of memory caching no edns for host");
}
} else {
log_addr(VERB_ALGO, "serviced query: EDNS fails, but "
"not stored because need DNSSEC for", &sq->addr,
sq->addrlen);
}
sq->status = serviced_query_UDP;
}
if(now.tv_sec > sq->last_sent_time.tv_sec ||
(now.tv_sec == sq->last_sent_time.tv_sec &&
now.tv_usec > sq->last_sent_time.tv_usec)) {
/* convert from microseconds to milliseconds */
int roundtime = ((int)(now.tv_sec - sq->last_sent_time.tv_sec))*1000
+ ((int)now.tv_usec - (int)sq->last_sent_time.tv_usec)/1000;
verbose(VERB_ALGO, "measured roundtrip at %d msec", roundtime);
log_assert(roundtime >= 0);
/* in case the system hibernated, do not enter a huge value,
* above this value gives trouble with server selection */
if(roundtime < 60000) {
if(!infra_rtt_update(outnet->infra, &sq->addr, sq->addrlen,
sq->zone, sq->zonelen, sq->qtype, roundtime,
sq->last_rtt, (time_t)now.tv_sec))
log_err("out of memory noting rtt.");
}
}
/* perform TC flag check and TCP fallback after updating our
* cache entries for EDNS status and RTT times */
if(LDNS_TC_WIRE(sldns_buffer_begin(c->buffer))) {
/* fallback to TCP */
/* this discards partial UDP contents */
if(sq->status == serviced_query_UDP_EDNS ||
sq->status == serviced_query_UDP_EDNS_FRAG ||
sq->status == serviced_query_UDP_EDNS_fallback)
/* if we have unfinished EDNS_fallback, start again */
sq->status = serviced_query_TCP_EDNS;
else sq->status = serviced_query_TCP;
serviced_tcp_initiate(sq, c->buffer);
return 0;
}
/* yay! an answer */
serviced_callbacks(sq, error, c, rep);
return 0;
}
struct serviced_query*
outnet_serviced_query(struct outside_network* outnet,
struct query_info* qinfo, uint16_t flags, int dnssec, int want_dnssec,
int nocaps, int check_ratelimit, int tcp_upstream, int ssl_upstream,
char* tls_auth_name, struct sockaddr_storage* addr, socklen_t addrlen,
uint8_t* zone, size_t zonelen, struct module_qstate* qstate,
comm_point_callback_type* callback, void* callback_arg,
sldns_buffer* buff, struct module_env* env, int* was_ratelimited)
{
struct serviced_query* sq;
struct service_callback* cb;
struct edns_string_addr* client_string_addr;
struct regional* region;
struct edns_option* backed_up_opt_list = qstate->edns_opts_back_out;
struct edns_option* per_upstream_opt_list = NULL;
time_t timenow = 0;
/* If we have an already populated EDNS option list make a copy since
* we may now add upstream specific EDNS options. */
/* Use a region that could be attached to a serviced_query, if it needs
* to be created. If an existing one is found then this region will be
* destroyed here. */
region = alloc_reg_obtain(env->alloc);
if(!region) return NULL;
if(qstate->edns_opts_back_out) {
per_upstream_opt_list = edns_opt_copy_region(
qstate->edns_opts_back_out, region);
if(!per_upstream_opt_list) {
alloc_reg_release(env->alloc, region);
return NULL;
}
qstate->edns_opts_back_out = per_upstream_opt_list;
}
if(!inplace_cb_query_call(env, qinfo, flags, addr, addrlen, zone,
zonelen, qstate, region)) {
alloc_reg_release(env->alloc, region);
return NULL;
}
/* Restore the option list; we can explicitly use the copied one from
* now on. */
per_upstream_opt_list = qstate->edns_opts_back_out;
qstate->edns_opts_back_out = backed_up_opt_list;
if((client_string_addr = edns_string_addr_lookup(
&env->edns_strings->client_strings, addr, addrlen))) {
edns_opt_list_append(&per_upstream_opt_list,
env->edns_strings->client_string_opcode,
client_string_addr->string_len,
client_string_addr->string, region);
}
serviced_gen_query(buff, qinfo->qname, qinfo->qname_len, qinfo->qtype,
qinfo->qclass, flags);
sq = lookup_serviced(outnet, buff, dnssec, addr, addrlen,
per_upstream_opt_list);
if(!sq) {
/* Check ratelimit only for new serviced_query */
if(check_ratelimit) {
timenow = *env->now;
if(!infra_ratelimit_inc(env->infra_cache, zone,
zonelen, timenow, env->cfg->ratelimit_backoff,
&qstate->qinfo, qstate->reply)) {
/* Can we pass through with slip factor? */
if(env->cfg->ratelimit_factor == 0 ||
ub_random_max(env->rnd,
env->cfg->ratelimit_factor) != 1) {
*was_ratelimited = 1;
alloc_reg_release(env->alloc, region);
return NULL;
}
log_nametypeclass(VERB_ALGO,
"ratelimit allowed through for "
"delegation point", zone,
LDNS_RR_TYPE_NS, LDNS_RR_CLASS_IN);
}
}
/* make new serviced query entry */
sq = serviced_create(outnet, buff, dnssec, want_dnssec, nocaps,
tcp_upstream, ssl_upstream, tls_auth_name, addr,
addrlen, zone, zonelen, (int)qinfo->qtype,
per_upstream_opt_list,
( ssl_upstream && env->cfg->pad_queries
? env->cfg->pad_queries_block_size : 0 ),
env->alloc, region);
if(!sq) {
if(check_ratelimit) {
infra_ratelimit_dec(env->infra_cache,
zone, zonelen, timenow);
}
return NULL;
}
if(!(cb = (struct service_callback*)regional_alloc(
sq->region, sizeof(*cb)))) {
if(check_ratelimit) {
infra_ratelimit_dec(env->infra_cache,
zone, zonelen, timenow);
}
(void)rbtree_delete(outnet->serviced, sq);
serviced_node_del(&sq->node, NULL);
return NULL;
}
/* No network action at this point; it will be invoked with the
* serviced_query timer instead to run outside of the mesh. */
} else {
/* We don't need this region anymore. */
alloc_reg_release(env->alloc, region);
/* duplicate entries are included in the callback list, because
* there is a counterpart registration by our caller that needs
* to be doubly-removed (with callbacks perhaps). */
if(!(cb = (struct service_callback*)regional_alloc(
sq->region, sizeof(*cb)))) {
return NULL;
}
}
/* add callback to list of callbacks */
cb->cb = callback;
cb->cb_arg = callback_arg;
cb->next = sq->cblist;
sq->cblist = cb;
return sq;
}
/** remove callback from list */
static void
callback_list_remove(struct serviced_query* sq, void* cb_arg)
{
struct service_callback** pp = &sq->cblist;
while(*pp) {
if((*pp)->cb_arg == cb_arg) {
struct service_callback* del = *pp;
*pp = del->next;
return;
}
pp = &(*pp)->next;
}
}
void outnet_serviced_query_stop(struct serviced_query* sq, void* cb_arg)
{
if(!sq)
return;
callback_list_remove(sq, cb_arg);
/* if callbacks() routine scheduled deletion, let it do that */
if(!sq->cblist && !sq->busy && !sq->to_be_deleted) {
(void)rbtree_delete(sq->outnet->serviced, sq);
serviced_delete(sq);
}
}
/** create fd to send to this destination */
static int
fd_for_dest(struct outside_network* outnet, struct sockaddr_storage* to_addr,
socklen_t to_addrlen)
{
struct sockaddr_storage* addr;
socklen_t addrlen;
int i, try, pnum, dscp;
struct port_if* pif;
/* create fd */
dscp = outnet->ip_dscp;
for(try = 0; try<1000; try++) {
int port = 0;
int freebind = 0;
int noproto = 0;
int inuse = 0;
int fd = -1;
/* select interface */
if(addr_is_ip6(to_addr, to_addrlen)) {
if(outnet->num_ip6 == 0) {
char to[64];
addr_to_str(to_addr, to_addrlen, to, sizeof(to));
verbose(VERB_QUERY, "need ipv6 to send, but no ipv6 outgoing interfaces, for %s", to);
return -1;
}
i = ub_random_max(outnet->rnd, outnet->num_ip6);
pif = &outnet->ip6_ifs[i];
} else {
if(outnet->num_ip4 == 0) {
char to[64];
addr_to_str(to_addr, to_addrlen, to, sizeof(to));
verbose(VERB_QUERY, "need ipv4 to send, but no ipv4 outgoing interfaces, for %s", to);
return -1;
}
i = ub_random_max(outnet->rnd, outnet->num_ip4);
pif = &outnet->ip4_ifs[i];
}
addr = &pif->addr;
addrlen = pif->addrlen;
#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
pnum = ub_random_max(outnet->rnd, pif->avail_total);
if(pnum < pif->inuse) {
/* port already open */
port = pif->out[pnum]->number;
} else {
/* unused ports in start part of array */
port = pif->avail_ports[pnum - pif->inuse];
}
#else
pnum = port = 0;
#endif
if(addr_is_ip6(to_addr, to_addrlen)) {
struct sockaddr_in6 sa = *(struct sockaddr_in6*)addr;
sa.sin6_port = (in_port_t)htons((uint16_t)port);
fd = create_udp_sock(AF_INET6, SOCK_DGRAM,
(struct sockaddr*)&sa, addrlen, 1, &inuse, &noproto,
0, 0, 0, NULL, 0, freebind, 0, dscp);
} else {
struct sockaddr_in* sa = (struct sockaddr_in*)addr;
sa->sin_port = (in_port_t)htons((uint16_t)port);
fd = create_udp_sock(AF_INET, SOCK_DGRAM,
(struct sockaddr*)addr, addrlen, 1, &inuse, &noproto,
0, 0, 0, NULL, 0, freebind, 0, dscp);
}
if(fd != -1) {
return fd;
}
if(!inuse) {
return -1;
}
}
/* too many tries */
log_err("cannot send probe, ports are in use");
return -1;
}
struct comm_point*
outnet_comm_point_for_udp(struct outside_network* outnet,
comm_point_callback_type* cb, void* cb_arg,
struct sockaddr_storage* to_addr, socklen_t to_addrlen)
{
struct comm_point* cp;
int fd = fd_for_dest(outnet, to_addr, to_addrlen);
if(fd == -1) {
return NULL;
}
cp = comm_point_create_udp(outnet->base, fd, outnet->udp_buff, 0,
cb, cb_arg, NULL);
if(!cp) {
log_err("malloc failure");
close(fd);
return NULL;
}
return cp;
}
/** setup SSL for comm point */
static int
setup_comm_ssl(struct comm_point* cp, struct outside_network* outnet,
int fd, char* host)
{
cp->ssl = outgoing_ssl_fd(outnet->sslctx, fd);
if(!cp->ssl) {
log_err("cannot create SSL object");
return 0;
}
#ifdef USE_WINSOCK
comm_point_tcp_win_bio_cb(cp, cp->ssl);
#endif
cp->ssl_shake_state = comm_ssl_shake_write;
/* https verification */
#ifdef HAVE_SSL
if(outnet->tls_use_sni) {
(void)SSL_set_tlsext_host_name(cp->ssl, host);
}
#endif
#ifdef HAVE_SSL_SET1_HOST
if((SSL_CTX_get_verify_mode(outnet->sslctx)&SSL_VERIFY_PEER)) {
/* because we set SSL_VERIFY_PEER, in netevent in
* ssl_handshake, it'll check if the certificate
* verification has succeeded */
/* SSL_VERIFY_PEER is set on the sslctx */
/* and the certificates to verify with are loaded into
* it with SSL_load_verify_locations or
* SSL_CTX_set_default_verify_paths */
/* setting the hostname makes openssl verify the
* host name in the x509 certificate in the
* SSL connection*/
if(!SSL_set1_host(cp->ssl, host)) {
log_err("SSL_set1_host failed");
return 0;
}
}
#elif defined(HAVE_X509_VERIFY_PARAM_SET1_HOST)
/* openssl 1.0.2 has this function that can be used for
* set1_host like verification */
if((SSL_CTX_get_verify_mode(outnet->sslctx)&SSL_VERIFY_PEER)) {
X509_VERIFY_PARAM* param = SSL_get0_param(cp->ssl);
# ifdef X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS
X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
# endif
if(!X509_VERIFY_PARAM_set1_host(param, host, strlen(host))) {
log_err("X509_VERIFY_PARAM_set1_host failed");
return 0;
}
}
#else
(void)host;
#endif /* HAVE_SSL_SET1_HOST */
return 1;
}
struct comm_point*
outnet_comm_point_for_tcp(struct outside_network* outnet,
comm_point_callback_type* cb, void* cb_arg,
struct sockaddr_storage* to_addr, socklen_t to_addrlen,
sldns_buffer* query, int timeout, int ssl, char* host)
{
struct comm_point* cp;
int fd = outnet_get_tcp_fd(to_addr, to_addrlen, outnet->tcp_mss, outnet->ip_dscp);
if(fd == -1) {
return 0;
}
fd_set_nonblock(fd);
if(!outnet_tcp_connect(fd, to_addr, to_addrlen)) {
/* outnet_tcp_connect has closed fd on error for us */
return 0;
}
cp = comm_point_create_tcp_out(outnet->base, 65552, cb, cb_arg);
if(!cp) {
log_err("malloc failure");
close(fd);
return 0;
}
cp->repinfo.remote_addrlen = to_addrlen;
memcpy(&cp->repinfo.remote_addr, to_addr, to_addrlen);
/* setup for SSL (if needed) */
if(ssl) {
if(!setup_comm_ssl(cp, outnet, fd, host)) {
log_err("cannot setup XoT");
comm_point_delete(cp);
return NULL;
}
}
/* set timeout on TCP connection */
comm_point_start_listening(cp, fd, timeout);
/* copy scratch buffer to cp->buffer */
sldns_buffer_copy(cp->buffer, query);
return cp;
}
/** setup the User-Agent HTTP header based on http-user-agent configuration */
static void
setup_http_user_agent(sldns_buffer* buf, struct config_file* cfg)
{
if(cfg->hide_http_user_agent) return;
if(cfg->http_user_agent==NULL || cfg->http_user_agent[0] == 0) {
sldns_buffer_printf(buf, "User-Agent: %s/%s\r\n", PACKAGE_NAME,
PACKAGE_VERSION);
} else {
sldns_buffer_printf(buf, "User-Agent: %s\r\n", cfg->http_user_agent);
}
}
/** setup http request headers in buffer for sending query to destination */
static int
setup_http_request(sldns_buffer* buf, char* host, char* path,
struct config_file* cfg)
{
sldns_buffer_clear(buf);
sldns_buffer_printf(buf, "GET /%s HTTP/1.1\r\n", path);
sldns_buffer_printf(buf, "Host: %s\r\n", host);
setup_http_user_agent(buf, cfg);
/* We do not really do multiple queries per connection,
* but this header setting is also not needed.
* sldns_buffer_printf(buf, "Connection: close\r\n") */
sldns_buffer_printf(buf, "\r\n");
if(sldns_buffer_position(buf)+10 > sldns_buffer_capacity(buf))
return 0; /* somehow buffer too short, but it is about 60K
and the request is only a couple bytes long. */
sldns_buffer_flip(buf);
return 1;
}
struct comm_point*
outnet_comm_point_for_http(struct outside_network* outnet,
comm_point_callback_type* cb, void* cb_arg,
struct sockaddr_storage* to_addr, socklen_t to_addrlen, int timeout,
int ssl, char* host, char* path, struct config_file* cfg)
{
/* cp calls cb with err=NETEVENT_DONE when transfer is done */
struct comm_point* cp;
int fd = outnet_get_tcp_fd(to_addr, to_addrlen, outnet->tcp_mss, outnet->ip_dscp);
if(fd == -1) {
return 0;
}
fd_set_nonblock(fd);
if(!outnet_tcp_connect(fd, to_addr, to_addrlen)) {
/* outnet_tcp_connect has closed fd on error for us */
return 0;
}
cp = comm_point_create_http_out(outnet->base, 65552, cb, cb_arg,
outnet->udp_buff);
if(!cp) {
log_err("malloc failure");
close(fd);
return 0;
}
cp->repinfo.remote_addrlen = to_addrlen;
memcpy(&cp->repinfo.remote_addr, to_addr, to_addrlen);
/* setup for SSL (if needed) */
if(ssl) {
if(!setup_comm_ssl(cp, outnet, fd, host)) {
log_err("cannot setup https");
comm_point_delete(cp);
return NULL;
}
}
/* set timeout on TCP connection */
comm_point_start_listening(cp, fd, timeout);
/* setup http request in cp->buffer */
if(!setup_http_request(cp->buffer, host, path, cfg)) {
log_err("error setting up http request");
comm_point_delete(cp);
return NULL;
}
return cp;
}
/** get memory used by waiting tcp entry (in use or not) */
static size_t
waiting_tcp_get_mem(struct waiting_tcp* w)
{
size_t s;
if(!w) return 0;
s = sizeof(*w) + w->pkt_len;
if(w->timer)
s += comm_timer_get_mem(w->timer);
return s;
}
/** get memory used by port if */
static size_t
if_get_mem(struct port_if* pif)
{
size_t s;
int i;
s = sizeof(*pif) +
#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
sizeof(int)*pif->avail_total +
#endif
sizeof(struct port_comm*)*pif->maxout;
for(i=0; i<pif->inuse; i++)
s += sizeof(*pif->out[i]) +
comm_point_get_mem(pif->out[i]->cp);
return s;
}
/** get memory used by waiting udp */
static size_t
waiting_udp_get_mem(struct pending* w)
{
size_t s;
s = sizeof(*w) + comm_timer_get_mem(w->timer) + w->pkt_len;
return s;
}
size_t outnet_get_mem(struct outside_network* outnet)
{
size_t i;
int k;
struct waiting_tcp* w;
struct pending* u;
struct serviced_query* sq;
struct service_callback* sb;
struct port_comm* pc;
size_t s = sizeof(*outnet) + sizeof(*outnet->base) +
sizeof(*outnet->udp_buff) +
sldns_buffer_capacity(outnet->udp_buff);
/* second buffer is not ours */
for(pc = outnet->unused_fds; pc; pc = pc->next) {
s += sizeof(*pc) + comm_point_get_mem(pc->cp);
}
for(k=0; k<outnet->num_ip4; k++)
s += if_get_mem(&outnet->ip4_ifs[k]);
for(k=0; k<outnet->num_ip6; k++)
s += if_get_mem(&outnet->ip6_ifs[k]);
for(u=outnet->udp_wait_first; u; u=u->next_waiting)
s += waiting_udp_get_mem(u);
s += sizeof(struct pending_tcp*)*outnet->num_tcp;
for(i=0; i<outnet->num_tcp; i++) {
s += sizeof(struct pending_tcp);
s += comm_point_get_mem(outnet->tcp_conns[i]->c);
if(outnet->tcp_conns[i]->query)
s += waiting_tcp_get_mem(outnet->tcp_conns[i]->query);
}
for(w=outnet->tcp_wait_first; w; w = w->next_waiting)
s += waiting_tcp_get_mem(w);
s += sizeof(*outnet->pending);
s += (sizeof(struct pending) + comm_timer_get_mem(NULL)) *
outnet->pending->count;
s += sizeof(*outnet->serviced);
s += outnet->svcd_overhead;
RBTREE_FOR(sq, struct serviced_query*, outnet->serviced) {
s += sizeof(*sq) + sq->qbuflen;
for(sb = sq->cblist; sb; sb = sb->next)
s += sizeof(*sb);
}
return s;
}
size_t
serviced_get_mem(struct serviced_query* sq)
{
struct service_callback* sb;
size_t s;
s = sizeof(*sq) + sq->qbuflen;
for(sb = sq->cblist; sb; sb = sb->next)
s += sizeof(*sb);
if(sq->status == serviced_query_UDP_EDNS ||
sq->status == serviced_query_UDP ||
sq->status == serviced_query_UDP_EDNS_FRAG ||
sq->status == serviced_query_UDP_EDNS_fallback) {
s += sizeof(struct pending);
s += comm_timer_get_mem(NULL);
} else {
/* does not have size of the pkt pointer */
/* always has a timer except on malloc failures */
/* these sizes are part of the main outside network mem */
/*
s += sizeof(struct waiting_tcp);
s += comm_timer_get_mem(NULL);
*/
}
return s;
}
diff --git a/contrib/unbound/services/rpz.c b/contrib/unbound/services/rpz.c
index 6ce83cb66a35..18d76c07bff3 100644
--- a/contrib/unbound/services/rpz.c
+++ b/contrib/unbound/services/rpz.c
@@ -1,2591 +1,2595 @@
/*
* services/rpz.c - rpz service
*
* Copyright (c) 2019, 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file contains functions to enable RPZ service.
*/
#include "config.h"
#include "services/rpz.h"
#include "util/config_file.h"
#include "sldns/wire2str.h"
#include "sldns/str2wire.h"
#include "util/data/dname.h"
#include "util/net_help.h"
#include "util/log.h"
#include "util/data/dname.h"
#include "util/locks.h"
#include "util/regional.h"
#include "util/data/msgencode.h"
#include "services/cache/dns.h"
#include "iterator/iterator.h"
#include "iterator/iter_delegpt.h"
#include "daemon/worker.h"
typedef struct resp_addr rpz_aclnode_type;
struct matched_delegation_point {
uint8_t* dname;
size_t dname_len;
};
/** string for RPZ action enum */
const char*
rpz_action_to_string(enum rpz_action a)
{
switch(a) {
case RPZ_NXDOMAIN_ACTION: return "rpz-nxdomain";
case RPZ_NODATA_ACTION: return "rpz-nodata";
case RPZ_PASSTHRU_ACTION: return "rpz-passthru";
case RPZ_DROP_ACTION: return "rpz-drop";
case RPZ_TCP_ONLY_ACTION: return "rpz-tcp-only";
case RPZ_INVALID_ACTION: return "rpz-invalid";
case RPZ_LOCAL_DATA_ACTION: return "rpz-local-data";
case RPZ_DISABLED_ACTION: return "rpz-disabled";
case RPZ_CNAME_OVERRIDE_ACTION: return "rpz-cname-override";
case RPZ_NO_OVERRIDE_ACTION: return "rpz-no-override";
default: return "rpz-unknown-action";
}
}
/** RPZ action enum for config string */
static enum rpz_action
rpz_config_to_action(char* a)
{
if(strcmp(a, "nxdomain") == 0) return RPZ_NXDOMAIN_ACTION;
else if(strcmp(a, "nodata") == 0) return RPZ_NODATA_ACTION;
else if(strcmp(a, "passthru") == 0) return RPZ_PASSTHRU_ACTION;
else if(strcmp(a, "drop") == 0) return RPZ_DROP_ACTION;
else if(strcmp(a, "tcp_only") == 0) return RPZ_TCP_ONLY_ACTION;
else if(strcmp(a, "cname") == 0) return RPZ_CNAME_OVERRIDE_ACTION;
else if(strcmp(a, "disabled") == 0) return RPZ_DISABLED_ACTION;
else return RPZ_INVALID_ACTION;
}
/** string for RPZ trigger enum */
static const char*
rpz_trigger_to_string(enum rpz_trigger r)
{
switch(r) {
case RPZ_QNAME_TRIGGER: return "rpz-qname";
case RPZ_CLIENT_IP_TRIGGER: return "rpz-client-ip";
case RPZ_RESPONSE_IP_TRIGGER: return "rpz-response-ip";
case RPZ_NSDNAME_TRIGGER: return "rpz-nsdname";
case RPZ_NSIP_TRIGGER: return "rpz-nsip";
case RPZ_INVALID_TRIGGER: return "rpz-invalid";
default: return "rpz-unknown-trigger";
}
}
/**
* Get the label that is just before the root label.
* @param dname: dname to work on
* @param maxdnamelen: maximum length of the dname
* @return: pointer to TLD label, NULL if not found or invalid dname
*/
static uint8_t*
get_tld_label(uint8_t* dname, size_t maxdnamelen)
{
uint8_t* prevlab = dname;
size_t dnamelen = 0;
/* one byte needed for label length */
if(dnamelen+1 > maxdnamelen)
return NULL;
/* only root label */
if(*dname == 0)
return NULL;
while(*dname) {
dnamelen += ((size_t)*dname)+1;
if(dnamelen+1 > maxdnamelen)
return NULL;
dname = dname+((size_t)*dname)+1;
if(*dname != 0)
prevlab = dname;
}
return prevlab;
}
/**
* The RR types that are to be ignored.
* DNSSEC RRs at the apex, and SOA and NS are ignored.
*/
static int
rpz_type_ignored(uint16_t rr_type)
{
switch(rr_type) {
case LDNS_RR_TYPE_SOA:
case LDNS_RR_TYPE_NS:
case LDNS_RR_TYPE_DNAME:
/* all DNSSEC-related RRs must be ignored */
case LDNS_RR_TYPE_DNSKEY:
case LDNS_RR_TYPE_DS:
case LDNS_RR_TYPE_RRSIG:
case LDNS_RR_TYPE_NSEC:
case LDNS_RR_TYPE_NSEC3:
case LDNS_RR_TYPE_NSEC3PARAM:
return 1;
default:
break;
}
return 0;
}
/**
* Classify RPZ action for RR type/rdata
* @param rr_type: the RR type
* @param rdatawl: RDATA with 2 bytes length
* @param rdatalen: the length of rdatawl (including its 2 bytes length)
* @return: the RPZ action
*/
static enum rpz_action
rpz_rr_to_action(uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen)
{
char* endptr;
uint8_t* rdata;
int rdatalabs;
uint8_t* tldlab = NULL;
switch(rr_type) {
case LDNS_RR_TYPE_SOA:
case LDNS_RR_TYPE_NS:
case LDNS_RR_TYPE_DNAME:
/* all DNSSEC-related RRs must be ignored */
case LDNS_RR_TYPE_DNSKEY:
case LDNS_RR_TYPE_DS:
case LDNS_RR_TYPE_RRSIG:
case LDNS_RR_TYPE_NSEC:
case LDNS_RR_TYPE_NSEC3:
case LDNS_RR_TYPE_NSEC3PARAM:
return RPZ_INVALID_ACTION;
case LDNS_RR_TYPE_CNAME:
break;
default:
return RPZ_LOCAL_DATA_ACTION;
}
/* use CNAME target to determine RPZ action */
log_assert(rr_type == LDNS_RR_TYPE_CNAME);
if(rdatalen < 3)
return RPZ_INVALID_ACTION;
rdata = rdatawl + 2; /* 2 bytes of rdata length */
if(dname_valid(rdata, rdatalen-2) != rdatalen-2)
return RPZ_INVALID_ACTION;
rdatalabs = dname_count_labels(rdata);
if(rdatalabs == 1)
return RPZ_NXDOMAIN_ACTION;
else if(rdatalabs == 2) {
if(dname_subdomain_c(rdata, (uint8_t*)&"\001*\000"))
return RPZ_NODATA_ACTION;
else if(dname_subdomain_c(rdata,
(uint8_t*)&"\014rpz-passthru\000"))
return RPZ_PASSTHRU_ACTION;
else if(dname_subdomain_c(rdata, (uint8_t*)&"\010rpz-drop\000"))
return RPZ_DROP_ACTION;
else if(dname_subdomain_c(rdata,
(uint8_t*)&"\014rpz-tcp-only\000"))
return RPZ_TCP_ONLY_ACTION;
}
/* all other TLDs starting with "rpz-" are invalid */
tldlab = get_tld_label(rdata, rdatalen-2);
if(tldlab && dname_lab_startswith(tldlab, "rpz-", &endptr))
return RPZ_INVALID_ACTION;
/* no special label found */
return RPZ_LOCAL_DATA_ACTION;
}
static enum localzone_type
rpz_action_to_localzone_type(enum rpz_action a)
{
switch(a) {
case RPZ_NXDOMAIN_ACTION: return local_zone_always_nxdomain;
case RPZ_NODATA_ACTION: return local_zone_always_nodata;
case RPZ_DROP_ACTION: return local_zone_always_deny;
case RPZ_PASSTHRU_ACTION: return local_zone_always_transparent;
case RPZ_LOCAL_DATA_ACTION: /* fallthrough */
case RPZ_CNAME_OVERRIDE_ACTION: return local_zone_redirect;
case RPZ_TCP_ONLY_ACTION: return local_zone_truncate;
case RPZ_INVALID_ACTION: /* fallthrough */
default: return local_zone_invalid;
}
}
enum respip_action
rpz_action_to_respip_action(enum rpz_action a)
{
switch(a) {
case RPZ_NXDOMAIN_ACTION: return respip_always_nxdomain;
case RPZ_NODATA_ACTION: return respip_always_nodata;
case RPZ_DROP_ACTION: return respip_always_deny;
case RPZ_PASSTHRU_ACTION: return respip_always_transparent;
case RPZ_LOCAL_DATA_ACTION: /* fallthrough */
case RPZ_CNAME_OVERRIDE_ACTION: return respip_redirect;
case RPZ_TCP_ONLY_ACTION: return respip_truncate;
case RPZ_INVALID_ACTION: /* fallthrough */
default: return respip_invalid;
}
}
static enum rpz_action
localzone_type_to_rpz_action(enum localzone_type lzt)
{
switch(lzt) {
case local_zone_always_nxdomain: return RPZ_NXDOMAIN_ACTION;
case local_zone_always_nodata: return RPZ_NODATA_ACTION;
case local_zone_always_deny: return RPZ_DROP_ACTION;
case local_zone_always_transparent: return RPZ_PASSTHRU_ACTION;
case local_zone_redirect: return RPZ_LOCAL_DATA_ACTION;
case local_zone_truncate: return RPZ_TCP_ONLY_ACTION;
case local_zone_invalid: /* fallthrough */
default: return RPZ_INVALID_ACTION;
}
}
enum rpz_action
respip_action_to_rpz_action(enum respip_action a)
{
switch(a) {
case respip_always_nxdomain: return RPZ_NXDOMAIN_ACTION;
case respip_always_nodata: return RPZ_NODATA_ACTION;
case respip_always_deny: return RPZ_DROP_ACTION;
case respip_always_transparent: return RPZ_PASSTHRU_ACTION;
case respip_redirect: return RPZ_LOCAL_DATA_ACTION;
case respip_truncate: return RPZ_TCP_ONLY_ACTION;
case respip_invalid: /* fallthrough */
default: return RPZ_INVALID_ACTION;
}
}
/**
* Get RPZ trigger for dname
* @param dname: dname containing RPZ trigger
* @param dname_len: length of the dname
* @return: RPZ trigger enum
*/
static enum rpz_trigger
rpz_dname_to_trigger(uint8_t* dname, size_t dname_len)
{
uint8_t* tldlab;
char* endptr;
if(dname_valid(dname, dname_len) != dname_len)
return RPZ_INVALID_TRIGGER;
tldlab = get_tld_label(dname, dname_len);
if(!tldlab || !dname_lab_startswith(tldlab, "rpz-", &endptr))
return RPZ_QNAME_TRIGGER;
if(dname_subdomain_c(tldlab,
(uint8_t*)&"\015rpz-client-ip\000"))
return RPZ_CLIENT_IP_TRIGGER;
else if(dname_subdomain_c(tldlab, (uint8_t*)&"\006rpz-ip\000"))
return RPZ_RESPONSE_IP_TRIGGER;
else if(dname_subdomain_c(tldlab, (uint8_t*)&"\013rpz-nsdname\000"))
return RPZ_NSDNAME_TRIGGER;
else if(dname_subdomain_c(tldlab, (uint8_t*)&"\010rpz-nsip\000"))
return RPZ_NSIP_TRIGGER;
return RPZ_QNAME_TRIGGER;
}
static inline struct clientip_synthesized_rrset*
rpz_clientip_synthesized_set_create(void)
{
struct clientip_synthesized_rrset* set = calloc(1, sizeof(*set));
if(set == NULL) {
return NULL;
}
set->region = regional_create();
if(set->region == NULL) {
free(set);
return NULL;
}
addr_tree_init(&set->entries);
lock_rw_init(&set->lock);
return set;
}
static void
rpz_clientip_synthesized_rr_delete(rbnode_type* n, void* ATTR_UNUSED(arg))
{
struct clientip_synthesized_rr* r = (struct clientip_synthesized_rr*)n->key;
lock_rw_destroy(&r->lock);
#ifdef THREADS_DISABLED
(void)r;
#endif
}
static inline void
rpz_clientip_synthesized_set_delete(struct clientip_synthesized_rrset* set)
{
if(set == NULL) {
return;
}
lock_rw_destroy(&set->lock);
traverse_postorder(&set->entries, rpz_clientip_synthesized_rr_delete, NULL);
regional_destroy(set->region);
free(set);
}
void
rpz_delete(struct rpz* r)
{
if(!r)
return;
local_zones_delete(r->local_zones);
local_zones_delete(r->nsdname_zones);
respip_set_delete(r->respip_set);
rpz_clientip_synthesized_set_delete(r->client_set);
rpz_clientip_synthesized_set_delete(r->ns_set);
regional_destroy(r->region);
free(r->taglist);
free(r->log_name);
free(r);
}
int
rpz_clear(struct rpz* r)
{
/* must hold write lock on auth_zone */
local_zones_delete(r->local_zones);
r->local_zones = NULL;
local_zones_delete(r->nsdname_zones);
r->nsdname_zones = NULL;
respip_set_delete(r->respip_set);
r->respip_set = NULL;
rpz_clientip_synthesized_set_delete(r->client_set);
r->client_set = NULL;
rpz_clientip_synthesized_set_delete(r->ns_set);
r->ns_set = NULL;
if(!(r->local_zones = local_zones_create())){
return 0;
}
r->nsdname_zones = local_zones_create();
if(r->nsdname_zones == NULL) {
return 0;
}
if(!(r->respip_set = respip_set_create())) {
return 0;
}
if(!(r->client_set = rpz_clientip_synthesized_set_create())) {
return 0;
}
if(!(r->ns_set = rpz_clientip_synthesized_set_create())) {
return 0;
}
return 1;
}
void
rpz_finish_config(struct rpz* r)
{
lock_rw_wrlock(&r->respip_set->lock);
addr_tree_init_parents(&r->respip_set->ip_tree);
lock_rw_unlock(&r->respip_set->lock);
lock_rw_wrlock(&r->client_set->lock);
addr_tree_init_parents(&r->client_set->entries);
lock_rw_unlock(&r->client_set->lock);
lock_rw_wrlock(&r->ns_set->lock);
addr_tree_init_parents(&r->ns_set->entries);
lock_rw_unlock(&r->ns_set->lock);
}
/** new rrset containing CNAME override, does not yet contain a dname */
static struct ub_packed_rrset_key*
new_cname_override(struct regional* region, uint8_t* ct, size_t ctlen)
{
struct ub_packed_rrset_key* rrset;
struct packed_rrset_data* pd;
uint16_t rdlength = htons(ctlen);
rrset = (struct ub_packed_rrset_key*)regional_alloc_zero(region,
sizeof(*rrset));
if(!rrset) {
log_err("out of memory");
return NULL;
}
rrset->entry.key = rrset;
pd = (struct packed_rrset_data*)regional_alloc_zero(region, sizeof(*pd));
if(!pd) {
log_err("out of memory");
return NULL;
}
pd->trust = rrset_trust_prim_noglue;
pd->security = sec_status_insecure;
pd->count = 1;
pd->rr_len = regional_alloc_zero(region, sizeof(*pd->rr_len));
pd->rr_ttl = regional_alloc_zero(region, sizeof(*pd->rr_ttl));
pd->rr_data = regional_alloc_zero(region, sizeof(*pd->rr_data));
if(!pd->rr_len || !pd->rr_ttl || !pd->rr_data) {
log_err("out of memory");
return NULL;
}
pd->rr_len[0] = ctlen+2;
pd->rr_ttl[0] = 3600;
pd->rr_data[0] = regional_alloc_zero(region, 2 /* rdlength */ + ctlen);
if(!pd->rr_data[0]) {
log_err("out of memory");
return NULL;
}
memmove(pd->rr_data[0], &rdlength, 2);
memmove(pd->rr_data[0]+2, ct, ctlen);
rrset->entry.data = pd;
rrset->rk.type = htons(LDNS_RR_TYPE_CNAME);
rrset->rk.rrset_class = htons(LDNS_RR_CLASS_IN);
return rrset;
}
struct rpz*
rpz_create(struct config_auth* p)
{
struct rpz* r = calloc(1, sizeof(*r));
if(!r)
goto err;
r->region = regional_create_custom(sizeof(struct regional));
if(!r->region) {
goto err;
}
if(!(r->local_zones = local_zones_create())){
goto err;
}
r->nsdname_zones = local_zones_create();
if(r->local_zones == NULL){
goto err;
}
if(!(r->respip_set = respip_set_create())) {
goto err;
}
r->client_set = rpz_clientip_synthesized_set_create();
if(r->client_set == NULL) {
goto err;
}
r->ns_set = rpz_clientip_synthesized_set_create();
if(r->ns_set == NULL) {
goto err;
}
r->taglistlen = p->rpz_taglistlen;
r->taglist = memdup(p->rpz_taglist, r->taglistlen);
if(p->rpz_action_override) {
r->action_override = rpz_config_to_action(p->rpz_action_override);
}
else
r->action_override = RPZ_NO_OVERRIDE_ACTION;
if(r->action_override == RPZ_CNAME_OVERRIDE_ACTION) {
uint8_t nm[LDNS_MAX_DOMAINLEN+1];
size_t nmlen = sizeof(nm);
if(!p->rpz_cname) {
log_err("rpz: override with cname action found, but no "
"rpz-cname-override configured");
goto err;
}
if(sldns_str2wire_dname_buf(p->rpz_cname, nm, &nmlen) != 0) {
log_err("rpz: cannot parse cname override: %s",
p->rpz_cname);
goto err;
}
r->cname_override = new_cname_override(r->region, nm, nmlen);
if(!r->cname_override) {
goto err;
}
}
r->log = p->rpz_log;
r->signal_nxdomain_ra = p->rpz_signal_nxdomain_ra;
if(p->rpz_log_name) {
if(!(r->log_name = strdup(p->rpz_log_name))) {
log_err("malloc failure on RPZ log_name strdup");
goto err;
}
}
return r;
err:
if(r) {
if(r->local_zones)
local_zones_delete(r->local_zones);
if(r->nsdname_zones)
local_zones_delete(r->nsdname_zones);
if(r->respip_set)
respip_set_delete(r->respip_set);
if(r->client_set != NULL)
rpz_clientip_synthesized_set_delete(r->client_set);
if(r->ns_set != NULL)
rpz_clientip_synthesized_set_delete(r->ns_set);
if(r->taglist)
free(r->taglist);
if(r->region)
regional_destroy(r->region);
free(r);
}
return NULL;
}
/**
* Remove RPZ zone name from dname
* Copy dname to newdname, without the originlen number of trailing bytes
*/
static size_t
strip_dname_origin(uint8_t* dname, size_t dnamelen, size_t originlen,
uint8_t* newdname, size_t maxnewdnamelen)
{
size_t newdnamelen;
if(dnamelen < originlen)
return 0;
newdnamelen = dnamelen - originlen;
if(newdnamelen+1 > maxnewdnamelen)
return 0;
memmove(newdname, dname, newdnamelen);
newdname[newdnamelen] = 0;
return newdnamelen + 1; /* + 1 for root label */
}
static void
rpz_insert_local_zones_trigger(struct local_zones* lz, uint8_t* dname,
size_t dnamelen, enum rpz_action a, uint16_t rrtype, uint16_t rrclass,
uint32_t ttl, uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len)
{
struct local_zone* z;
enum localzone_type tp = local_zone_always_transparent;
int dnamelabs = dname_count_labels(dname);
int newzone = 0;
if(a == RPZ_INVALID_ACTION) {
char str[255+1];
if(rrtype == LDNS_RR_TYPE_SOA || rrtype == LDNS_RR_TYPE_NS ||
rrtype == LDNS_RR_TYPE_DNAME ||
rrtype == LDNS_RR_TYPE_DNSKEY ||
rrtype == LDNS_RR_TYPE_RRSIG ||
rrtype == LDNS_RR_TYPE_NSEC ||
rrtype == LDNS_RR_TYPE_NSEC3PARAM ||
rrtype == LDNS_RR_TYPE_NSEC3 ||
rrtype == LDNS_RR_TYPE_DS) {
free(dname);
return; /* no need to log these types as unsupported */
}
dname_str(dname, str);
verbose(VERB_ALGO, "rpz: qname trigger, %s skipping unsupported action: %s",
str, rpz_action_to_string(a));
free(dname);
return;
}
lock_rw_wrlock(&lz->lock);
/* exact match */
z = local_zones_find(lz, dname, dnamelen, dnamelabs, LDNS_RR_CLASS_IN);
if(z != NULL && a != RPZ_LOCAL_DATA_ACTION) {
char* rrstr = sldns_wire2str_rr(rr, rr_len);
if(rrstr == NULL) {
log_err("malloc error while inserting rpz nsdname trigger");
free(dname);
lock_rw_unlock(&lz->lock);
return;
}
if(rrstr[0])
rrstr[strlen(rrstr)-1]=0; /* remove newline */
verbose(VERB_ALGO, "rpz: skipping duplicate record: '%s'", rrstr);
free(rrstr);
free(dname);
lock_rw_unlock(&lz->lock);
return;
}
if(z == NULL) {
tp = rpz_action_to_localzone_type(a);
z = local_zones_add_zone(lz, dname, dnamelen,
dnamelabs, rrclass, tp);
if(z == NULL) {
log_warn("rpz: create failed");
lock_rw_unlock(&lz->lock);
/* dname will be free'd in failed local_zone_create() */
return;
}
newzone = 1;
}
if(a == RPZ_LOCAL_DATA_ACTION) {
char* rrstr = sldns_wire2str_rr(rr, rr_len);
if(rrstr == NULL) {
log_err("malloc error while inserting rpz nsdname trigger");
free(dname);
lock_rw_unlock(&lz->lock);
return;
}
lock_rw_wrlock(&z->lock);
local_zone_enter_rr(z, dname, dnamelen, dnamelabs, rrtype,
rrclass, ttl, rdata, rdata_len, rrstr);
lock_rw_unlock(&z->lock);
free(rrstr);
}
if(!newzone) {
free(dname);
}
lock_rw_unlock(&lz->lock);
}
static void
rpz_log_dname(char const* msg, uint8_t* dname, size_t dname_len)
{
char buf[LDNS_MAX_DOMAINLEN+1];
(void)dname_len;
dname_str(dname, buf);
verbose(VERB_ALGO, "rpz: %s: <%s>", msg, buf);
}
static void
rpz_insert_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl,
uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len)
{
if(a == RPZ_INVALID_ACTION) {
verbose(VERB_ALGO, "rpz: skipping invalid action");
free(dname);
return;
}
rpz_insert_local_zones_trigger(r->local_zones, dname, dnamelen, a, rrtype,
rrclass, ttl, rdata, rdata_len, rr, rr_len);
}
static int
rpz_strip_nsdname_suffix(uint8_t* dname, size_t maxdnamelen,
uint8_t** stripdname, size_t* stripdnamelen)
{
uint8_t* tldstart = get_tld_label(dname, maxdnamelen);
uint8_t swap;
if(tldstart == NULL) {
if(dname == NULL) {
*stripdname = NULL;
*stripdnamelen = 0;
return 0;
}
*stripdname = memdup(dname, maxdnamelen);
if(!*stripdname) {
*stripdnamelen = 0;
log_err("malloc failure for rpz strip suffix");
return 0;
}
*stripdnamelen = maxdnamelen;
return 1;
}
/* shorten the domain name briefly,
* then we allocate a new name with the correct length */
swap = *tldstart;
*tldstart = 0;
(void)dname_count_size_labels(dname, stripdnamelen);
*stripdname = memdup(dname, *stripdnamelen);
*tldstart = swap;
if(!*stripdname) {
*stripdnamelen = 0;
log_err("malloc failure for rpz strip suffix");
return 0;
}
return 1;
}
static void
rpz_insert_nsdname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl,
uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len)
{
uint8_t* dname_stripped = NULL;
size_t dnamelen_stripped = 0;
rpz_strip_nsdname_suffix(dname, dnamelen, &dname_stripped,
&dnamelen_stripped);
if(a == RPZ_INVALID_ACTION) {
verbose(VERB_ALGO, "rpz: skipping invalid action");
free(dname_stripped);
return;
}
/* dname_stripped is consumed or freed by the insert routine */
rpz_insert_local_zones_trigger(r->nsdname_zones, dname_stripped,
dnamelen_stripped, a, rrtype, rrclass, ttl, rdata, rdata_len,
rr, rr_len);
}
static int
rpz_insert_ipaddr_based_trigger(struct respip_set* set, struct sockaddr_storage* addr,
socklen_t addrlen, int net, enum rpz_action a, uint16_t rrtype,
uint16_t rrclass, uint32_t ttl, uint8_t* rdata, size_t rdata_len,
uint8_t* rr, size_t rr_len)
{
struct resp_addr* node;
char* rrstr;
enum respip_action respa = rpz_action_to_respip_action(a);
lock_rw_wrlock(&set->lock);
rrstr = sldns_wire2str_rr(rr, rr_len);
if(rrstr == NULL) {
log_err("malloc error while inserting rpz ipaddr based trigger");
lock_rw_unlock(&set->lock);
return 0;
}
node = respip_sockaddr_find_or_create(set, addr, addrlen, net, 1, rrstr);
if(node == NULL) {
lock_rw_unlock(&set->lock);
free(rrstr);
return 0;
}
lock_rw_wrlock(&node->lock);
lock_rw_unlock(&set->lock);
node->action = respa;
if(a == RPZ_LOCAL_DATA_ACTION) {
respip_enter_rr(set->region, node, rrtype,
rrclass, ttl, rdata, rdata_len, rrstr, "");
}
lock_rw_unlock(&node->lock);
free(rrstr);
return 1;
}
static inline struct clientip_synthesized_rr*
rpz_clientip_ensure_entry(struct clientip_synthesized_rrset* set,
struct sockaddr_storage* addr, socklen_t addrlen, int net)
{
int insert_ok;
struct clientip_synthesized_rr* node =
(struct clientip_synthesized_rr*)addr_tree_find(&set->entries,
addr, addrlen, net);
if(node != NULL) { return node; }
/* node does not yet exist => allocate one */
node = regional_alloc_zero(set->region, sizeof(*node));
if(node == NULL) {
log_err("out of memory");
return NULL;
}
lock_rw_init(&node->lock);
node->action = RPZ_INVALID_ACTION;
insert_ok = addr_tree_insert(&set->entries, &node->node,
addr, addrlen, net);
if (!insert_ok) {
log_warn("rpz: unexpected: unable to insert clientip address node");
/* we can not free the just allocated node.
* theoretically a memleak */
return NULL;
}
return node;
}
static void
rpz_report_rrset_error(const char* msg, uint8_t* rr, size_t rr_len) {
char* rrstr = sldns_wire2str_rr(rr, rr_len);
if(rrstr == NULL) {
log_err("malloc error while inserting rpz clientip based record");
return;
}
log_err("rpz: unexpected: unable to insert %s: %s", msg, rrstr);
free(rrstr);
}
/* from localzone.c; difference is we don't have a dname */
static struct local_rrset*
rpz_clientip_new_rrset(struct regional* region,
struct clientip_synthesized_rr* raddr, uint16_t rrtype, uint16_t rrclass)
{
struct packed_rrset_data* pd;
struct local_rrset* rrset = (struct local_rrset*)
regional_alloc_zero(region, sizeof(*rrset));
if(rrset == NULL) {
log_err("out of memory");
return NULL;
}
rrset->next = raddr->data;
raddr->data = rrset;
rrset->rrset = (struct ub_packed_rrset_key*)
regional_alloc_zero(region, sizeof(*rrset->rrset));
if(rrset->rrset == NULL) {
log_err("out of memory");
return NULL;
}
rrset->rrset->entry.key = rrset->rrset;
pd = (struct packed_rrset_data*)regional_alloc_zero(region, sizeof(*pd));
if(pd == NULL) {
log_err("out of memory");
return NULL;
}
pd->trust = rrset_trust_prim_noglue;
pd->security = sec_status_insecure;
rrset->rrset->entry.data = pd;
rrset->rrset->rk.type = htons(rrtype);
rrset->rrset->rk.rrset_class = htons(rrclass);
rrset->rrset->rk.dname = regional_alloc_zero(region, 1);
if(rrset->rrset->rk.dname == NULL) {
log_err("out of memory");
return NULL;
}
rrset->rrset->rk.dname_len = 1;
return rrset;
}
static int
rpz_clientip_enter_rr(struct regional* region, struct clientip_synthesized_rr* raddr,
uint16_t rrtype, uint16_t rrclass, time_t ttl, uint8_t* rdata,
size_t rdata_len)
{
struct local_rrset* rrset;
if (rrtype == LDNS_RR_TYPE_CNAME && raddr->data != NULL) {
log_err("CNAME response-ip data can not co-exist with other "
"client-ip data");
return 0;
}
rrset = rpz_clientip_new_rrset(region, raddr, rrtype, rrclass);
if(raddr->data == NULL) {
return 0;
}
return rrset_insert_rr(region, rrset->rrset->entry.data, rdata, rdata_len, ttl, "");
}
static int
rpz_clientip_insert_trigger_rr(struct clientip_synthesized_rrset* set, struct sockaddr_storage* addr,
socklen_t addrlen, int net, enum rpz_action a, uint16_t rrtype,
uint16_t rrclass, uint32_t ttl, uint8_t* rdata, size_t rdata_len,
uint8_t* rr, size_t rr_len)
{
struct clientip_synthesized_rr* node;
lock_rw_wrlock(&set->lock);
node = rpz_clientip_ensure_entry(set, addr, addrlen, net);
if(node == NULL) {
lock_rw_unlock(&set->lock);
rpz_report_rrset_error("client ip address", rr, rr_len);
return 0;
}
lock_rw_wrlock(&node->lock);
lock_rw_unlock(&set->lock);
node->action = a;
if(a == RPZ_LOCAL_DATA_ACTION) {
if(!rpz_clientip_enter_rr(set->region, node, rrtype,
rrclass, ttl, rdata, rdata_len)) {
verbose(VERB_ALGO, "rpz: unable to insert clientip rr");
lock_rw_unlock(&node->lock);
return 0;
}
}
lock_rw_unlock(&node->lock);
return 1;
}
static int
rpz_insert_clientip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl,
uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len)
{
struct sockaddr_storage addr;
socklen_t addrlen;
int net, af;
if(a == RPZ_INVALID_ACTION) {
return 0;
}
if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af)) {
verbose(VERB_ALGO, "rpz: unable to parse client ip");
return 0;
}
return rpz_clientip_insert_trigger_rr(r->client_set, &addr, addrlen, net,
a, rrtype, rrclass, ttl, rdata, rdata_len, rr, rr_len);
}
static int
rpz_insert_nsip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl,
uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len)
{
struct sockaddr_storage addr;
socklen_t addrlen;
int net, af;
if(a == RPZ_INVALID_ACTION) {
return 0;
}
if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af)) {
verbose(VERB_ALGO, "rpz: unable to parse ns ip");
return 0;
}
return rpz_clientip_insert_trigger_rr(r->ns_set, &addr, addrlen, net,
a, rrtype, rrclass, ttl, rdata, rdata_len, rr, rr_len);
}
/** Insert RR into RPZ's respip_set */
static int
rpz_insert_response_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
enum rpz_action a, uint16_t rrtype, uint16_t rrclass, uint32_t ttl,
uint8_t* rdata, size_t rdata_len, uint8_t* rr, size_t rr_len)
{
struct sockaddr_storage addr;
socklen_t addrlen;
int net, af;
if(a == RPZ_INVALID_ACTION) {
return 0;
}
if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af)) {
verbose(VERB_ALGO, "rpz: unable to parse response ip");
return 0;
}
if(a == RPZ_INVALID_ACTION ||
rpz_action_to_respip_action(a) == respip_invalid) {
char str[255+1];
dname_str(dname, str);
verbose(VERB_ALGO, "rpz: respip trigger, %s skipping unsupported action: %s",
str, rpz_action_to_string(a));
return 0;
}
return rpz_insert_ipaddr_based_trigger(r->respip_set, &addr, addrlen, net,
a, rrtype, rrclass, ttl, rdata, rdata_len, rr, rr_len);
}
int
rpz_insert_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, uint8_t* dname,
size_t dnamelen, uint16_t rr_type, uint16_t rr_class, uint32_t rr_ttl,
uint8_t* rdatawl, size_t rdatalen, uint8_t* rr, size_t rr_len)
{
size_t policydnamelen;
/* name is free'd in local_zone delete */
enum rpz_trigger t;
enum rpz_action a;
uint8_t* policydname;
if(rpz_type_ignored(rr_type)) {
/* this rpz action is not valid, eg. this is the SOA or NS RR */
return 1;
}
if(!dname_subdomain_c(dname, azname)) {
char* dname_str = sldns_wire2str_dname(dname, dnamelen);
char* azname_str = sldns_wire2str_dname(azname, aznamelen);
if(dname_str && azname_str) {
log_err("rpz: name of record (%s) to insert into RPZ is not a "
"subdomain of the configured name of the RPZ zone (%s)",
dname_str, azname_str);
} else {
log_err("rpz: name of record to insert into RPZ is not a "
"subdomain of the configured name of the RPZ zone");
}
free(dname_str);
free(azname_str);
return 0;
}
log_assert(dnamelen >= aznamelen);
if(!(policydname = calloc(1, (dnamelen-aznamelen)+1))) {
log_err("malloc error while inserting RPZ RR");
return 0;
}
a = rpz_rr_to_action(rr_type, rdatawl, rdatalen);
if(!(policydnamelen = strip_dname_origin(dname, dnamelen, aznamelen,
policydname, (dnamelen-aznamelen)+1))) {
free(policydname);
return 0;
}
t = rpz_dname_to_trigger(policydname, policydnamelen);
if(t == RPZ_INVALID_TRIGGER) {
free(policydname);
verbose(VERB_ALGO, "rpz: skipping invalid trigger");
return 1;
}
if(t == RPZ_QNAME_TRIGGER) {
/* policydname will be consumed, no free */
rpz_insert_qname_trigger(r, policydname, policydnamelen,
a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr,
rr_len);
} else if(t == RPZ_RESPONSE_IP_TRIGGER) {
rpz_insert_response_ip_trigger(r, policydname, policydnamelen,
a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr,
rr_len);
free(policydname);
} else if(t == RPZ_CLIENT_IP_TRIGGER) {
rpz_insert_clientip_trigger(r, policydname, policydnamelen,
a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr,
rr_len);
free(policydname);
} else if(t == RPZ_NSIP_TRIGGER) {
rpz_insert_nsip_trigger(r, policydname, policydnamelen,
a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr,
rr_len);
free(policydname);
} else if(t == RPZ_NSDNAME_TRIGGER) {
rpz_insert_nsdname_trigger(r, policydname, policydnamelen,
a, rr_type, rr_class, rr_ttl, rdatawl, rdatalen, rr,
rr_len);
free(policydname);
} else {
free(policydname);
verbose(VERB_ALGO, "rpz: skipping unsupported trigger: %s",
rpz_trigger_to_string(t));
}
return 1;
}
/**
* Find RPZ local-zone by qname.
* @param zones: local-zone tree
* @param qname: qname
* @param qname_len: length of qname
* @param qclass: qclass
* @param only_exact: if 1 only exact (non wildcard) matches are returned
* @param wr: get write lock for local-zone if 1, read lock if 0
* @param zones_keep_lock: if set do not release the r->local_zones lock, this
* makes the caller of this function responsible for releasing the lock.
* @return: NULL or local-zone holding rd or wr lock
*/
static struct local_zone*
rpz_find_zone(struct local_zones* zones, uint8_t* qname, size_t qname_len, uint16_t qclass,
int only_exact, int wr, int zones_keep_lock)
{
uint8_t* ce;
size_t ce_len;
int ce_labs;
uint8_t wc[LDNS_MAX_DOMAINLEN+1];
int exact;
struct local_zone* z = NULL;
if(wr) {
lock_rw_wrlock(&zones->lock);
} else {
lock_rw_rdlock(&zones->lock);
}
z = local_zones_find_le(zones, qname, qname_len,
dname_count_labels(qname),
LDNS_RR_CLASS_IN, &exact);
if(!z || (only_exact && !exact)) {
if(!zones_keep_lock) {
lock_rw_unlock(&zones->lock);
}
return NULL;
}
if(wr) {
lock_rw_wrlock(&z->lock);
} else {
lock_rw_rdlock(&z->lock);
}
if(!zones_keep_lock) {
lock_rw_unlock(&zones->lock);
}
if(exact)
return z;
/* No exact match found, lookup wildcard. closest encloser must
* be the shared parent between the qname and the best local
* zone match, append '*' to that and do another lookup. */
ce = dname_get_shared_topdomain(z->name, qname);
if(!ce /* should not happen */) {
lock_rw_unlock(&z->lock);
if(zones_keep_lock) {
lock_rw_unlock(&zones->lock);
}
return NULL;
}
ce_labs = dname_count_size_labels(ce, &ce_len);
if(ce_len+2 > sizeof(wc)) {
lock_rw_unlock(&z->lock);
if(zones_keep_lock) {
lock_rw_unlock(&zones->lock);
}
return NULL;
}
wc[0] = 1; /* length of wildcard label */
wc[1] = (uint8_t)'*'; /* wildcard label */
memmove(wc+2, ce, ce_len);
lock_rw_unlock(&z->lock);
if(!zones_keep_lock) {
if(wr) {
lock_rw_wrlock(&zones->lock);
} else {
lock_rw_rdlock(&zones->lock);
}
}
z = local_zones_find_le(zones, wc,
ce_len+2, ce_labs+1, qclass, &exact);
if(!z || !exact) {
lock_rw_unlock(&zones->lock);
return NULL;
}
if(wr) {
lock_rw_wrlock(&z->lock);
} else {
lock_rw_rdlock(&z->lock);
}
if(!zones_keep_lock) {
lock_rw_unlock(&zones->lock);
}
return z;
}
/** Find entry for RR type in the list of rrsets for the clientip. */
static struct local_rrset*
rpz_find_synthesized_rrset(uint16_t qtype,
struct clientip_synthesized_rr* data)
{
struct local_rrset* cursor = data->data;
while( cursor != NULL) {
struct packed_rrset_key* packed_rrset = &cursor->rrset->rk;
if(htons(qtype) == packed_rrset->type) {
return cursor;
}
cursor = cursor->next;
}
return NULL;
}
/**
* Remove RR from RPZ's local-data
* @param z: local-zone for RPZ, holding write lock
* @param policydname: dname of RR to remove
* @param policydnamelen: length of policydname
* @param rr_type: RR type of RR to remove
* @param rdata: rdata of RR to remove
* @param rdatalen: length of rdata
* @return: 1 if zone must be removed after RR deletion
*/
static int
rpz_data_delete_rr(struct local_zone* z, uint8_t* policydname,
size_t policydnamelen, uint16_t rr_type, uint8_t* rdata,
size_t rdatalen)
{
struct local_data* ld;
struct packed_rrset_data* d;
size_t index;
ld = local_zone_find_data(z, policydname, policydnamelen,
dname_count_labels(policydname));
if(ld) {
struct local_rrset* prev=NULL, *p=ld->rrsets;
while(p && ntohs(p->rrset->rk.type) != rr_type) {
prev = p;
p = p->next;
}
if(!p)
return 0;
d = (struct packed_rrset_data*)p->rrset->entry.data;
if(packed_rrset_find_rr(d, rdata, rdatalen, &index)) {
if(d->count == 1) {
/* no memory recycling for zone deletions ... */
if(prev) prev->next = p->next;
else ld->rrsets = p->next;
}
if(d->count > 1) {
if(!local_rrset_remove_rr(d, index))
return 0;
}
}
}
if(ld && ld->rrsets)
return 0;
return 1;
}
/**
* Remove RR from RPZ's respip set
* @param raddr: respip node
* @param rr_type: RR type of RR to remove
* @param rdata: rdata of RR to remove
* @param rdatalen: length of rdata
* @return: 1 if zone must be removed after RR deletion
*/
static int
rpz_rrset_delete_rr(struct resp_addr* raddr, uint16_t rr_type, uint8_t* rdata,
size_t rdatalen)
{
size_t index;
struct packed_rrset_data* d;
if(!raddr->data)
return 1;
d = raddr->data->entry.data;
if(ntohs(raddr->data->rk.type) != rr_type) {
return 0;
}
if(packed_rrset_find_rr(d, rdata, rdatalen, &index)) {
if(d->count == 1) {
/* regional alloc'd */
raddr->data->entry.data = NULL;
raddr->data = NULL;
return 1;
}
if(d->count > 1) {
if(!local_rrset_remove_rr(d, index))
return 0;
}
}
return 0;
}
/** Remove RR from rpz localzones structure */
static void
rpz_remove_local_zones_trigger(struct local_zones* zones, uint8_t* dname,
size_t dnamelen, enum rpz_action a, uint16_t rr_type,
uint16_t rr_class, uint8_t* rdatawl, size_t rdatalen)
{
struct local_zone* z;
int delete_zone = 1;
z = rpz_find_zone(zones, dname, dnamelen, rr_class,
1 /* only exact */, 1 /* wr lock */, 1 /* keep lock*/);
if(!z) {
verbose(VERB_ALGO, "rpz: cannot remove RR from IXFR, "
"RPZ domain not found");
return;
}
if(a == RPZ_LOCAL_DATA_ACTION)
delete_zone = rpz_data_delete_rr(z, dname,
dnamelen, rr_type, rdatawl, rdatalen);
else if(a != localzone_type_to_rpz_action(z->type)) {
lock_rw_unlock(&z->lock);
lock_rw_unlock(&zones->lock);
return;
}
lock_rw_unlock(&z->lock);
if(delete_zone) {
local_zones_del_zone(zones, z);
}
lock_rw_unlock(&zones->lock);
}
/** Remove RR from RPZ's local-zone */
static void
rpz_remove_qname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
enum rpz_action a, uint16_t rr_type, uint16_t rr_class,
uint8_t* rdatawl, size_t rdatalen)
{
rpz_remove_local_zones_trigger(r->local_zones, dname, dnamelen,
a, rr_type, rr_class, rdatawl, rdatalen);
}
static void
rpz_remove_response_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
enum rpz_action a, uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen)
{
struct resp_addr* node;
struct sockaddr_storage addr;
socklen_t addrlen;
int net, af;
int delete_respip = 1;
if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af))
return;
lock_rw_wrlock(&r->respip_set->lock);
if(!(node = (struct resp_addr*)addr_tree_find(
&r->respip_set->ip_tree, &addr, addrlen, net))) {
verbose(VERB_ALGO, "rpz: cannot remove RR from IXFR, "
"RPZ domain not found");
lock_rw_unlock(&r->respip_set->lock);
return;
}
lock_rw_wrlock(&node->lock);
if(a == RPZ_LOCAL_DATA_ACTION) {
/* remove RR, signal whether RR can be removed */
delete_respip = rpz_rrset_delete_rr(node, rr_type, rdatawl,
rdatalen);
}
lock_rw_unlock(&node->lock);
if(delete_respip)
respip_sockaddr_delete(r->respip_set, node);
lock_rw_unlock(&r->respip_set->lock);
}
/** find and remove type from list of local_rrset entries*/
static void
del_local_rrset_from_list(struct local_rrset** list_head, uint16_t dtype)
{
struct local_rrset* prev=NULL, *p=*list_head;
while(p && ntohs(p->rrset->rk.type) != dtype) {
prev = p;
p = p->next;
}
if(!p)
return; /* rrset type not found */
/* unlink it */
if(prev) prev->next = p->next;
else *list_head = p->next;
/* no memory recycling for zone deletions ... */
}
/** Delete client-ip trigger RR from its RRset and perhaps also the rrset
* from the linked list. Returns if the local data is empty and the node can
* be deleted too, or not. */
static int rpz_remove_clientip_rr(struct clientip_synthesized_rr* node,
uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen)
{
struct local_rrset* rrset;
struct packed_rrset_data* d;
size_t index;
rrset = rpz_find_synthesized_rrset(rr_type, node);
if(rrset == NULL)
return 0; /* type not found, ignore */
d = (struct packed_rrset_data*)rrset->rrset->entry.data;
if(!packed_rrset_find_rr(d, rdatawl, rdatalen, &index))
return 0; /* RR not found, ignore */
if(d->count == 1) {
/* regional alloc'd */
/* delete the type entry from the list */
del_local_rrset_from_list(&node->data, rr_type);
/* if the list is empty, the node can be removed too */
if(node->data == NULL)
return 1;
} else if (d->count > 1) {
if(!local_rrset_remove_rr(d, index))
return 0;
}
return 0;
}
/** remove trigger RR from clientip_syntheized set tree. */
static void
rpz_clientip_remove_trigger_rr(struct clientip_synthesized_rrset* set,
struct sockaddr_storage* addr, socklen_t addrlen, int net,
enum rpz_action a, uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen)
{
struct clientip_synthesized_rr* node;
int delete_node = 1;
lock_rw_wrlock(&set->lock);
node = (struct clientip_synthesized_rr*)addr_tree_find(&set->entries,
addr, addrlen, net);
if(node == NULL) {
/* netblock not found */
verbose(VERB_ALGO, "rpz: cannot remove RR from IXFR, "
"RPZ address, netblock not found");
lock_rw_unlock(&set->lock);
return;
}
lock_rw_wrlock(&node->lock);
if(a == RPZ_LOCAL_DATA_ACTION) {
/* remove RR, signal whether entry can be removed */
delete_node = rpz_remove_clientip_rr(node, rr_type, rdatawl,
rdatalen);
} else if(a != node->action) {
/* ignore the RR with different action specification */
delete_node = 0;
}
if(delete_node) {
rbtree_delete(&set->entries, node->node.node.key);
}
lock_rw_unlock(&set->lock);
lock_rw_unlock(&node->lock);
if(delete_node) {
lock_rw_destroy(&node->lock);
}
}
/** Remove clientip trigger RR from RPZ. */
static void
rpz_remove_clientip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
enum rpz_action a, uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen)
{
struct sockaddr_storage addr;
socklen_t addrlen;
int net, af;
if(a == RPZ_INVALID_ACTION)
return;
if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af))
return;
rpz_clientip_remove_trigger_rr(r->client_set, &addr, addrlen, net,
a, rr_type, rdatawl, rdatalen);
}
/** Remove nsip trigger RR from RPZ. */
static void
rpz_remove_nsip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
enum rpz_action a, uint16_t rr_type, uint8_t* rdatawl, size_t rdatalen)
{
struct sockaddr_storage addr;
socklen_t addrlen;
int net, af;
if(a == RPZ_INVALID_ACTION)
return;
if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af))
return;
rpz_clientip_remove_trigger_rr(r->ns_set, &addr, addrlen, net,
a, rr_type, rdatawl, rdatalen);
}
/** Remove nsdname trigger RR from RPZ. */
static void
rpz_remove_nsdname_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
enum rpz_action a, uint16_t rr_type, uint16_t rr_class,
uint8_t* rdatawl, size_t rdatalen)
{
uint8_t* dname_stripped = NULL;
size_t dnamelen_stripped = 0;
if(a == RPZ_INVALID_ACTION)
return;
if(!rpz_strip_nsdname_suffix(dname, dnamelen, &dname_stripped,
&dnamelen_stripped))
return;
rpz_remove_local_zones_trigger(r->nsdname_zones, dname_stripped,
dnamelen_stripped, a, rr_type, rr_class, rdatawl, rdatalen);
free(dname_stripped);
}
void
rpz_remove_rr(struct rpz* r, uint8_t* azname, size_t aznamelen, uint8_t* dname,
size_t dnamelen, uint16_t rr_type, uint16_t rr_class, uint8_t* rdatawl,
size_t rdatalen)
{
size_t policydnamelen;
enum rpz_trigger t;
enum rpz_action a;
uint8_t* policydname;
if(rpz_type_ignored(rr_type)) {
/* this rpz action is not valid, eg. this is the SOA or NS RR */
return;
}
if(!dname_subdomain_c(dname, azname)) {
/* not subdomain of the RPZ zone. */
return;
}
if(!(policydname = calloc(1, LDNS_MAX_DOMAINLEN + 1)))
return;
a = rpz_rr_to_action(rr_type, rdatawl, rdatalen);
if(a == RPZ_INVALID_ACTION) {
free(policydname);
return;
}
if(!(policydnamelen = strip_dname_origin(dname, dnamelen, aznamelen,
policydname, LDNS_MAX_DOMAINLEN + 1))) {
free(policydname);
return;
}
t = rpz_dname_to_trigger(policydname, policydnamelen);
if(t == RPZ_INVALID_TRIGGER) {
/* skipping invalid trigger */
free(policydname);
return;
}
if(t == RPZ_QNAME_TRIGGER) {
rpz_remove_qname_trigger(r, policydname, policydnamelen, a,
rr_type, rr_class, rdatawl, rdatalen);
} else if(t == RPZ_RESPONSE_IP_TRIGGER) {
rpz_remove_response_ip_trigger(r, policydname, policydnamelen,
a, rr_type, rdatawl, rdatalen);
} else if(t == RPZ_CLIENT_IP_TRIGGER) {
rpz_remove_clientip_trigger(r, policydname, policydnamelen, a,
rr_type, rdatawl, rdatalen);
} else if(t == RPZ_NSIP_TRIGGER) {
rpz_remove_nsip_trigger(r, policydname, policydnamelen, a,
rr_type, rdatawl, rdatalen);
} else if(t == RPZ_NSDNAME_TRIGGER) {
rpz_remove_nsdname_trigger(r, policydname, policydnamelen, a,
rr_type, rr_class, rdatawl, rdatalen);
}
/* else it was an unsupported trigger, also skipped. */
free(policydname);
}
/** print log information for an applied RPZ policy. Based on local-zone's
* lz_inform_print().
* The repinfo contains the reply address. If it is NULL, the module
* state is used to report the first IP address (if any).
* The dname is used, for the applied rpz, if NULL, addrnode is used.
*/
static void
log_rpz_apply(char* trigger, uint8_t* dname, struct addr_tree_node* addrnode,
enum rpz_action a, struct query_info* qinfo,
struct comm_reply* repinfo, struct module_qstate* ms, char* log_name)
{
char ip[128], txt[512], portstr[32];
char dnamestr[LDNS_MAX_DOMAINLEN+1];
uint16_t port = 0;
if(dname) {
dname_str(dname, dnamestr);
} else if(addrnode) {
char addrbuf[128];
addr_to_str(&addrnode->addr, addrnode->addrlen, addrbuf, sizeof(addrbuf));
snprintf(dnamestr, sizeof(dnamestr), "%s/%d", addrbuf, addrnode->net);
} else {
dnamestr[0]=0;
}
if(repinfo) {
addr_to_str(&repinfo->client_addr, repinfo->client_addrlen, ip, sizeof(ip));
port = ntohs(((struct sockaddr_in*)&repinfo->client_addr)->sin_port);
} else if(ms && ms->mesh_info && ms->mesh_info->reply_list) {
addr_to_str(&ms->mesh_info->reply_list->query_reply.client_addr,
ms->mesh_info->reply_list->query_reply.client_addrlen,
ip, sizeof(ip));
port = ntohs(((struct sockaddr_in*)&ms->mesh_info->reply_list->query_reply.client_addr)->sin_port);
} else {
ip[0]=0;
port = 0;
}
snprintf(portstr, sizeof(portstr), "@%u", (unsigned)port);
snprintf(txt, sizeof(txt), "rpz: applied %s%s%s%s%s%s %s %s%s",
(log_name?"[":""), (log_name?log_name:""), (log_name?"] ":""),
(strcmp(trigger,"qname")==0?"":trigger),
(strcmp(trigger,"qname")==0?"":" "),
dnamestr, rpz_action_to_string(a),
(ip[0]?ip:""), (ip[0]?portstr:""));
log_nametypeclass(0, txt, qinfo->qname, qinfo->qtype, qinfo->qclass);
}
static struct clientip_synthesized_rr*
rpz_ipbased_trigger_lookup(struct clientip_synthesized_rrset* set,
struct sockaddr_storage* addr, socklen_t addrlen, char* triggername)
{
struct clientip_synthesized_rr* raddr = NULL;
enum rpz_action action = RPZ_INVALID_ACTION;
lock_rw_rdlock(&set->lock);
raddr = (struct clientip_synthesized_rr*)addr_tree_lookup(&set->entries,
addr, addrlen);
if(raddr != NULL) {
lock_rw_rdlock(&raddr->lock);
action = raddr->action;
if(verbosity >= VERB_ALGO) {
char ip[256], net[256];
addr_to_str(addr, addrlen, ip, sizeof(ip));
addr_to_str(&raddr->node.addr, raddr->node.addrlen,
net, sizeof(net));
verbose(VERB_ALGO, "rpz: trigger %s %s/%d on %s action=%s",
triggername, net, raddr->node.net, ip, rpz_action_to_string(action));
}
}
lock_rw_unlock(&set->lock);
return raddr;
}
static inline
struct clientip_synthesized_rr*
rpz_resolve_client_action_and_zone(struct auth_zones* az, struct query_info* qinfo,
struct comm_reply* repinfo, uint8_t* taglist, size_t taglen,
struct ub_server_stats* stats,
/* output parameters */
struct local_zone** z_out, struct auth_zone** a_out, struct rpz** r_out)
{
struct clientip_synthesized_rr* node = NULL;
struct auth_zone* a = NULL;
struct rpz* r = NULL;
struct local_zone* z = NULL;
lock_rw_rdlock(&az->rpz_lock);
for(a = az->rpz_first; a; a = a->rpz_az_next) {
lock_rw_rdlock(&a->lock);
r = a->rpz;
if(r->disabled) {
lock_rw_unlock(&a->lock);
continue;
}
if(r->taglist && !taglist_intersect(r->taglist,
r->taglistlen, taglist, taglen)) {
lock_rw_unlock(&a->lock);
continue;
}
z = rpz_find_zone(r->local_zones, qinfo->qname, qinfo->qname_len,
qinfo->qclass, 0, 0, 0);
node = rpz_ipbased_trigger_lookup(r->client_set,
&repinfo->client_addr, repinfo->client_addrlen,
"clientip");
if((z || node) && r->action_override == RPZ_DISABLED_ACTION) {
if(r->log)
log_rpz_apply((node?"clientip":"qname"),
(z?z->name:NULL),
(node?&node->node:NULL),
r->action_override,
qinfo, repinfo, NULL, r->log_name);
stats->rpz_action[r->action_override]++;
if(z != NULL) {
lock_rw_unlock(&z->lock);
z = NULL;
}
if(node != NULL) {
lock_rw_unlock(&node->lock);
node = NULL;
}
}
if(z || node) {
break;
}
/* not found in this auth_zone */
lock_rw_unlock(&a->lock);
}
lock_rw_unlock(&az->rpz_lock);
*r_out = r;
*a_out = a;
*z_out = z;
return node;
}
static inline int
rpz_is_udp_query(struct comm_reply* repinfo) {
return repinfo != NULL
? (repinfo->c != NULL
? repinfo->c->type == comm_udp
: 0)
: 0;
}
/** encode answer consisting of 1 rrset */
static int
rpz_local_encode(struct module_env* env, struct query_info* qinfo,
struct edns_data* edns, struct comm_reply* repinfo, sldns_buffer* buf,
struct regional* temp, struct ub_packed_rrset_key* rrset, int ansec,
int rcode, struct ub_packed_rrset_key* soa_rrset)
{
struct reply_info rep;
uint16_t udpsize;
struct ub_packed_rrset_key* rrsetlist[3];
memset(&rep, 0, sizeof(rep));
rep.flags = (uint16_t)((BIT_QR | BIT_AA | BIT_RA) | rcode);
rep.qdcount = 1;
rep.rrset_count = ansec;
rep.rrsets = rrsetlist;
if(ansec > 0) {
rep.an_numrrsets = 1;
rep.rrsets[0] = rrset;
rep.ttl = ((struct packed_rrset_data*)rrset->entry.data)->rr_ttl[0];
}
if(soa_rrset != NULL) {
rep.ar_numrrsets = 1;
rep.rrsets[rep.rrset_count] = soa_rrset;
rep.rrset_count ++;
if(rep.ttl < ((struct packed_rrset_data*)soa_rrset->entry.data)->rr_ttl[0]) {
rep.ttl = ((struct packed_rrset_data*)soa_rrset->entry.data)->rr_ttl[0];
}
}
udpsize = edns->udp_size;
edns->edns_version = EDNS_ADVERTISED_VERSION;
edns->udp_size = EDNS_ADVERTISED_SIZE;
edns->ext_rcode = 0;
edns->bits &= EDNS_DO;
if(!inplace_cb_reply_local_call(env, qinfo, NULL, &rep, rcode, edns,
repinfo, temp, env->now_tv) ||
!reply_info_answer_encode(qinfo, &rep,
*(uint16_t*)sldns_buffer_begin(buf), sldns_buffer_read_u16_at(buf, 2),
buf, 0, 0, temp, udpsize, edns, (int)(edns->bits&EDNS_DO), 0)) {
error_encode(buf, (LDNS_RCODE_SERVFAIL|BIT_AA), qinfo,
*(uint16_t*)sldns_buffer_begin(buf),
sldns_buffer_read_u16_at(buf, 2), edns);
}
return 1;
}
/** allocate SOA record ubrrsetkey in region */
static struct ub_packed_rrset_key*
make_soa_ubrrset(struct auth_zone* auth_zone, struct auth_rrset* soa,
struct regional* temp)
{
struct ub_packed_rrset_key csoa;
if(!soa)
return NULL;
memset(&csoa, 0, sizeof(csoa));
csoa.entry.key = &csoa;
csoa.rk.rrset_class = htons(LDNS_RR_CLASS_IN);
csoa.rk.type = htons(LDNS_RR_TYPE_SOA);
csoa.rk.flags |= PACKED_RRSET_FIXEDTTL
| PACKED_RRSET_RPZ;
csoa.rk.dname = auth_zone->name;
csoa.rk.dname_len = auth_zone->namelen;
csoa.entry.hash = rrset_key_hash(&csoa.rk);
csoa.entry.data = soa->data;
return respip_copy_rrset(&csoa, temp);
}
static void
rpz_apply_clientip_localdata_action(struct clientip_synthesized_rr* raddr,
struct module_env* env, struct query_info* qinfo,
struct edns_data* edns, struct comm_reply* repinfo, sldns_buffer* buf,
struct regional* temp, struct auth_zone* auth_zone)
{
struct local_rrset* rrset;
enum rpz_action action = RPZ_INVALID_ACTION;
struct ub_packed_rrset_key* rp = NULL;
struct ub_packed_rrset_key* rsoa = NULL;
int rcode = LDNS_RCODE_NOERROR|BIT_AA;
int rrset_count = 1;
/* prepare synthesized answer for client */
action = raddr->action;
if(action == RPZ_LOCAL_DATA_ACTION && raddr->data == NULL ) {
verbose(VERB_ALGO, "rpz: bug: local-data action but no local data");
return;
}
/* check query type / rr type */
rrset = rpz_find_synthesized_rrset(qinfo->qtype, raddr);
if(rrset == NULL) {
verbose(VERB_ALGO, "rpz: unable to find local-data for query");
rrset_count = 0;
goto nodata;
}
rp = respip_copy_rrset(rrset->rrset, temp);
if(!rp) {
verbose(VERB_ALGO, "rpz: local data action: out of memory");
return;
}
rp->rk.flags |= PACKED_RRSET_FIXEDTTL | PACKED_RRSET_RPZ;
rp->rk.dname = qinfo->qname;
rp->rk.dname_len = qinfo->qname_len;
rp->entry.hash = rrset_key_hash(&rp->rk);
nodata:
if(auth_zone) {
struct auth_rrset* soa = NULL;
soa = auth_zone_get_soa_rrset(auth_zone);
if(soa) {
rsoa = make_soa_ubrrset(auth_zone, soa, temp);
if(!rsoa) {
verbose(VERB_ALGO, "rpz: local data action soa: out of memory");
return;
}
}
}
rpz_local_encode(env, qinfo, edns, repinfo, buf, temp, rp,
rrset_count, rcode, rsoa);
}
/** add additional section SOA record to the reply.
* Since this gets fed into the normal iterator answer creation, it
* gets minimal-responses applied to it, that can remove the additional SOA
* again. */
static int
rpz_add_soa(struct reply_info* rep, struct module_qstate* ms,
struct auth_zone* az)
{
struct auth_rrset* soa = NULL;
struct ub_packed_rrset_key* rsoa = NULL;
struct ub_packed_rrset_key** prevrrsets;
if(!az) return 1;
soa = auth_zone_get_soa_rrset(az);
if(!soa) return 1;
if(!rep) return 0;
rsoa = make_soa_ubrrset(az, soa, ms->region);
if(!rsoa) return 0;
prevrrsets = rep->rrsets;
rep->rrsets = regional_alloc_zero(ms->region,
sizeof(*rep->rrsets)*(rep->rrset_count+1));
if(!rep->rrsets)
return 0;
if(prevrrsets && rep->rrset_count > 0)
memcpy(rep->rrsets, prevrrsets, rep->rrset_count*sizeof(*rep->rrsets));
rep->rrset_count++;
rep->ar_numrrsets++;
rep->rrsets[rep->rrset_count-1] = rsoa;
return 1;
}
static inline struct dns_msg*
rpz_dns_msg_new(struct regional* region)
{
struct dns_msg* msg =
(struct dns_msg*)regional_alloc(region,
sizeof(struct dns_msg));
if(msg == NULL) { return NULL; }
memset(msg, 0, sizeof(struct dns_msg));
return msg;
}
static inline struct dns_msg*
rpz_synthesize_nodata(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms,
struct query_info* qinfo, struct auth_zone* az)
{
struct dns_msg* msg = rpz_dns_msg_new(ms->region);
if(msg == NULL) { return msg; }
msg->qinfo = *qinfo;
msg->rep = construct_reply_info_base(ms->region,
LDNS_RCODE_NOERROR | BIT_QR | BIT_AA | BIT_RA,
1, /* qd */
0, /* ttl */
0, /* prettl */
0, /* expttl */
0, /* an */
0, /* ns */
0, /* ar */
0, /* total */
sec_status_insecure,
LDNS_EDE_NONE);
if(msg->rep)
msg->rep->authoritative = 1;
if(!rpz_add_soa(msg->rep, ms, az))
return NULL;
return msg;
}
static inline struct dns_msg*
rpz_synthesize_nxdomain(struct rpz* r, struct module_qstate* ms,
struct query_info* qinfo, struct auth_zone* az)
{
struct dns_msg* msg = rpz_dns_msg_new(ms->region);
uint16_t flags;
if(msg == NULL) { return msg; }
msg->qinfo = *qinfo;
flags = LDNS_RCODE_NXDOMAIN | BIT_QR | BIT_AA | BIT_RA;
if(r->signal_nxdomain_ra)
flags &= ~BIT_RA;
msg->rep = construct_reply_info_base(ms->region,
flags,
1, /* qd */
0, /* ttl */
0, /* prettl */
0, /* expttl */
0, /* an */
0, /* ns */
0, /* ar */
0, /* total */
sec_status_insecure,
LDNS_EDE_NONE);
if(msg->rep)
msg->rep->authoritative = 1;
if(!rpz_add_soa(msg->rep, ms, az))
return NULL;
return msg;
}
static inline struct dns_msg*
rpz_synthesize_localdata_from_rrset(struct rpz* ATTR_UNUSED(r), struct module_qstate* ms,
struct query_info* qi, struct local_rrset* rrset, struct auth_zone* az)
{
struct dns_msg* msg = NULL;
struct reply_info* new_reply_info;
struct ub_packed_rrset_key* rp;
msg = rpz_dns_msg_new(ms->region);
if(msg == NULL) { return NULL; }
new_reply_info = construct_reply_info_base(ms->region,
LDNS_RCODE_NOERROR | BIT_QR | BIT_AA | BIT_RA,
1, /* qd */
0, /* ttl */
0, /* prettl */
0, /* expttl */
1, /* an */
0, /* ns */
0, /* ar */
1, /* total */
sec_status_insecure,
LDNS_EDE_NONE);
if(new_reply_info == NULL) {
log_err("out of memory");
return NULL;
}
new_reply_info->authoritative = 1;
rp = respip_copy_rrset(rrset->rrset, ms->region);
if(rp == NULL) {
log_err("out of memory");
return NULL;
}
rp->rk.dname = qi->qname;
rp->rk.dname_len = qi->qname_len;
/* this rrset is from the rpz data, or synthesized.
* It is not actually from the network, so we flag it with this
* flags as a fake RRset. If later the cache is used to look up
* rrsets, then the fake ones are not returned (if you look without
* the flag). For like CNAME lookups from the iterator or A, AAAA
* lookups for nameserver targets, it would use the without flag
* actual data. So that the actual network data and fake data
* are kept track of separately. */
rp->rk.flags |= PACKED_RRSET_RPZ;
new_reply_info->rrsets[0] = rp;
msg->rep = new_reply_info;
if(!rpz_add_soa(msg->rep, ms, az))
return NULL;
return msg;
}
static inline struct dns_msg*
rpz_synthesize_nsip_localdata(struct rpz* r, struct module_qstate* ms,
struct clientip_synthesized_rr* data, struct auth_zone* az)
{
struct query_info* qi = &ms->qinfo;
struct local_rrset* rrset;
rrset = rpz_find_synthesized_rrset(qi->qtype, data);
if(rrset == NULL) {
verbose(VERB_ALGO, "rpz: nsip: no matching local data found");
return NULL;
}
return rpz_synthesize_localdata_from_rrset(r, ms, &ms->qinfo, rrset, az);
}
/* copy'n'paste from localzone.c */
static struct local_rrset*
local_data_find_type(struct local_data* data, uint16_t type, int alias_ok)
{
struct local_rrset* p;
type = htons(type);
for(p = data->rrsets; p; p = p->next) {
if(p->rrset->rk.type == type)
return p;
if(alias_ok && p->rrset->rk.type == htons(LDNS_RR_TYPE_CNAME))
return p;
}
return NULL;
}
/* based on localzone.c:local_data_answer() */
static inline struct dns_msg*
rpz_synthesize_nsdname_localdata(struct rpz* r, struct module_qstate* ms,
struct local_zone* z, struct matched_delegation_point const* match,
struct auth_zone* az)
{
struct local_data key;
struct local_data* ld;
struct local_rrset* rrset;
if(match->dname == NULL) { return NULL; }
key.node.key = &key;
key.name = match->dname;
key.namelen = match->dname_len;
key.namelabs = dname_count_labels(match->dname);
rpz_log_dname("nsdname local data", key.name, key.namelen);
ld = (struct local_data*)rbtree_search(&z->data, &key.node);
if(ld == NULL) {
verbose(VERB_ALGO, "rpz: nsdname: impossible: qname not found");
return NULL;
}
rrset = local_data_find_type(ld, ms->qinfo.qtype, 1);
if(rrset == NULL) {
verbose(VERB_ALGO, "rpz: nsdname: no matching local data found");
return NULL;
}
return rpz_synthesize_localdata_from_rrset(r, ms, &ms->qinfo, rrset, az);
}
/* like local_data_answer for qname triggers after a cname */
static struct dns_msg*
rpz_synthesize_qname_localdata_msg(struct rpz* r, struct module_qstate* ms,
struct query_info* qinfo, struct local_zone* z, struct auth_zone* az)
{
struct local_data key;
struct local_data* ld;
struct local_rrset* rrset;
key.node.key = &key;
key.name = qinfo->qname;
key.namelen = qinfo->qname_len;
key.namelabs = dname_count_labels(qinfo->qname);
ld = (struct local_data*)rbtree_search(&z->data, &key.node);
if(ld == NULL) {
verbose(VERB_ALGO, "rpz: qname after cname: name not found");
return NULL;
}
rrset = local_data_find_type(ld, qinfo->qtype, 1);
if(rrset == NULL) {
verbose(VERB_ALGO, "rpz: qname after cname: type not found");
return NULL;
}
return rpz_synthesize_localdata_from_rrset(r, ms, qinfo, rrset, az);
}
static int
rpz_synthesize_qname_localdata(struct module_env* env, struct rpz* r,
struct local_zone* z, enum localzone_type lzt, struct query_info* qinfo,
struct edns_data* edns, sldns_buffer* buf, struct regional* temp,
struct comm_reply* repinfo, struct ub_server_stats* stats)
{
struct local_data* ld = NULL;
int ret = 0;
if(r->action_override == RPZ_CNAME_OVERRIDE_ACTION) {
qinfo->local_alias = regional_alloc_zero(temp, sizeof(struct local_rrset));
if(qinfo->local_alias == NULL) {
return 0; /* out of memory */
}
qinfo->local_alias->rrset = regional_alloc_init(temp, r->cname_override,
sizeof(*r->cname_override));
if(qinfo->local_alias->rrset == NULL) {
return 0; /* out of memory */
}
qinfo->local_alias->rrset->rk.dname = qinfo->qname;
qinfo->local_alias->rrset->rk.dname_len = qinfo->qname_len;
if(r->log) {
log_rpz_apply("qname", z->name, NULL, RPZ_CNAME_OVERRIDE_ACTION,
qinfo, repinfo, NULL, r->log_name);
}
stats->rpz_action[RPZ_CNAME_OVERRIDE_ACTION]++;
return 0;
}
if(lzt == local_zone_redirect && local_data_answer(z, env, qinfo,
edns, repinfo, buf, temp, dname_count_labels(qinfo->qname),
&ld, lzt, -1, NULL, 0, NULL, 0)) {
if(r->log) {
log_rpz_apply("qname", z->name, NULL,
localzone_type_to_rpz_action(lzt), qinfo,
repinfo, NULL, r->log_name);
}
stats->rpz_action[localzone_type_to_rpz_action(lzt)]++;
return !qinfo->local_alias;
}
ret = local_zones_zone_answer(z, env, qinfo, edns, repinfo, buf, temp,
0 /* no local data used */, lzt);
if(r->signal_nxdomain_ra && LDNS_RCODE_WIRE(sldns_buffer_begin(buf))
== LDNS_RCODE_NXDOMAIN)
LDNS_RA_CLR(sldns_buffer_begin(buf));
if(r->log) {
log_rpz_apply("qname", z->name, NULL, localzone_type_to_rpz_action(lzt),
qinfo, repinfo, NULL, r->log_name);
}
stats->rpz_action[localzone_type_to_rpz_action(lzt)]++;
return ret;
}
static struct clientip_synthesized_rr*
rpz_delegation_point_ipbased_trigger_lookup(struct rpz* rpz, struct iter_qstate* is)
{
struct delegpt_addr* cursor;
struct clientip_synthesized_rr* action = NULL;
if(is->dp == NULL) { return NULL; }
for(cursor = is->dp->target_list;
cursor != NULL;
cursor = cursor->next_target) {
if(cursor->bogus) { continue; }
action = rpz_ipbased_trigger_lookup(rpz->ns_set, &cursor->addr,
cursor->addrlen, "nsip");
if(action != NULL) { return action; }
}
return NULL;
}
static struct dns_msg*
rpz_apply_nsip_trigger(struct module_qstate* ms, struct rpz* r,
struct clientip_synthesized_rr* raddr, struct auth_zone* az)
{
enum rpz_action action = raddr->action;
struct dns_msg* ret = NULL;
if(r->action_override != RPZ_NO_OVERRIDE_ACTION) {
verbose(VERB_ALGO, "rpz: using override action=%s (replaces=%s)",
rpz_action_to_string(r->action_override), rpz_action_to_string(action));
action = r->action_override;
}
if(action == RPZ_LOCAL_DATA_ACTION && raddr->data == NULL) {
verbose(VERB_ALGO, "rpz: bug: nsip local data action but no local data");
ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az);
goto done;
}
switch(action) {
case RPZ_NXDOMAIN_ACTION:
ret = rpz_synthesize_nxdomain(r, ms, &ms->qinfo, az);
break;
case RPZ_NODATA_ACTION:
ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az);
break;
case RPZ_TCP_ONLY_ACTION:
/* basically a passthru here but the tcp-only will be
* honored before the query gets sent. */
- ms->respip_action_info->action = respip_truncate;
+ ms->tcp_required = 1;
ret = NULL;
break;
case RPZ_DROP_ACTION:
ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az);
ms->is_drop = 1;
break;
case RPZ_LOCAL_DATA_ACTION:
ret = rpz_synthesize_nsip_localdata(r, ms, raddr, az);
if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); }
break;
case RPZ_PASSTHRU_ACTION:
ret = NULL;
ms->rpz_passthru = 1;
break;
default:
verbose(VERB_ALGO, "rpz: nsip: bug: unhandled or invalid action: '%s'",
rpz_action_to_string(action));
ret = NULL;
}
done:
if(r->log)
log_rpz_apply("nsip", NULL, &raddr->node,
action, &ms->qinfo, NULL, ms, r->log_name);
if(ms->env->worker)
ms->env->worker->stats.rpz_action[action]++;
lock_rw_unlock(&raddr->lock);
return ret;
}
static struct dns_msg*
rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r,
struct local_zone* z, struct matched_delegation_point const* match,
struct auth_zone* az)
{
struct dns_msg* ret = NULL;
enum rpz_action action = localzone_type_to_rpz_action(z->type);
if(r->action_override != RPZ_NO_OVERRIDE_ACTION) {
verbose(VERB_ALGO, "rpz: using override action=%s (replaces=%s)",
rpz_action_to_string(r->action_override), rpz_action_to_string(action));
action = r->action_override;
}
switch(action) {
case RPZ_NXDOMAIN_ACTION:
ret = rpz_synthesize_nxdomain(r, ms, &ms->qinfo, az);
break;
case RPZ_NODATA_ACTION:
ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az);
break;
case RPZ_TCP_ONLY_ACTION:
/* basically a passthru here but the tcp-only will be
* honored before the query gets sent. */
- ms->respip_action_info->action = respip_truncate;
+ ms->tcp_required = 1;
ret = NULL;
break;
case RPZ_DROP_ACTION:
ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az);
ms->is_drop = 1;
break;
case RPZ_LOCAL_DATA_ACTION:
ret = rpz_synthesize_nsdname_localdata(r, ms, z, match, az);
if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); }
break;
case RPZ_PASSTHRU_ACTION:
ret = NULL;
ms->rpz_passthru = 1;
break;
default:
verbose(VERB_ALGO, "rpz: nsip: bug: unhandled or invalid action: '%s'",
rpz_action_to_string(action));
ret = NULL;
}
if(r->log)
log_rpz_apply("nsdname", match->dname, NULL,
action, &ms->qinfo, NULL, ms, r->log_name);
if(ms->env->worker)
ms->env->worker->stats.rpz_action[action]++;
lock_rw_unlock(&z->lock);
return ret;
}
static struct local_zone*
rpz_delegation_point_zone_lookup(struct delegpt* dp, struct local_zones* zones,
uint16_t qclass,
/* output parameter */
struct matched_delegation_point* match)
{
struct delegpt_ns* nameserver;
struct local_zone* z = NULL;
/* the rpz specs match the nameserver names (NS records), not the
* name of the delegation point itself, to the nsdname triggers */
for(nameserver = dp->nslist;
nameserver != NULL;
nameserver = nameserver->next) {
z = rpz_find_zone(zones, nameserver->name, nameserver->namelen,
qclass, 0, 0, 0);
if(z != NULL) {
match->dname = nameserver->name;
match->dname_len = nameserver->namelen;
if(verbosity >= VERB_ALGO) {
char nm[255+1], zn[255+1];
dname_str(match->dname, nm);
dname_str(z->name, zn);
if(strcmp(nm, zn) != 0)
verbose(VERB_ALGO, "rpz: trigger nsdname %s on %s action=%s",
zn, nm, rpz_action_to_string(localzone_type_to_rpz_action(z->type)));
else
verbose(VERB_ALGO, "rpz: trigger nsdname %s action=%s",
nm, rpz_action_to_string(localzone_type_to_rpz_action(z->type)));
}
break;
}
}
return z;
}
struct dns_msg*
rpz_callback_from_iterator_module(struct module_qstate* ms, struct iter_qstate* is)
{
struct auth_zones* az;
struct auth_zone* a;
struct clientip_synthesized_rr* raddr = NULL;
struct rpz* r = NULL;
struct local_zone* z = NULL;
struct matched_delegation_point match = {0};
if(ms->rpz_passthru) {
verbose(VERB_ALGO, "query is rpz_passthru, no further processing");
return NULL;
}
if(ms->env == NULL || ms->env->auth_zones == NULL) { return 0; }
az = ms->env->auth_zones;
verbose(VERB_ALGO, "rpz: iterator module callback: have_rpz=%d", az->rpz_first != NULL);
lock_rw_rdlock(&az->rpz_lock);
/* precedence of RPZ works, loosely, like this:
* CNAMEs in order of the CNAME chain. rpzs in the order they are
* configured. In an RPZ: first client-IP addr, then QNAME, then
* response IP, then NSDNAME, then NSIP. Longest match first. Smallest
* one from a set. */
/* we use the precedence rules for the topics and triggers that
* are pertinent at this stage of the resolve processing */
for(a = az->rpz_first; a != NULL; a = a->rpz_az_next) {
lock_rw_rdlock(&a->lock);
r = a->rpz;
if(r->disabled) {
lock_rw_unlock(&a->lock);
continue;
}
/* the nsdname has precedence over the nsip triggers */
z = rpz_delegation_point_zone_lookup(is->dp, r->nsdname_zones,
ms->qinfo.qclass, &match);
if(z != NULL) {
lock_rw_unlock(&a->lock);
break;
}
raddr = rpz_delegation_point_ipbased_trigger_lookup(r, is);
if(raddr != NULL) {
lock_rw_unlock(&a->lock);
break;
}
lock_rw_unlock(&a->lock);
}
lock_rw_unlock(&az->rpz_lock);
if(raddr == NULL && z == NULL)
return NULL;
if(raddr != NULL) {
if(z) {
lock_rw_unlock(&z->lock);
}
return rpz_apply_nsip_trigger(ms, r, raddr, a);
}
return rpz_apply_nsdname_trigger(ms, r, z, &match, a);
}
struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms,
struct iter_qstate* is)
{
struct auth_zones* az;
struct auth_zone* a = NULL;
struct rpz* r = NULL;
struct local_zone* z = NULL;
enum localzone_type lzt;
struct dns_msg* ret = NULL;
if(ms->rpz_passthru) {
verbose(VERB_ALGO, "query is rpz_passthru, no further processing");
return NULL;
}
if(ms->env == NULL || ms->env->auth_zones == NULL) { return 0; }
az = ms->env->auth_zones;
lock_rw_rdlock(&az->rpz_lock);
for(a = az->rpz_first; a; a = a->rpz_az_next) {
lock_rw_rdlock(&a->lock);
r = a->rpz;
if(r->disabled) {
lock_rw_unlock(&a->lock);
continue;
}
z = rpz_find_zone(r->local_zones, is->qchase.qname,
is->qchase.qname_len, is->qchase.qclass, 0, 0, 0);
if(z && r->action_override == RPZ_DISABLED_ACTION) {
if(r->log)
log_rpz_apply("qname", z->name, NULL,
r->action_override,
&ms->qinfo, NULL, ms, r->log_name);
if(ms->env->worker)
ms->env->worker->stats.rpz_action[r->action_override]++;
lock_rw_unlock(&z->lock);
z = NULL;
}
if(z) {
break;
}
/* not found in this auth_zone */
lock_rw_unlock(&a->lock);
}
lock_rw_unlock(&az->rpz_lock);
if(z == NULL)
return NULL;
if(r->action_override == RPZ_NO_OVERRIDE_ACTION) {
lzt = z->type;
} else {
lzt = rpz_action_to_localzone_type(r->action_override);
}
if(verbosity >= VERB_ALGO) {
char nm[255+1], zn[255+1];
dname_str(is->qchase.qname, nm);
dname_str(z->name, zn);
if(strcmp(zn, nm) != 0)
verbose(VERB_ALGO, "rpz: qname trigger after cname %s on %s, with action=%s",
zn, nm, rpz_action_to_string(localzone_type_to_rpz_action(lzt)));
else
verbose(VERB_ALGO, "rpz: qname trigger after cname %s, with action=%s",
nm, rpz_action_to_string(localzone_type_to_rpz_action(lzt)));
}
switch(localzone_type_to_rpz_action(lzt)) {
case RPZ_NXDOMAIN_ACTION:
ret = rpz_synthesize_nxdomain(r, ms, &is->qchase, a);
break;
case RPZ_NODATA_ACTION:
ret = rpz_synthesize_nodata(r, ms, &is->qchase, a);
break;
case RPZ_TCP_ONLY_ACTION:
/* basically a passthru here but the tcp-only will be
* honored before the query gets sent. */
- ms->respip_action_info->action = respip_truncate;
+ ms->tcp_required = 1;
ret = NULL;
break;
case RPZ_DROP_ACTION:
ret = rpz_synthesize_nodata(r, ms, &is->qchase, a);
ms->is_drop = 1;
break;
case RPZ_LOCAL_DATA_ACTION:
ret = rpz_synthesize_qname_localdata_msg(r, ms, &is->qchase, z, a);
if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, &is->qchase, a); }
break;
case RPZ_PASSTHRU_ACTION:
ret = NULL;
ms->rpz_passthru = 1;
break;
default:
verbose(VERB_ALGO, "rpz: qname trigger after cname: bug: unhandled or invalid action: '%s'",
rpz_action_to_string(localzone_type_to_rpz_action(lzt)));
ret = NULL;
}
+ if(r->log)
+ log_rpz_apply("qname", (z?z->name:NULL), NULL,
+ localzone_type_to_rpz_action(lzt),
+ &is->qchase, NULL, ms, r->log_name);
lock_rw_unlock(&z->lock);
lock_rw_unlock(&a->lock);
return ret;
}
static int
rpz_apply_maybe_clientip_trigger(struct auth_zones* az, struct module_env* env,
struct query_info* qinfo, struct edns_data* edns, struct comm_reply* repinfo,
uint8_t* taglist, size_t taglen, struct ub_server_stats* stats,
sldns_buffer* buf, struct regional* temp,
/* output parameters */
struct local_zone** z_out, struct auth_zone** a_out, struct rpz** r_out,
int* passthru)
{
int ret = 0;
enum rpz_action client_action;
struct clientip_synthesized_rr* node = rpz_resolve_client_action_and_zone(
az, qinfo, repinfo, taglist, taglen, stats, z_out, a_out, r_out);
client_action = ((node == NULL) ? RPZ_INVALID_ACTION : node->action);
if(client_action == RPZ_PASSTHRU_ACTION) {
*passthru = 1;
}
if(*z_out == NULL || (client_action != RPZ_INVALID_ACTION &&
client_action != RPZ_PASSTHRU_ACTION)) {
if(client_action == RPZ_PASSTHRU_ACTION
|| client_action == RPZ_INVALID_ACTION
|| (client_action == RPZ_TCP_ONLY_ACTION
&& !rpz_is_udp_query(repinfo))) {
ret = 0;
goto done;
}
stats->rpz_action[client_action]++;
if(client_action == RPZ_LOCAL_DATA_ACTION) {
rpz_apply_clientip_localdata_action(node, env, qinfo,
edns, repinfo, buf, temp, *a_out);
} else {
if(*r_out && (*r_out)->log)
log_rpz_apply(
(node?"clientip":"qname"),
((*z_out)?(*z_out)->name:NULL),
(node?&node->node:NULL),
client_action, qinfo, repinfo, NULL,
(*r_out)->log_name);
local_zones_zone_answer(*z_out /*likely NULL, no zone*/, env, qinfo, edns,
repinfo, buf, temp, 0 /* no local data used */,
rpz_action_to_localzone_type(client_action));
if(*r_out && (*r_out)->signal_nxdomain_ra &&
LDNS_RCODE_WIRE(sldns_buffer_begin(buf))
== LDNS_RCODE_NXDOMAIN)
LDNS_RA_CLR(sldns_buffer_begin(buf));
}
ret = 1;
goto done;
}
ret = -1;
done:
if(node != NULL) {
lock_rw_unlock(&node->lock);
}
return ret;
}
int
rpz_callback_from_worker_request(struct auth_zones* az, struct module_env* env,
struct query_info* qinfo, struct edns_data* edns, sldns_buffer* buf,
struct regional* temp, struct comm_reply* repinfo, uint8_t* taglist,
size_t taglen, struct ub_server_stats* stats, int* passthru)
{
struct rpz* r = NULL;
struct auth_zone* a = NULL;
struct local_zone* z = NULL;
int ret;
enum localzone_type lzt;
int clientip_trigger = rpz_apply_maybe_clientip_trigger(az, env, qinfo,
edns, repinfo, taglist, taglen, stats, buf, temp, &z, &a, &r,
passthru);
if(clientip_trigger >= 0) {
if(a) {
lock_rw_unlock(&a->lock);
}
if(z) {
lock_rw_unlock(&z->lock);
}
return clientip_trigger;
}
if(z == NULL) {
if(a) {
lock_rw_unlock(&a->lock);
}
return 0;
}
log_assert(r);
if(r->action_override == RPZ_NO_OVERRIDE_ACTION) {
lzt = z->type;
} else {
lzt = rpz_action_to_localzone_type(r->action_override);
}
if(r->action_override == RPZ_PASSTHRU_ACTION ||
lzt == local_zone_always_transparent /* RPZ_PASSTHRU_ACTION */) {
*passthru = 1;
}
if(verbosity >= VERB_ALGO) {
char nm[255+1], zn[255+1];
dname_str(qinfo->qname, nm);
dname_str(z->name, zn);
if(strcmp(zn, nm) != 0)
verbose(VERB_ALGO, "rpz: qname trigger %s on %s with action=%s",
zn, nm, rpz_action_to_string(localzone_type_to_rpz_action(lzt)));
else
verbose(VERB_ALGO, "rpz: qname trigger %s with action=%s",
nm, rpz_action_to_string(localzone_type_to_rpz_action(lzt)));
}
ret = rpz_synthesize_qname_localdata(env, r, z, lzt, qinfo, edns, buf, temp,
repinfo, stats);
lock_rw_unlock(&z->lock);
lock_rw_unlock(&a->lock);
return ret;
}
void rpz_enable(struct rpz* r)
{
if(!r)
return;
r->disabled = 0;
}
void rpz_disable(struct rpz* r)
{
if(!r)
return;
r->disabled = 1;
}
diff --git a/contrib/unbound/sldns/str2wire.c b/contrib/unbound/sldns/str2wire.c
index 45e247613745..fdd40e0f2238 100644
--- a/contrib/unbound/sldns/str2wire.c
+++ b/contrib/unbound/sldns/str2wire.c
@@ -1,2864 +1,2865 @@
/**
* str2wire.c - read txt presentation of RRs
*
* (c) NLnet Labs, 2005-2006
*
* See the file LICENSE for the license
*/
/**
* \file
*
* Parses text to wireformat.
*/
#include "config.h"
#include "sldns/str2wire.h"
#include "sldns/wire2str.h"
#include "sldns/sbuffer.h"
#include "sldns/parse.h"
#include "sldns/parseutil.h"
#include <ctype.h>
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
/** bits for the offset */
#define RET_OFFSET_MASK (((unsigned)(~LDNS_WIREPARSE_MASK))>>LDNS_WIREPARSE_SHIFT)
/** return an error */
#define RET_ERR(e, off) ((int)(((e)&LDNS_WIREPARSE_MASK)|(((off)&RET_OFFSET_MASK)<<LDNS_WIREPARSE_SHIFT)))
/** Move parse error but keep its ID */
#define RET_ERR_SHIFT(e, move) RET_ERR(LDNS_WIREPARSE_ERROR(e), LDNS_WIREPARSE_OFFSET(e)+(move));
/*
* No special care is taken, all dots are translated into
* label separators.
* @param rel: true if the domain is not absolute (not terminated in .).
* The output is then still terminated with a '0' rootlabel.
*/
static int sldns_str2wire_dname_buf_rel(const char* str, uint8_t* buf,
size_t* olen, int* rel)
{
size_t len;
const char *s;
uint8_t *q, *pq, label_len;
if(rel) *rel = 0;
len = strlen((char*)str);
/* octet representation can make strings a lot longer than actual length */
if (len > LDNS_MAX_DOMAINLEN * 4) {
return RET_ERR(LDNS_WIREPARSE_ERR_DOMAINNAME_OVERFLOW, 0);
}
if (0 == len) {
return RET_ERR(LDNS_WIREPARSE_ERR_DOMAINNAME_UNDERFLOW, 0);
}
/* root label */
if (1 == len && *str == '.') {
if(*olen < 1)
return RET_ERR(LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL, 0);
buf[0] = 0;
*olen = 1;
return LDNS_WIREPARSE_ERR_OK;
}
/* get on with the rest */
/* s is on the current character in the string
* pq points to where the labellength is going to go
* label_len keeps track of the current label's length
* q builds the dname inside the buf array
*/
len = 0;
if(*olen < 1)
return RET_ERR(LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL, 0);
q = buf+1;
pq = buf;
label_len = 0;
for (s = str; *s; s++, q++) {
if (q >= buf + *olen)
return RET_ERR(LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL, q-buf);
if (q >= buf + LDNS_MAX_DOMAINLEN)
return RET_ERR(LDNS_WIREPARSE_ERR_DOMAINNAME_OVERFLOW, q-buf);
switch (*s) {
case '.':
if (label_len > LDNS_MAX_LABELLEN) {
return RET_ERR(LDNS_WIREPARSE_ERR_LABEL_OVERFLOW, q-buf);
}
if (label_len == 0) {
return RET_ERR(LDNS_WIREPARSE_ERR_EMPTY_LABEL, q-buf);
}
len += label_len + 1;
*q = 0;
*pq = label_len;
label_len = 0;
pq = q;
break;
case '\\':
/* octet value or literal char */
s += 1;
if (!sldns_parse_escape(q, &s)) {
*q = 0;
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_BAD_ESCAPE, q-buf);
}
s -= 1;
label_len++;
break;
default:
*q = (uint8_t)*s;
label_len++;
}
}
/* add root label if last char was not '.' */
if(label_len != 0) {
if(rel) *rel = 1;
if (q >= buf + *olen)
return RET_ERR(LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL, q-buf);
if (q >= buf + LDNS_MAX_DOMAINLEN) {
return RET_ERR(LDNS_WIREPARSE_ERR_DOMAINNAME_OVERFLOW, q-buf);
}
if (label_len > LDNS_MAX_LABELLEN) {
return RET_ERR(LDNS_WIREPARSE_ERR_LABEL_OVERFLOW, q-buf);
}
if (label_len == 0) { /* label_len 0 but not . at end? */
return RET_ERR(LDNS_WIREPARSE_ERR_EMPTY_LABEL, q-buf);
}
len += label_len + 1;
*pq = label_len;
*q = 0;
}
len++;
*olen = len;
return LDNS_WIREPARSE_ERR_OK;
}
int sldns_str2wire_dname_buf(const char* str, uint8_t* buf, size_t* len)
{
return sldns_str2wire_dname_buf_rel(str, buf, len, NULL);
}
int sldns_str2wire_dname_buf_origin(const char* str, uint8_t* buf, size_t* len,
uint8_t* origin, size_t origin_len)
{
size_t dlen = *len;
int rel = 0;
int s = sldns_str2wire_dname_buf_rel(str, buf, &dlen, &rel);
if(s) return s;
if(rel && origin && dlen > 0) {
if((unsigned)dlen >= 0x00ffffffU ||
(unsigned)origin_len >= 0x00ffffffU)
/* guard against integer overflow in addition */
return RET_ERR(LDNS_WIREPARSE_ERR_GENERAL, *len);
if(dlen + origin_len - 1 > LDNS_MAX_DOMAINLEN)
return RET_ERR(LDNS_WIREPARSE_ERR_DOMAINNAME_OVERFLOW,
LDNS_MAX_DOMAINLEN);
if(dlen + origin_len - 1 > *len)
return RET_ERR(LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL,
*len);
memmove(buf+dlen-1, origin, origin_len);
*len = dlen + origin_len - 1;
} else
*len = dlen;
return LDNS_WIREPARSE_ERR_OK;
}
uint8_t* sldns_str2wire_dname(const char* str, size_t* len)
{
uint8_t dname[LDNS_MAX_DOMAINLEN+1];
*len = sizeof(dname);
if(sldns_str2wire_dname_buf(str, dname, len) == 0) {
uint8_t* r;
if(*len > sizeof(dname)) return NULL;
r = (uint8_t*)malloc(*len);
if(r) return memcpy(r, dname, *len);
}
*len = 0;
return NULL;
}
/** read owner name */
static int
rrinternal_get_owner(sldns_buffer* strbuf, uint8_t* rr, size_t* len,
size_t* dname_len, uint8_t* origin, size_t origin_len, uint8_t* prev,
size_t prev_len, char* token, size_t token_len)
{
/* split the rr in its parts -1 signals trouble */
if(sldns_bget_token(strbuf, token, "\t\n ", token_len) == -1) {
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX,
sldns_buffer_position(strbuf));
}
if(token_len < 2) /* make sure there is space to read "@" or "" */
return RET_ERR(LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL,
sldns_buffer_position(strbuf));
if(token[0]=='@' && token[1]=='\0') {
uint8_t* tocopy;
if (origin) {
*dname_len = origin_len;
tocopy = origin;
} else if (prev) {
*dname_len = prev_len;
tocopy = prev;
} else {
/* default to root */
*dname_len = 1;
tocopy = (uint8_t*)"\0";
}
if(*len < *dname_len)
return RET_ERR(LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL,
sldns_buffer_position(strbuf));
memmove(rr, tocopy, *dname_len);
} else if(*token == '\0') {
/* no ownername was given, try prev, if that fails
* origin, else default to root */
uint8_t* tocopy;
if(prev) {
*dname_len = prev_len;
tocopy = prev;
} else if(origin) {
*dname_len = origin_len;
tocopy = origin;
} else {
*dname_len = 1;
tocopy = (uint8_t*)"\0";
}
if(*len < *dname_len)
return RET_ERR(LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL,
sldns_buffer_position(strbuf));
memmove(rr, tocopy, *dname_len);
} else {
size_t dlen = *len;
int s = sldns_str2wire_dname_buf_origin(token, rr, &dlen,
origin, origin_len);
if(s) return RET_ERR_SHIFT(s,
sldns_buffer_position(strbuf)-strlen(token));
*dname_len = dlen;
}
return LDNS_WIREPARSE_ERR_OK;
}
/** read ttl */
static int
rrinternal_get_ttl(sldns_buffer* strbuf, char* token, size_t token_len,
int* not_there, uint32_t* ttl, uint32_t default_ttl)
{
const char* endptr;
int overflow;
if(sldns_bget_token(strbuf, token, "\t\n ", token_len) == -1) {
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_TTL,
sldns_buffer_position(strbuf));
}
*ttl = (uint32_t) sldns_str2period(token, &endptr, &overflow);
if(overflow) {
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_INTEGER_OVERFLOW,
sldns_buffer_position(strbuf));
}
if (strlen(token) > 0 && !isdigit((unsigned char)token[0])) {
*not_there = 1;
/* ah, it's not there or something */
if (default_ttl == 0) {
*ttl = LDNS_DEFAULT_TTL;
} else {
*ttl = default_ttl;
}
}
return LDNS_WIREPARSE_ERR_OK;
}
/** read class */
static int
rrinternal_get_class(sldns_buffer* strbuf, char* token, size_t token_len,
int* not_there, uint16_t* cl)
{
/* if 'not_there' then we got token from previous parse routine */
if(!*not_there) {
/* parse new token for class */
if(sldns_bget_token(strbuf, token, "\t\n ", token_len) == -1) {
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_CLASS,
sldns_buffer_position(strbuf));
}
} else *not_there = 0;
*cl = sldns_get_rr_class_by_name(token);
/* class can be left out too, assume IN, current token must be type */
if(*cl == 0 && strcmp(token, "CLASS0") != 0) {
*not_there = 1;
*cl = LDNS_RR_CLASS_IN;
}
return LDNS_WIREPARSE_ERR_OK;
}
/** read type */
static int
rrinternal_get_type(sldns_buffer* strbuf, char* token, size_t token_len,
int* not_there, uint16_t* tp)
{
/* if 'not_there' then we got token from previous parse routine */
if(!*not_there) {
/* parse new token for type */
if(sldns_bget_token(strbuf, token, "\t\n ", token_len) == -1) {
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_TYPE,
sldns_buffer_position(strbuf));
}
}
*tp = sldns_get_rr_type_by_name(token);
if(*tp == 0 && strcmp(token, "TYPE0") != 0) {
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_TYPE,
sldns_buffer_position(strbuf));
}
return LDNS_WIREPARSE_ERR_OK;
}
/** put type, class, ttl into rr buffer */
static int
rrinternal_write_typeclassttl(sldns_buffer* strbuf, uint8_t* rr, size_t len,
size_t dname_len, uint16_t tp, uint16_t cl, uint32_t ttl, int question)
{
if(question) {
/* question is : name, type, class */
if(dname_len + 4 > len)
return RET_ERR(LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL,
sldns_buffer_position(strbuf));
sldns_write_uint16(rr+dname_len, tp);
sldns_write_uint16(rr+dname_len+2, cl);
return LDNS_WIREPARSE_ERR_OK;
}
/* type(2), class(2), ttl(4), rdatalen(2 (later)) = 10 */
if(dname_len + 10 > len)
return RET_ERR(LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL,
sldns_buffer_position(strbuf));
sldns_write_uint16(rr+dname_len, tp);
sldns_write_uint16(rr+dname_len+2, cl);
sldns_write_uint32(rr+dname_len+4, ttl);
sldns_write_uint16(rr+dname_len+8, 0); /* rdatalen placeholder */
return LDNS_WIREPARSE_ERR_OK;
}
/** find delimiters for type */
static const char*
rrinternal_get_delims(sldns_rdf_type rdftype, size_t r_cnt, size_t r_max)
{
switch(rdftype) {
case LDNS_RDF_TYPE_B64 :
case LDNS_RDF_TYPE_HEX : /* These rdf types may con- */
case LDNS_RDF_TYPE_LOC : /* tain whitespace, only if */
case LDNS_RDF_TYPE_WKS : /* it is the last rd field. */
case LDNS_RDF_TYPE_IPSECKEY :
case LDNS_RDF_TYPE_NSEC : if (r_cnt == r_max - 1) {
return "\n";
}
break;
default : break;
}
return "\n\t ";
}
/* Syntactic sugar for sldns_rr_new_frm_str_internal */
static int
sldns_rdf_type_maybe_quoted(sldns_rdf_type rdf_type)
{
return rdf_type == LDNS_RDF_TYPE_STR ||
rdf_type == LDNS_RDF_TYPE_LONG_STR;
}
/** see if rdata is quoted */
static int
rrinternal_get_quoted(sldns_buffer* strbuf, const char** delimiters,
sldns_rdf_type rdftype)
{
if(sldns_rdf_type_maybe_quoted(rdftype) &&
sldns_buffer_remaining(strbuf) > 0) {
/* skip spaces */
while(sldns_buffer_remaining(strbuf) > 0 &&
(*(sldns_buffer_current(strbuf)) == ' ' ||
*(sldns_buffer_current(strbuf)) == '\t')) {
sldns_buffer_skip(strbuf, 1);
}
if(sldns_buffer_remaining(strbuf) > 0 &&
*(sldns_buffer_current(strbuf)) == '\"') {
*delimiters = "\"\0";
sldns_buffer_skip(strbuf, 1);
return 1;
}
}
return 0;
}
/** spool hex data into rdata */
static int
rrinternal_spool_hex(char* token, uint8_t* rr, size_t rr_len,
size_t rr_cur_len, size_t* cur_hex_data_size, size_t hex_data_size)
{
char* p = token;
while(*p) {
if(isspace((unsigned char)*p)) {
p++;
continue;
}
if(!isxdigit((unsigned char)*p))
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_RDATA,
p-token);
if(*cur_hex_data_size >= hex_data_size)
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_RDATA,
p-token);
/* extra robust check */
if(rr_cur_len+(*cur_hex_data_size)/2 >= rr_len)
return RET_ERR(LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL,
p-token);
/* see if 16s or 1s */
if( ((*cur_hex_data_size)&1) == 0) {
rr[rr_cur_len+(*cur_hex_data_size)/2] =
(uint8_t)sldns_hexdigit_to_int(*p)*16;
} else {
rr[rr_cur_len+(*cur_hex_data_size)/2] +=
(uint8_t)sldns_hexdigit_to_int(*p);
}
p++;
(*cur_hex_data_size)++;
}
return LDNS_WIREPARSE_ERR_OK;
}
/** read unknown rr type format */
static int
rrinternal_parse_unknown(sldns_buffer* strbuf, char* token, size_t token_len,
uint8_t* rr, size_t* rr_len, size_t* rr_cur_len, size_t pre_data_pos)
{
const char* delim = "\n\t ";
size_t hex_data_size, cur_hex_data_size;
/* go back to before \#
* and skip it while setting delimiters better
*/
sldns_buffer_set_position(strbuf, pre_data_pos);
if(sldns_bget_token(strbuf, token, delim, token_len) == -1)
return LDNS_WIREPARSE_ERR_GENERAL; /* should not fail */
/* read rdata octet length */
if(sldns_bget_token(strbuf, token, delim, token_len) == -1) {
/* something goes very wrong here */
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_RDATA,
sldns_buffer_position(strbuf));
}
hex_data_size = (size_t)atoi(token);
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));
}
/* copy hex chars into hex str (2 chars per byte) */
hex_data_size *= 2;
cur_hex_data_size = 0;
while(cur_hex_data_size < hex_data_size) {
int status;
ssize_t c = sldns_bget_token(strbuf, token, delim, token_len);
if((status = rrinternal_spool_hex(token, rr, *rr_len,
*rr_cur_len, &cur_hex_data_size, hex_data_size)) != 0)
return RET_ERR_SHIFT(status,
sldns_buffer_position(strbuf)-strlen(token));
if(c == -1) {
if(cur_hex_data_size != hex_data_size)
return RET_ERR(
LDNS_WIREPARSE_ERR_SYNTAX_RDATA,
sldns_buffer_position(strbuf));
break;
}
}
*rr_cur_len += hex_data_size/2;
return LDNS_WIREPARSE_ERR_OK;
}
/** parse normal RR rdata element */
static int
rrinternal_parse_rdf(sldns_buffer* strbuf, char* token, size_t token_len,
uint8_t* rr, size_t rr_len, size_t* rr_cur_len, sldns_rdf_type rdftype,
uint16_t rr_type, size_t r_cnt, size_t r_max, size_t dname_len,
uint8_t* origin, size_t origin_len)
{
size_t len;
int status;
switch(rdftype) {
case LDNS_RDF_TYPE_DNAME:
/* check if the origin should be used or concatenated */
if(strcmp(token, "@") == 0) {
uint8_t* tocopy;
size_t copylen;
if(origin) {
copylen = origin_len;
tocopy = origin;
} else if(rr_type == LDNS_RR_TYPE_SOA) {
copylen = dname_len;
tocopy = rr; /* copy rr owner name */
} else {
copylen = 1;
tocopy = (uint8_t*)"\0";
}
if((*rr_cur_len) + copylen > rr_len)
return RET_ERR(
LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL,
sldns_buffer_position(strbuf));
memmove(rr+*rr_cur_len, tocopy, copylen);
(*rr_cur_len) += copylen;
} else {
size_t dlen = rr_len - (*rr_cur_len);
int s = sldns_str2wire_dname_buf_origin(token,
rr+*rr_cur_len, &dlen, origin, origin_len);
if(s) return RET_ERR_SHIFT(s,
sldns_buffer_position(strbuf)-strlen(token));
(*rr_cur_len) += dlen;
}
return LDNS_WIREPARSE_ERR_OK;
case LDNS_RDF_TYPE_HEX:
case LDNS_RDF_TYPE_B64:
/* When this is the last rdata field, then the
* rest should be read in (cause then these
* rdf types may contain spaces). */
if(r_cnt == r_max - 1) {
size_t tlen = strlen(token);
(void)sldns_bget_token(strbuf, token+tlen, "\n",
token_len - tlen);
}
break;
default:
break;
}
len = rr_len - (*rr_cur_len);
if((status=sldns_str2wire_rdf_buf(token, rr+(*rr_cur_len), &len,
rdftype)) != 0)
return RET_ERR_SHIFT(status,
sldns_buffer_position(strbuf)-strlen(token));
*rr_cur_len += len;
return LDNS_WIREPARSE_ERR_OK;
}
/**
* Parse one rdf token. Takes care of quotes and parenthesis.
*/
static int
sldns_parse_rdf_token(sldns_buffer* strbuf, char* token, size_t token_len,
int* quoted, int* parens, size_t* pre_data_pos,
const char* delimiters, sldns_rdf_type rdftype, size_t* token_strlen)
{
size_t slen;
/* skip spaces and tabs */
while(sldns_buffer_remaining(strbuf) > 0 && !*quoted &&
(*(sldns_buffer_current(strbuf)) == ' ' ||
*(sldns_buffer_current(strbuf)) == '\t')) {
sldns_buffer_skip(strbuf, 1);
}
*pre_data_pos = sldns_buffer_position(strbuf);
if(sldns_bget_token_par(strbuf, token, (*quoted)?"\"":delimiters,
token_len, parens, (*quoted)?NULL:" \t") == -1) {
return 0;
}
slen = strlen(token);
/* 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[slen-1] == '"' || token[slen-1] == '\'')) {
/* move token two smaller (quotes) with endnull */
memmove(token, token+1, slen-2);
token[slen-2] = 0;
slen -= 2;
*quoted = 1;
} else if(!*quoted && sldns_rdf_type_maybe_quoted(rdftype) &&
slen >= 2 &&
(token[0] == '"' || token[0] == '\'')) {
/* got the start quote (remove it) but read remainder
* of quoted string as well into remainder of token */
memmove(token, token+1, slen-1);
token[slen-1] = 0;
slen -= 1;
*quoted = 1;
/* rewind buffer over skipped whitespace */
while(sldns_buffer_position(strbuf) > 0 &&
(sldns_buffer_current(strbuf)[-1] == ' ' ||
sldns_buffer_current(strbuf)[-1] == '\t')) {
sldns_buffer_skip(strbuf, -1);
}
if(sldns_bget_token_par(strbuf, token+slen,
"\"", token_len-slen,
parens, NULL) == -1) {
return 0;
}
slen = strlen(token);
}
*token_strlen = slen;
return 1;
}
/** Add space and one more rdf token onto the existing token string. */
static int
sldns_affix_token(sldns_buffer* strbuf, char* token, size_t* token_len,
int* quoted, int* parens, size_t* pre_data_pos,
const char* delimiters, sldns_rdf_type rdftype, size_t* token_strlen)
{
size_t addlen = *token_len - *token_strlen;
size_t addstrlen = 0;
/* add space */
/* when addlen < 2, the token buffer is full considering the NULL byte
* from strlen and will lead to buffer overflow with the second
* assignment below. */
if(addlen < 2) return 0;
token[*token_strlen] = ' ';
token[++(*token_strlen)] = 0;
/* read another token */
addlen = *token_len - *token_strlen;
if(!sldns_parse_rdf_token(strbuf, token+*token_strlen, addlen, quoted,
parens, pre_data_pos, delimiters, rdftype, &addstrlen))
return 0;
(*token_strlen) += addstrlen;
return 1;
}
static int sldns_str2wire_svcparam_key_cmp(const void *a, const void *b)
{
return sldns_read_uint16(*(uint8_t**) a)
- sldns_read_uint16(*(uint8_t**) b);
}
/**
* Add constraints to the SVCB RRs which involve the whole set
*/
static int sldns_str2wire_check_svcbparams(uint8_t* rdata, uint16_t rdata_len)
{
size_t nparams = 0, i;
uint8_t new_rdata[LDNS_MAX_RDFLEN];
uint8_t* new_rdata_ptr = new_rdata;
uint8_t* svcparams[MAX_NUMBER_OF_SVCPARAMS];
uint8_t* rdata_ptr = rdata;
uint16_t rdata_remaining = rdata_len;
/* find the SvcParams */
while (rdata_remaining) {
uint16_t svcbparam_len;
svcparams[nparams] = rdata_ptr;
if (rdata_remaining < 4)
return LDNS_WIREPARSE_ERR_SVCPARAM_BROKEN_RDATA;
svcbparam_len = sldns_read_uint16(rdata_ptr + 2);
rdata_remaining -= 4;
rdata_ptr += 4;
if (rdata_remaining < svcbparam_len)
return LDNS_WIREPARSE_ERR_SVCPARAM_BROKEN_RDATA;
rdata_remaining -= svcbparam_len;
rdata_ptr += svcbparam_len;
nparams += 1;
if (nparams >= MAX_NUMBER_OF_SVCPARAMS)
return LDNS_WIREPARSE_ERR_SVCB_TOO_MANY_PARAMS;
}
/* In draft-ietf-dnsop-svcb-https-06 Section 7:
*
* In wire format, the keys are represented by their numeric
* values in network byte order, concatenated in ascending order.
*/
qsort((void *)svcparams
,nparams
,sizeof(uint8_t*)
,sldns_str2wire_svcparam_key_cmp);
/* The code below revolves around semantic errors in the SVCParam set.
* So long as we do not distinguish between running Unbound as a primary
* or as a secondary, we default to secondary behavior and we ignore the
* semantic errors. */
#ifdef SVCB_SEMANTIC_ERRORS
{
uint8_t* mandatory = NULL;
/* In draft-ietf-dnsop-svcb-https-06 Section 7:
*
* Keys (...) MUST NOT appear more than once.
*
* If they key has already been seen, we have a duplicate
*/
for(i=0; i < nparams; i++) {
uint16_t key = sldns_read_uint16(svcparams[i]);
if(i + 1 < nparams && key == sldns_read_uint16(svcparams[i+1]))
return LDNS_WIREPARSE_ERR_SVCB_DUPLICATE_KEYS;
if(key == SVCB_KEY_MANDATORY)
mandatory = svcparams[i];
}
/* 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);
/* Guaranteed by sldns_str2wire_svcparam_key_value */
assert(mandatory_nkeys > 0);
for(i=0; i < mandatory_nkeys; i++) {
uint16_t mandatory_key = sldns_read_uint16(
mandatory
+ 2 * sizeof(uint16_t)
+ i * sizeof(uint16_t));
uint8_t found = 0;
size_t j;
for(j=0; j < nparams; j++) {
if(mandatory_key == sldns_read_uint16(svcparams[j])) {
found = 1;
break;
}
}
if(!found)
return LDNS_WIREPARSE_ERR_SVCB_MANDATORY_MISSING_PARAM;
}
}
}
#endif
/* Write rdata in correct order */
for (i = 0; i < nparams; i++) {
uint16_t svcparam_len = sldns_read_uint16(svcparams[i] + 2)
+ 2 * sizeof(uint16_t);
if ((unsigned)(new_rdata_ptr - new_rdata) + svcparam_len > sizeof(new_rdata))
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
memcpy(new_rdata_ptr, svcparams[i], svcparam_len);
new_rdata_ptr += svcparam_len;
}
memcpy(rdata, new_rdata, rdata_len);
return LDNS_WIREPARSE_ERR_OK;
}
/** parse rdata from string into rr buffer(-remainder after dname). */
static int
rrinternal_parse_rdata(sldns_buffer* strbuf, char* token, size_t token_len,
uint8_t* rr, size_t* rr_len, size_t dname_len, uint16_t rr_type,
uint8_t* origin, size_t origin_len)
{
const sldns_rr_descriptor *desc = sldns_rr_descript((uint16_t)rr_type);
size_t r_cnt, r_min, r_max;
size_t rr_cur_len = dname_len + 10, pre_data_pos, token_strlen;
int was_unknown_rr_format = 0, parens = 0, status, quoted;
const char* delimiters;
sldns_rdf_type rdftype;
/* a desc is always returned */
if(!desc) return LDNS_WIREPARSE_ERR_GENERAL;
r_max = sldns_rr_descriptor_maximum(desc);
r_min = sldns_rr_descriptor_minimum(desc);
/* robust check */
if(rr_cur_len > *rr_len)
return RET_ERR(LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL,
sldns_buffer_position(strbuf));
/* because number of fields can be variable, we can't rely on
* _maximum() only */
for(r_cnt=0; r_cnt < r_max; r_cnt++) {
rdftype = sldns_rr_descriptor_field_type(desc, r_cnt);
delimiters = rrinternal_get_delims(rdftype, r_cnt, r_max);
quoted = rrinternal_get_quoted(strbuf, &delimiters, rdftype);
if(!sldns_parse_rdf_token(strbuf, token, token_len, &quoted,
&parens, &pre_data_pos, delimiters, rdftype,
&token_strlen))
break;
/* rfc3597 specifies that any type can be represented
* with \# method, which can contain spaces...
* it does specify size though... */
/* unknown RR data */
if(token_strlen>=2 && strncmp(token, "\\#", 2) == 0 &&
!quoted && (token_strlen == 2 || token[2]==' ' ||
token[2]=='\t')) {
was_unknown_rr_format = 1;
if((status=rrinternal_parse_unknown(strbuf, token,
token_len, rr, rr_len, &rr_cur_len,
pre_data_pos)) != 0)
return status;
} else if(token_strlen > 0 || quoted) {
if(rdftype == LDNS_RDF_TYPE_HIP) {
/* affix the HIT and PK fields, with a space */
if(!sldns_affix_token(strbuf, token,
&token_len, &quoted, &parens,
&pre_data_pos, delimiters,
rdftype, &token_strlen))
break;
if(!sldns_affix_token(strbuf, token,
&token_len, &quoted, &parens,
&pre_data_pos, delimiters,
rdftype, &token_strlen))
break;
} else if(rdftype == LDNS_RDF_TYPE_INT16_DATA &&
strcmp(token, "0")!=0) {
/* affix len and b64 fields */
if(!sldns_affix_token(strbuf, token,
&token_len, &quoted, &parens,
&pre_data_pos, delimiters,
rdftype, &token_strlen))
break;
}
/* normal RR */
if((status=rrinternal_parse_rdf(strbuf, token,
token_len, rr, *rr_len, &rr_cur_len, rdftype,
rr_type, r_cnt, r_max, dname_len, origin,
origin_len)) != 0) {
return status;
}
}
}
if(!was_unknown_rr_format && r_cnt+1 < r_min) {
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_MISSING_VALUE,
sldns_buffer_position(strbuf));
}
while(parens != 0) {
/* read remainder, must be "" */
if(sldns_bget_token_par(strbuf, token, "\n", token_len,
&parens, " \t") == -1) {
if(parens != 0)
return RET_ERR(LDNS_WIREPARSE_ERR_PARENTHESIS,
sldns_buffer_position(strbuf));
break;
}
if(strcmp(token, "") != 0)
return RET_ERR(LDNS_WIREPARSE_ERR_PARENTHESIS,
sldns_buffer_position(strbuf));
}
/* write rdata length */
sldns_write_uint16(rr+dname_len+8, (uint16_t)(rr_cur_len-dname_len-10));
*rr_len = rr_cur_len;
/* SVCB/HTTPS handling */
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;
rdata_len -= sizeof(uint16_t);
rdata += sizeof(uint16_t);
/* skip 2nd rdata field dname */
while (rdata_len && *rdata != 0) {
uint8_t label_len;
if (*rdata & 0xC0)
return LDNS_WIREPARSE_ERR_OK;
label_len = *rdata + 1;
if (rdata_len < label_len)
return LDNS_WIREPARSE_ERR_OK;
rdata_len -= label_len;
rdata += label_len;
}
/* The root label is one more character, so smaller
* than 1 + 1 means no Svcparam Keys */
if (rdata_len < 2 || *rdata != 0)
return LDNS_WIREPARSE_ERR_OK;
rdata_len -= 1;
rdata += 1;
return sldns_str2wire_check_svcbparams(rdata, rdata_len);
}
return LDNS_WIREPARSE_ERR_OK;
}
/*
* trailing spaces are allowed
* leading spaces are not allowed
* allow ttl to be optional
* class is optional too
* if ttl is missing, and default_ttl is 0, use DEF_TTL
* allow ttl to be written as 1d3h
* So the RR should look like. e.g.
* miek.nl. 3600 IN MX 10 elektron.atoom.net
* or
* miek.nl. 1h IN MX 10 elektron.atoom.net
* or
* miek.nl. IN MX 10 elektron.atoom.net
*/
static int
sldns_str2wire_rr_buf_internal(const char* str, uint8_t* rr, size_t* len,
size_t* dname_len, uint32_t default_ttl, uint8_t* origin,
size_t origin_len, uint8_t* prev, size_t prev_len, int question)
{
int status;
int not_there = 0;
char token[LDNS_MAX_RDFLEN+1];
uint32_t ttl = 0;
uint16_t tp = 0, cl = 0;
size_t ddlen = 0;
/* string in buffer */
sldns_buffer strbuf;
sldns_buffer_init_frm_data(&strbuf, (uint8_t*)str, strlen(str));
if(!dname_len) dname_len = &ddlen;
/* parse the owner */
if((status=rrinternal_get_owner(&strbuf, rr, len, dname_len, origin,
origin_len, prev, prev_len, token, sizeof(token))) != 0)
return status;
/* parse the [ttl] [class] <type> */
if((status=rrinternal_get_ttl(&strbuf, token, sizeof(token),
&not_there, &ttl, default_ttl)) != 0)
return status;
if((status=rrinternal_get_class(&strbuf, token, sizeof(token),
&not_there, &cl)) != 0)
return status;
if((status=rrinternal_get_type(&strbuf, token, sizeof(token),
&not_there, &tp)) != 0)
return status;
/* put ttl, class, type into the rr result */
if((status=rrinternal_write_typeclassttl(&strbuf, rr, *len, *dname_len, tp, cl,
ttl, question)) != 0)
return status;
/* for a question-RR we are done, no rdata */
if(question) {
*len = *dname_len + 4;
return LDNS_WIREPARSE_ERR_OK;
}
/* rdata */
if((status=rrinternal_parse_rdata(&strbuf, token, sizeof(token),
rr, len, *dname_len, tp, origin, origin_len)) != 0)
return status;
return LDNS_WIREPARSE_ERR_OK;
}
int sldns_str2wire_rr_buf(const char* str, uint8_t* rr, size_t* len,
size_t* dname_len, uint32_t default_ttl, uint8_t* origin,
size_t origin_len, uint8_t* prev, size_t prev_len)
{
return sldns_str2wire_rr_buf_internal(str, rr, len, dname_len,
default_ttl, origin, origin_len, prev, prev_len, 0);
}
int sldns_str2wire_rr_question_buf(const char* str, uint8_t* rr, size_t* len,
size_t* dname_len, uint8_t* origin, size_t origin_len, uint8_t* prev,
size_t prev_len)
{
return sldns_str2wire_rr_buf_internal(str, rr, len, dname_len,
0, origin, origin_len, prev, prev_len, 1);
}
uint16_t sldns_wirerr_get_type(uint8_t* rr, size_t len, size_t dname_len)
{
if(len < dname_len+2)
return 0;
return sldns_read_uint16(rr+dname_len);
}
uint16_t sldns_wirerr_get_class(uint8_t* rr, size_t len, size_t dname_len)
{
if(len < dname_len+4)
return 0;
return sldns_read_uint16(rr+dname_len+2);
}
uint32_t sldns_wirerr_get_ttl(uint8_t* rr, size_t len, size_t dname_len)
{
if(len < dname_len+8)
return 0;
return sldns_read_uint32(rr+dname_len+4);
}
uint16_t sldns_wirerr_get_rdatalen(uint8_t* rr, size_t len, size_t dname_len)
{
if(len < dname_len+10)
return 0;
return sldns_read_uint16(rr+dname_len+8);
}
uint8_t* sldns_wirerr_get_rdata(uint8_t* rr, size_t len, size_t dname_len)
{
if(len < dname_len+10)
return NULL;
return rr+dname_len+10;
}
uint8_t* sldns_wirerr_get_rdatawl(uint8_t* rr, size_t len, size_t dname_len)
{
if(len < dname_len+10)
return NULL;
return rr+dname_len+8;
}
const char* sldns_get_errorstr_parse(int e)
{
sldns_lookup_table *lt;
lt = sldns_lookup_by_id(sldns_wireparse_errors, LDNS_WIREPARSE_ERROR(e));
return lt?lt->name:"unknown error";
}
/* Strip whitespace from the start and the end of <line>. */
char *
sldns_strip_ws(char *line)
{
char *s = line, *e;
for (s = line; *s && isspace((unsigned char)*s); s++)
;
for (e = strchr(s, 0); e > s+2 && isspace((unsigned char)e[-1]) && e[-2] != '\\'; e--)
;
*e = 0;
return s;
}
int sldns_fp2wire_rr_buf(FILE* in, uint8_t* rr, size_t* len, size_t* dname_len,
struct sldns_file_parse_state* parse_state)
{
char line[LDNS_RR_BUF_SIZE+1];
ssize_t size;
/* read an entire line in from the file */
if((size = sldns_fget_token_l(in, line, LDNS_PARSE_SKIP_SPACE,
LDNS_RR_BUF_SIZE, parse_state?&parse_state->lineno:NULL))
== -1) {
/* if last line was empty, we are now at feof, which is not
* always a parse error (happens when for instance last line
* was a comment)
*/
return LDNS_WIREPARSE_ERR_SYNTAX;
}
/* we can have the situation, where we've read ok, but still got
* no bytes to play with, in this case size is 0 */
if(size == 0) {
if(*len > 0)
rr[0] = 0;
*len = 0;
*dname_len = 0;
return LDNS_WIREPARSE_ERR_OK;
}
if(strncmp(line, "$ORIGIN", 7) == 0 && isspace((unsigned char)line[7])) {
int s;
strlcpy((char*)rr, line, *len);
*len = 0;
*dname_len = 0;
if(!parse_state) return LDNS_WIREPARSE_ERR_OK;
parse_state->origin_len = sizeof(parse_state->origin);
s = sldns_str2wire_dname_buf(sldns_strip_ws(line+8),
parse_state->origin, &parse_state->origin_len);
if(s) parse_state->origin_len = 0;
return s;
} else if(strncmp(line, "$TTL", 4) == 0 && isspace((unsigned char)line[4])) {
const char* end = NULL;
int overflow = 0;
strlcpy((char*)rr, line, *len);
*len = 0;
*dname_len = 0;
if(!parse_state) return LDNS_WIREPARSE_ERR_OK;
parse_state->default_ttl = sldns_str2period(
sldns_strip_ws(line+5), &end, &overflow);
if(overflow)
return LDNS_WIREPARSE_ERR_SYNTAX_INTEGER_OVERFLOW;
} else if (strncmp(line, "$INCLUDE", 8) == 0) {
strlcpy((char*)rr, line, *len);
*len = 0;
*dname_len = 0;
return LDNS_WIREPARSE_ERR_INCLUDE;
} else if (strncmp(line, "$", 1) == 0) {
strlcpy((char*)rr, line, *len);
*len = 0;
*dname_len = 0;
return LDNS_WIREPARSE_ERR_INCLUDE;
} else {
int r = sldns_str2wire_rr_buf(line, rr, len, dname_len,
parse_state?parse_state->default_ttl:0,
(parse_state&&parse_state->origin_len)?
parse_state->origin:NULL,
parse_state?parse_state->origin_len:0,
(parse_state&&parse_state->prev_rr_len)?
parse_state->prev_rr:NULL,
parse_state?parse_state->prev_rr_len:0);
if(r == LDNS_WIREPARSE_ERR_OK && (*dname_len) != 0 &&
parse_state &&
(*dname_len) <= sizeof(parse_state->prev_rr)) {
memmove(parse_state->prev_rr, rr, *dname_len);
parse_state->prev_rr_len = (*dname_len);
}
if(r == LDNS_WIREPARSE_ERR_OK && parse_state) {
parse_state->default_ttl = sldns_wirerr_get_ttl(
rr, *len, *dname_len);
}
return r;
}
return LDNS_WIREPARSE_ERR_OK;
}
static int
sldns_str2wire_svcparam_key_lookup(const char *key, size_t key_len)
{
char buf[64];
char *endptr;
unsigned long int key_value;
if (key_len >= 4 && key_len <= 8 && !strncmp(key, "key", 3)) {
memcpy(buf, key + 3, key_len - 3);
buf[key_len - 3] = 0;
key_value = strtoul(buf, &endptr, 10);
if (endptr > buf /* digits seen */
&& *endptr == 0 /* no non-digit chars after digits */
&& key_value <= 65535) /* no overflow */
return key_value;
} else switch (key_len) {
case 3:
if (!strncmp(key, "ech", key_len))
return SVCB_KEY_ECH;
break;
case 4:
if (!strncmp(key, "alpn", key_len))
return SVCB_KEY_ALPN;
if (!strncmp(key, "port", key_len))
return SVCB_KEY_PORT;
break;
case 7:
if (!strncmp(key, "dohpath", key_len))
return SVCB_KEY_DOHPATH;
break;
case 8:
if (!strncmp(key, "ipv4hint", key_len))
return SVCB_KEY_IPV4HINT;
if (!strncmp(key, "ipv6hint", key_len))
return SVCB_KEY_IPV6HINT;
break;
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:
break;
}
/* Although the returned value might be used by the caller,
* the parser has erred, so the zone will not be loaded.
*/
return -1;
}
static int
sldns_str2wire_svcparam_port(const char* val, uint8_t* rd, size_t* rd_len)
{
unsigned long int port;
char *endptr;
if (*rd_len < 6)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
port = strtoul(val, &endptr, 10);
if (endptr > val /* digits seen */
&& *endptr == 0 /* no non-digit chars after digits */
&& port <= 65535) { /* no overflow */
sldns_write_uint16(rd, SVCB_KEY_PORT);
sldns_write_uint16(rd + 2, sizeof(uint16_t));
sldns_write_uint16(rd + 4, port);
*rd_len = 6;
return LDNS_WIREPARSE_ERR_OK;
}
return LDNS_WIREPARSE_ERR_SVCB_PORT_VALUE_SYNTAX;
}
static int
sldns_str2wire_svcbparam_ipv4hint(const char* val, uint8_t* rd, size_t* rd_len)
{
size_t count;
char ip_str[INET_ADDRSTRLEN+1];
char *next_ip_str;
size_t i;
for (i = 0, count = 1; val[i]; i++) {
if (val[i] == ',')
count += 1;
if (count > SVCB_MAX_COMMA_SEPARATED_VALUES) {
return LDNS_WIREPARSE_ERR_SVCB_IPV4_TOO_MANY_ADDRESSES;
}
}
if (*rd_len < (LDNS_IP4ADDRLEN * count) + 4)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
/* count is number of comma's in val + 1; so the actual number of IPv4
* addresses in val
*/
sldns_write_uint16(rd, SVCB_KEY_IPV4HINT);
sldns_write_uint16(rd + 2, LDNS_IP4ADDRLEN * count);
*rd_len = 4;
while (count) {
if (!(next_ip_str = strchr(val, ','))) {
if (inet_pton(AF_INET, val, rd + *rd_len) != 1)
break;
*rd_len += LDNS_IP4ADDRLEN;
assert(count == 1);
} else if (next_ip_str - val >= (int)sizeof(ip_str))
break;
else {
memcpy(ip_str, val, next_ip_str - val);
ip_str[next_ip_str - val] = 0;
if (inet_pton(AF_INET, ip_str, rd + *rd_len) != 1) {
break;
}
*rd_len += LDNS_IP4ADDRLEN;
val = next_ip_str + 1;
}
count--;
}
if (count) /* verify that we parsed all values */
return LDNS_WIREPARSE_ERR_SYNTAX_IP4;
return LDNS_WIREPARSE_ERR_OK;
}
static int
sldns_str2wire_svcbparam_ipv6hint(const char* val, uint8_t* rd, size_t* rd_len)
{
size_t count;
char ip_str[INET6_ADDRSTRLEN+1];
char *next_ip_str;
size_t i;
for (i = 0, count = 1; val[i]; i++) {
if (val[i] == ',')
count += 1;
if (count > SVCB_MAX_COMMA_SEPARATED_VALUES) {
return LDNS_WIREPARSE_ERR_SVCB_IPV6_TOO_MANY_ADDRESSES;
}
}
if (*rd_len < (LDNS_IP6ADDRLEN * count) + 4)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
/* count is number of comma's in val + 1; so the actual number of IPv6
* addresses in val
*/
sldns_write_uint16(rd, SVCB_KEY_IPV6HINT);
sldns_write_uint16(rd + 2, LDNS_IP6ADDRLEN * count);
*rd_len = 4;
while (count) {
if (!(next_ip_str = strchr(val, ','))) {
if (inet_pton(AF_INET6, val, rd + *rd_len) != 1)
break;
*rd_len += LDNS_IP6ADDRLEN;
assert(count == 1);
} else if (next_ip_str - val >= (int)sizeof(ip_str))
break;
else {
memcpy(ip_str, val, next_ip_str - val);
ip_str[next_ip_str - val] = 0;
if (inet_pton(AF_INET6, ip_str, rd + *rd_len) != 1) {
break;
}
*rd_len += LDNS_IP6ADDRLEN;
val = next_ip_str + 1;
}
count--;
}
if (count) /* verify that we parsed all values */
return LDNS_WIREPARSE_ERR_SYNTAX_IP6;
return LDNS_WIREPARSE_ERR_OK;
}
/* compare function used for sorting uint16_t's */
static int
sldns_network_uint16_cmp(const void *a, const void *b)
{
return ((int)sldns_read_uint16(a)) - ((int)sldns_read_uint16(b));
}
static int
sldns_str2wire_svcbparam_mandatory(const char* val, uint8_t* rd, size_t* rd_len)
{
size_t i, count, val_len;
char* next_key;
val_len = strlen(val);
for (i = 0, count = 1; val[i]; i++) {
if (val[i] == ',')
count += 1;
if (count > SVCB_MAX_COMMA_SEPARATED_VALUES) {
return LDNS_WIREPARSE_ERR_SVCB_MANDATORY_TOO_MANY_KEYS;
}
}
if (sizeof(uint16_t) * (count + 2) > *rd_len)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
sldns_write_uint16(rd, SVCB_KEY_MANDATORY);
sldns_write_uint16(rd + 2, sizeof(uint16_t) * count);
*rd_len = 4;
while (1) {
int svcparamkey;
if (!(next_key = strchr(val, ','))) {
svcparamkey = sldns_str2wire_svcparam_key_lookup(val, val_len);
if (svcparamkey < 0) {
return LDNS_WIREPARSE_ERR_SVCB_UNKNOWN_KEY;
}
sldns_write_uint16(rd + *rd_len, svcparamkey);
*rd_len += 2;
break;
} else {
svcparamkey = sldns_str2wire_svcparam_key_lookup(val, next_key - val);
if (svcparamkey < 0) {
return LDNS_WIREPARSE_ERR_SVCB_UNKNOWN_KEY;
}
sldns_write_uint16(rd + *rd_len,
svcparamkey);
*rd_len += 2;
}
val_len -= next_key - val + 1;
val = next_key + 1; /* skip the comma */
}
/* In draft-ietf-dnsop-svcb-https-06 Section 7:
*
* "In wire format, the keys are represented by their numeric
* values in network byte order, concatenated in ascending order."
*/
qsort((void *)(rd + 4), count, sizeof(uint16_t), sldns_network_uint16_cmp);
/* The code below revolves around semantic errors in the SVCParam set.
* So long as we do not distinguish between running Unbound as a primary
* or as a secondary, we default to secondary behavior and we ignore the
* semantic errors. */
#ifdef SVCB_SEMANTIC_ERRORS
/* In draft-ietf-dnsop-svcb-https-06 Section 8
* automatically mandatory MUST NOT appear in its own value-list
*/
if (sldns_read_uint16(rd + 4) == SVCB_KEY_MANDATORY)
return LDNS_WIREPARSE_ERR_SVCB_MANDATORY_IN_MANDATORY;
/* Guarantee key uniqueness. After the sort we only need to
* compare neighbouring keys */
if (count > 1) {
for (i = 0; i < count - 1; i++) {
uint8_t* current_pos = (rd + 4 + (sizeof(uint16_t) * i));
uint16_t key = sldns_read_uint16(current_pos);
if (key == sldns_read_uint16(current_pos + 2)) {
return LDNS_WIREPARSE_ERR_SVCB_MANDATORY_DUPLICATE_KEY;
}
}
}
#endif
return LDNS_WIREPARSE_ERR_OK;
}
static int
sldns_str2wire_svcbparam_ech_value(const char* val, uint8_t* rd, size_t* rd_len)
{
uint8_t buffer[LDNS_MAX_RDFLEN];
int wire_len;
/* single 0 represents empty buffer */
if(strcmp(val, "0") == 0) {
if (*rd_len < 4)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
sldns_write_uint16(rd, SVCB_KEY_ECH);
sldns_write_uint16(rd + 2, 0);
return LDNS_WIREPARSE_ERR_OK;
}
wire_len = sldns_b64_pton(val, buffer, LDNS_MAX_RDFLEN);
if (wire_len <= 0) {
return LDNS_WIREPARSE_ERR_SYNTAX_B64;
} else if ((unsigned)wire_len + 4 > *rd_len) {
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
} else {
sldns_write_uint16(rd, SVCB_KEY_ECH);
sldns_write_uint16(rd + 2, wire_len);
memcpy(rd + 4, buffer, wire_len);
*rd_len = 4 + wire_len;
return LDNS_WIREPARSE_ERR_OK;
}
}
static const char*
sldns_str2wire_svcbparam_parse_next_unescaped_comma(const char *val)
{
while (*val) {
/* Only return when the comma is not escaped*/
if (*val == '\\'){
++val;
if (!*val)
break;
} else if (*val == ',')
return val;
val++;
}
return NULL;
}
/* The source is already properly unescaped, this double unescaping is purely to allow for
* comma's in comma separated alpn lists.
*
* In draft-ietf-dnsop-svcb-https-06 Section 7:
* To enable simpler parsing, this SvcParamValue MUST NOT contain escape sequences.
*/
static size_t
sldns_str2wire_svcbparam_parse_copy_unescaped(uint8_t *dst,
const char *src, size_t len)
{
uint8_t *orig_dst = dst;
while (len) {
if (*src == '\\') {
src++;
len--;
if (!len)
break;
}
*dst++ = *src++;
len--;
}
return (size_t)(dst - orig_dst);
}
static int
sldns_str2wire_svcbparam_alpn_value(const char* val,
uint8_t* rd, size_t* rd_len)
{
uint8_t unescaped_dst[LDNS_MAX_RDFLEN];
uint8_t *dst = unescaped_dst;
const char *next_str;
size_t str_len;
size_t dst_len;
size_t val_len;
val_len = strlen(val);
if (val_len > sizeof(unescaped_dst)) {
return LDNS_WIREPARSE_ERR_SVCB_ALPN_KEY_TOO_LARGE;
}
while (val_len) {
size_t key_len;
str_len = (next_str = sldns_str2wire_svcbparam_parse_next_unescaped_comma(val))
? (size_t)(next_str - val) : val_len;
if (str_len > 255) {
return LDNS_WIREPARSE_ERR_SVCB_ALPN_KEY_TOO_LARGE;
}
key_len = sldns_str2wire_svcbparam_parse_copy_unescaped(dst + 1, val, str_len);
*dst++ = key_len;
dst += key_len;
if (!next_str)
break;
/* skip the comma in the next iteration */
val_len -= next_str - val + 1;
val = next_str + 1;
}
dst_len = dst - unescaped_dst;
if (*rd_len < 4 + dst_len)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
sldns_write_uint16(rd, SVCB_KEY_ALPN);
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;
}
static int
sldns_str2wire_svcparam_value(const char *key, size_t key_len,
const char *val, uint8_t* rd, size_t* rd_len)
{
size_t str_len;
int svcparamkey = sldns_str2wire_svcparam_key_lookup(key, key_len);
if (svcparamkey < 0) {
return LDNS_WIREPARSE_ERR_SVCB_UNKNOWN_KEY;
}
/* key without value */
if (val == NULL) {
switch (svcparamkey) {
#ifdef SVCB_SEMANTIC_ERRORS
case SVCB_KEY_MANDATORY:
case SVCB_KEY_ALPN:
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:
if (*rd_len < 4)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
sldns_write_uint16(rd, svcparamkey);
sldns_write_uint16(rd + 2, 0);
*rd_len = 4;
return LDNS_WIREPARSE_ERR_OK;
}
}
/* value is non-empty */
switch (svcparamkey) {
case SVCB_KEY_PORT:
return sldns_str2wire_svcparam_port(val, rd, rd_len);
case SVCB_KEY_IPV4HINT:
return sldns_str2wire_svcbparam_ipv4hint(val, rd, rd_len);
case SVCB_KEY_IPV6HINT:
return sldns_str2wire_svcbparam_ipv6hint(val, rd, rd_len);
case SVCB_KEY_MANDATORY:
return sldns_str2wire_svcbparam_mandatory(val, rd, rd_len);
#ifdef SVCB_SEMANTIC_ERRORS
case SVCB_KEY_NO_DEFAULT_ALPN:
return LDNS_WIREPARSE_ERR_SVCB_NO_DEFAULT_ALPN_VALUE;
#endif
case SVCB_KEY_ECH:
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)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
sldns_write_uint16(rd, svcparamkey);
sldns_write_uint16(rd + 2, str_len);
memcpy(rd + 4, val, str_len);
*rd_len = 4 + str_len;
return LDNS_WIREPARSE_ERR_OK;
}
return LDNS_WIREPARSE_ERR_GENERAL;
}
static int sldns_str2wire_svcparam_buf(const char* str, uint8_t* rd, size_t* rd_len)
{
const char* eq_pos;
char unescaped_val[LDNS_MAX_RDFLEN];
char* val_out = unescaped_val;
const char* val_in;
eq_pos = strchr(str, '=');
/* case: key=value */
if (eq_pos != NULL && eq_pos[1]) {
val_in = eq_pos + 1;
/* unescape characters and "" blocks */
if (*val_in == '"') {
val_in++;
while (*val_in != '"'
&& (size_t)(val_out - unescaped_val + 1) < sizeof(unescaped_val)
&& sldns_parse_char( (uint8_t*) val_out, &val_in)) {
val_out++;
}
} else {
while ((size_t)(val_out - unescaped_val + 1) < sizeof(unescaped_val)
&& sldns_parse_char( (uint8_t*) val_out, &val_in)) {
val_out++;
}
}
*val_out = 0;
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])) {
return sldns_str2wire_svcparam_value(str, eq_pos - str, NULL, rd, rd_len);
}
/* case: key */
else {
return sldns_str2wire_svcparam_value(str, strlen(str), NULL, rd, rd_len);
}
}
int sldns_str2wire_rdf_buf(const char* str, uint8_t* rd, size_t* len,
sldns_rdf_type rdftype)
{
switch (rdftype) {
case LDNS_RDF_TYPE_DNAME:
return sldns_str2wire_dname_buf(str, rd, len);
case LDNS_RDF_TYPE_INT8:
return sldns_str2wire_int8_buf(str, rd, len);
case LDNS_RDF_TYPE_INT16:
return sldns_str2wire_int16_buf(str, rd, len);
case LDNS_RDF_TYPE_INT32:
return sldns_str2wire_int32_buf(str, rd, len);
case LDNS_RDF_TYPE_A:
return sldns_str2wire_a_buf(str, rd, len);
case LDNS_RDF_TYPE_AAAA:
return sldns_str2wire_aaaa_buf(str, rd, len);
case LDNS_RDF_TYPE_STR:
return sldns_str2wire_str_buf(str, rd, len);
case LDNS_RDF_TYPE_APL:
return sldns_str2wire_apl_buf(str, rd, len);
case LDNS_RDF_TYPE_B64:
return sldns_str2wire_b64_buf(str, rd, len);
case LDNS_RDF_TYPE_B32_EXT:
return sldns_str2wire_b32_ext_buf(str, rd, len);
case LDNS_RDF_TYPE_HEX:
return sldns_str2wire_hex_buf(str, rd, len);
case LDNS_RDF_TYPE_NSEC:
return sldns_str2wire_nsec_buf(str, rd, len);
case LDNS_RDF_TYPE_TYPE:
return sldns_str2wire_type_buf(str, rd, len);
case LDNS_RDF_TYPE_CLASS:
return sldns_str2wire_class_buf(str, rd, len);
case LDNS_RDF_TYPE_CERT_ALG:
return sldns_str2wire_cert_alg_buf(str, rd, len);
case LDNS_RDF_TYPE_ALG:
return sldns_str2wire_alg_buf(str, rd, len);
case LDNS_RDF_TYPE_TIME:
return sldns_str2wire_time_buf(str, rd, len);
case LDNS_RDF_TYPE_PERIOD:
return sldns_str2wire_period_buf(str, rd, len);
case LDNS_RDF_TYPE_TSIGTIME:
return sldns_str2wire_tsigtime_buf(str, rd, len);
case LDNS_RDF_TYPE_LOC:
return sldns_str2wire_loc_buf(str, rd, len);
case LDNS_RDF_TYPE_WKS:
return sldns_str2wire_wks_buf(str, rd, len);
case LDNS_RDF_TYPE_NSAP:
return sldns_str2wire_nsap_buf(str, rd, len);
case LDNS_RDF_TYPE_ATMA:
return sldns_str2wire_atma_buf(str, rd, len);
case LDNS_RDF_TYPE_IPSECKEY:
return sldns_str2wire_ipseckey_buf(str, rd, len);
case LDNS_RDF_TYPE_NSEC3_SALT:
return sldns_str2wire_nsec3_salt_buf(str, rd, len);
case LDNS_RDF_TYPE_NSEC3_NEXT_OWNER:
return sldns_str2wire_b32_ext_buf(str, rd, len);
case LDNS_RDF_TYPE_ILNP64:
return sldns_str2wire_ilnp64_buf(str, rd, len);
case LDNS_RDF_TYPE_EUI48:
return sldns_str2wire_eui48_buf(str, rd, len);
case LDNS_RDF_TYPE_EUI64:
return sldns_str2wire_eui64_buf(str, rd, len);
case LDNS_RDF_TYPE_TAG:
return sldns_str2wire_tag_buf(str, rd, len);
case LDNS_RDF_TYPE_LONG_STR:
return sldns_str2wire_long_str_buf(str, rd, len);
case LDNS_RDF_TYPE_TSIGERROR:
return sldns_str2wire_tsigerror_buf(str, rd, len);
case LDNS_RDF_TYPE_HIP:
return sldns_str2wire_hip_buf(str, rd, len);
case LDNS_RDF_TYPE_INT16_DATA:
return sldns_str2wire_int16_data_buf(str, rd, len);
case LDNS_RDF_TYPE_SVCPARAM:
return sldns_str2wire_svcparam_buf(str, rd, len);
case LDNS_RDF_TYPE_UNKNOWN:
case LDNS_RDF_TYPE_SERVICE:
return LDNS_WIREPARSE_ERR_NOT_IMPL;
case LDNS_RDF_TYPE_NONE:
default:
break;
}
return LDNS_WIREPARSE_ERR_GENERAL;
}
int sldns_str2wire_int8_buf(const char* str, uint8_t* rd, size_t* len)
{
char* end;
uint8_t r = (uint8_t)strtol((char*)str, &end, 10);
if(*end != 0)
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_INT, end-(char*)str);
if(*len < 1)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
rd[0] = r;
*len = 1;
return LDNS_WIREPARSE_ERR_OK;
}
int sldns_str2wire_int16_buf(const char* str, uint8_t* rd, size_t* len)
{
char* end;
uint16_t r = (uint16_t)strtol((char*)str, &end, 10);
if(*end != 0)
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_INT, end-(char*)str);
if(*len < 2)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
sldns_write_uint16(rd, r);
*len = 2;
return LDNS_WIREPARSE_ERR_OK;
}
int sldns_str2wire_int32_buf(const char* str, uint8_t* rd, size_t* len)
{
char* end;
uint32_t r;
errno = 0; /* must set to zero before call,
note race condition on errno */
if(*str == '-')
r = (uint32_t)strtol((char*)str, &end, 10);
else r = (uint32_t)strtoul((char*)str, &end, 10);
if(*end != 0)
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_INT, end-(char*)str);
if(errno == ERANGE)
return LDNS_WIREPARSE_ERR_SYNTAX_INTEGER_OVERFLOW;
if(*len < 4)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
sldns_write_uint32(rd, r);
*len = 4;
return LDNS_WIREPARSE_ERR_OK;
}
int sldns_str2wire_a_buf(const char* str, uint8_t* rd, size_t* len)
{
struct in_addr address;
if(inet_pton(AF_INET, (char*)str, &address) != 1)
return LDNS_WIREPARSE_ERR_SYNTAX_IP4;
if(*len < sizeof(address))
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
memmove(rd, &address, sizeof(address));
*len = sizeof(address);
return LDNS_WIREPARSE_ERR_OK;
}
int sldns_str2wire_aaaa_buf(const char* str, uint8_t* rd, size_t* len)
{
#ifdef AF_INET6
uint8_t address[LDNS_IP6ADDRLEN + 1];
if(inet_pton(AF_INET6, (char*)str, address) != 1)
return LDNS_WIREPARSE_ERR_SYNTAX_IP6;
if(*len < LDNS_IP6ADDRLEN)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
memmove(rd, address, LDNS_IP6ADDRLEN);
*len = LDNS_IP6ADDRLEN;
return LDNS_WIREPARSE_ERR_OK;
#else
return LDNS_WIREPARSE_ERR_NOT_IMPL;
#endif
}
int sldns_str2wire_str_buf(const char* str, uint8_t* rd, size_t* len)
{
uint8_t ch = 0;
size_t sl = 0;
const char* s = str;
/* skip length byte */
if(*len < 1)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
/* read characters */
while(sldns_parse_char(&ch, &s)) {
if(sl >= 255)
return RET_ERR(LDNS_WIREPARSE_ERR_INVALID_STR, s-str);
if(*len < sl+2)
return RET_ERR(LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL,
s-str);
rd[++sl] = ch;
}
if(!s)
return LDNS_WIREPARSE_ERR_SYNTAX_BAD_ESCAPE;
rd[0] = (uint8_t)sl;
*len = sl+1;
return LDNS_WIREPARSE_ERR_OK;
}
int sldns_str2wire_apl_buf(const char* str, uint8_t* rd, size_t* len)
{
const char *my_str = str;
char my_ip_str[64];
size_t ip_str_len;
uint16_t family;
int negation;
size_t adflength = 0;
uint8_t data[16+4];
uint8_t prefix;
size_t i;
if(*my_str == '\0') {
/* empty APL element, no data, no string */
*len = 0;
return LDNS_WIREPARSE_ERR_OK;
}
/* [!]afi:address/prefix */
if (strlen(my_str) < 2
|| strchr(my_str, ':') == NULL
|| strchr(my_str, '/') == NULL
|| strchr(my_str, ':') > strchr(my_str, '/')) {
return LDNS_WIREPARSE_ERR_INVALID_STR;
}
if (my_str[0] == '!') {
negation = 1;
my_str += 1;
} else {
negation = 0;
}
family = (uint16_t) atoi(my_str);
my_str = strchr(my_str, ':') + 1;
/* need ip addr and only ip addr for inet_pton */
ip_str_len = (size_t) (strchr(my_str, '/') - my_str);
if(ip_str_len+1 > sizeof(my_ip_str))
return LDNS_WIREPARSE_ERR_INVALID_STR;
(void)strlcpy(my_ip_str, my_str, sizeof(my_ip_str));
my_ip_str[ip_str_len] = 0;
if (family == 1) {
/* ipv4 */
if(inet_pton(AF_INET, my_ip_str, data+4) == 0)
return LDNS_WIREPARSE_ERR_INVALID_STR;
for (i = 0; i < 4; i++) {
if (data[i+4] != 0) {
adflength = i + 1;
}
}
} else if (family == 2) {
/* ipv6 */
if (inet_pton(AF_INET6, my_ip_str, data+4) == 0)
return LDNS_WIREPARSE_ERR_INVALID_STR;
for (i = 0; i < 16; i++) {
if (data[i+4] != 0) {
adflength = i + 1;
}
}
} else {
/* unknown family */
return LDNS_WIREPARSE_ERR_INVALID_STR;
}
my_str = strchr(my_str, '/') + 1;
prefix = (uint8_t) atoi(my_str);
sldns_write_uint16(data, family);
data[2] = prefix;
data[3] = (uint8_t)adflength;
if (negation) {
/* set bit 1 of byte 3 */
data[3] = data[3] | 0x80;
}
if(*len < 4+adflength)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
memmove(rd, data, 4+adflength);
*len = 4+adflength;
return LDNS_WIREPARSE_ERR_OK;
}
int sldns_str2wire_b64_buf(const char* str, uint8_t* rd, size_t* len)
{
size_t sz = sldns_b64_pton_calculate_size(strlen(str));
int n;
if(strcmp(str, "0") == 0) {
*len = 0;
return LDNS_WIREPARSE_ERR_OK;
}
if(*len < sz)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
n = sldns_b64_pton(str, rd, *len);
if(n < 0)
return LDNS_WIREPARSE_ERR_SYNTAX_B64;
*len = (size_t)n;
return LDNS_WIREPARSE_ERR_OK;
}
int sldns_str2wire_b32_ext_buf(const char* str, uint8_t* rd, size_t* len)
{
size_t slen = strlen(str);
size_t sz = sldns_b32_pton_calculate_size(slen);
int n;
if(*len < 1+sz)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
rd[0] = (uint8_t)sz;
n = sldns_b32_pton_extended_hex(str, slen, rd+1, *len-1);
if(n < 0)
return LDNS_WIREPARSE_ERR_SYNTAX_B32_EXT;
*len = (size_t)n+1;
return LDNS_WIREPARSE_ERR_OK;
}
/** see if the string ends, or ends in whitespace */
static int
sldns_is_last_of_string(const char* str)
{
if(*str == 0) return 1;
while(isspace((unsigned char)*str))
str++;
if(*str == 0) return 1;
return 0;
}
int sldns_str2wire_hex_buf(const char* str, uint8_t* rd, size_t* len)
{
const char* s = str;
size_t dlen = 0; /* number of hexdigits parsed */
while(*s) {
if(isspace((unsigned char)*s)) {
s++;
continue;
}
if(dlen == 0 && *s == '0' && sldns_is_last_of_string(s+1)) {
*len = 0;
return LDNS_WIREPARSE_ERR_OK;
}
if(!isxdigit((unsigned char)*s))
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_HEX, s-str);
if(*len < dlen/2 + 1)
return RET_ERR(LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL,
s-str);
if((dlen&1)==0)
rd[dlen/2] = (uint8_t)sldns_hexdigit_to_int(*s++) * 16;
else rd[dlen/2] += (uint8_t)sldns_hexdigit_to_int(*s++);
dlen++;
}
if((dlen&1)!=0)
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_HEX, s-str);
*len = dlen/2;
return LDNS_WIREPARSE_ERR_OK;
}
int sldns_str2wire_nsec_buf(const char* str, uint8_t* rd, size_t* len)
{
const char *delim = "\n\t ";
char token[64]; /* for a type name */
size_t type_count = 0;
int block;
size_t used = 0;
uint16_t maxtype = 0;
uint8_t typebits[8192]; /* 65536 bits */
uint8_t window_in_use[256];
/* string in buffer */
sldns_buffer strbuf;
sldns_buffer_init_frm_data(&strbuf, (uint8_t*)str, strlen(str));
/* parse the types */
memset(typebits, 0, sizeof(typebits));
memset(window_in_use, 0, sizeof(window_in_use));
while(sldns_buffer_remaining(&strbuf) > 0 &&
sldns_bget_token(&strbuf, token, delim, sizeof(token)) != -1) {
uint16_t t = sldns_get_rr_type_by_name(token);
if(token[0] == 0)
continue;
if(t == 0 && strcmp(token, "TYPE0") != 0)
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_TYPE,
sldns_buffer_position(&strbuf));
typebits[t/8] |= (0x80>>(t%8));
window_in_use[t/256] = 1;
type_count++;
if(t > maxtype) maxtype = t;
}
/* empty NSEC bitmap */
if(type_count == 0) {
*len = 0;
return LDNS_WIREPARSE_ERR_OK;
}
/* encode windows {u8 windowblock, u8 bitmaplength, 0-32u8 bitmap},
* block is 0-255 upper octet of types, length if 0-32. */
for(block = 0; block <= (int)maxtype/256; block++) {
int i, blocklen = 0;
if(!window_in_use[block])
continue;
for(i=0; i<32; i++) {
if(typebits[block*32+i] != 0)
blocklen = i+1;
}
if(blocklen == 0)
continue; /* empty window should have been !in_use */
if(used+blocklen+2 > *len)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
rd[used+0] = (uint8_t)block;
rd[used+1] = (uint8_t)blocklen;
for(i=0; i<blocklen; i++) {
rd[used+2+i] = typebits[block*32+i];
}
used += blocklen+2;
}
*len = used;
return LDNS_WIREPARSE_ERR_OK;
}
int sldns_str2wire_type_buf(const char* str, uint8_t* rd, size_t* len)
{
uint16_t t = sldns_get_rr_type_by_name(str);
if(t == 0 && strcmp(str, "TYPE0") != 0)
return LDNS_WIREPARSE_ERR_SYNTAX_TYPE;
if(*len < 2)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
sldns_write_uint16(rd, t);
*len = 2;
return LDNS_WIREPARSE_ERR_OK;
}
int sldns_str2wire_class_buf(const char* str, uint8_t* rd, size_t* len)
{
uint16_t c = sldns_get_rr_class_by_name(str);
if(c == 0 && strcmp(str, "CLASS0") != 0)
return LDNS_WIREPARSE_ERR_SYNTAX_CLASS;
if(*len < 2)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
sldns_write_uint16(rd, c);
*len = 2;
return LDNS_WIREPARSE_ERR_OK;
}
/* An certificate alg field can either be specified as a 8 bits number
* or by its symbolic name. Handle both */
int sldns_str2wire_cert_alg_buf(const char* str, uint8_t* rd, size_t* len)
{
sldns_lookup_table *lt = sldns_lookup_by_name(sldns_cert_algorithms,
str);
if(*len < 2)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
if(lt) {
sldns_write_uint16(rd, (uint16_t)lt->id);
} else {
int s = sldns_str2wire_int16_buf(str, rd, len);
if(s) return s;
if(sldns_read_uint16(rd) == 0)
return LDNS_WIREPARSE_ERR_CERT_BAD_ALGORITHM;
}
*len = 2;
return LDNS_WIREPARSE_ERR_OK;
}
/* An alg field can either be specified as a 8 bits number
* or by its symbolic name. Handle both */
int sldns_str2wire_alg_buf(const char* str, uint8_t* rd, size_t* len)
{
sldns_lookup_table *lt = sldns_lookup_by_name(sldns_algorithms, str);
if(*len < 1)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
if(lt) {
rd[0] = (uint8_t)lt->id;
*len = 1;
} else {
/* try as-is (a number) */
return sldns_str2wire_int8_buf(str, rd, len);
}
return LDNS_WIREPARSE_ERR_OK;
}
int sldns_str2wire_tsigerror_buf(const char* str, uint8_t* rd, size_t* len)
{
sldns_lookup_table *lt = sldns_lookup_by_name(sldns_tsig_errors, str);
if(*len < 2)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
if(lt) {
sldns_write_uint16(rd, (uint16_t)lt->id);
*len = 2;
} else {
/* try as-is (a number) */
return sldns_str2wire_int16_buf(str, rd, len);
}
return LDNS_WIREPARSE_ERR_OK;
}
int sldns_str2wire_time_buf(const char* str, uint8_t* rd, size_t* len)
{
/* convert a time YYYYDDMMHHMMSS to wireformat */
struct tm tm;
if(*len < 4)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
/* Try to scan the time... */
memset(&tm, 0, sizeof(tm));
if (strlen(str) == 14 && 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) {
tm.tm_year -= 1900;
tm.tm_mon--;
/* Check values */
if (tm.tm_year < 70)
return LDNS_WIREPARSE_ERR_SYNTAX_TIME;
if (tm.tm_mon < 0 || tm.tm_mon > 11)
return LDNS_WIREPARSE_ERR_SYNTAX_TIME;
if (tm.tm_mday < 1 || tm.tm_mday > 31)
return LDNS_WIREPARSE_ERR_SYNTAX_TIME;
if (tm.tm_hour < 0 || tm.tm_hour > 23)
return LDNS_WIREPARSE_ERR_SYNTAX_TIME;
if (tm.tm_min < 0 || tm.tm_min > 59)
return LDNS_WIREPARSE_ERR_SYNTAX_TIME;
if (tm.tm_sec < 0 || tm.tm_sec > 59)
return LDNS_WIREPARSE_ERR_SYNTAX_TIME;
sldns_write_uint32(rd, (uint32_t)sldns_mktime_from_utc(&tm));
} else {
/* handle it as 32 bits timestamp */
char *end;
uint32_t l = (uint32_t)strtol((char*)str, &end, 10);
if(*end != 0)
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_TIME,
end-(char*)str);
sldns_write_uint32(rd, l);
}
*len = 4;
return LDNS_WIREPARSE_ERR_OK;
}
int sldns_str2wire_tsigtime_buf(const char* str, uint8_t* rd, size_t* len)
{
char* end;
uint64_t t = (uint64_t)strtol((char*)str, &end, 10);
uint16_t high;
uint32_t low;
if(*end != 0)
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_TIME, end-str);
if(*len < 6)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
high = (uint16_t)(t>>32);
low = (uint32_t)(t);
sldns_write_uint16(rd, high);
sldns_write_uint32(rd+2, low);
*len = 6;
return LDNS_WIREPARSE_ERR_OK;
}
int sldns_str2wire_period_buf(const char* str, uint8_t* rd, size_t* len)
{
const char* end;
int overflow;
uint32_t p = sldns_str2period(str, &end, &overflow);
if(*end != 0)
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_PERIOD, end-str);
if(overflow)
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_INTEGER_OVERFLOW,
end-str);
if(*len < 4)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
sldns_write_uint32(rd, p);
*len = 4;
return LDNS_WIREPARSE_ERR_OK;
}
/** read "<digits>[.<digits>][mM]" into mantissa exponent format for LOC type */
static int
loc_parse_cm(char* my_str, char** endstr, uint8_t* m, uint8_t* e)
{
uint32_t meters = 0, cm = 0, val;
char* cm_endstr;
while (isblank((unsigned char)*my_str)) {
my_str++;
}
meters = (uint32_t)strtol(my_str, &my_str, 10);
if (*my_str == '.') {
my_str++;
cm = (uint32_t)strtol(my_str, &cm_endstr, 10);
if(cm_endstr == my_str + 1)
cm *= 10;
my_str = cm_endstr;
}
if (meters >= 1) {
*e = 2;
val = meters;
} else {
*e = 0;
val = cm;
}
while(val >= 10) {
(*e)++;
val /= 10;
}
*m = (uint8_t)val;
if (*e > 9)
return 0;
if (*my_str == 'm' || *my_str == 'M') {
my_str++;
}
*endstr = my_str;
return 1;
}
int sldns_str2wire_loc_buf(const char* str, uint8_t* rd, size_t* len)
{
uint32_t latitude = 0;
uint32_t longitude = 0;
uint32_t altitude = 0;
uint32_t equator = (uint32_t)1<<31; /* 2**31 */
/* only support version 0 */
uint32_t h = 0;
uint32_t m = 0;
uint8_t size_b = 1, size_e = 2;
uint8_t horiz_pre_b = 1, horiz_pre_e = 6;
uint8_t vert_pre_b = 1, vert_pre_e = 3;
double s = 0.0;
int northerness;
int easterness;
char *my_str = (char *) str;
if (isdigit((unsigned char) *my_str)) {
h = (uint32_t) strtol(my_str, &my_str, 10);
} else {
return LDNS_WIREPARSE_ERR_INVALID_STR;
}
while (isblank((unsigned char) *my_str)) {
my_str++;
}
if (isdigit((unsigned char) *my_str)) {
m = (uint32_t) strtol(my_str, &my_str, 10);
} else if (*my_str == 'N' || *my_str == 'S') {
goto north;
} else {
return LDNS_WIREPARSE_ERR_INVALID_STR;
}
while (isblank((unsigned char) *my_str)) {
my_str++;
}
if (isdigit((unsigned char) *my_str)) {
s = strtod(my_str, &my_str);
}
/* skip blanks before northerness */
while (isblank((unsigned char) *my_str)) {
my_str++;
}
north:
if (*my_str == 'N') {
northerness = 1;
} else if (*my_str == 'S') {
northerness = 0;
} else {
return LDNS_WIREPARSE_ERR_INVALID_STR;
}
my_str++;
/* store number */
s = 1000.0 * s;
/* add a little to make floor in conversion a round */
s += 0.0005;
latitude = (uint32_t) s;
latitude += 1000 * 60 * m;
latitude += 1000 * 60 * 60 * h;
if (northerness) {
latitude = equator + latitude;
} else {
latitude = equator - latitude;
}
while (isblank((unsigned char)*my_str)) {
my_str++;
}
if (isdigit((unsigned char) *my_str)) {
h = (uint32_t) strtol(my_str, &my_str, 10);
} else {
return LDNS_WIREPARSE_ERR_INVALID_STR;
}
while (isblank((unsigned char) *my_str)) {
my_str++;
}
if (isdigit((unsigned char) *my_str)) {
m = (uint32_t) strtol(my_str, &my_str, 10);
} else if (*my_str == 'E' || *my_str == 'W') {
goto east;
} else {
return LDNS_WIREPARSE_ERR_INVALID_STR;
}
while (isblank((unsigned char)*my_str)) {
my_str++;
}
if (isdigit((unsigned char) *my_str)) {
s = strtod(my_str, &my_str);
}
/* skip blanks before easterness */
while (isblank((unsigned char)*my_str)) {
my_str++;
}
east:
if (*my_str == 'E') {
easterness = 1;
} else if (*my_str == 'W') {
easterness = 0;
} else {
return LDNS_WIREPARSE_ERR_INVALID_STR;
}
my_str++;
/* store number */
s *= 1000.0;
/* add a little to make floor in conversion a round */
s += 0.0005;
longitude = (uint32_t) s;
longitude += 1000 * 60 * m;
longitude += 1000 * 60 * 60 * h;
if (easterness) {
longitude += equator;
} else {
longitude = equator - longitude;
}
altitude = (uint32_t)(strtod(my_str, &my_str)*100.0 +
10000000.0 + 0.5);
if (*my_str == 'm' || *my_str == 'M') {
my_str++;
}
if (strlen(my_str) > 0) {
if(!loc_parse_cm(my_str, &my_str, &size_b, &size_e))
return LDNS_WIREPARSE_ERR_INVALID_STR;
}
if (strlen(my_str) > 0) {
if(!loc_parse_cm(my_str, &my_str, &horiz_pre_b, &horiz_pre_e))
return LDNS_WIREPARSE_ERR_INVALID_STR;
}
if (strlen(my_str) > 0) {
if(!loc_parse_cm(my_str, &my_str, &vert_pre_b, &vert_pre_e))
return LDNS_WIREPARSE_ERR_INVALID_STR;
}
if(*len < 16)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
rd[0] = 0;
rd[1] = ((size_b << 4) & 0xf0) | (size_e & 0x0f);
rd[2] = ((horiz_pre_b << 4) & 0xf0) | (horiz_pre_e & 0x0f);
rd[3] = ((vert_pre_b << 4) & 0xf0) | (vert_pre_e & 0x0f);
sldns_write_uint32(rd + 4, latitude);
sldns_write_uint32(rd + 8, longitude);
sldns_write_uint32(rd + 12, altitude);
*len = 16;
return LDNS_WIREPARSE_ERR_OK;
}
static void
ldns_tolower_str(char* s)
{
if(s) {
while(*s) {
*s = (char)tolower((unsigned char)*s);
s++;
}
}
}
int sldns_str2wire_wks_buf(const char* str, uint8_t* rd, size_t* len)
{
int rd_len = 1;
int have_proto = 0;
char token[50], proto_str[50];
sldns_buffer strbuf;
sldns_buffer_init_frm_data(&strbuf, (uint8_t*)str, strlen(str));
proto_str[0]=0;
/* check we have one byte for proto */
if(*len < 1)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
while(sldns_bget_token(&strbuf, token, "\t\n ", sizeof(token)) > 0) {
ldns_tolower_str(token);
if(!have_proto) {
struct protoent *p = getprotobyname(token);
have_proto = 1;
if(p) rd[0] = (uint8_t)p->p_proto;
else if(strcasecmp(token, "tcp")==0) rd[0]=6;
else if(strcasecmp(token, "udp")==0) rd[0]=17;
else rd[0] = (uint8_t)atoi(token);
(void)strlcpy(proto_str, token, sizeof(proto_str));
} else {
int serv_port;
- struct servent *serv = getservbyname(token, proto_str);
- if(serv) serv_port=(int)ntohs((uint16_t)serv->s_port);
+ if(atoi(token) != 0) serv_port=atoi(token);
+ else if(strcmp(token, "0") == 0) serv_port=0;
else if(strcasecmp(token, "domain")==0) serv_port=53;
else {
- serv_port = atoi(token);
- if(serv_port == 0 && strcmp(token, "0") != 0) {
+ struct servent *serv = getservbyname(token, proto_str);
+ if(serv) serv_port=(int)ntohs((uint16_t)serv->s_port);
+ else {
#ifdef HAVE_ENDSERVENT
endservent();
#endif
#ifdef HAVE_ENDPROTOENT
endprotoent();
#endif
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX,
sldns_buffer_position(&strbuf));
}
- if(serv_port < 0 || serv_port > 65535) {
+ }
+ if(serv_port < 0 || serv_port > 65535) {
#ifdef HAVE_ENDSERVENT
- endservent();
+ endservent();
#endif
#ifdef HAVE_ENDPROTOENT
- endprotoent();
+ endprotoent();
#endif
- return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX,
- sldns_buffer_position(&strbuf));
- }
+ return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX,
+ sldns_buffer_position(&strbuf));
}
if(rd_len < 1+serv_port/8+1) {
/* bitmap is larger, init new bytes at 0 */
if(*len < 1+(size_t)serv_port/8+1) {
#ifdef HAVE_ENDSERVENT
endservent();
#endif
#ifdef HAVE_ENDPROTOENT
endprotoent();
#endif
return RET_ERR(
LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL,
sldns_buffer_position(&strbuf));
}
memset(rd+rd_len, 0, 1+(size_t)serv_port/8+1-rd_len);
rd_len = 1+serv_port/8+1;
}
rd[1+ serv_port/8] |= (1 << (7 - serv_port % 8));
}
}
*len = (size_t)rd_len;
#ifdef HAVE_ENDSERVENT
endservent();
#endif
#ifdef HAVE_ENDPROTOENT
endprotoent();
#endif
return LDNS_WIREPARSE_ERR_OK;
}
int sldns_str2wire_nsap_buf(const char* str, uint8_t* rd, size_t* len)
{
const char* s = str;
size_t slen;
size_t dlen = 0; /* number of hexdigits parsed */
/* just a hex string with optional dots? */
if (s[0] != '0' || s[1] != 'x')
return LDNS_WIREPARSE_ERR_INVALID_STR;
s += 2;
slen = strlen(s);
if(slen > LDNS_MAX_RDFLEN*2)
return LDNS_WIREPARSE_ERR_LABEL_OVERFLOW;
while(*s) {
if(isspace((unsigned char)*s) || *s == '.') {
s++;
continue;
}
if(!isxdigit((unsigned char)*s))
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_HEX, s-str);
if(*len < dlen/2 + 1)
return RET_ERR(LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL,
s-str);
if((dlen&1)==0)
rd[dlen/2] = (uint8_t)sldns_hexdigit_to_int(*s++) * 16;
else rd[dlen/2] += sldns_hexdigit_to_int(*s++);
dlen++;
}
if((dlen&1)!=0)
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_HEX, s-str);
*len = dlen/2;
return LDNS_WIREPARSE_ERR_OK;
}
int sldns_str2wire_atma_buf(const char* str, uint8_t* rd, size_t* len)
{
const char* s = str;
size_t slen = strlen(str);
size_t dlen = 0; /* number of hexdigits parsed */
/* just a hex string with optional dots? */
/* notimpl e.164 format */
if(slen > LDNS_MAX_RDFLEN*2)
return LDNS_WIREPARSE_ERR_LABEL_OVERFLOW;
while(*s) {
if(isspace((unsigned char)*s) || *s == '.') {
s++;
continue;
}
if(!isxdigit((unsigned char)*s))
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_HEX, s-str);
if(*len < dlen/2 + 1)
return RET_ERR(LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL,
s-str);
if((dlen&1)==0)
rd[dlen/2] = (uint8_t)sldns_hexdigit_to_int(*s++) * 16;
else rd[dlen/2] += sldns_hexdigit_to_int(*s++);
dlen++;
}
if((dlen&1)!=0)
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_HEX, s-str);
*len = dlen/2;
return LDNS_WIREPARSE_ERR_OK;
}
int sldns_str2wire_ipseckey_buf(const char* str, uint8_t* rd, size_t* len)
{
size_t gwlen = 0, keylen = 0;
int s;
uint8_t gwtype;
char token[512];
sldns_buffer strbuf;
sldns_buffer_init_frm_data(&strbuf, (uint8_t*)str, strlen(str));
if(*len < 3)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
/* precedence */
if(sldns_bget_token(&strbuf, token, "\t\n ", sizeof(token)) <= 0)
return RET_ERR(LDNS_WIREPARSE_ERR_INVALID_STR,
sldns_buffer_position(&strbuf));
rd[0] = (uint8_t)atoi(token);
/* gateway_type */
if(sldns_bget_token(&strbuf, token, "\t\n ", sizeof(token)) <= 0)
return RET_ERR(LDNS_WIREPARSE_ERR_INVALID_STR,
sldns_buffer_position(&strbuf));
rd[1] = (uint8_t)atoi(token);
gwtype = rd[1];
/* algorithm */
if(sldns_bget_token(&strbuf, token, "\t\n ", sizeof(token)) <= 0)
return RET_ERR(LDNS_WIREPARSE_ERR_INVALID_STR,
sldns_buffer_position(&strbuf));
rd[2] = (uint8_t)atoi(token);
/* gateway */
if(sldns_bget_token(&strbuf, token, "\t\n ", sizeof(token)) <= 0)
return RET_ERR(LDNS_WIREPARSE_ERR_INVALID_STR,
sldns_buffer_position(&strbuf));
if(gwtype == 0) {
/* NOGATEWAY */
if(strcmp(token, ".") != 0)
return RET_ERR(LDNS_WIREPARSE_ERR_INVALID_STR,
sldns_buffer_position(&strbuf));
gwlen = 0;
} else if(gwtype == 1) {
/* IP4 */
gwlen = *len - 3;
s = sldns_str2wire_a_buf(token, rd+3, &gwlen);
if(s) return RET_ERR_SHIFT(s, sldns_buffer_position(&strbuf));
} else if(gwtype == 2) {
/* IP6 */
gwlen = *len - 3;
s = sldns_str2wire_aaaa_buf(token, rd+3, &gwlen);
if(s) return RET_ERR_SHIFT(s, sldns_buffer_position(&strbuf));
} else if(gwtype == 3) {
/* DNAME */
gwlen = *len - 3;
s = sldns_str2wire_dname_buf(token, rd+3, &gwlen);
if(s) return RET_ERR_SHIFT(s, sldns_buffer_position(&strbuf));
} else {
/* unknown gateway type */
return RET_ERR(LDNS_WIREPARSE_ERR_INVALID_STR,
sldns_buffer_position(&strbuf));
}
/* double check for size */
if(*len < 3 + gwlen)
return RET_ERR(LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL,
sldns_buffer_position(&strbuf));
/* publickey in remainder of strbuf */
keylen = *len - 3 - gwlen;
s = sldns_str2wire_b64_buf((const char*)sldns_buffer_current(&strbuf),
rd+3+gwlen, &keylen);
if(s) return RET_ERR_SHIFT(s, sldns_buffer_position(&strbuf));
*len = 3 + gwlen + keylen;
return LDNS_WIREPARSE_ERR_OK;
}
int sldns_str2wire_nsec3_salt_buf(const char* str, uint8_t* rd, size_t* len)
{
int i, salt_length_str = (int)strlen(str);
if (salt_length_str == 1 && str[0] == '-') {
salt_length_str = 0;
} else if (salt_length_str % 2 != 0) {
return LDNS_WIREPARSE_ERR_SYNTAX_HEX;
}
if (salt_length_str > 512)
return LDNS_WIREPARSE_ERR_SYNTAX_HEX;
if(*len < 1+(size_t)salt_length_str / 2)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
rd[0] = (uint8_t) (salt_length_str / 2);
for (i = 0; i < salt_length_str; i += 2) {
if (isxdigit((unsigned char)str[i]) &&
isxdigit((unsigned char)str[i+1])) {
rd[1+i/2] = (uint8_t)(sldns_hexdigit_to_int(str[i])*16
+ sldns_hexdigit_to_int(str[i+1]));
} else {
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_HEX, i);
}
}
*len = 1 + (size_t)rd[0];
return LDNS_WIREPARSE_ERR_OK;
}
int sldns_str2wire_ilnp64_buf(const char* str, uint8_t* rd, size_t* len)
{
unsigned int a, b, c, d;
uint16_t shorts[4];
int l;
if(*len < sizeof(shorts))
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
if (sscanf(str, "%4x:%4x:%4x:%4x%n", &a, &b, &c, &d, &l) != 4 ||
l != (int)strlen(str) || /* more data to read */
strpbrk(str, "+-") /* signed hexes */
)
return LDNS_WIREPARSE_ERR_SYNTAX_ILNP64;
shorts[0] = htons(a);
shorts[1] = htons(b);
shorts[2] = htons(c);
shorts[3] = htons(d);
memmove(rd, &shorts, sizeof(shorts));
*len = sizeof(shorts);
return LDNS_WIREPARSE_ERR_OK;
}
int sldns_str2wire_eui48_buf(const char* str, uint8_t* rd, size_t* len)
{
unsigned int a, b, c, d, e, f;
int l;
if(*len < 6)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
if (sscanf(str, "%2x-%2x-%2x-%2x-%2x-%2x%n",
&a, &b, &c, &d, &e, &f, &l) != 6 ||
l != (int)strlen(str))
return LDNS_WIREPARSE_ERR_SYNTAX_EUI48;
rd[0] = a;
rd[1] = b;
rd[2] = c;
rd[3] = d;
rd[4] = e;
rd[5] = f;
*len = 6;
return LDNS_WIREPARSE_ERR_OK;
}
int sldns_str2wire_eui64_buf(const char* str, uint8_t* rd, size_t* len)
{
unsigned int a, b, c, d, e, f, g, h;
int l;
if(*len < 8)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
if (sscanf(str, "%2x-%2x-%2x-%2x-%2x-%2x-%2x-%2x%n",
&a, &b, &c, &d, &e, &f, &g, &h, &l) != 8 ||
l != (int)strlen(str))
return LDNS_WIREPARSE_ERR_SYNTAX_EUI64;
rd[0] = a;
rd[1] = b;
rd[2] = c;
rd[3] = d;
rd[4] = e;
rd[5] = f;
rd[6] = g;
rd[7] = h;
*len = 8;
return LDNS_WIREPARSE_ERR_OK;
}
int sldns_str2wire_tag_buf(const char* str, uint8_t* rd, size_t* len)
{
size_t slen = strlen(str);
const char* ptr;
if (slen > 255)
return LDNS_WIREPARSE_ERR_SYNTAX_TAG;
if(*len < slen+1)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
for (ptr = str; *ptr; ptr++) {
if(!isalnum((unsigned char)*ptr))
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_TAG, ptr-str);
}
rd[0] = (uint8_t)slen;
memmove(rd+1, str, slen);
*len = slen+1;
return LDNS_WIREPARSE_ERR_OK;
}
int sldns_str2wire_long_str_buf(const char* str, uint8_t* rd, size_t* len)
{
uint8_t ch = 0;
const char* pstr = str;
size_t length = 0;
/* Fill data with parsed bytes */
while (sldns_parse_char(&ch, &pstr)) {
if(*len < length+1)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
rd[length++] = ch;
}
if(!pstr)
return LDNS_WIREPARSE_ERR_SYNTAX_BAD_ESCAPE;
*len = length;
return LDNS_WIREPARSE_ERR_OK;
}
int sldns_str2wire_hip_buf(const char* str, uint8_t* rd, size_t* len)
{
char* s, *end;
int e;
size_t hitlen, pklen = 0;
/* presentation format:
* pk-algo HIThex pubkeybase64
* wireformat:
* hitlen[1byte] pkalgo[1byte] pubkeylen[2byte] [hit] [pubkey] */
if(*len < 4)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
/* read PK algorithm */
rd[1] = (uint8_t)strtol((char*)str, &s, 10);
if(*s != ' ')
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_INT, s-(char*)str);
s++;
while(*s == ' ')
s++;
/* read HIT hex tag */
/* zero terminate the tag (replace later) */
end = strchr(s, ' ');
if(!end) return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX, s-(char*)str);
*end = 0;
hitlen = *len - 4;
if((e = sldns_str2wire_hex_buf(s, rd+4, &hitlen)) != 0) {
*end = ' ';
return RET_ERR_SHIFT(e, s-(char*)str);
}
if(hitlen > 255) {
*end = ' ';
return RET_ERR(LDNS_WIREPARSE_ERR_LABEL_OVERFLOW, s-(char*)str+255*2);
}
rd[0] = (uint8_t)hitlen;
*end = ' ';
s = end+1;
/* read pubkey base64 sequence */
pklen = *len - 4 - hitlen;
if((e = sldns_str2wire_b64_buf(s, rd+4+hitlen, &pklen)) != 0)
return RET_ERR_SHIFT(e, s-(char*)str);
if(pklen > 65535)
return RET_ERR(LDNS_WIREPARSE_ERR_LABEL_OVERFLOW, s-(char*)str+65535);
sldns_write_uint16(rd+2, (uint16_t)pklen);
*len = 4 + hitlen + pklen;
return LDNS_WIREPARSE_ERR_OK;
}
int sldns_str2wire_int16_data_buf(const char* str, uint8_t* rd, size_t* len)
{
char* s;
int n;
n = strtol(str, &s, 10);
if(n < 0) /* negative number not allowed */
return LDNS_WIREPARSE_ERR_SYNTAX;
if(*len < ((size_t)n)+2)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
if(n > 65535)
return LDNS_WIREPARSE_ERR_LABEL_OVERFLOW;
if(n == 0) {
sldns_write_uint16(rd, 0);
*len = 2;
return LDNS_WIREPARSE_ERR_OK;
}
if(*s != ' ')
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_INT, s-(char*)str);
s++;
while(*s == ' ')
s++;
n = sldns_b64_pton(s, rd+2, (*len)-2);
if(n < 0)
return LDNS_WIREPARSE_ERR_SYNTAX_B64;
sldns_write_uint16(rd, (uint16_t)n);
*len = ((size_t)n)+2;
return LDNS_WIREPARSE_ERR_OK;
}
diff --git a/contrib/unbound/sldns/wire2str.c b/contrib/unbound/sldns/wire2str.c
index e6278ff560da..2b5dc0513f81 100644
--- a/contrib/unbound/sldns/wire2str.c
+++ b/contrib/unbound/sldns/wire2str.c
@@ -1,2374 +1,2456 @@
/*
* wire2str.c
*
* conversion routines from the wire format
* to the presentation format (strings)
*
* (c) NLnet Labs, 2004-2006
*
* See the file LICENSE for the license
*/
/**
* \file
*
* Contains functions to translate the wireformat to text
* representation, as well as functions to print them.
*/
#include "config.h"
#include "sldns/wire2str.h"
#include "sldns/str2wire.h"
#include "sldns/rrdef.h"
#include "sldns/pkthdr.h"
#include "sldns/parseutil.h"
#include "sldns/sbuffer.h"
#include "sldns/keyraw.h"
#include "util/data/dname.h"
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#include <sys/time.h>
#include <stdarg.h>
#include <ctype.h>
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
/* lookup tables for standard DNS stuff */
/* Taken from RFC 2535, section 7. */
static sldns_lookup_table sldns_algorithms_data[] = {
{ LDNS_RSAMD5, "RSAMD5" },
{ LDNS_DH, "DH" },
{ LDNS_DSA, "DSA" },
{ LDNS_ECC, "ECC" },
{ LDNS_RSASHA1, "RSASHA1" },
{ LDNS_DSA_NSEC3, "DSA-NSEC3-SHA1" },
{ LDNS_RSASHA1_NSEC3, "RSASHA1-NSEC3-SHA1" },
{ LDNS_RSASHA256, "RSASHA256"},
{ LDNS_RSASHA512, "RSASHA512"},
{ LDNS_ECC_GOST, "ECC-GOST"},
{ LDNS_ECDSAP256SHA256, "ECDSAP256SHA256"},
{ LDNS_ECDSAP384SHA384, "ECDSAP384SHA384"},
{ LDNS_ED25519, "ED25519"},
{ LDNS_ED448, "ED448"},
{ LDNS_INDIRECT, "INDIRECT" },
{ LDNS_PRIVATEDNS, "PRIVATEDNS" },
{ LDNS_PRIVATEOID, "PRIVATEOID" },
{ 0, NULL }
};
sldns_lookup_table* sldns_algorithms = sldns_algorithms_data;
/* hash algorithms in DS record */
static sldns_lookup_table sldns_hashes_data[] = {
{ LDNS_SHA1, "SHA1" },
{ LDNS_SHA256, "SHA256" },
{ LDNS_HASH_GOST, "HASH-GOST" },
{ LDNS_SHA384, "SHA384" },
{ 0, NULL }
};
sldns_lookup_table* sldns_hashes = sldns_hashes_data;
/* Taken from RFC 4398 */
static sldns_lookup_table sldns_cert_algorithms_data[] = {
{ LDNS_CERT_PKIX, "PKIX" },
{ LDNS_CERT_SPKI, "SPKI" },
{ LDNS_CERT_PGP, "PGP" },
{ LDNS_CERT_IPKIX, "IPKIX" },
{ LDNS_CERT_ISPKI, "ISPKI" },
{ LDNS_CERT_IPGP, "IPGP" },
{ LDNS_CERT_ACPKIX, "ACPKIX" },
{ LDNS_CERT_IACPKIX, "IACPKIX" },
{ LDNS_CERT_URI, "URI" },
{ LDNS_CERT_OID, "OID" },
{ 0, NULL }
};
sldns_lookup_table* sldns_cert_algorithms = sldns_cert_algorithms_data;
/* if these are used elsewhere */
static sldns_lookup_table sldns_rcodes_data[] = {
{ LDNS_RCODE_NOERROR, "NOERROR" },
{ LDNS_RCODE_FORMERR, "FORMERR" },
{ LDNS_RCODE_SERVFAIL, "SERVFAIL" },
{ LDNS_RCODE_NXDOMAIN, "NXDOMAIN" },
{ LDNS_RCODE_NOTIMPL, "NOTIMPL" },
{ LDNS_RCODE_REFUSED, "REFUSED" },
{ LDNS_RCODE_YXDOMAIN, "YXDOMAIN" },
{ LDNS_RCODE_YXRRSET, "YXRRSET" },
{ LDNS_RCODE_NXRRSET, "NXRRSET" },
{ LDNS_RCODE_NOTAUTH, "NOTAUTH" },
{ LDNS_RCODE_NOTZONE, "NOTZONE" },
{ 0, NULL }
};
sldns_lookup_table* sldns_rcodes = sldns_rcodes_data;
static sldns_lookup_table sldns_opcodes_data[] = {
{ LDNS_PACKET_QUERY, "QUERY" },
{ LDNS_PACKET_IQUERY, "IQUERY" },
{ LDNS_PACKET_STATUS, "STATUS" },
{ LDNS_PACKET_NOTIFY, "NOTIFY" },
{ LDNS_PACKET_UPDATE, "UPDATE" },
{ 0, NULL }
};
sldns_lookup_table* sldns_opcodes = sldns_opcodes_data;
static sldns_lookup_table sldns_wireparse_errors_data[] = {
{ LDNS_WIREPARSE_ERR_OK, "no parse error" },
{ LDNS_WIREPARSE_ERR_GENERAL, "parse error" },
{ LDNS_WIREPARSE_ERR_DOMAINNAME_OVERFLOW, "Domainname length overflow" },
{ LDNS_WIREPARSE_ERR_DOMAINNAME_UNDERFLOW, "Domainname length underflow (zero length)" },
{ LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL, "buffer too small" },
{ LDNS_WIREPARSE_ERR_LABEL_OVERFLOW, "Label length overflow" },
{ LDNS_WIREPARSE_ERR_EMPTY_LABEL, "Empty label" },
{ LDNS_WIREPARSE_ERR_SYNTAX_BAD_ESCAPE, "Syntax error, bad escape sequence" },
{ LDNS_WIREPARSE_ERR_SYNTAX, "Syntax error, could not parse the RR" },
{ LDNS_WIREPARSE_ERR_SYNTAX_TTL, "Syntax error, could not parse the RR's TTL" },
{ LDNS_WIREPARSE_ERR_SYNTAX_TYPE, "Syntax error, could not parse the RR's type" },
{ LDNS_WIREPARSE_ERR_SYNTAX_CLASS, "Syntax error, could not parse the RR's class" },
{ LDNS_WIREPARSE_ERR_SYNTAX_RDATA, "Syntax error, could not parse the RR's rdata" },
{ LDNS_WIREPARSE_ERR_SYNTAX_MISSING_VALUE, "Syntax error, value expected" },
{ LDNS_WIREPARSE_ERR_INVALID_STR, "Conversion error, string expected" },
{ LDNS_WIREPARSE_ERR_SYNTAX_B64, "Conversion error, b64 encoding expected" },
{ LDNS_WIREPARSE_ERR_SYNTAX_B32_EXT, "Conversion error, b32 ext encoding expected" },
{ LDNS_WIREPARSE_ERR_SYNTAX_HEX, "Conversion error, hex encoding expected" },
{ LDNS_WIREPARSE_ERR_CERT_BAD_ALGORITHM, "Bad algorithm type for CERT record" },
{ LDNS_WIREPARSE_ERR_SYNTAX_TIME, "Conversion error, time encoding expected" },
{ LDNS_WIREPARSE_ERR_SYNTAX_PERIOD, "Conversion error, time period encoding expected" },
{ LDNS_WIREPARSE_ERR_SYNTAX_ILNP64, "Conversion error, 4 colon separated hex numbers expected" },
{ LDNS_WIREPARSE_ERR_SYNTAX_EUI48,
"Conversion error, 6 two character hex numbers "
"separated by dashes expected (i.e. xx-xx-xx-xx-xx-xx" },
{ LDNS_WIREPARSE_ERR_SYNTAX_EUI64,
"Conversion error, 8 two character hex numbers "
"separated by dashes expected (i.e. xx-xx-xx-xx-xx-xx-xx-xx" },
{ LDNS_WIREPARSE_ERR_SYNTAX_TAG,
"Conversion error, a non-zero sequence of US-ASCII letters "
"and numbers in lower case expected" },
{ LDNS_WIREPARSE_ERR_NOT_IMPL, "not implemented" },
{ LDNS_WIREPARSE_ERR_SYNTAX_INT, "Conversion error, integer expected" },
{ LDNS_WIREPARSE_ERR_SYNTAX_IP4, "Conversion error, ip4 addr expected" },
{ LDNS_WIREPARSE_ERR_SYNTAX_IP6, "Conversion error, ip6 addr expected" },
{ LDNS_WIREPARSE_ERR_SYNTAX_INTEGER_OVERFLOW, "Syntax error, integer overflow" },
{ LDNS_WIREPARSE_ERR_INCLUDE, "$INCLUDE directive was seen in the zone" },
{ LDNS_WIREPARSE_ERR_PARENTHESIS, "Parse error, parenthesis mismatch" },
{ LDNS_WIREPARSE_ERR_SVCB_UNKNOWN_KEY, "Unknown SvcParamKey"},
{ LDNS_WIREPARSE_ERR_SVCB_MISSING_PARAM, "SvcParam is missing a SvcParamValue"},
{ LDNS_WIREPARSE_ERR_SVCB_DUPLICATE_KEYS, "Duplicate SVCB key found"},
{ LDNS_WIREPARSE_ERR_SVCB_MANDATORY_TOO_MANY_KEYS, "Too many keys in mandatory" },
{ LDNS_WIREPARSE_ERR_SVCB_TOO_MANY_PARAMS,
"Too many SvcParams. Unbound only allows 63 entries" },
{ LDNS_WIREPARSE_ERR_SVCB_MANDATORY_MISSING_PARAM,
"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,
"mandatory MUST not be included as mandatory parameter" },
{ LDNS_WIREPARSE_ERR_SVCB_PORT_VALUE_SYNTAX,
"Could not parse port SvcParamValue" },
{ LDNS_WIREPARSE_ERR_SVCB_IPV4_TOO_MANY_ADDRESSES,
"Too many IPv4 addresses in ipv4hint" },
{ LDNS_WIREPARSE_ERR_SVCB_IPV6_TOO_MANY_ADDRESSES,
"Too many IPv6 addresses in ipv6hint" },
{ LDNS_WIREPARSE_ERR_SVCB_ALPN_KEY_TOO_LARGE,
"Alpn strings need to be smaller than 255 chars"},
{ LDNS_WIREPARSE_ERR_SVCB_NO_DEFAULT_ALPN_VALUE,
"No-default-alpn should not have a value" },
{ LDNS_WIREPARSE_ERR_SVCPARAM_BROKEN_RDATA,
"General SVCParam error" },
{ 0, NULL }
};
sldns_lookup_table* sldns_wireparse_errors = sldns_wireparse_errors_data;
static sldns_lookup_table sldns_edns_flags_data[] = {
{ 3600, "do"},
{ 0, NULL}
};
sldns_lookup_table* sldns_edns_flags = sldns_edns_flags_data;
static sldns_lookup_table sldns_edns_options_data[] = {
{ 1, "LLQ" },
{ 2, "UL" },
{ 3, "NSID" },
/* 4 draft-cheshire-edns0-owner-option */
{ 5, "DAU" },
{ 6, "DHU" },
{ 7, "N3U" },
{ 8, "edns-client-subnet" },
+ { 10, "COOKIE" },
{ 11, "edns-tcp-keepalive"},
{ 12, "Padding" },
{ 15, "EDE"},
{ 0, NULL}
};
sldns_lookup_table* sldns_edns_options = sldns_edns_options_data;
+/* From RFC8914 5.2 Table 3, the "Extended DNS Error Codes" registry. */
+static sldns_lookup_table sldns_edns_ede_codes_data[] = {
+ { LDNS_EDE_NONE, "None" },
+ { LDNS_EDE_OTHER, "Other Error" },
+ { LDNS_EDE_UNSUPPORTED_DNSKEY_ALG, "Unsupported DNSKEY Algorithm" },
+ { LDNS_EDE_UNSUPPORTED_DS_DIGEST, "Unsupported DS Digest Type" },
+ { LDNS_EDE_STALE_ANSWER, "Stale Answer" },
+ { LDNS_EDE_FORGED_ANSWER, "Forged Answer" },
+ { LDNS_EDE_DNSSEC_INDETERMINATE, "DNSSEC Indeterminate" },
+ { LDNS_EDE_DNSSEC_BOGUS, "DNSSEC Bogus" },
+ { LDNS_EDE_SIGNATURE_EXPIRED, "Signature Expired" },
+ { LDNS_EDE_SIGNATURE_NOT_YET_VALID, "Signature Not Yet Valid" },
+ { LDNS_EDE_DNSKEY_MISSING, "DNSKEY Missing" },
+ { LDNS_EDE_RRSIGS_MISSING, "RRSIGs Missing" },
+ { LDNS_EDE_NO_ZONE_KEY_BIT_SET, "No Zone Key Bit Set" },
+ { LDNS_EDE_NSEC_MISSING, "NSEC Missing" },
+ { LDNS_EDE_CACHED_ERROR, "Cached Error" },
+ { LDNS_EDE_NOT_READY, "Not Ready" },
+ { LDNS_EDE_BLOCKED, "Blocked" },
+ { LDNS_EDE_CENSORED, "Censored" },
+ { LDNS_EDE_FILTERED, "Filtered" },
+ { LDNS_EDE_PROHIBITED, "Prohibited" },
+ { LDNS_EDE_STALE_NXDOMAIN_ANSWER, "Stale NXDOMAIN Answer" },
+ { LDNS_EDE_NOT_AUTHORITATIVE, "Not Authoritative" },
+ { LDNS_EDE_NOT_SUPPORTED, "Not Supported" },
+ { LDNS_EDE_NO_REACHABLE_AUTHORITY, "No Reachable Authority" },
+ { LDNS_EDE_NETWORK_ERROR, "Network Error" },
+ { LDNS_EDE_INVALID_DATA, "Invalid Data" },
+ { 0, NULL}
+};
+sldns_lookup_table* sldns_edns_ede_codes = sldns_edns_ede_codes_data;
+
static sldns_lookup_table sldns_tsig_errors_data[] = {
{ LDNS_TSIG_ERROR_NOERROR, "NOERROR" },
{ LDNS_RCODE_FORMERR, "FORMERR" },
{ LDNS_RCODE_SERVFAIL, "SERVFAIL" },
{ LDNS_RCODE_NXDOMAIN, "NXDOMAIN" },
{ LDNS_RCODE_NOTIMPL, "NOTIMPL" },
{ LDNS_RCODE_REFUSED, "REFUSED" },
{ LDNS_RCODE_YXDOMAIN, "YXDOMAIN" },
{ LDNS_RCODE_YXRRSET, "YXRRSET" },
{ LDNS_RCODE_NXRRSET, "NXRRSET" },
{ LDNS_RCODE_NOTAUTH, "NOTAUTH" },
{ LDNS_RCODE_NOTZONE, "NOTZONE" },
{ LDNS_TSIG_ERROR_BADSIG, "BADSIG" },
{ LDNS_TSIG_ERROR_BADKEY, "BADKEY" },
{ LDNS_TSIG_ERROR_BADTIME, "BADTIME" },
{ LDNS_TSIG_ERROR_BADMODE, "BADMODE" },
{ LDNS_TSIG_ERROR_BADNAME, "BADNAME" },
{ LDNS_TSIG_ERROR_BADALG, "BADALG" },
{ 0, NULL }
};
sldns_lookup_table* sldns_tsig_errors = sldns_tsig_errors_data;
/* draft-ietf-dnsop-svcb-https-06: 6. Initial SvcParamKeys */
const char *svcparamkey_strs[] = {
"mandatory", "alpn", "no-default-alpn", "port",
"ipv4hint", "ech", "ipv6hint", "dohpath"
};
char* sldns_wire2str_pkt(uint8_t* data, size_t len)
{
size_t slen = (size_t)sldns_wire2str_pkt_buf(data, len, NULL, 0);
char* result = (char*)malloc(slen+1);
if(!result) return NULL;
sldns_wire2str_pkt_buf(data, len, result, slen+1);
return result;
}
char* sldns_wire2str_rr(uint8_t* rr, size_t len)
{
size_t slen = (size_t)sldns_wire2str_rr_buf(rr, len, NULL, 0);
char* result = (char*)malloc(slen+1);
if(!result) return NULL;
sldns_wire2str_rr_buf(rr, len, result, slen+1);
return result;
}
char* sldns_wire2str_type(uint16_t rrtype)
{
char buf[16];
sldns_wire2str_type_buf(rrtype, buf, sizeof(buf));
return strdup(buf);
}
char* sldns_wire2str_class(uint16_t rrclass)
{
char buf[16];
sldns_wire2str_class_buf(rrclass, buf, sizeof(buf));
return strdup(buf);
}
char* sldns_wire2str_dname(uint8_t* dname, size_t dname_len)
{
size_t slen=(size_t)sldns_wire2str_dname_buf(dname, dname_len, NULL, 0);
char* result = (char*)malloc(slen+1);
if(!result) return NULL;
sldns_wire2str_dname_buf(dname, dname_len, result, slen+1);
return result;
}
char* sldns_wire2str_rcode(int rcode)
{
char buf[16];
sldns_wire2str_rcode_buf(rcode, buf, sizeof(buf));
return strdup(buf);
}
int sldns_wire2str_pkt_buf(uint8_t* d, size_t dlen, char* s, size_t slen)
{
/* use arguments as temporary variables */
return sldns_wire2str_pkt_scan(&d, &dlen, &s, &slen);
}
int sldns_wire2str_rr_buf(uint8_t* d, size_t dlen, char* s, size_t slen)
{
/* use arguments as temporary variables */
return sldns_wire2str_rr_scan(&d, &dlen, &s, &slen, NULL, 0, NULL);
}
int sldns_wire2str_rrquestion_buf(uint8_t* d, size_t dlen, char* s, size_t slen)
{
/* use arguments as temporary variables */
return sldns_wire2str_rrquestion_scan(&d, &dlen, &s, &slen, NULL, 0, NULL);
}
int sldns_wire2str_rdata_buf(uint8_t* rdata, size_t rdata_len, char* str,
size_t str_len, uint16_t rrtype)
{
/* use arguments as temporary variables */
return sldns_wire2str_rdata_scan(&rdata, &rdata_len, &str, &str_len,
rrtype, NULL, 0, NULL);
}
int sldns_wire2str_rr_unknown_buf(uint8_t* d, size_t dlen, char* s, size_t slen)
{
/* use arguments as temporary variables */
return sldns_wire2str_rr_unknown_scan(&d, &dlen, &s, &slen, NULL, 0, NULL);
}
int sldns_wire2str_rr_comment_buf(uint8_t* rr, size_t rrlen, size_t dname_len,
char* s, size_t slen)
{
uint16_t rrtype = sldns_wirerr_get_type(rr, rrlen, dname_len);
return sldns_wire2str_rr_comment_print(&s, &slen, rr, rrlen, dname_len,
rrtype);
}
int sldns_wire2str_type_buf(uint16_t rrtype, char* s, size_t slen)
{
/* use arguments as temporary variables */
return sldns_wire2str_type_print(&s, &slen, rrtype);
}
int sldns_wire2str_class_buf(uint16_t rrclass, char* s, size_t slen)
{
/* use arguments as temporary variables */
return sldns_wire2str_class_print(&s, &slen, rrclass);
}
int sldns_wire2str_rcode_buf(int rcode, char* s, size_t slen)
{
/* use arguments as temporary variables */
return sldns_wire2str_rcode_print(&s, &slen, rcode);
}
int sldns_wire2str_opcode_buf(int opcode, char* s, size_t slen)
{
/* use arguments as temporary variables */
return sldns_wire2str_opcode_print(&s, &slen, opcode);
}
int sldns_wire2str_dname_buf(uint8_t* d, size_t dlen, char* s, size_t slen)
{
/* use arguments as temporary variables */
return sldns_wire2str_dname_scan(&d, &dlen, &s, &slen, NULL, 0, NULL);
}
int sldns_str_vprint(char** str, size_t* slen, const char* format, va_list args)
{
int w = vsnprintf(*str, *slen, format, args);
if(w < 0) {
/* error in printout */
return 0;
} else if((size_t)w >= *slen) {
*str = NULL; /* we do not want str to point outside of buffer*/
*slen = 0;
} else {
*str += w;
*slen -= w;
}
return w;
}
int sldns_str_print(char** str, size_t* slen, const char* format, ...)
{
int w;
va_list args;
va_start(args, format);
w = sldns_str_vprint(str, slen, format, args);
va_end(args);
return w;
}
/** print hex format into text buffer for specified length */
static int print_hex_buf(char** s, size_t* slen, uint8_t* buf, size_t len)
{
const char* hex = "0123456789ABCDEF";
size_t i;
for(i=0; i<len; i++) {
(void)sldns_str_print(s, slen, "%c%c", hex[(buf[i]&0xf0)>>4],
hex[buf[i]&0x0f]);
}
return (int)len*2;
}
/** print remainder of buffer in hex format with prefixed text */
static int print_remainder_hex(const char* pref, uint8_t** d, size_t* dlen,
char** s, size_t* slen)
{
int w = 0;
w += sldns_str_print(s, slen, "%s", pref);
w += print_hex_buf(s, slen, *d, *dlen);
*d += *dlen;
*dlen = 0;
return w;
}
int sldns_wire2str_pkt_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen)
{
int w = 0, comprloop = 0;
unsigned qdcount, ancount, nscount, arcount, i;
uint8_t* pkt = *d;
size_t pktlen = *dlen;
if(*dlen >= LDNS_HEADER_SIZE) {
qdcount = (unsigned)LDNS_QDCOUNT(*d);
ancount = (unsigned)LDNS_ANCOUNT(*d);
nscount = (unsigned)LDNS_NSCOUNT(*d);
arcount = (unsigned)LDNS_ARCOUNT(*d);
} else {
qdcount = ancount = nscount = arcount = 0;
}
w += sldns_wire2str_header_scan(d, dlen, s, slen);
w += sldns_str_print(s, slen, "\n");
w += sldns_str_print(s, slen, ";; QUESTION SECTION:\n");
for(i=0; i<qdcount; i++) {
w += sldns_wire2str_rrquestion_scan(d, dlen, s, slen,
pkt, pktlen, &comprloop);
if(!*dlen) break;
}
w += sldns_str_print(s, slen, "\n");
w += sldns_str_print(s, slen, ";; ANSWER SECTION:\n");
for(i=0; i<ancount; i++) {
w += sldns_wire2str_rr_scan(d, dlen, s, slen, pkt, pktlen, &comprloop);
if(!*dlen) break;
}
w += sldns_str_print(s, slen, "\n");
w += sldns_str_print(s, slen, ";; AUTHORITY SECTION:\n");
for(i=0; i<nscount; i++) {
w += sldns_wire2str_rr_scan(d, dlen, s, slen, pkt, pktlen, &comprloop);
if(!*dlen) break;
}
w += sldns_str_print(s, slen, "\n");
w += sldns_str_print(s, slen, ";; ADDITIONAL SECTION:\n");
for(i=0; i<arcount; i++) {
w += sldns_wire2str_rr_scan(d, dlen, s, slen, pkt, pktlen, &comprloop);
if(!*dlen) break;
}
/* other fields: WHEN(time), SERVER(IP) not available here. */
w += sldns_str_print(s, slen, ";; MSG SIZE rcvd: %d\n", (int)pktlen);
if(*dlen > 0) {
w += print_remainder_hex(";; trailing garbage 0x",
d, dlen, s, slen);
w += sldns_str_print(s, slen, "\n");
}
return w;
}
/** scan type, class and ttl and printout, for rr */
static int sldns_rr_tcttl_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
int w = 0;
uint16_t t, c;
uint32_t ttl;
if(*dl < 8) {
if(*dl < 4)
return w + print_remainder_hex("; Error malformed 0x",
d, dl, s, sl);
/* these print values or 0x.. if none left */
t = sldns_read_uint16(*d);
c = sldns_read_uint16((*d)+2);
(*d)+=4;
(*dl)-=4;
w += sldns_wire2str_class_print(s, sl, c);
w += sldns_str_print(s, sl, "\t");
w += sldns_wire2str_type_print(s, sl, t);
if(*dl == 0)
return w + sldns_str_print(s, sl, "; Error no ttl");
return w + print_remainder_hex(
"; Error malformed ttl 0x", d, dl, s, sl);
}
t = sldns_read_uint16(*d);
c = sldns_read_uint16((*d)+2);
ttl = sldns_read_uint32((*d)+4);
(*d)+=8;
(*dl)-=8;
w += sldns_str_print(s, sl, "%lu\t", (unsigned long)ttl);
w += sldns_wire2str_class_print(s, sl, c);
w += sldns_str_print(s, sl, "\t");
w += sldns_wire2str_type_print(s, sl, t);
return w;
}
int sldns_wire2str_rr_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen,
uint8_t* pkt, size_t pktlen, int* comprloop)
{
int w = 0;
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 */
return sldns_wire2str_edns_scan(d, dlen, s, slen, pkt, pktlen);
}
/* try to scan the rdata with pretty-printing, but if that fails, then
* scan the rdata as an unknown RR type */
w += sldns_wire2str_dname_scan(d, dlen, s, slen, pkt, pktlen, comprloop);
w += sldns_str_print(s, slen, "\t");
dname_off = rrlen-(*dlen);
if(*dlen == 4) {
/* like a question-RR */
uint16_t t = sldns_read_uint16(*d);
uint16_t c = sldns_read_uint16((*d)+2);
(*d)+=4;
(*dlen)-=4;
w += sldns_wire2str_class_print(s, slen, c);
w += sldns_str_print(s, slen, "\t");
w += sldns_wire2str_type_print(s, slen, t);
w += sldns_str_print(s, slen, " ; Error no ttl,rdata\n");
return w;
}
if(*dlen < 8) {
if(*dlen == 0)
return w + sldns_str_print(s, slen, ";Error missing RR\n");
w += print_remainder_hex(";Error partial RR 0x", d, dlen, s, slen);
return w + sldns_str_print(s, slen, "\n");
}
rrtype = sldns_read_uint16(*d);
w += sldns_rr_tcttl_scan(d, dlen, s, slen);
w += sldns_str_print(s, slen, "\t");
/* rdata */
if(*dlen < 2) {
if(*dlen == 0)
return w + sldns_str_print(s, slen, ";Error missing rdatalen\n");
w += print_remainder_hex(";Error missing rdatalen 0x",
d, dlen, s, slen);
return w + sldns_str_print(s, slen, "\n");
}
rdlen = sldns_read_uint16(*d);
ordlen = rdlen;
(*d)+=2;
(*dlen)-=2;
if(*dlen < rdlen) {
w += sldns_str_print(s, slen, "\\# %u ", (unsigned)rdlen);
if(*dlen == 0)
return w + sldns_str_print(s, slen, ";Error missing rdata\n");
w += print_remainder_hex(";Error partial rdata 0x", d, dlen, s, slen);
return w + sldns_str_print(s, slen, "\n");
}
w += sldns_wire2str_rdata_scan(d, &rdlen, s, slen, rrtype, pkt, pktlen,
comprloop);
(*dlen) -= (ordlen-rdlen);
/* default comment */
w += sldns_wire2str_rr_comment_print(s, slen, rr, rrlen, dname_off,
rrtype);
w += sldns_str_print(s, slen, "\n");
return w;
}
int sldns_wire2str_rrquestion_scan(uint8_t** d, size_t* dlen, char** s,
size_t* slen, uint8_t* pkt, size_t pktlen, int* comprloop)
{
int w = 0;
uint16_t t, c;
w += sldns_wire2str_dname_scan(d, dlen, s, slen, pkt, pktlen, comprloop);
w += sldns_str_print(s, slen, "\t");
if(*dlen < 4) {
if(*dlen == 0)
return w + sldns_str_print(s, slen, "Error malformed\n");
w += print_remainder_hex("Error malformed 0x", d, dlen, s, slen);
return w + sldns_str_print(s, slen, "\n");
}
t = sldns_read_uint16(*d);
c = sldns_read_uint16((*d)+2);
(*d)+=4;
(*dlen)-=4;
w += sldns_wire2str_class_print(s, slen, c);
w += sldns_str_print(s, slen, "\t");
w += sldns_wire2str_type_print(s, slen, t);
w += sldns_str_print(s, slen, "\n");
return w;
}
int sldns_wire2str_rr_unknown_scan(uint8_t** d, size_t* dlen, char** s,
size_t* slen, uint8_t* pkt, size_t pktlen, int* comprloop)
{
size_t rdlen, ordlen;
int w = 0;
w += sldns_wire2str_dname_scan(d, dlen, s, slen, pkt, pktlen, comprloop);
w += sldns_str_print(s, slen, "\t");
w += sldns_rr_tcttl_scan(d, dlen, s, slen);
w += sldns_str_print(s, slen, "\t");
if(*dlen < 2) {
if(*dlen == 0)
return w + sldns_str_print(s, slen, ";Error missing rdatalen\n");
w += print_remainder_hex(";Error missing rdatalen 0x",
d, dlen, s, slen);
return w + sldns_str_print(s, slen, "\n");
}
rdlen = sldns_read_uint16(*d);
ordlen = rdlen;
(*d) += 2;
(*dlen) -= 2;
if(*dlen < rdlen) {
w += sldns_str_print(s, slen, "\\# %u ", (unsigned)rdlen);
if(*dlen == 0)
return w + sldns_str_print(s, slen, ";Error missing rdata\n");
w += print_remainder_hex(";Error partial rdata 0x", d, dlen, s, slen);
return w + sldns_str_print(s, slen, "\n");
}
w += sldns_wire2str_rdata_unknown_scan(d, &rdlen, s, slen);
(*dlen) -= (ordlen-rdlen);
w += sldns_str_print(s, slen, "\n");
return w;
}
/** print rr comment for type DNSKEY */
static int rr_comment_dnskey(char** s, size_t* slen, uint8_t* rr,
size_t rrlen, size_t dname_off)
{
size_t rdlen;
uint8_t* rdata;
int flags, w = 0;
if(rrlen < dname_off + 10) return 0;
rdlen = sldns_read_uint16(rr+dname_off+8);
if(rrlen < dname_off + 10 + rdlen) return 0;
if(rdlen < 2) return 0;
rdata = rr + dname_off + 10;
flags = (int)sldns_read_uint16(rdata);
w += sldns_str_print(s, slen, " ;{");
/* id */
w += sldns_str_print(s, slen, "id = %u",
sldns_calc_keytag_raw(rdata, rdlen));
/* flags */
if((flags&LDNS_KEY_ZONE_KEY)) {
if((flags&LDNS_KEY_SEP_KEY))
w += sldns_str_print(s, slen, " (ksk)");
else w += sldns_str_print(s, slen, " (zsk)");
}
/* keysize */
if(rdlen > 4) {
w += sldns_str_print(s, slen, ", ");
w += sldns_str_print(s, slen, "size = %db",
(int)sldns_rr_dnskey_key_size_raw(
(unsigned char*)rdata+4, rdlen-4, (int)(rdata[3])));
}
w += sldns_str_print(s, slen, "}");
return w;
}
/** print rr comment for type RRSIG */
static int rr_comment_rrsig(char** s, size_t* slen, uint8_t* rr,
size_t rrlen, size_t dname_off)
{
size_t rdlen;
uint8_t* rdata;
if(rrlen < dname_off + 10) return 0;
rdlen = sldns_read_uint16(rr+dname_off+8);
if(rrlen < dname_off + 10 + rdlen) return 0;
rdata = rr + dname_off + 10;
if(rdlen < 18) return 0;
return sldns_str_print(s, slen, " ;{id = %d}",
(int)sldns_read_uint16(rdata+16));
}
/** print rr comment for type NSEC3 */
static int rr_comment_nsec3(char** s, size_t* slen, uint8_t* rr,
size_t rrlen, size_t dname_off)
{
size_t rdlen;
uint8_t* rdata;
int w = 0;
if(rrlen < dname_off + 10) return 0;
rdlen = sldns_read_uint16(rr+dname_off+8);
if(rrlen < dname_off + 10 + rdlen) return 0;
rdata = rr + dname_off + 10;
if(rdlen < 2) return 0;
if((rdata[1] & LDNS_NSEC3_VARS_OPTOUT_MASK))
w += sldns_str_print(s, slen, " ;{flags: optout}");
return w;
}
int sldns_wire2str_rr_comment_print(char** s, size_t* slen, uint8_t* rr,
size_t rrlen, size_t dname_off, uint16_t rrtype)
{
if(rrtype == LDNS_RR_TYPE_DNSKEY) {
return rr_comment_dnskey(s, slen, rr, rrlen, dname_off);
} else if(rrtype == LDNS_RR_TYPE_RRSIG) {
return rr_comment_rrsig(s, slen, rr, rrlen, dname_off);
} else if(rrtype == LDNS_RR_TYPE_NSEC3) {
return rr_comment_nsec3(s, slen, rr, rrlen, dname_off);
}
return 0;
}
int sldns_wire2str_header_scan(uint8_t** d, size_t* dlen, char** s,
size_t* slen)
{
int w = 0;
int opcode, rcode;
w += sldns_str_print(s, slen, ";; ->>HEADER<<- ");
if(*dlen == 0)
return w+sldns_str_print(s, slen, "Error empty packet");
if(*dlen < 4)
return w+print_remainder_hex("Error header too short 0x", d, dlen, s, slen);
opcode = (int)LDNS_OPCODE_WIRE(*d);
rcode = (int)LDNS_RCODE_WIRE(*d);
w += sldns_str_print(s, slen, "opcode: ");
w += sldns_wire2str_opcode_print(s, slen, opcode);
w += sldns_str_print(s, slen, ", ");
w += sldns_str_print(s, slen, "rcode: ");
w += sldns_wire2str_rcode_print(s, slen, rcode);
w += sldns_str_print(s, slen, ", ");
w += sldns_str_print(s, slen, "id: %d\n", (int)LDNS_ID_WIRE(*d));
w += sldns_str_print(s, slen, ";; flags:");
if(LDNS_QR_WIRE(*d)) w += sldns_str_print(s, slen, " qr");
if(LDNS_AA_WIRE(*d)) w += sldns_str_print(s, slen, " aa");
if(LDNS_TC_WIRE(*d)) w += sldns_str_print(s, slen, " tc");
if(LDNS_RD_WIRE(*d)) w += sldns_str_print(s, slen, " rd");
if(LDNS_CD_WIRE(*d)) w += sldns_str_print(s, slen, " cd");
if(LDNS_RA_WIRE(*d)) w += sldns_str_print(s, slen, " ra");
if(LDNS_AD_WIRE(*d)) w += sldns_str_print(s, slen, " ad");
if(LDNS_Z_WIRE(*d)) w += sldns_str_print(s, slen, " z");
w += sldns_str_print(s, slen, " ; ");
if(*dlen < LDNS_HEADER_SIZE)
return w+print_remainder_hex("Error header too short 0x", d, dlen, s, slen);
w += sldns_str_print(s, slen, "QUERY: %d, ", (int)LDNS_QDCOUNT(*d));
w += sldns_str_print(s, slen, "ANSWER: %d, ", (int)LDNS_ANCOUNT(*d));
w += sldns_str_print(s, slen, "AUTHORITY: %d, ", (int)LDNS_NSCOUNT(*d));
w += sldns_str_print(s, slen, "ADDITIONAL: %d ", (int)LDNS_ARCOUNT(*d));
*d += LDNS_HEADER_SIZE;
*dlen -= LDNS_HEADER_SIZE;
return w;
}
int sldns_wire2str_rdata_scan(uint8_t** d, size_t* dlen, char** s,
size_t* slen, uint16_t rrtype, uint8_t* pkt, size_t pktlen,
int* comprloop)
{
/* try to prettyprint, but if that fails, use unknown format */
uint8_t* origd = *d;
char* origs = *s;
size_t origdlen = *dlen, origslen = *slen;
size_t r_cnt, r_max;
sldns_rdf_type rdftype;
int w = 0, n;
const sldns_rr_descriptor *desc = sldns_rr_descript(rrtype);
if(!desc) /* unknown format */
return sldns_wire2str_rdata_unknown_scan(d, dlen, s, slen);
/* dlen equals the rdatalen for the rdata */
r_max = sldns_rr_descriptor_maximum(desc);
for(r_cnt=0; r_cnt < r_max; r_cnt++) {
if(*dlen == 0) {
if(r_cnt < sldns_rr_descriptor_minimum(desc))
goto failed;
break; /* nothing more to print */
}
rdftype = sldns_rr_descriptor_field_type(desc, r_cnt);
if(r_cnt != 0)
w += sldns_str_print(s, slen, " ");
n = sldns_wire2str_rdf_scan(d, dlen, s, slen, rdftype,
pkt, pktlen, comprloop);
if(n == -1) {
failed:
/* failed, use unknown format */
*d = origd; *s = origs;
*dlen = origdlen; *slen = origslen;
return sldns_wire2str_rdata_unknown_scan(d, dlen,
s, slen);
}
w += n;
}
if(*dlen != 0) {
goto failed;
}
return w;
}
int sldns_wire2str_rdata_unknown_scan(uint8_t** d, size_t* dlen, char** s,
size_t* slen)
{
int w = 0;
/* print length */
w += sldns_str_print(s, slen, "\\# %u", (unsigned)*dlen);
/* print rdlen in hex */
if(*dlen != 0)
w += sldns_str_print(s, slen, " ");
w += print_hex_buf(s, slen, *d, *dlen);
(*d) += *dlen;
(*dlen) = 0;
return w;
}
/** print and escape one character for a domain dname */
static int dname_char_print(char** s, size_t* slen, uint8_t c)
{
if(c == '.' || c == ';' || c == '(' || c == ')' || c == '\\')
return sldns_str_print(s, slen, "\\%c", c);
else if(!(isascii((unsigned char)c) && isgraph((unsigned char)c)))
return sldns_str_print(s, slen, "\\%03u", (unsigned)c);
/* plain printout */
if(*slen) {
**s = (char)c;
(*s)++;
(*slen)--;
}
return 1;
}
int sldns_wire2str_dname_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen,
uint8_t* pkt, size_t pktlen, int* comprloop)
{
int w = 0;
/* spool labels onto the string, use compression if its there */
uint8_t* pos = *d;
unsigned i, counter=0;
unsigned maxcompr = MAX_COMPRESS_PTRS; /* loop detection, max compr ptrs */
int in_buf = 1;
size_t dname_len = 0;
if(comprloop) {
if(*comprloop != 0)
maxcompr = 30; /* for like ipv6 reverse name, per label */
if(*comprloop > 4)
maxcompr = 4; /* just don't want to spend time, any more */
}
if(*dlen == 0) return sldns_str_print(s, slen, "ErrorMissingDname");
if(*pos == 0) {
(*d)++;
(*dlen)--;
return sldns_str_print(s, slen, ".");
}
while((!pkt || pos < pkt+pktlen) && *pos) {
/* read label length */
uint8_t labellen = *pos++;
if(in_buf) { (*d)++; (*dlen)--; }
/* find out what sort of label we have */
if((labellen&0xc0) == 0xc0) {
/* compressed */
uint16_t target = 0;
if(in_buf && *dlen == 0)
return w + sldns_str_print(s, slen,
"ErrorPartialDname");
else if(!in_buf && pos+1 > pkt+pktlen)
return w + sldns_str_print(s, slen,
"ErrorPartialDname");
target = ((labellen&0x3f)<<8) | *pos;
if(in_buf) { (*d)++; (*dlen)--; }
/* move to target, if possible */
if(!pkt || target >= pktlen)
return w + sldns_str_print(s, slen,
"ErrorComprPtrOutOfBounds");
if(counter++ > maxcompr) {
if(comprloop && *comprloop < 10)
(*comprloop)++;
return w + sldns_str_print(s, slen,
"ErrorComprPtrLooped");
}
in_buf = 0;
pos = pkt+target;
continue;
} else if((labellen&0xc0)) {
/* notimpl label type */
w += sldns_str_print(s, slen,
"ErrorLABELTYPE%xIsUnknown",
(int)(labellen&0xc0));
return w;
}
/* spool label characters, end with '.' */
if(in_buf && *dlen < (size_t)labellen)
labellen = (uint8_t)*dlen;
else if(!in_buf && pos+(size_t)labellen > pkt+pktlen)
labellen = (uint8_t)(pkt + pktlen - pos);
dname_len += ((size_t)labellen)+1;
if(dname_len > LDNS_MAX_DOMAINLEN) {
/* dname_len counts the uncompressed length we have
* seen so far, and the domain name has become too
* long, prevent the loop from printing overly long
* content. */
w += sldns_str_print(s, slen,
"ErrorDomainNameTooLong");
return w;
}
for(i=0; i<(unsigned)labellen; i++) {
w += dname_char_print(s, slen, *pos++);
}
if(in_buf) {
(*d) += labellen;
(*dlen) -= labellen;
if(*dlen == 0) break;
}
w += sldns_str_print(s, slen, ".");
}
/* skip over final root label */
if(in_buf && *dlen > 0) { (*d)++; (*dlen)--; }
/* in case we printed no labels, terminate dname */
if(w == 0) w += sldns_str_print(s, slen, ".");
return w;
}
int sldns_wire2str_opcode_print(char** s, size_t* slen, int opcode)
{
sldns_lookup_table *lt = sldns_lookup_by_id(sldns_opcodes, opcode);
if (lt && lt->name) {
return sldns_str_print(s, slen, "%s", lt->name);
}
return sldns_str_print(s, slen, "OPCODE%u", (unsigned)opcode);
}
int sldns_wire2str_rcode_print(char** s, size_t* slen, int rcode)
{
sldns_lookup_table *lt = sldns_lookup_by_id(sldns_rcodes, rcode);
if (lt && lt->name) {
return sldns_str_print(s, slen, "%s", lt->name);
}
return sldns_str_print(s, slen, "RCODE%u", (unsigned)rcode);
}
int sldns_wire2str_class_print(char** s, size_t* slen, uint16_t rrclass)
{
sldns_lookup_table *lt = sldns_lookup_by_id(sldns_rr_classes,
(int)rrclass);
if (lt && lt->name) {
return sldns_str_print(s, slen, "%s", lt->name);
}
return sldns_str_print(s, slen, "CLASS%u", (unsigned)rrclass);
}
int sldns_wire2str_type_print(char** s, size_t* slen, uint16_t rrtype)
{
const sldns_rr_descriptor *descriptor = sldns_rr_descript(rrtype);
if (descriptor && descriptor->_name) {
return sldns_str_print(s, slen, "%s", descriptor->_name);
}
return sldns_str_print(s, slen, "TYPE%u", (unsigned)rrtype);
}
int sldns_wire2str_edns_option_code_print(char** s, size_t* slen,
uint16_t opcode)
{
sldns_lookup_table *lt = sldns_lookup_by_id(sldns_edns_options,
(int)opcode);
if (lt && lt->name) {
return sldns_str_print(s, slen, "%s", lt->name);
}
return sldns_str_print(s, slen, "OPT%u", (unsigned)opcode);
}
int sldns_wire2str_class_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen)
{
uint16_t c;
if(*dlen == 0) return 0;
if(*dlen < 2) return print_remainder_hex("Error malformed 0x", d, dlen, s, slen);
c = sldns_read_uint16(*d);
(*d)+=2;
(*dlen)-=2;
return sldns_wire2str_class_print(s, slen, c);
}
int sldns_wire2str_type_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen)
{
uint16_t t;
if(*dlen == 0) return 0;
if(*dlen < 2) return print_remainder_hex("Error malformed 0x", d, dlen, s, slen);
t = sldns_read_uint16(*d);
(*d)+=2;
(*dlen)-=2;
return sldns_wire2str_type_print(s, slen, t);
}
int sldns_wire2str_ttl_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen)
{
uint32_t ttl;
if(*dlen == 0) return 0;
if(*dlen < 4) return print_remainder_hex("Error malformed 0x", d, dlen, s, slen);
ttl = sldns_read_uint32(*d);
(*d)+=4;
(*dlen)-=4;
return sldns_str_print(s, slen, "%u", (unsigned)ttl);
}
static int
sldns_print_svcparamkey(char** s, size_t* slen, uint16_t svcparamkey)
{
if (svcparamkey < SVCPARAMKEY_COUNT) {
return sldns_str_print(s, slen, "%s", svcparamkey_strs[svcparamkey]);
}
else {
return sldns_str_print(s, slen, "key%d", (int)svcparamkey);
}
}
static int sldns_wire2str_svcparam_port2str(char** s,
size_t* slen, uint16_t data_len, uint8_t* data)
{
int w = 0;
if (data_len != 2)
return -1; /* wireformat error, a short is 2 bytes */
w = sldns_str_print(s, slen, "=%d", (int)sldns_read_uint16(data));
return w;
}
static int sldns_wire2str_svcparam_ipv4hint2str(char** s,
size_t* slen, uint16_t data_len, uint8_t* data)
{
char ip_str[INET_ADDRSTRLEN + 1];
int w = 0;
assert(data_len > 0);
if ((data_len % LDNS_IP4ADDRLEN) == 0) {
if (inet_ntop(AF_INET, data, ip_str, sizeof(ip_str)) == NULL)
return -1; /* wireformat error, incorrect size or inet family */
w += sldns_str_print(s, slen, "=%s", ip_str);
data += LDNS_IP4ADDRLEN;
while ((data_len -= LDNS_IP4ADDRLEN) > 0) {
if (inet_ntop(AF_INET, data, ip_str, sizeof(ip_str)) == NULL)
return -1; /* wireformat error, incorrect size or inet family */
w += sldns_str_print(s, slen, ",%s", ip_str);
data += LDNS_IP4ADDRLEN;
}
} else
return -1;
return w;
}
static int sldns_wire2str_svcparam_ipv6hint2str(char** s,
size_t* slen, uint16_t data_len, uint8_t* data)
{
char ip_str[INET6_ADDRSTRLEN + 1];
int w = 0;
assert(data_len > 0);
if ((data_len % LDNS_IP6ADDRLEN) == 0) {
if (inet_ntop(AF_INET6, data, ip_str, sizeof(ip_str)) == NULL)
return -1; /* wireformat error, incorrect size or inet family */
w += sldns_str_print(s, slen, "=%s", ip_str);
data += LDNS_IP6ADDRLEN;
while ((data_len -= LDNS_IP6ADDRLEN) > 0) {
if (inet_ntop(AF_INET6, data, ip_str, sizeof(ip_str)) == NULL)
return -1; /* wireformat error, incorrect size or inet family */
w += sldns_str_print(s, slen, ",%s", ip_str);
data += LDNS_IP6ADDRLEN;
}
} else
return -1;
return w;
}
static int sldns_wire2str_svcparam_mandatory2str(char** s,
size_t* slen, uint16_t data_len, uint8_t* data)
{
int w = 0;
assert(data_len > 0);
if (data_len % sizeof(uint16_t))
return -1; /* wireformat error, data_len must be multiple of shorts */
w += sldns_str_print(s, slen, "=");
w += sldns_print_svcparamkey(s, slen, sldns_read_uint16(data));
data += 2;
while ((data_len -= sizeof(uint16_t))) {
w += sldns_str_print(s, slen, ",");
w += sldns_print_svcparamkey(s, slen, sldns_read_uint16(data));
data += 2;
}
return w;
}
static int sldns_wire2str_svcparam_alpn2str(char** s,
size_t* slen, uint16_t data_len, uint8_t* data)
{
uint8_t *dp = (void *)data;
int w = 0;
assert(data_len > 0); /* Guaranteed by sldns_wire2str_svcparam_scan */
w += sldns_str_print(s, slen, "=\"");
while (data_len) {
/* alpn is list of length byte (str_len) followed by a string of that size */
uint8_t i, str_len = *dp++;
if (str_len > --data_len)
return -1;
for (i = 0; i < str_len; i++) {
if (dp[i] == '"' || dp[i] == '\\')
w += sldns_str_print(s, slen, "\\\\\\%c", dp[i]);
else if (dp[i] == ',')
w += sldns_str_print(s, slen, "\\\\%c", dp[i]);
else if (!isprint(dp[i]))
w += sldns_str_print(s, slen, "\\%03u", (unsigned) dp[i]);
else
w += sldns_str_print(s, slen, "%c", dp[i]);
}
dp += str_len;
if ((data_len -= str_len))
w += sldns_str_print(s, slen, "%s", ",");
}
w += sldns_str_print(s, slen, "\"");
return w;
}
static int sldns_wire2str_svcparam_ech2str(char** s,
size_t* slen, uint16_t data_len, uint8_t* data)
{
int size;
int w = 0;
assert(data_len > 0); /* Guaranteed by sldns_wire2str_svcparam_scan */
w += sldns_str_print(s, slen, "=\"");
if ((size = sldns_b64_ntop(data, data_len, *s, *slen)) < 0)
return -1;
(*s) += size;
(*slen) -= size;
w += sldns_str_print(s, slen, "\"");
return w + size;
}
int sldns_wire2str_svcparam_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen)
{
uint8_t ch;
uint16_t svcparamkey, data_len;
int written_chars = 0;
int r, i;
/* verify that we have enough data to read svcparamkey and data_len */
if(*dlen < 4)
return -1;
svcparamkey = sldns_read_uint16(*d);
data_len = sldns_read_uint16(*d+2);
*d += 4;
*dlen -= 4;
/* verify that we have data_len data */
if (data_len > *dlen)
return -1;
written_chars += sldns_print_svcparamkey(s, slen, svcparamkey);
if (!data_len) {
/* Some SvcParams MUST have values */
switch (svcparamkey) {
case SVCB_KEY_ALPN:
case SVCB_KEY_PORT:
case SVCB_KEY_IPV4HINT:
case SVCB_KEY_IPV6HINT:
case SVCB_KEY_MANDATORY:
case SVCB_KEY_DOHPATH:
return -1;
default:
return written_chars;
}
}
switch (svcparamkey) {
case SVCB_KEY_PORT:
r = sldns_wire2str_svcparam_port2str(s, slen, data_len, *d);
break;
case SVCB_KEY_IPV4HINT:
r = sldns_wire2str_svcparam_ipv4hint2str(s, slen, data_len, *d);
break;
case SVCB_KEY_IPV6HINT:
r = sldns_wire2str_svcparam_ipv6hint2str(s, slen, data_len, *d);
break;
case SVCB_KEY_MANDATORY:
r = sldns_wire2str_svcparam_mandatory2str(s, slen, data_len, *d);
break;
case SVCB_KEY_NO_DEFAULT_ALPN:
return -1; /* wireformat error, should not have a value */
case SVCB_KEY_ALPN:
r = sldns_wire2str_svcparam_alpn2str(s, slen, data_len, *d);
break;
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, "=\"");
for (i = 0; i < data_len; i++) {
ch = (*d)[i];
if (ch == '"' || ch == '\\')
r += sldns_str_print(s, slen, "\\%c", ch);
else if (!isprint(ch))
r += sldns_str_print(s, slen, "\\%03u", (unsigned) ch);
else
r += sldns_str_print(s, slen, "%c", ch);
}
r += sldns_str_print(s, slen, "\"");
break;
}
if (r <= 0)
return -1; /* wireformat error */
written_chars += r;
*d += data_len;
*dlen -= data_len;
return written_chars;
}
int sldns_wire2str_rdf_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen,
int rdftype, uint8_t* pkt, size_t pktlen, int* comprloop)
{
if(*dlen == 0) return 0;
switch(rdftype) {
case LDNS_RDF_TYPE_NONE:
return 0;
case LDNS_RDF_TYPE_DNAME:
return sldns_wire2str_dname_scan(d, dlen, s, slen, pkt, pktlen, comprloop);
case LDNS_RDF_TYPE_INT8:
return sldns_wire2str_int8_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_INT16:
return sldns_wire2str_int16_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_INT32:
return sldns_wire2str_int32_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_PERIOD:
return sldns_wire2str_period_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_TSIGTIME:
return sldns_wire2str_tsigtime_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_A:
return sldns_wire2str_a_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_AAAA:
return sldns_wire2str_aaaa_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_STR:
return sldns_wire2str_str_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_APL:
return sldns_wire2str_apl_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_B32_EXT:
return sldns_wire2str_b32_ext_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_B64:
return sldns_wire2str_b64_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_HEX:
return sldns_wire2str_hex_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_NSEC:
return sldns_wire2str_nsec_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_NSEC3_SALT:
return sldns_wire2str_nsec3_salt_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_TYPE:
return sldns_wire2str_type_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_CLASS:
return sldns_wire2str_class_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_CERT_ALG:
return sldns_wire2str_cert_alg_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_ALG:
return sldns_wire2str_alg_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_UNKNOWN:
return sldns_wire2str_unknown_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_TIME:
return sldns_wire2str_time_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_LOC:
return sldns_wire2str_loc_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_WKS:
case LDNS_RDF_TYPE_SERVICE:
return sldns_wire2str_wks_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_NSAP:
return sldns_wire2str_nsap_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_ATMA:
return sldns_wire2str_atma_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_IPSECKEY:
return sldns_wire2str_ipseckey_scan(d, dlen, s, slen, pkt,
pktlen, comprloop);
case LDNS_RDF_TYPE_HIP:
return sldns_wire2str_hip_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_INT16_DATA:
return sldns_wire2str_int16_data_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_NSEC3_NEXT_OWNER:
return sldns_wire2str_b32_ext_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_ILNP64:
return sldns_wire2str_ilnp64_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_EUI48:
return sldns_wire2str_eui48_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_EUI64:
return sldns_wire2str_eui64_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_TAG:
return sldns_wire2str_tag_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_LONG_STR:
return sldns_wire2str_long_str_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_SVCPARAM:
return sldns_wire2str_svcparam_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_TSIGERROR:
return sldns_wire2str_tsigerror_scan(d, dlen, s, slen);
}
/* unknown rdf type */
return -1;
}
int sldns_wire2str_int8_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
int w;
if(*dl < 1) return -1;
w = sldns_str_print(s, sl, "%u", (unsigned)**d);
(*d)++;
(*dl)--;
return w;
}
int sldns_wire2str_int16_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
int w;
if(*dl < 2) return -1;
w = sldns_str_print(s, sl, "%lu", (unsigned long)sldns_read_uint16(*d));
(*d)+=2;
(*dl)-=2;
return w;
}
int sldns_wire2str_int32_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
int w;
if(*dl < 4) return -1;
w = sldns_str_print(s, sl, "%lu", (unsigned long)sldns_read_uint32(*d));
(*d)+=4;
(*dl)-=4;
return w;
}
int sldns_wire2str_period_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
int w;
if(*dl < 4) return -1;
w = sldns_str_print(s, sl, "%u", (unsigned)sldns_read_uint32(*d));
(*d)+=4;
(*dl)-=4;
return w;
}
int sldns_wire2str_tsigtime_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
/* tsigtime is 48 bits network order unsigned integer */
int w;
uint64_t tsigtime = 0;
uint64_t d0, d1, d2, d3, d4, d5;
if(*dl < 6) return -1;
d0 = (*d)[0]; /* cast to uint64 for shift operations */
d1 = (*d)[1];
d2 = (*d)[2];
d3 = (*d)[3];
d4 = (*d)[4];
d5 = (*d)[5];
tsigtime = (d0<<40) | (d1<<32) | (d2<<24) | (d3<<16) | (d4<<8) | d5;
#ifndef USE_WINSOCK
w = sldns_str_print(s, sl, "%llu", (long long)tsigtime);
#else
w = sldns_str_print(s, sl, "%I64u", (long long)tsigtime);
#endif
(*d)+=6;
(*dl)-=6;
return w;
}
int sldns_wire2str_a_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
char buf[32];
int w;
if(*dl < 4) return -1;
if(!inet_ntop(AF_INET, *d, buf, (socklen_t)sizeof(buf)))
return -1;
w = sldns_str_print(s, sl, "%s", buf);
(*d)+=4;
(*dl)-=4;
return w;
}
int sldns_wire2str_aaaa_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
#ifdef AF_INET6
char buf[64];
int w;
if(*dl < 16) return -1;
if(!inet_ntop(AF_INET6, *d, buf, (socklen_t)sizeof(buf)))
return -1;
w = sldns_str_print(s, sl, "%s", buf);
(*d)+=16;
(*dl)-=16;
return w;
#else
return -1;
#endif
}
/** printout escaped TYPE_STR character */
static int str_char_print(char** s, size_t* sl, uint8_t c)
{
if(isprint((unsigned char)c) || c == '\t') {
if(c == '\"' || c == '\\')
return sldns_str_print(s, sl, "\\%c", c);
if(*sl) {
**s = (char)c;
(*s)++;
(*sl)--;
}
return 1;
}
return sldns_str_print(s, sl, "\\%03u", (unsigned)c);
}
int sldns_wire2str_str_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
int w = 0;
size_t i, len;
if(*dl < 1) return -1;
len = **d;
if(*dl < 1+len) return -1;
(*d)++;
(*dl)--;
w += sldns_str_print(s, sl, "\"");
for(i=0; i<len; i++)
w += str_char_print(s, sl, (*d)[i]);
w += sldns_str_print(s, sl, "\"");
(*d)+=len;
(*dl)-=len;
return w;
}
int sldns_wire2str_apl_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
int i, w = 0;
uint16_t family;
uint8_t negation, prefix, adflength;
if(*dl < 4) return -1;
family = sldns_read_uint16(*d);
prefix = (*d)[2];
negation = ((*d)[3] & LDNS_APL_NEGATION);
adflength = ((*d)[3] & LDNS_APL_MASK);
if(*dl < 4+(size_t)adflength) return -1;
if(family != LDNS_APL_IP4 && family != LDNS_APL_IP6)
return -1; /* unknown address family */
if(negation)
w += sldns_str_print(s, sl, "!");
w += sldns_str_print(s, sl, "%u:", (unsigned)family);
if(family == LDNS_APL_IP4) {
/* check if prefix <32 ? */
/* address is variable length 0 - 4 */
for(i=0; i<4; i++) {
if(i > 0)
w += sldns_str_print(s, sl, ".");
if(i < (int)adflength)
w += sldns_str_print(s, sl, "%d", (*d)[4+i]);
else w += sldns_str_print(s, sl, "0");
}
} else if(family == LDNS_APL_IP6) {
/* check if prefix <128 ? */
/* address is variable length 0 - 16 */
for(i=0; i<16; i++) {
if(i%2 == 0 && i>0)
w += sldns_str_print(s, sl, ":");
if(i < (int)adflength)
w += sldns_str_print(s, sl, "%02x", (*d)[4+i]);
else w += sldns_str_print(s, sl, "00");
}
}
w += sldns_str_print(s, sl, "/%u", (unsigned)prefix);
(*d) += 4+adflength;
(*dl) -= 4+adflength;
return w;
}
int sldns_wire2str_b32_ext_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
size_t datalen;
size_t sz;
if(*dl < 1) return -1;
datalen = (*d)[0];
if(*dl < 1+datalen) return -1;
sz = sldns_b32_ntop_calculate_size(datalen);
if(*sl < sz+1) {
(*d) += datalen+1;
(*dl) -= (datalen+1);
return (int)sz; /* out of space really, but would need buffer
in order to truncate the output */
}
sldns_b32_ntop_extended_hex((*d)+1, datalen, *s, *sl);
(*d) += datalen+1;
(*dl) -= (datalen+1);
(*s) += sz;
(*sl) -= sz;
return (int)sz;
}
/** scan number of bytes from wire into b64 presentation format */
static int sldns_wire2str_b64_scan_num(uint8_t** d, size_t* dl, char** s,
size_t* sl, size_t num)
{
/* b64_ntop_calculate size includes null at the end */
size_t sz = sldns_b64_ntop_calculate_size(num)-1;
if(*sl < sz+1) {
(*d) += num;
(*dl) -= num;
return (int)sz; /* out of space really, but would need buffer
in order to truncate the output */
}
sldns_b64_ntop(*d, num, *s, *sl);
(*d) += num;
(*dl) -= num;
(*s) += sz;
(*sl) -= sz;
return (int)sz;
}
int sldns_wire2str_b64_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
if(*dl == 0) {
return sldns_str_print(s, sl, "0");
}
return sldns_wire2str_b64_scan_num(d, dl, s, sl, *dl);
}
int sldns_wire2str_hex_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
if(*dl == 0) {
return sldns_str_print(s, sl, "0");
}
return print_remainder_hex("", d, dl, s, sl);
}
int sldns_wire2str_nsec_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
uint8_t* p = *d;
size_t pl = *dl;
unsigned i, bit, window, block_len;
uint16_t t;
int w = 0;
/* check for errors */
while(pl) {
if(pl < 2) return -1;
block_len = (unsigned)p[1];
if(pl < 2+block_len) return -1;
p += block_len+2;
pl -= block_len+2;
}
/* do it */
p = *d;
pl = *dl;
while(pl) {
if(pl < 2) return -1; /* cannot happen */
window = (unsigned)p[0];
block_len = (unsigned)p[1];
if(pl < 2+block_len) return -1; /* cannot happen */
p += 2;
for(i=0; i<block_len; i++) {
if(p[i] == 0) continue;
/* base type number for this octet */
t = ((window)<<8) | (i << 3);
for(bit=0; bit<8; bit++) {
if((p[i]&(0x80>>bit))) {
if(w) w += sldns_str_print(s, sl, " ");
w += sldns_wire2str_type_print(s, sl,
t+bit);
}
}
}
p += block_len;
pl -= block_len+2;
}
(*d) += *dl;
(*dl) = 0;
return w;
}
int sldns_wire2str_nsec3_salt_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
size_t salt_len;
int w;
if(*dl < 1) return -1;
salt_len = (size_t)(*d)[0];
if(*dl < 1+salt_len) return -1;
(*d)++;
(*dl)--;
if(salt_len == 0) {
return sldns_str_print(s, sl, "-");
}
w = print_hex_buf(s, sl, *d, salt_len);
(*dl)-=salt_len;
(*d)+=salt_len;
return w;
}
int sldns_wire2str_cert_alg_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
sldns_lookup_table *lt;
int data, w;
if(*dl < 2) return -1;
data = (int)sldns_read_uint16(*d);
lt = sldns_lookup_by_id(sldns_cert_algorithms, data);
if(lt && lt->name)
w = sldns_str_print(s, sl, "%s", lt->name);
else w = sldns_str_print(s, sl, "%d", data);
(*dl)-=2;
(*d)+=2;
return w;
}
int sldns_wire2str_alg_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
/* don't use algorithm mnemonics in the presentation format
* this kind of got sneaked into the rfc's */
return sldns_wire2str_int8_scan(d, dl, s, sl);
}
int sldns_wire2str_unknown_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
return sldns_wire2str_rdata_unknown_scan(d, dl, s, sl);
}
int sldns_wire2str_time_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
/* create a YYYYMMDDHHMMSS string if possible */
struct tm tm;
char date_buf[16];
uint32_t t;
memset(&tm, 0, sizeof(tm));
if(*dl < 4) return -1;
t = sldns_read_uint32(*d);
date_buf[15]=0;
if(sldns_serial_arithmetics_gmtime_r(t, time(NULL), &tm) &&
strftime(date_buf, 15, "%Y%m%d%H%M%S", &tm)) {
(*d) += 4;
(*dl) -= 4;
return sldns_str_print(s, sl, "%s", date_buf);
}
return -1;
}
static int
loc_cm_print(char** str, size_t* sl, uint8_t mantissa, uint8_t exponent)
{
int w = 0;
uint8_t i;
/* is it 0.<two digits> ? */
if(exponent < 2) {
if(exponent == 1)
mantissa *= 10;
return sldns_str_print(str, sl, "0.%02ld", (long)mantissa);
}
/* always <digit><string of zeros> */
w += sldns_str_print(str, sl, "%d", (int)mantissa);
for(i=0; i<exponent-2; i++)
w += sldns_str_print(str, sl, "0");
return w;
}
int sldns_wire2str_loc_scan(uint8_t** d, size_t* dl, char** str, size_t* sl)
{
/* we could do checking (ie degrees < 90 etc)? */
uint8_t version;
uint8_t size;
uint8_t horizontal_precision;
uint8_t vertical_precision;
uint32_t longitude;
uint32_t latitude;
uint32_t altitude;
char northerness;
char easterness;
uint32_t h;
uint32_t m;
double s;
uint32_t equator = (uint32_t)1 << 31; /* 2**31 */
int w = 0;
if(*dl < 16) return -1;
version = (*d)[0];
if(version != 0)
return sldns_wire2str_hex_scan(d, dl, str, sl);
size = (*d)[1];
horizontal_precision = (*d)[2];
vertical_precision = (*d)[3];
latitude = sldns_read_uint32((*d)+4);
longitude = sldns_read_uint32((*d)+8);
altitude = sldns_read_uint32((*d)+12);
if (latitude > equator) {
northerness = 'N';
latitude = latitude - equator;
} else {
northerness = 'S';
latitude = equator - latitude;
}
h = latitude / (1000 * 60 * 60);
latitude = latitude % (1000 * 60 * 60);
m = latitude / (1000 * 60);
latitude = latitude % (1000 * 60);
s = (double) latitude / 1000.0;
w += sldns_str_print(str, sl, "%02u %02u %06.3f %c ",
h, m, s, northerness);
if (longitude > equator) {
easterness = 'E';
longitude = longitude - equator;
} else {
easterness = 'W';
longitude = equator - longitude;
}
h = longitude / (1000 * 60 * 60);
longitude = longitude % (1000 * 60 * 60);
m = longitude / (1000 * 60);
longitude = longitude % (1000 * 60);
s = (double) longitude / (1000.0);
w += sldns_str_print(str, sl, "%02u %02u %06.3f %c ",
h, m, s, easterness);
s = ((double) altitude) / 100;
s -= 100000;
if(altitude%100 != 0)
w += sldns_str_print(str, sl, "%.2f", s);
else
w += sldns_str_print(str, sl, "%.0f", s);
w += sldns_str_print(str, sl, "m ");
w += loc_cm_print(str, sl, (size & 0xf0) >> 4, size & 0x0f);
w += sldns_str_print(str, sl, "m ");
w += loc_cm_print(str, sl, (horizontal_precision & 0xf0) >> 4,
horizontal_precision & 0x0f);
w += sldns_str_print(str, sl, "m ");
w += loc_cm_print(str, sl, (vertical_precision & 0xf0) >> 4,
vertical_precision & 0x0f);
w += sldns_str_print(str, sl, "m");
(*d)+=16;
(*dl)-=16;
return w;
}
int sldns_wire2str_wks_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
/* protocol, followed by bitmap of services */
const char* proto_name = NULL;
struct protoent *protocol;
struct servent *service;
uint8_t protocol_nr;
int bit, port, w = 0;
size_t i;
/* we cannot print with strings because they
* are not portable, the presentation format may
* not be able to be read in on another computer. */
int print_symbols = 0;
/* protocol */
if(*dl < 1) return -1;
protocol_nr = (*d)[0];
(*d)++;
(*dl)--;
protocol = getprotobynumber((int)protocol_nr);
if(protocol && (protocol->p_name != NULL)) {
w += sldns_str_print(s, sl, "%s", protocol->p_name);
proto_name = protocol->p_name;
} else if(protocol_nr == 6) {
w += sldns_str_print(s, sl, "tcp");
} else if(protocol_nr == 17) {
w += sldns_str_print(s, sl, "udp");
} else {
w += sldns_str_print(s, sl, "%u", (unsigned)protocol_nr);
}
for(i=0; i<*dl; i++) {
if((*d)[i] == 0)
continue;
for(bit=0; bit<8; bit++) {
if(!(((*d)[i])&(0x80>>bit)))
continue;
port = (int)i*8 + bit;
if(!print_symbols)
service = NULL;
else
service = getservbyport(
(int)htons((uint16_t)port), proto_name);
if(service && service->s_name)
w += sldns_str_print(s, sl, " %s",
service->s_name);
else w += sldns_str_print(s, sl, " %u",
(unsigned)port);
}
}
#ifdef HAVE_ENDSERVENT
endservent();
#endif
#ifdef HAVE_ENDPROTOENT
endprotoent();
#endif
(*d) += *dl;
(*dl) = 0;
return w;
}
int sldns_wire2str_nsap_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
return print_remainder_hex("0x", d, dl, s, sl);
}
int sldns_wire2str_atma_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
return print_remainder_hex("", d, dl, s, sl);
}
/* internal scan routine that can modify arguments on failure */
static int sldns_wire2str_ipseckey_scan_internal(uint8_t** d, size_t* dl,
char** s, size_t* sl, uint8_t* pkt, size_t pktlen, int* comprloop)
{
/* http://www.ietf.org/internet-drafts/draft-ietf-ipseckey-rr-12.txt*/
uint8_t precedence, gateway_type, algorithm;
int w = 0;
if(*dl < 3) return -1;
precedence = (*d)[0];
gateway_type = (*d)[1];
algorithm = (*d)[2];
if(gateway_type > 3)
return -1; /* unknown */
(*d)+=3;
(*dl)-=3;
w += sldns_str_print(s, sl, "%d %d %d ",
(int)precedence, (int)gateway_type, (int)algorithm);
switch(gateway_type) {
case 0: /* no gateway */
w += sldns_str_print(s, sl, ".");
break;
case 1: /* ip4 */
w += sldns_wire2str_a_scan(d, dl, s, sl);
break;
case 2: /* ip6 */
w += sldns_wire2str_aaaa_scan(d, dl, s, sl);
break;
case 3: /* dname */
w += sldns_wire2str_dname_scan(d, dl, s, sl, pkt, pktlen, comprloop);
break;
default: /* unknown */
return -1;
}
if(*dl < 1)
return -1;
w += sldns_str_print(s, sl, " ");
w += sldns_wire2str_b64_scan_num(d, dl, s, sl, *dl);
return w;
}
int sldns_wire2str_ipseckey_scan(uint8_t** d, size_t* dl, char** s, size_t* sl,
uint8_t* pkt, size_t pktlen, int* comprloop)
{
uint8_t* od = *d;
char* os = *s;
size_t odl = *dl, osl = *sl;
int w=sldns_wire2str_ipseckey_scan_internal(d, dl, s, sl, pkt, pktlen, comprloop);
if(w == -1) {
*d = od;
*s = os;
*dl = odl;
*sl = osl;
return -1;
}
return w;
}
int sldns_wire2str_hip_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
int w;
uint8_t algo, hitlen;
uint16_t pklen;
/* read lengths */
if(*dl < 4)
return -1;
hitlen = (*d)[0];
algo = (*d)[1];
pklen = sldns_read_uint16((*d)+2);
if(*dl < (size_t)4 + (size_t)hitlen + (size_t)pklen)
return -1;
/* write: algo hit pubkey */
w = sldns_str_print(s, sl, "%u ", (unsigned)algo);
w += print_hex_buf(s, sl, (*d)+4, hitlen);
w += sldns_str_print(s, sl, " ");
(*d)+=4+hitlen;
(*dl)-= (4+hitlen);
w += sldns_wire2str_b64_scan_num(d, dl, s, sl, pklen);
return w;
}
int sldns_wire2str_int16_data_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
int w;
uint16_t n;
if(*dl < 2)
return -1;
n = sldns_read_uint16(*d);
if(*dl < 2+(size_t)n)
return -1;
(*d)+=2;
(*dl)-=2;
if(n == 0) {
return sldns_str_print(s, sl, "0");
}
w = sldns_str_print(s, sl, "%u ", (unsigned)n);
w += sldns_wire2str_b64_scan_num(d, dl, s, sl, n);
return w;
}
int sldns_wire2str_nsec3_next_owner_scan(uint8_t** d, size_t* dl, char** s,
size_t* sl)
{
return sldns_wire2str_b32_ext_scan(d, dl, s, sl);
}
int sldns_wire2str_ilnp64_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
int w;
if(*dl < 8)
return -1;
w = sldns_str_print(s, sl, "%.4x:%.4x:%.4x:%.4x",
sldns_read_uint16(*d), sldns_read_uint16((*d)+2),
sldns_read_uint16((*d)+4), sldns_read_uint16((*d)+6));
(*d)+=8;
(*dl)-=8;
return w;
}
int sldns_wire2str_eui48_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
int w;
if(*dl < 6)
return -1;
w = sldns_str_print(s, sl, "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x",
(*d)[0], (*d)[1], (*d)[2], (*d)[3], (*d)[4], (*d)[5]);
(*d)+=6;
(*dl)-=6;
return w;
}
int sldns_wire2str_eui64_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
int w;
if(*dl < 8)
return -1;
w = sldns_str_print(s, sl, "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x-%.2x",
(*d)[0], (*d)[1], (*d)[2], (*d)[3], (*d)[4], (*d)[5],
(*d)[6], (*d)[7]);
(*d)+=8;
(*dl)-=8;
return w;
}
int sldns_wire2str_tag_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
size_t i, n;
int w = 0;
if(*dl < 1)
return -1;
n = (size_t)((*d)[0]);
if(*dl < 1+n)
return -1;
for(i=0; i<n; i++)
if(!isalnum((unsigned char)(*d)[i+1]))
return -1;
for(i=0; i<n; i++)
w += sldns_str_print(s, sl, "%c", (char)(*d)[i+1]);
(*d)+=n+1;
(*dl)-=(n+1);
return w;
}
int sldns_wire2str_long_str_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
size_t i;
int w = 0;
w += sldns_str_print(s, sl, "\"");
for(i=0; i<*dl; i++)
w += str_char_print(s, sl, (*d)[i]);
w += sldns_str_print(s, sl, "\"");
(*d)+=*dl;
(*dl)=0;
return w;
}
int sldns_wire2str_tsigerror_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
sldns_lookup_table *lt;
int data, w;
if(*dl < 2) return -1;
data = (int)sldns_read_uint16(*d);
lt = sldns_lookup_by_id(sldns_tsig_errors, data);
if(lt && lt->name)
w = sldns_str_print(s, sl, "%s", lt->name);
else w = sldns_str_print(s, sl, "%d", data);
(*dl)-=2;
(*d)+=2;
return w;
}
int sldns_wire2str_edns_llq_print(char** s, size_t* sl, uint8_t* data,
size_t len)
{
/* LLQ constants */
const char* llq_errors[] = {"NO-ERROR", "SERV-FULL", "STATIC",
"FORMAT-ERR", "NO-SUCH-LLQ", "BAD-VERS", "UNKNOWN_ERR"};
const unsigned int llq_errors_num = 7;
const char* llq_opcodes[] = {"LLQ-SETUP", "LLQ-REFRESH", "LLQ-EVENT"};
const unsigned int llq_opcodes_num = 3;
uint16_t version, llq_opcode, error_code;
uint64_t llq_id;
uint32_t lease_life; /* Requested or granted life of LLQ, in seconds */
int w = 0;
/* read the record */
if(len != 18) {
w += sldns_str_print(s, sl, "malformed LLQ ");
w += print_hex_buf(s, sl, data, len);
return w;
}
version = sldns_read_uint16(data);
llq_opcode = sldns_read_uint16(data+2);
error_code = sldns_read_uint16(data+4);
memmove(&llq_id, data+6, sizeof(llq_id));
lease_life = sldns_read_uint32(data+14);
/* print it */
w += sldns_str_print(s, sl, "v%d ", (int)version);
if(llq_opcode < llq_opcodes_num)
w += sldns_str_print(s, sl, "%s", llq_opcodes[llq_opcode]);
else w += sldns_str_print(s, sl, "opcode %d", (int)llq_opcode);
if(error_code < llq_errors_num)
w += sldns_str_print(s, sl, " %s", llq_errors[error_code]);
else w += sldns_str_print(s, sl, " error %d", (int)error_code);
#ifndef USE_WINSOCK
w += sldns_str_print(s, sl, " id %llx lease-life %lu",
(unsigned long long)llq_id, (unsigned long)lease_life);
#else
w += sldns_str_print(s, sl, " id %I64x lease-life %lu",
(unsigned long long)llq_id, (unsigned long)lease_life);
#endif
return w;
}
int sldns_wire2str_edns_ul_print(char** s, size_t* sl, uint8_t* data,
size_t len)
{
uint32_t lease;
int w = 0;
if(len != 4) {
w += sldns_str_print(s, sl, "malformed UL ");
w += print_hex_buf(s, sl, data, len);
return w;
}
lease = sldns_read_uint32(data);
w += sldns_str_print(s, sl, "lease %lu", (unsigned long)lease);
return w;
}
int sldns_wire2str_edns_nsid_print(char** s, size_t* sl, uint8_t* data,
size_t len)
{
int w = 0;
size_t i, printed=0;
w += print_hex_buf(s, sl, data, len);
for(i=0; i<len; i++) {
if(isprint((unsigned char)data[i]) || data[i] == '\t') {
if(!printed) {
w += sldns_str_print(s, sl, " (");
printed = 1;
}
w += sldns_str_print(s, sl, "%c", (char)data[i]);
}
}
if(printed)
w += sldns_str_print(s, sl, ")");
return w;
}
int sldns_wire2str_edns_dau_print(char** s, size_t* sl, uint8_t* data,
size_t len)
{
sldns_lookup_table *lt;
size_t i;
int w = 0;
for(i=0; i<len; i++) {
lt = sldns_lookup_by_id(sldns_algorithms, (int)data[i]);
if(lt && lt->name)
w += sldns_str_print(s, sl, " %s", lt->name);
else w += sldns_str_print(s, sl, " %d", (int)data[i]);
}
return w;
}
int sldns_wire2str_edns_dhu_print(char** s, size_t* sl, uint8_t* data,
size_t len)
{
sldns_lookup_table *lt;
size_t i;
int w = 0;
for(i=0; i<len; i++) {
lt = sldns_lookup_by_id(sldns_hashes, (int)data[i]);
if(lt && lt->name)
w += sldns_str_print(s, sl, " %s", lt->name);
else w += sldns_str_print(s, sl, " %d", (int)data[i]);
}
return w;
}
int sldns_wire2str_edns_n3u_print(char** s, size_t* sl, uint8_t* data,
size_t len)
{
size_t i;
int w = 0;
for(i=0; i<len; i++) {
if(data[i] == 1)
w += sldns_str_print(s, sl, " SHA1");
else w += sldns_str_print(s, sl, " %d", (int)data[i]);
}
return w;
}
int sldns_wire2str_edns_subnet_print(char** s, size_t* sl, uint8_t* data,
size_t len)
{
int w = 0;
uint16_t family;
uint8_t source, scope;
if(len < 4) {
w += sldns_str_print(s, sl, "malformed subnet ");
w += print_hex_buf(s, sl, data, len);
return w;
}
family = sldns_read_uint16(data);
source = data[2];
scope = data[3];
if(family == 1) {
/* IP4 */
char buf[64];
uint8_t ip4[4];
memset(ip4, 0, sizeof(ip4));
if(len-4 > 4) {
w += sldns_str_print(s, sl, "trailingdata:");
w += print_hex_buf(s, sl, data+4+4, len-4-4);
w += sldns_str_print(s, sl, " ");
len = 4+4;
}
memmove(ip4, data+4, len-4);
if(!inet_ntop(AF_INET, ip4, buf, (socklen_t)sizeof(buf))) {
w += sldns_str_print(s, sl, "ip4ntoperror ");
w += print_hex_buf(s, sl, data+4+4, len-4-4);
} else {
w += sldns_str_print(s, sl, "%s", buf);
}
} else if(family == 2) {
/* IP6 */
char buf[64];
uint8_t ip6[16];
memset(ip6, 0, sizeof(ip6));
if(len-4 > 16) {
w += sldns_str_print(s, sl, "trailingdata:");
w += print_hex_buf(s, sl, data+4+16, len-4-16);
w += sldns_str_print(s, sl, " ");
len = 4+16;
}
memmove(ip6, data+4, len-4);
#ifdef AF_INET6
if(!inet_ntop(AF_INET6, ip6, buf, (socklen_t)sizeof(buf))) {
w += sldns_str_print(s, sl, "ip6ntoperror ");
w += print_hex_buf(s, sl, data+4+4, len-4-4);
} else {
w += sldns_str_print(s, sl, "%s", buf);
}
#else
w += print_hex_buf(s, sl, data+4+4, len-4-4);
#endif
} else {
/* unknown */
w += sldns_str_print(s, sl, "family %d ",
(int)family);
w += print_hex_buf(s, sl, data, len);
}
w += sldns_str_print(s, sl, "/%d scope /%d", (int)source, (int)scope);
return w;
}
static int sldns_wire2str_edns_keepalive_print(char** s, size_t* sl,
uint8_t* data, size_t len)
{
int w = 0;
uint16_t timeout;
if(!(len == 0 || len == 2)) {
w += sldns_str_print(s, sl, "malformed keepalive ");
w += print_hex_buf(s, sl, data, len);
return w;
}
if(len == 0 ) {
w += sldns_str_print(s, sl, "no timeout value (only valid for client option) ");
} else {
timeout = sldns_read_uint16(data);
w += sldns_str_print(s, sl, "timeout value in units of 100ms %u", (int)timeout);
}
return w;
}
+int sldns_wire2str_edns_ede_print(char** s, size_t* sl,
+ uint8_t* data, size_t len)
+{
+ uint16_t ede_code;
+ int w = 0;
+ sldns_lookup_table *lt;
+ size_t i;
+ int printable;
+
+ if(len < 2) {
+ w += sldns_str_print(s, sl, "malformed ede ");
+ w += print_hex_buf(s, sl, data, len);
+ return w;
+ }
+
+ ede_code = sldns_read_uint16(data);
+ lt = sldns_lookup_by_id(sldns_edns_ede_codes, (int)ede_code);
+ if(lt && lt->name)
+ w += sldns_str_print(s, sl, "%s", lt->name);
+ else w += sldns_str_print(s, sl, "%d", (int)ede_code);
+
+ if(len == 2)
+ return w;
+
+ w += sldns_str_print(s, sl, " ");
+
+ /* If it looks like text, show it as text. */
+ printable=1;
+ for(i=2; i<len; i++) {
+ if(isprint((unsigned char)data[i]) || data[i] == '\t')
+ continue;
+ printable = 0;
+ break;
+ }
+ if(printable) {
+ w += sldns_str_print(s, sl, "\"");
+ for(i=2; i<len; i++) {
+ w += str_char_print(s, sl, data[i]);
+ }
+ w += sldns_str_print(s, sl, "\"");
+ } else {
+ w += print_hex_buf(s, sl, data+2, len-2);
+ }
+ return w;
+}
+
int sldns_wire2str_edns_option_print(char** s, size_t* sl,
uint16_t option_code, uint8_t* optdata, size_t optlen)
{
int w = 0;
w += sldns_wire2str_edns_option_code_print(s, sl, option_code);
w += sldns_str_print(s, sl, ": ");
switch(option_code) {
case LDNS_EDNS_LLQ:
w += sldns_wire2str_edns_llq_print(s, sl, optdata, optlen);
break;
case LDNS_EDNS_UL:
w += sldns_wire2str_edns_ul_print(s, sl, optdata, optlen);
break;
case LDNS_EDNS_NSID:
w += sldns_wire2str_edns_nsid_print(s, sl, optdata, optlen);
break;
case LDNS_EDNS_DAU:
w += sldns_wire2str_edns_dau_print(s, sl, optdata, optlen);
break;
case LDNS_EDNS_DHU:
w += sldns_wire2str_edns_dhu_print(s, sl, optdata, optlen);
break;
case LDNS_EDNS_N3U:
w += sldns_wire2str_edns_n3u_print(s, sl, optdata, optlen);
break;
case LDNS_EDNS_CLIENT_SUBNET:
w += sldns_wire2str_edns_subnet_print(s, sl, optdata, optlen);
break;
case LDNS_EDNS_KEEPALIVE:
w += sldns_wire2str_edns_keepalive_print(s, sl, optdata, optlen);
break;
case LDNS_EDNS_PADDING:
w += print_hex_buf(s, sl, optdata, optlen);
break;
+ case LDNS_EDNS_EDE:
+ w += sldns_wire2str_edns_ede_print(s, sl, optdata, optlen);
+ break;
default:
/* unknown option code */
w += print_hex_buf(s, sl, optdata, optlen);
break;
}
return w;
}
/** print the edns options to string */
static int
print_edns_opts(char** s, size_t* sl, uint8_t* rdata, size_t rdatalen)
{
uint16_t option_code, option_len;
int w = 0;
while(rdatalen > 0) {
/* option name */
if(rdatalen < 4) {
w += sldns_str_print(s, sl, " ; malformed: ");
w += print_hex_buf(s, sl, rdata, rdatalen);
return w;
}
option_code = sldns_read_uint16(rdata);
option_len = sldns_read_uint16(rdata+2);
rdata += 4;
rdatalen -= 4;
/* option value */
if(rdatalen < (size_t)option_len) {
w += sldns_str_print(s, sl, " ; malformed ");
w += sldns_wire2str_edns_option_code_print(s, sl,
option_code);
w += sldns_str_print(s, sl, ": ");
w += print_hex_buf(s, sl, rdata, rdatalen);
return w;
}
w += sldns_str_print(s, sl, " ; ");
w += sldns_wire2str_edns_option_print(s, sl, option_code,
rdata, option_len);
rdata += option_len;
rdatalen -= option_len;
}
return w;
}
int sldns_wire2str_edns_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len, uint8_t* pkt, size_t pktlen)
{
int w = 0;
uint8_t ext_rcode, edns_version;
uint16_t udpsize, edns_bits, rdatalen;
w += sldns_str_print(str, str_len, "; EDNS:");
/* some input checks, domain name */
if(*data_len < 1+10)
return w + print_remainder_hex("Error malformed 0x",
data, data_len, str, str_len);
if(*data[0] != 0) {
return w + print_remainder_hex("Error nonrootdname 0x",
data, data_len, str, str_len);
}
(*data)++;
(*data_len)--;
/* check type and read fixed contents */
if(sldns_read_uint16((*data)) != LDNS_RR_TYPE_OPT) {
return w + print_remainder_hex("Error nottypeOPT 0x",
data, data_len, str, str_len);
}
udpsize = sldns_read_uint16((*data)+2);
ext_rcode = (*data)[4];
edns_version = (*data)[5];
edns_bits = sldns_read_uint16((*data)+6);
rdatalen = sldns_read_uint16((*data)+8);
(*data)+=10;
(*data_len)-=10;
w += sldns_str_print(str, str_len, " version: %u;",
(unsigned)edns_version);
w += sldns_str_print(str, str_len, " flags:");
if((edns_bits & LDNS_EDNS_MASK_DO_BIT))
w += sldns_str_print(str, str_len, " do");
/* the extended rcode is the value set, shifted four bits,
* and or'd with the original rcode */
if(ext_rcode) {
int rc = ((int)ext_rcode)<<4;
if(pkt && pktlen >= LDNS_HEADER_SIZE)
rc |= LDNS_RCODE_WIRE(pkt);
w += sldns_str_print(str, str_len, " ; ext-rcode: %d", rc);
}
w += sldns_str_print(str, str_len, " ; udp: %u", (unsigned)udpsize);
if(rdatalen) {
if((size_t)*data_len < rdatalen) {
w += sldns_str_print(str, str_len,
" ; Error EDNS rdata too short; ");
rdatalen = (uint16_t)*data_len;
}
w += print_edns_opts(str, str_len, *data, rdatalen);
(*data) += rdatalen;
(*data_len) -= rdatalen;
}
w += sldns_str_print(str, str_len, "\n");
return w;
}
diff --git a/contrib/unbound/sldns/wire2str.h b/contrib/unbound/sldns/wire2str.h
index 548c66300d92..a7c58b85a5fb 100644
--- a/contrib/unbound/sldns/wire2str.h
+++ b/contrib/unbound/sldns/wire2str.h
@@ -1,1053 +1,1066 @@
/**
* wire2str.h - txt presentation of RRs
*
* (c) NLnet Labs, 2005-2006
*
* See the file LICENSE for the license
*/
/**
* \file
*
* Contains functions to translate the wireformat to text
* representation, as well as functions to print them.
*/
#ifndef LDNS_WIRE2STR_H
#define LDNS_WIRE2STR_H
#ifdef __cplusplus
extern "C" {
#endif
struct sldns_struct_lookup_table;
/* lookup tables for standard DNS stuff */
/** Taken from RFC 2535, section 7. */
extern struct sldns_struct_lookup_table* sldns_algorithms;
/** DS record hash algorithms */
extern struct sldns_struct_lookup_table* sldns_hashes;
/** Taken from RFC 2538, section 2.1. */
extern struct sldns_struct_lookup_table* sldns_cert_algorithms;
/** Response codes */
extern struct sldns_struct_lookup_table* sldns_rcodes;
/** Operation codes */
extern struct sldns_struct_lookup_table* sldns_opcodes;
/** EDNS flags */
extern struct sldns_struct_lookup_table* sldns_edns_flags;
/** EDNS option codes */
extern struct sldns_struct_lookup_table* sldns_edns_options;
+/** EDNS EDE codes */
+extern struct sldns_struct_lookup_table* sldns_edns_ede_codes;
/** error string from wireparse */
extern struct sldns_struct_lookup_table* sldns_wireparse_errors;
/** tsig errors are the rcodes with extra (higher) values */
extern struct sldns_struct_lookup_table* sldns_tsig_errors;
/**
* Convert wireformat packet to a string representation
* @param data: wireformat packet data (starting at ID bytes).
* @param len: length of packet.
* @return string(malloced) or NULL on failure.
*/
char* sldns_wire2str_pkt(uint8_t* data, size_t len);
/**
* Convert wireformat RR to a string representation.
* @param rr: the wireformat RR, in uncompressed form. Starts at the domain
* name start, ends with the rdata of the RR.
* @param len: length of the rr wireformat.
* @return string(malloced) or NULL on failure.
*/
char* sldns_wire2str_rr(uint8_t* rr, size_t len);
/**
* Convert wire dname to a string.
* @param dname: the dname in uncompressed wireformat.
* @param dname_len: length of the dname.
* @return string or NULL on failure.
*/
char* sldns_wire2str_dname(uint8_t* dname, size_t dname_len);
/**
* Convert wire RR type to a string, 'MX', 'TYPE1234'...
* @param rrtype: the RR type in host order.
* @return malloced string with the RR type or NULL on malloc failure.
*/
char* sldns_wire2str_type(uint16_t rrtype);
/**
* Convert wire RR class to a string, 'IN', 'CLASS1'.
* @param rrclass: the RR class in host order.
* @return malloced string with the RR class or NULL on malloc failure.
*/
char* sldns_wire2str_class(uint16_t rrclass);
/**
* Convert wire packet rcode to a string, 'NOERROR', 'NXDOMAIN'...
* @param rcode: as integer, host order
* @return malloced string with the rcode or NULL on malloc failure.
*/
char* sldns_wire2str_rcode(int rcode);
/**
* Print to string, move string along for next content. With va_list.
* @param str: string buffer. Adjusted at end to after the output.
* @param slen: length of the string buffer. Adjusted at end.
* @param format: printf format string.
* @param args: arguments for printf.
* @return number of characters needed. Can be larger than slen.
*/
int sldns_str_vprint(char** str, size_t* slen, const char* format, va_list args);
/**
* Print to string, move string along for next content.
* @param str: string buffer. Adjusted at end to after the output.
* @param slen: length of the string buffer. Adjusted at end.
* @param format: printf format string and arguments for it.
* @return number of characters needed. Can be larger than slen.
*/
int sldns_str_print(char** str, size_t* slen, const char* format, ...)
ATTR_FORMAT(printf, 3, 4);
/**
* Convert wireformat packet to a string representation with user buffer
* It appends every RR with default comments.
* For more formatter options use the function: TBD(TODO)
* @param data: wireformat packet data (starting at ID bytes).
* @param data_len: length of packet.
* @param str: the string buffer for the output.
* If you pass NULL as the str the return value of the function is
* the str_len you need for the entire packet. It does not include
* the 0 byte at the end.
* @param str_len: the size of the string buffer. If more is needed, it'll
* silently truncate the output to fit in the buffer.
* @return the number of characters for this element, excluding zerobyte.
* Is larger or equal than str_len if output was truncated.
*/
int sldns_wire2str_pkt_buf(uint8_t* data, size_t data_len, char* str,
size_t str_len);
/**
* Scan wireformat packet to a string representation with user buffer
* It appends every RR with default comments.
* For more formatter options use the function: TBD(TODO)
* @param data: wireformat packet data (starting at ID bytes).
* @param data_len: length of packet.
* @param str: the string buffer for the output.
* @param str_len: the size of the string buffer.
* @return number of characters for string.
* returns the number of characters that are needed (except terminating null),
* so it may return a value larger than str_len.
* On error you get less output (i.e. shorter output in str (null terminated))
* On exit the data, data_len, str and str_len values are adjusted to move them
* from their original position along the input and output for the content
* that has been consumed (and produced) by this function. If the end of the
* output string is reached, *str_len is set to 0. The output string is null
* terminated (shortening the output if necessary). If the end of the input
* is reached *data_len is set to 0.
*/
int sldns_wire2str_pkt_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat rr to string, with user buffers. It shifts the arguments
* to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @param pkt: packet for decompression, if NULL no decompression.
* @param pktlen: length of packet buffer.
* @param comprloop: if pkt, bool detects compression loops.
* @return number of characters (except null) needed to print.
*/
int sldns_wire2str_rr_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len, uint8_t* pkt, size_t pktlen, int* comprloop);
/**
* Scan wireformat question rr to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @param pkt: packet for decompression, if NULL no decompression.
* @param pktlen: length of packet buffer.
* @param comprloop: if pkt, bool detects compression loops.
* @return number of characters (except null) needed to print.
*/
int sldns_wire2str_rrquestion_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len, uint8_t* pkt, size_t pktlen, int* comprloop);
/**
* Scan wireformat RR to string in unknown RR format, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @param pkt: packet for decompression, if NULL no decompression.
* @param pktlen: length of packet buffer.
* @param comprloop: if pkt, bool detects compression loops.
* @return number of characters (except null) needed to print.
*/
int sldns_wire2str_rr_unknown_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len, uint8_t* pkt, size_t pktlen, int* comprloop);
/**
* Print to string the RR-information comment in default format,
* with user buffers. Moves string along.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @param rr: wireformat data.
* @param rrlen: length of data buffer.
* @param dname_off: offset in buffer behind owner dname, the compressed size
* of the owner name.
* @param rrtype: type of the RR, host format.
* @return number of characters (except null) needed to print.
*/
int sldns_wire2str_rr_comment_print(char** str, size_t* str_len, uint8_t* rr,
size_t rrlen, size_t dname_off, uint16_t rrtype);
/**
* Scan wireformat packet header to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
*/
int sldns_wire2str_header_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat rdata to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer. The length of the rdata in the
* buffer. The rdatalen itself has already been scanned, the data
* points to the rdata after the rdatalen.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @param rrtype: RR type of Rdata, host format.
* @param pkt: packet for decompression, if NULL no decompression.
* @param pktlen: length of packet buffer.
* @param comprloop: if pkt, bool detects compression loops.
* @return number of characters (except null) needed to print.
*/
int sldns_wire2str_rdata_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len, uint16_t rrtype, uint8_t* pkt, size_t pktlen,
int* comprloop);
/**
* Scan wireformat rdata to string in unknown format, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer, the length of the rdata in buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
*/
int sldns_wire2str_rdata_unknown_scan(uint8_t** data, size_t* data_len,
char** str, size_t* str_len);
/**
* Scan wireformat domain name to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @param pkt: packet for decompression, if NULL no decompression.
* @param pktlen: length of packet buffer.
* @param comprloop: inout bool, that is set true if compression loop failure
* happens. Pass in 0, if passsed in as true, a lower bound is set
* on compression loops to stop arbitrary long packet parse times.
* This is meant so you can set it to 0 at the start of a list of dnames,
* and then scan all of them in sequence, if a loop happens, it becomes
* true and then it becomes more strict for the next dnames in the list.
* You can leave it at NULL if there is no pkt (pkt is NULL too).
* @return number of characters (except null) needed to print.
*/
int sldns_wire2str_dname_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len, uint8_t* pkt, size_t pktlen, int* comprloop);
/**
* Scan wireformat rr type to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
*/
int sldns_wire2str_type_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat rr class to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
*/
int sldns_wire2str_class_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat rr ttl to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
*/
int sldns_wire2str_ttl_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Print host format rr type to string. Moves string along, user buffers.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @param rrtype: host format rr type.
* @return number of characters (except null) needed to print.
*/
int sldns_wire2str_type_print(char** str, size_t* str_len, uint16_t rrtype);
/**
* Print host format rr class to string. Moves string along, user buffers.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @param rrclass: host format rr class.
* @return number of characters (except null) needed to print.
*/
int sldns_wire2str_class_print(char** str, size_t* str_len, uint16_t rrclass);
/**
* Print host format rcode to string. Moves string along, user buffers.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @param rcode: host format rcode number.
* @return number of characters (except null) needed to print.
*/
int sldns_wire2str_rcode_print(char** str, size_t* str_len, int rcode);
/**
* Print host format opcode to string. Moves string along, user buffers.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @param opcode: host format opcode number.
* @return number of characters (except null) needed to print.
*/
int sldns_wire2str_opcode_print(char** str, size_t* str_len, int opcode);
/**
* Print host format EDNS0 option to string. Moves string along, user buffers.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @param opcode: host format option number.
* @return number of characters (except null) needed to print.
*/
int sldns_wire2str_edns_option_code_print(char** str, size_t* str_len,
uint16_t opcode);
/**
* Convert RR to string presentation format, on one line. User buffer.
* @param rr: wireformat RR data
* @param rr_len: length of the rr wire data.
* @param str: the string buffer to write to.
* If you pass NULL as the str, the return value of the function is
* the str_len you need for the entire packet. It does not include
* the 0 byte at the end.
* @param str_len: the size of the string buffer. If more is needed, it'll
* silently truncate the output to fit in the buffer.
* @return the number of characters for this element, excluding zerobyte.
* Is larger or equal than str_len if output was truncated.
*/
int sldns_wire2str_rr_buf(uint8_t* rr, size_t rr_len, char* str,
size_t str_len);
/**
* Convert question RR to string presentation format, on one line. User buffer.
* @param rr: wireformat RR data
* @param rr_len: length of the rr wire data.
* @param str: the string buffer to write to.
* If you pass NULL as the str, the return value of the function is
* the str_len you need for the entire packet. It does not include
* the 0 byte at the end.
* @param str_len: the size of the string buffer. If more is needed, it'll
* silently truncate the output to fit in the buffer.
* @return the number of characters for this element, excluding zerobyte.
* Is larger or equal than str_len if output was truncated.
*/
int sldns_wire2str_rrquestion_buf(uint8_t* rr, size_t rr_len, char* str,
size_t str_len);
/**
* 3597 printout of an RR in unknown rr format.
* There are more format and comment options available for printout
* with the function: TBD(TODO)
* @param rr: wireformat RR data
* @param rr_len: length of the rr wire data.
* @param str: the string buffer to write to.
* If you pass NULL as the str, the return value of the function is
* the str_len you need for the entire rr. It does not include
* the 0 byte at the end.
* @param str_len: the size of the string buffer. If more is needed, it'll
* silently truncate the output to fit in the buffer.
* @return the number of characters for this element, excluding zerobyte.
* Is larger or equal than str_len if output was truncated.
*/
int sldns_wire2str_rr_unknown_buf(uint8_t* rr, size_t rr_len, char* str,
size_t str_len);
/**
* This creates the comment to print after the RR. ; keytag=... , and other
* basic comments for RRs.
* There are more format and comment options available for printout
* with the function: TBD(TODO)
* @param rr: wireformat RR data
* @param rr_len: length of the rr wire data.
* @param dname_len: length of the dname in front of the RR.
* @param str: the string buffer to write to.
* If you pass NULL as the str, the return value of the function is
* the str_len you need for the entire comment. It does not include
* the 0 byte at the end.
* @param str_len: the size of the string buffer. If more is needed, it'll
* silently truncate the output to fit in the buffer.
* @return the number of characters for this element, excluding zerobyte.
* Is larger or equal than str_len if output was truncated.
*/
int sldns_wire2str_rr_comment_buf(uint8_t* rr, size_t rr_len, size_t dname_len,
char* str, size_t str_len);
/**
* Convert RDATA to string presentation format, on one line. User buffer.
* @param rdata: wireformat rdata part of an RR.
* @param rdata_len: length of the rr wire data.
* @param str: the string buffer to write to.
* If you pass NULL as the str, the return value of the function is
* the str_len you need for the entire packet. It does not include
* the 0 byte at the end.
* @param str_len: the size of the string buffer. If more is needed, it'll
* silently truncate the output to fit in the buffer.
* @param rrtype: rr type of the data
* @return the number of characters for this element, excluding zerobyte.
* Is larger or equal than str_len if output was truncated.
*/
int sldns_wire2str_rdata_buf(uint8_t* rdata, size_t rdata_len, char* str,
size_t str_len, uint16_t rrtype);
/**
* Convert wire RR type to a string, 'MX', 'TYPE12'. With user buffer.
* @param rrtype: the RR type in host order.
* @param str: the string to write to.
* @param len: length of str.
* @return the number of characters for this element, excluding zerobyte.
* Is larger or equal than str_len if output was truncated.
*/
int sldns_wire2str_type_buf(uint16_t rrtype, char* str, size_t len);
/**
* Convert wire RR class to a string, 'IN', 'CLASS12'. With user buffer.
* @param rrclass: the RR class in host order.
* @param str: the string to write to.
* @param len: length of str.
* @return the number of characters for this element, excluding zerobyte.
* Is larger or equal than str_len if output was truncated.
*/
int sldns_wire2str_class_buf(uint16_t rrclass, char* str, size_t len);
/**
* Convert wire RR rcode to a string, 'NOERROR', 'NXDOMAIN'. With user buffer.
* @param rcode: rcode as integer in host order
* @param str: the string to write to.
* @param len: length of str.
* @return the number of characters for this element, excluding zerobyte.
* Is larger or equal than str_len if output was truncated.
*/
int sldns_wire2str_rcode_buf(int rcode, char* str, size_t len);
/**
* Convert host format opcode to a string. 'QUERY', 'NOTIFY', 'UPDATE'.
* With user buffer.
* @param opcode: opcode as integer in host order
* @param str: the string to write to.
* @param len: length of str.
* @return the number of characters for this element, excluding zerobyte.
* Is larger or equal than str_len if output was truncated.
*/
int sldns_wire2str_opcode_buf(int opcode, char* str, size_t len);
/**
* Convert wire dname to a string, "example.com.". With user buffer.
* @param dname: the dname in uncompressed wireformat.
* @param dname_len: length of the dname.
* @param str: the string to write to.
* @param len: length of string.
* @return the number of characters for this element, excluding zerobyte.
* Is larger or equal than str_len if output was truncated.
*/
int sldns_wire2str_dname_buf(uint8_t* dname, size_t dname_len, char* str,
size_t len);
/**
* Convert wire SVCB to a string with user buffer.
* @param d: the SVCB data in uncompressed wireformat.
* @param dlen: length of the SVCB data.
* @param s: the string to write to.
* @param slen: length of string.
* @return the number of characters for this element, excluding zerobyte.
* Is larger or equal than str_len if output was truncated.
*/
int sldns_wire2str_svcparam_scan(uint8_t** d, size_t* dlen, char** s,
size_t* slen);
/**
* Scan wireformat rdf field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @param rdftype: the type of the rdata field, enum sldns_rdf_type.
* @param pkt: packet for decompression, if NULL no decompression.
* @param pktlen: length of packet buffer.
* @param comprloop: if pkt, bool detects compression loops.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_rdf_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len, int rdftype, uint8_t* pkt, size_t pktlen,
int* comprloop);
/**
* Scan wireformat int8 field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_int8_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat int16 field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_int16_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat int32 field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_int32_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat period field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_period_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat tsigtime field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_tsigtime_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat ip4 A field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_a_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat ip6 AAAA field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_aaaa_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat str field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_str_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat apl field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_apl_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat b32_ext field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_b32_ext_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat b64 field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_b64_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat hex field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_hex_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat nsec bitmap field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_nsec_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat nsec3_salt field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_nsec3_salt_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat cert_alg field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_cert_alg_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat alg field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_alg_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat type unknown field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_unknown_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat time field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_time_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat LOC field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_loc_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat WKS field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_wks_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat NSAP field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_nsap_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat ATMA field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_atma_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat IPSECKEY field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @param pkt: packet for decompression, if NULL no decompression.
* @param pktlen: length of packet buffer.
* @param comprloop: if pkt, bool detects compression loops.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_ipseckey_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len, uint8_t* pkt, size_t pktlen, int* comprloop);
/**
* Scan wireformat HIP (algo, HIT, pubkey) field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_hip_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat int16_data field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_int16_data_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat tsigerror field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_tsigerror_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat nsec3_next_owner field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_nsec3_next_owner_scan(uint8_t** data, size_t* data_len,
char** str, size_t* str_len);
/**
* Scan wireformat ILNP64 field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_ilnp64_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat EUI48 field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_eui48_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat EUI64 field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_eui64_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat TAG field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_tag_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat long_str field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_long_str_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Print EDNS LLQ option data to string. User buffers, moves string pointers.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @param option_data: buffer with EDNS option code data.
* @param option_len: length of the data for this option.
* @return number of characters (except null) needed to print.
*/
int sldns_wire2str_edns_llq_print(char** str, size_t* str_len,
uint8_t* option_data, size_t option_len);
/**
* Print EDNS UL option data to string. User buffers, moves string pointers.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @param option_data: buffer with EDNS option code data.
* @param option_len: length of the data for this option.
* @return number of characters (except null) needed to print.
*/
int sldns_wire2str_edns_ul_print(char** str, size_t* str_len,
uint8_t* option_data, size_t option_len);
/**
* Print EDNS NSID option data to string. User buffers, moves string pointers.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @param option_data: buffer with EDNS option code data.
* @param option_len: length of the data for this option.
* @return number of characters (except null) needed to print.
*/
int sldns_wire2str_edns_nsid_print(char** str, size_t* str_len,
uint8_t* option_data, size_t option_len);
/**
* Print EDNS DAU option data to string. User buffers, moves string pointers.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @param option_data: buffer with EDNS option code data.
* @param option_len: length of the data for this option.
* @return number of characters (except null) needed to print.
*/
int sldns_wire2str_edns_dau_print(char** str, size_t* str_len,
uint8_t* option_data, size_t option_len);
/**
* Print EDNS DHU option data to string. User buffers, moves string pointers.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @param option_data: buffer with EDNS option code data.
* @param option_len: length of the data for this option.
* @return number of characters (except null) needed to print.
*/
int sldns_wire2str_edns_dhu_print(char** str, size_t* str_len,
uint8_t* option_data, size_t option_len);
/**
* Print EDNS N3U option data to string. User buffers, moves string pointers.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @param option_data: buffer with EDNS option code data.
* @param option_len: length of the data for this option.
* @return number of characters (except null) needed to print.
*/
int sldns_wire2str_edns_n3u_print(char** str, size_t* str_len,
uint8_t* option_data, size_t option_len);
/**
* Print EDNS SUBNET option data to string. User buffers, moves string pointers.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @param option_data: buffer with EDNS option code data.
* @param option_len: length of the data for this option.
* @return number of characters (except null) needed to print.
*/
int sldns_wire2str_edns_subnet_print(char** str, size_t* str_len,
uint8_t* option_data, size_t option_len);
+/**
+ * Print EDNS EDE option data to string. User buffers, moves string pointers.
+ * @param str: string buffer.
+ * @param str_len: length of string buffer.
+ * @param option_data: buffer with EDNS option code data.
+ * @param option_len: length of the data for this option.
+ * @return number of characters (except null) needed to print.
+ */
+int sldns_wire2str_edns_ede_print(char** str, size_t* str_len,
+ uint8_t* option_data, size_t option_len);
+
/**
* Print an EDNS option as OPT: VALUE. User buffers, moves string pointers.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @param option_code: host format EDNS option code.
* @param option_data: buffer with EDNS option code data.
* @param option_len: length of the data for this option.
* @return number of characters (except null) needed to print.
*/
int sldns_wire2str_edns_option_print(char** str, size_t* str_len,
uint16_t option_code, uint8_t* option_data, size_t option_len);
/**
* Scan wireformat EDNS OPT to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @param pkt: packet with header and other info (may be NULL)
* @param pktlen: length of packet buffer.
* @return number of characters (except null) needed to print.
*/
int sldns_wire2str_edns_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len, uint8_t* pkt, size_t pktlen);
#ifdef __cplusplus
}
#endif
#endif /* LDNS_WIRE2STR_H */
diff --git a/contrib/unbound/smallapp/unbound-checkconf.c b/contrib/unbound/smallapp/unbound-checkconf.c
index f03cc7c2e6ba..f5f0ab332c86 100644
--- a/contrib/unbound/smallapp/unbound-checkconf.c
+++ b/contrib/unbound/smallapp/unbound-checkconf.c
@@ -1,1034 +1,1051 @@
/*
* smallapp/unbound-checkconf.c - config file checker for unbound.conf file.
*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* The config checker checks for syntax and other errors in the unbound.conf
* file, and can be used to check for errors before the server is started
* or sigHUPped.
* Exit status 1 means an error.
*/
#include "config.h"
#include <ctype.h>
#include "util/log.h"
#include "util/config_file.h"
#include "util/module.h"
#include "util/net_help.h"
#include "util/regional.h"
#include "iterator/iterator.h"
#include "iterator/iter_fwd.h"
#include "iterator/iter_hints.h"
#include "validator/validator.h"
#include "services/localzone.h"
#include "services/listen_dnsport.h"
#include "services/view.h"
#include "services/authzone.h"
#include "respip/respip.h"
#include "sldns/sbuffer.h"
#include "sldns/str2wire.h"
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_GLOB_H
#include <glob.h>
#endif
#ifdef WITH_PYTHONMODULE
#include "pythonmod/pythonmod.h"
#endif
#ifdef CLIENT_SUBNET
#include "edns-subnet/subnet-whitelist.h"
#endif
/** Give checkconf usage, and exit (1). */
static void
usage(void)
{
printf("Usage: local-unbound-checkconf [file]\n");
printf(" Checks unbound configuration file for errors.\n");
printf("file if omitted %s is used.\n", CONFIGFILE);
printf("-o option print value of option to stdout.\n");
printf("-f output full pathname with chroot applied, eg. with -o pidfile.\n");
printf("-h show this usage help.\n");
printf("Version %s\n", PACKAGE_VERSION);
printf("BSD licensed, see LICENSE in source package for details.\n");
printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
exit(1);
}
/**
* Print given option to stdout
* @param cfg: config
* @param opt: option name without trailing :.
* This is different from config_set_option.
* @param final: if final pathname with chroot applied has to be printed.
*/
static void
print_option(struct config_file* cfg, const char* opt, int final)
{
if(strcmp(opt, "pidfile") == 0 && final) {
char *p = fname_after_chroot(cfg->pidfile, cfg, 1);
if(!p) fatal_exit("out of memory");
printf("%s\n", p);
free(p);
return;
}
if(strcmp(opt, "auto-trust-anchor-file") == 0 && final) {
struct config_strlist* s = cfg->auto_trust_anchor_file_list;
for(; s; s=s->next) {
char *p = fname_after_chroot(s->str, cfg, 1);
if(!p) fatal_exit("out of memory");
printf("%s\n", p);
free(p);
}
return;
}
if(!config_get_option(cfg, opt, config_print_func, stdout))
fatal_exit("cannot print option '%s'", opt);
}
/** check if module works with config */
static void
check_mod(struct config_file* cfg, struct module_func_block* fb)
{
struct module_env env;
memset(&env, 0, sizeof(env));
env.cfg = cfg;
env.scratch = regional_create();
env.scratch_buffer = sldns_buffer_new(BUFSIZ);
if(!env.scratch || !env.scratch_buffer)
fatal_exit("out of memory");
if(!edns_known_options_init(&env))
fatal_exit("out of memory");
if(!(*fb->init)(&env, 0)) {
fatal_exit("bad config for %s module", fb->name);
}
(*fb->deinit)(&env, 0);
sldns_buffer_free(env.scratch_buffer);
regional_destroy(env.scratch);
edns_known_options_delete(&env);
}
/** true if addr is a localhost address, 127.0.0.1 or ::1 (with maybe "@port"
* after it) */
static int
str_addr_is_localhost(const char* a)
{
if(strncmp(a, "127.", 4) == 0) return 1;
if(strncmp(a, "::1", 3) == 0) return 1;
return 0;
}
/** check do-not-query-localhost */
static void
donotquerylocalhostcheck(struct config_file* cfg)
{
if(cfg->donotquery_localhost) {
struct config_stub* p;
struct config_strlist* s;
for(p=cfg->forwards; p; p=p->next) {
for(s=p->addrs; s; s=s->next) {
if(str_addr_is_localhost(s->str)) {
fprintf(stderr, "unbound-checkconf: warning: forward-addr: '%s' is specified for forward-zone: '%s', but do-not-query-localhost: yes means that the address will not be used for lookups.\n",
s->str, p->name);
}
}
}
for(p=cfg->stubs; p; p=p->next) {
for(s=p->addrs; s; s=s->next) {
if(str_addr_is_localhost(s->str)) {
fprintf(stderr, "unbound-checkconf: warning: stub-addr: '%s' is specified for stub-zone: '%s', but do-not-query-localhost: yes means that the address will not be used for lookups.\n",
s->str, p->name);
}
}
}
}
}
/** check localzones */
static void
localzonechecks(struct config_file* cfg)
{
struct local_zones* zs;
if(!(zs = local_zones_create()))
fatal_exit("out of memory");
if(!local_zones_apply_cfg(zs, cfg))
fatal_exit("failed local-zone, local-data configuration");
local_zones_delete(zs);
}
/** checks for acl and views */
static void
acl_view_tag_checks(struct config_file* cfg, struct views* views)
{
int d;
struct sockaddr_storage a;
socklen_t alen;
struct config_str2list* acl;
struct config_str3list* s3;
struct config_strbytelist* sb;
/* acl_view */
for(acl=cfg->acl_view; acl; acl = acl->next) {
struct view* v;
if(!netblockstrtoaddr(acl->str, UNBOUND_DNS_PORT, &a, &alen,
&d)) {
fatal_exit("cannot parse access-control-view "
"address %s %s", acl->str, acl->str2);
}
v = views_find_view(views, acl->str2, 0);
if(!v) {
fatal_exit("cannot find view for "
"access-control-view: %s %s",
acl->str, acl->str2);
}
lock_rw_unlock(&v->lock);
}
/* acl_tags */
for(sb=cfg->acl_tags; sb; sb = sb->next) {
if(!netblockstrtoaddr(sb->str, UNBOUND_DNS_PORT, &a, &alen,
&d)) {
fatal_exit("cannot parse access-control-tags "
"address %s", sb->str);
}
}
/* acl_tag_actions */
for(s3=cfg->acl_tag_actions; s3; s3 = s3->next) {
enum localzone_type t;
if(!netblockstrtoaddr(s3->str, UNBOUND_DNS_PORT, &a, &alen,
&d)) {
fatal_exit("cannot parse access-control-tag-actions "
"address %s %s %s",
s3->str, s3->str2, s3->str3);
}
if(find_tag_id(cfg, s3->str2) == -1) {
fatal_exit("cannot parse tag %s (define-tag it), "
"for access-control-tag-actions: %s %s %s",
s3->str2, s3->str, s3->str2, s3->str3);
}
if(!local_zone_str2type(s3->str3, &t)) {
fatal_exit("cannot parse access control action type %s"
" for access-control-tag-actions: %s %s %s",
s3->str3, s3->str, s3->str2, s3->str3);
}
}
/* acl_tag_datas */
for(s3=cfg->acl_tag_datas; s3; s3 = s3->next) {
char buf[65536];
uint8_t rr[LDNS_RR_BUF_SIZE];
size_t len = sizeof(rr);
int res;
if(!netblockstrtoaddr(s3->str, UNBOUND_DNS_PORT, &a, &alen,
&d)) {
fatal_exit("cannot parse access-control-tag-datas address %s %s '%s'",
s3->str, s3->str2, s3->str3);
}
if(find_tag_id(cfg, s3->str2) == -1) {
fatal_exit("cannot parse tag %s (define-tag it), "
"for access-control-tag-datas: %s %s '%s'",
s3->str2, s3->str, s3->str2, s3->str3);
}
/* '.' is sufficient for validation, and it makes the call to
* sldns_wirerr_get_type() simpler below. */
snprintf(buf, sizeof(buf), "%s %s", ".", s3->str3);
res = sldns_str2wire_rr_buf(buf, rr, &len, NULL, 3600, NULL,
0, NULL, 0);
if(res != 0) {
fatal_exit("cannot parse rr data [char %d] parse error %s, for access-control-tag-datas: %s %s '%s'",
(int)LDNS_WIREPARSE_OFFSET(res)-2,
sldns_get_errorstr_parse(res),
s3->str, s3->str2, s3->str3);
}
}
}
/** check view and response-ip configuration */
static void
view_and_respipchecks(struct config_file* cfg)
{
struct views* views = NULL;
struct respip_set* respip = NULL;
int ignored = 0;
if(!(views = views_create()))
fatal_exit("Could not create views: out of memory");
if(!(respip = respip_set_create()))
fatal_exit("Could not create respip set: out of memory");
if(!views_apply_cfg(views, cfg))
fatal_exit("Could not set up views");
if(!respip_global_apply_cfg(respip, cfg))
fatal_exit("Could not setup respip set");
if(!respip_views_apply_cfg(views, cfg, &ignored))
fatal_exit("Could not setup per-view respip sets");
acl_view_tag_checks(cfg, views);
views_delete(views);
respip_set_delete(respip);
}
/** emit warnings for IP in hosts */
static void
warn_hosts(const char* typ, struct config_stub* list)
{
struct sockaddr_storage a;
socklen_t alen;
struct config_stub* s;
struct config_strlist* h;
for(s=list; s; s=s->next) {
for(h=s->hosts; h; h=h->next) {
if(extstrtoaddr(h->str, &a, &alen, UNBOUND_DNS_PORT)) {
fprintf(stderr, "unbound-checkconf: warning:"
" %s %s: \"%s\" is an IP%s address, "
"and when looked up as a host name "
"during use may not resolve.\n",
s->name, typ, h->str,
addr_is_ip6(&a, alen)?"6":"4");
}
}
}
}
/** check interface strings */
static void
interfacechecks(struct config_file* cfg)
{
int d;
struct sockaddr_storage a;
socklen_t alen;
int i, j, i2, j2;
char*** resif = NULL;
int* num_resif = NULL;
char portbuf[32];
snprintf(portbuf, sizeof(portbuf), "%d", cfg->port);
if(cfg->num_ifs != 0) {
resif = (char***)calloc(cfg->num_ifs, sizeof(char**));
if(!resif) fatal_exit("malloc failure");
num_resif = (int*)calloc(cfg->num_ifs, sizeof(int));
if(!num_resif) fatal_exit("malloc failure");
}
for(i=0; i<cfg->num_ifs; i++) {
/* search for duplicates in IP or ifname arguments */
for(i2=0; i2<i; i2++) {
if(strcmp(cfg->ifs[i], cfg->ifs[i2]) == 0) {
fatal_exit("interface: %s present twice, "
"cannot bind same ports twice.",
cfg->ifs[i]);
}
}
if(!resolve_interface_names(&cfg->ifs[i], 1, NULL, &resif[i],
&num_resif[i])) {
fatal_exit("could not resolve interface names, for %s",
cfg->ifs[i]);
}
/* check for port combinations that are not supported */
if(if_is_pp2(resif[i][0], portbuf, cfg->proxy_protocol_port)) {
if(if_is_dnscrypt(resif[i][0], portbuf,
cfg->dnscrypt_port)) {
fatal_exit("PROXYv2 and DNSCrypt combination not "
"supported!");
} else if(if_is_https(resif[i][0], portbuf,
cfg->https_port)) {
fatal_exit("PROXYv2 and DoH combination not "
"supported!");
}
}
/* search for duplicates in the returned addresses */
for(j=0; j<num_resif[i]; j++) {
if(!extstrtoaddr(resif[i][j], &a, &alen, cfg->port)) {
if(strcmp(cfg->ifs[i], resif[i][j]) != 0)
fatal_exit("cannot parse interface address '%s' from the interface specified as '%s'",
resif[i][j], cfg->ifs[i]);
else
fatal_exit("cannot parse interface specified as '%s'",
cfg->ifs[i]);
}
for(i2=0; i2<i; i2++) {
for(j2=0; j2<num_resif[i2]; j2++) {
if(strcmp(resif[i][j], resif[i2][j2])
== 0) {
char info1[1024], info2[1024];
if(strcmp(cfg->ifs[i], resif[i][j]) != 0)
snprintf(info1, sizeof(info1), "address %s from interface: %s", resif[i][j], cfg->ifs[i]);
else snprintf(info1, sizeof(info1), "interface: %s", cfg->ifs[i]);
if(strcmp(cfg->ifs[i2], resif[i2][j2]) != 0)
snprintf(info2, sizeof(info2), "address %s from interface: %s", resif[i2][j2], cfg->ifs[i2]);
else snprintf(info2, sizeof(info2), "interface: %s", cfg->ifs[i2]);
fatal_exit("%s present twice, cannot bind the same ports twice. The first entry is %s and the second is %s", resif[i][j], info2, info1);
}
}
}
}
}
for(i=0; i<cfg->num_ifs; i++) {
config_del_strarray(resif[i], num_resif[i]);
}
free(resif);
free(num_resif);
for(i=0; i<cfg->num_out_ifs; i++) {
if(!ipstrtoaddr(cfg->out_ifs[i], UNBOUND_DNS_PORT, &a, &alen) &&
!netblockstrtoaddr(cfg->out_ifs[i], UNBOUND_DNS_PORT, &a, &alen, &d)) {
fatal_exit("cannot parse outgoing-interface "
"specified as '%s'", cfg->out_ifs[i]);
}
for(j=0; j<cfg->num_out_ifs; j++) {
if(i!=j && strcmp(cfg->out_ifs[i], cfg->out_ifs[j])==0)
fatal_exit("outgoing-interface: %s present "
"twice, cannot bind same ports twice.",
cfg->out_ifs[i]);
}
}
}
/** check interface-automatic-ports */
static void
ifautomaticportschecks(char* ifautomaticports)
{
char* now = ifautomaticports;
while(now && *now) {
char* after;
int extraport;
while(isspace((unsigned char)*now))
now++;
if(!*now)
break;
after = now;
extraport = (int)strtol(now, &after, 10);
if(extraport < 0 || extraport > 65535)
fatal_exit("interface-automatic-ports: port out of range at position %d in '%s'", (int)(now-ifautomaticports)+1, ifautomaticports);
if(extraport == 0 && now == after)
fatal_exit("interface-automatic-ports: parse error at position %d in '%s'", (int)(now-ifautomaticports)+1, ifautomaticports);
now = after;
}
}
/** check acl ips */
static void
aclchecks(struct config_file* cfg)
{
int d;
struct sockaddr_storage a;
socklen_t alen;
struct config_str2list* acl;
for(acl=cfg->acls; acl; acl = acl->next) {
if(!netblockstrtoaddr(acl->str, UNBOUND_DNS_PORT, &a, &alen,
&d)) {
fatal_exit("cannot parse access control address %s %s",
acl->str, acl->str2);
}
}
}
/** check tcp connection limit ips */
static void
tcpconnlimitchecks(struct config_file* cfg)
{
int d;
struct sockaddr_storage a;
socklen_t alen;
struct config_str2list* tcl;
for(tcl=cfg->tcp_connection_limits; tcl; tcl = tcl->next) {
if(!netblockstrtoaddr(tcl->str, UNBOUND_DNS_PORT, &a, &alen,
&d)) {
fatal_exit("cannot parse tcp connection limit address %s %s",
tcl->str, tcl->str2);
}
}
}
/** true if fname is a file */
static int
is_file(const char* fname)
{
struct stat buf;
if(stat(fname, &buf) < 0) {
if(errno==EACCES) {
printf("warning: no search permission for one of the directories in path: %s\n", fname);
return 1;
}
perror(fname);
return 0;
}
if(S_ISDIR(buf.st_mode)) {
printf("%s is not a file\n", fname);
return 0;
}
return 1;
}
/** true if fname is a directory */
static int
is_dir(const char* fname)
{
struct stat buf;
if(stat(fname, &buf) < 0) {
if(errno==EACCES) {
printf("warning: no search permission for one of the directories in path: %s\n", fname);
return 1;
}
perror(fname);
return 0;
}
if(!(S_ISDIR(buf.st_mode))) {
printf("%s is not a directory\n", fname);
return 0;
}
return 1;
}
/** get base dir of a fname */
static char*
basedir(char* fname)
{
char* rev;
if(!fname) fatal_exit("out of memory");
rev = strrchr(fname, '/');
if(!rev) return NULL;
if(fname == rev) return NULL;
rev[0] = 0;
return fname;
}
/** check chroot for a file string */
static void
check_chroot_string(const char* desc, char** ss,
const char* chrootdir, struct config_file* cfg)
{
char* str = *ss;
if(str && str[0]) {
*ss = fname_after_chroot(str, cfg, 1);
if(!*ss) fatal_exit("out of memory");
if(!is_file(*ss)) {
if(chrootdir && chrootdir[0])
fatal_exit("%s: \"%s\" does not exist in "
"chrootdir %s", desc, str, chrootdir);
else
fatal_exit("%s: \"%s\" does not exist",
desc, str);
}
/* put in a new full path for continued checking */
free(str);
}
}
/** check file list, every file must be inside the chroot location */
static void
check_chroot_filelist(const char* desc, struct config_strlist* list,
const char* chrootdir, struct config_file* cfg)
{
struct config_strlist* p;
for(p=list; p; p=p->next) {
check_chroot_string(desc, &p->str, chrootdir, cfg);
}
}
/** check file list, with wildcard processing */
static void
check_chroot_filelist_wild(const char* desc, struct config_strlist* list,
const char* chrootdir, struct config_file* cfg)
{
struct config_strlist* p;
for(p=list; p; p=p->next) {
#ifdef HAVE_GLOB
if(strchr(p->str, '*') || strchr(p->str, '[') ||
strchr(p->str, '?') || strchr(p->str, '{') ||
strchr(p->str, '~')) {
char* s = p->str;
/* adjust whole pattern for chroot and check later */
p->str = fname_after_chroot(p->str, cfg, 1);
free(s);
} else
#endif /* HAVE_GLOB */
check_chroot_string(desc, &p->str, chrootdir, cfg);
}
}
#ifdef CLIENT_SUBNET
/** check ECS configuration */
static void
ecs_conf_checks(struct config_file* cfg)
{
struct ecs_whitelist* whitelist = NULL;
if(!(whitelist = ecs_whitelist_create()))
fatal_exit("Could not create ednssubnet whitelist: out of memory");
if(!ecs_whitelist_apply_cfg(whitelist, cfg))
fatal_exit("Could not setup ednssubnet whitelist");
ecs_whitelist_delete(whitelist);
}
#endif /* CLIENT_SUBNET */
/** check that the modules exist, are compiled in */
static void
check_modules_exist(const char* module_conf)
{
const char** names = module_list_avail();
const char* s = module_conf;
while(*s) {
int i = 0;
int is_ok = 0;
while(*s && isspace((unsigned char)*s))
s++;
if(!*s) break;
while(names[i]) {
if(strncmp(names[i], s, strlen(names[i])) == 0) {
is_ok = 1;
break;
}
i++;
}
if(is_ok == 0) {
char n[64];
size_t j;
n[0]=0;
n[sizeof(n)-1]=0;
for(j=0; j<sizeof(n)-1; j++) {
if(!s[j] || isspace((unsigned char)s[j])) {
n[j] = 0;
break;
}
n[j] = s[j];
}
fatal_exit("module_conf lists module '%s' but that "
"module is not available.", n);
}
s += strlen(names[i]);
}
}
/** check configuration for errors */
static void
morechecks(struct config_file* cfg)
{
warn_hosts("stub-host", cfg->stubs);
warn_hosts("forward-host", cfg->forwards);
interfacechecks(cfg);
ifautomaticportschecks(cfg->if_automatic_ports);
aclchecks(cfg);
tcpconnlimitchecks(cfg);
if(cfg->verbosity < 0)
fatal_exit("verbosity value < 0");
if(cfg->num_threads <= 0 || cfg->num_threads > 10000)
fatal_exit("num_threads value weird");
if(!cfg->do_ip4 && !cfg->do_ip6)
fatal_exit("ip4 and ip6 are both disabled, pointless");
if(!cfg->do_ip4 && cfg->prefer_ip4)
fatal_exit("cannot prefer and disable ip4, pointless");
if(!cfg->do_ip6 && cfg->prefer_ip6)
fatal_exit("cannot prefer and disable ip6, pointless");
if(!cfg->do_udp && !cfg->do_tcp)
fatal_exit("udp and tcp are both disabled, pointless");
if(cfg->edns_buffer_size > cfg->msg_buffer_size)
fatal_exit("edns-buffer-size larger than msg-buffer-size, "
"answers will not fit in processing buffer");
#ifdef UB_ON_WINDOWS
w_config_adjust_directory(cfg);
#endif
if(cfg->chrootdir && cfg->chrootdir[0] &&
cfg->chrootdir[strlen(cfg->chrootdir)-1] == '/')
fatal_exit("chootdir %s has trailing slash '/' please remove.",
cfg->chrootdir);
if(cfg->chrootdir && cfg->chrootdir[0] &&
!is_dir(cfg->chrootdir)) {
fatal_exit("bad chroot directory");
}
if(cfg->directory && cfg->directory[0]) {
char* ad = fname_after_chroot(cfg->directory, cfg, 0);
if(!ad) fatal_exit("out of memory");
if(!is_dir(ad)) fatal_exit("bad chdir directory");
free(ad);
}
if( (cfg->chrootdir && cfg->chrootdir[0]) ||
(cfg->directory && cfg->directory[0])) {
if(cfg->pidfile && cfg->pidfile[0]) {
char* ad = (cfg->pidfile[0]=='/')?strdup(cfg->pidfile):
fname_after_chroot(cfg->pidfile, cfg, 1);
char* bd = basedir(ad);
if(bd && !is_dir(bd))
fatal_exit("pidfile directory does not exist");
free(ad);
}
if(cfg->logfile && cfg->logfile[0]) {
char* ad = fname_after_chroot(cfg->logfile, cfg, 1);
char* bd = basedir(ad);
if(bd && !is_dir(bd))
fatal_exit("logfile directory does not exist");
free(ad);
}
}
check_chroot_filelist("file with root-hints",
cfg->root_hints, cfg->chrootdir, cfg);
check_chroot_filelist("trust-anchor-file",
cfg->trust_anchor_file_list, cfg->chrootdir, cfg);
check_chroot_filelist("auto-trust-anchor-file",
cfg->auto_trust_anchor_file_list, cfg->chrootdir, cfg);
check_chroot_filelist_wild("trusted-keys-file",
cfg->trusted_keys_file_list, cfg->chrootdir, cfg);
+ if(cfg->disable_edns_do && strstr(cfg->module_conf, "validator")
+ && (cfg->trust_anchor_file_list
+ || cfg->trust_anchor_list
+ || cfg->auto_trust_anchor_file_list
+ || cfg->trusted_keys_file_list)) {
+ char* key = NULL;
+ if(cfg->auto_trust_anchor_file_list)
+ key = cfg->auto_trust_anchor_file_list->str;
+ if(!key && cfg->trust_anchor_file_list)
+ key = cfg->trust_anchor_file_list->str;
+ if(!key && cfg->trust_anchor_list)
+ key = cfg->trust_anchor_list->str;
+ if(!key && cfg->trusted_keys_file_list)
+ key = cfg->trusted_keys_file_list->str;
+ if(!key) key = "";
+ fatal_exit("disable-edns-do does not allow DNSSEC to work, but the validator module uses a trust anchor %s, turn off disable-edns-do or disable validation", key);
+ }
#ifdef USE_IPSECMOD
if(cfg->ipsecmod_enabled && strstr(cfg->module_conf, "ipsecmod")) {
/* only check hook if enabled */
check_chroot_string("ipsecmod-hook", &cfg->ipsecmod_hook,
cfg->chrootdir, cfg);
}
#endif
/* remove chroot setting so that modules are not stripping pathnames */
free(cfg->chrootdir);
cfg->chrootdir = NULL;
/* check that the modules listed in module_conf exist */
check_modules_exist(cfg->module_conf);
/* Respip is known to *not* work with dns64. */
if(strcmp(cfg->module_conf, "iterator") != 0
&& strcmp(cfg->module_conf, "validator iterator") != 0
&& strcmp(cfg->module_conf, "dns64 validator iterator") != 0
&& strcmp(cfg->module_conf, "dns64 iterator") != 0
&& strcmp(cfg->module_conf, "respip iterator") != 0
&& strcmp(cfg->module_conf, "respip validator iterator") != 0
&& strcmp(cfg->module_conf, "respip dns64 validator iterator") != 0
&& strcmp(cfg->module_conf, "respip dns64 iterator") != 0
#ifdef WITH_PYTHONMODULE
&& strcmp(cfg->module_conf, "python iterator") != 0
&& strcmp(cfg->module_conf, "python respip iterator") != 0
&& strcmp(cfg->module_conf, "python validator iterator") != 0
&& strcmp(cfg->module_conf, "python respip validator iterator") != 0
&& strcmp(cfg->module_conf, "validator python iterator") != 0
&& strcmp(cfg->module_conf, "dns64 python iterator") != 0
&& strcmp(cfg->module_conf, "dns64 python validator iterator") != 0
&& strcmp(cfg->module_conf, "dns64 validator python iterator") != 0
&& strcmp(cfg->module_conf, "python dns64 iterator") != 0
&& strcmp(cfg->module_conf, "python dns64 validator iterator") != 0
#endif
#ifdef WITH_DYNLIBMODULE
&& strcmp(cfg->module_conf, "dynlib iterator") != 0
&& strcmp(cfg->module_conf, "dynlib dynlib iterator") != 0
&& strcmp(cfg->module_conf, "dynlib dynlib dynlib iterator") != 0
&& strcmp(cfg->module_conf, "python dynlib iterator") != 0
&& strcmp(cfg->module_conf, "python dynlib dynlib iterator") != 0
&& strcmp(cfg->module_conf, "python dynlib dynlib dynlib iterator") != 0
&& strcmp(cfg->module_conf, "dynlib respip iterator") != 0
&& strcmp(cfg->module_conf, "dynlib validator iterator") != 0
&& strcmp(cfg->module_conf, "dynlib dynlib validator iterator") != 0
&& strcmp(cfg->module_conf, "dynlib dynlib dynlib validator iterator") != 0
&& strcmp(cfg->module_conf, "python dynlib validator iterator") != 0
&& strcmp(cfg->module_conf, "python dynlib dynlib validator iterator") != 0
&& strcmp(cfg->module_conf, "python dynlib dynlib dynlib validator iterator") != 0
&& strcmp(cfg->module_conf, "dynlib respip validator iterator") != 0
&& strcmp(cfg->module_conf, "validator dynlib iterator") != 0
&& strcmp(cfg->module_conf, "dns64 dynlib iterator") != 0
&& strcmp(cfg->module_conf, "dns64 dynlib validator iterator") != 0
&& strcmp(cfg->module_conf, "dns64 validator dynlib iterator") != 0
&& strcmp(cfg->module_conf, "dynlib dns64 iterator") != 0
&& strcmp(cfg->module_conf, "dynlib dns64 validator iterator") != 0
&& strcmp(cfg->module_conf, "dynlib dns64 cachedb iterator") != 0
&& strcmp(cfg->module_conf, "dynlib dns64 validator cachedb iterator") != 0
&& strcmp(cfg->module_conf, "dns64 dynlib cachedb iterator") != 0
&& strcmp(cfg->module_conf, "dns64 dynlib validator cachedb iterator") != 0
&& strcmp(cfg->module_conf, "dynlib cachedb iterator") != 0
&& strcmp(cfg->module_conf, "dynlib respip cachedb iterator") != 0
&& strcmp(cfg->module_conf, "dynlib validator cachedb iterator") != 0
&& strcmp(cfg->module_conf, "dynlib respip validator cachedb iterator") != 0
&& strcmp(cfg->module_conf, "cachedb dynlib iterator") != 0
&& strcmp(cfg->module_conf, "respip cachedb dynlib iterator") != 0
&& strcmp(cfg->module_conf, "validator cachedb dynlib iterator") != 0
&& strcmp(cfg->module_conf, "respip validator cachedb dynlib iterator") != 0
&& strcmp(cfg->module_conf, "validator dynlib cachedb iterator") != 0
&& strcmp(cfg->module_conf, "respip validator dynlib cachedb iterator") != 0
&& strcmp(cfg->module_conf, "dynlib subnetcache iterator") != 0
&& strcmp(cfg->module_conf, "dynlib respip subnetcache iterator") != 0
&& strcmp(cfg->module_conf, "subnetcache dynlib iterator") != 0
&& strcmp(cfg->module_conf, "respip subnetcache dynlib iterator") != 0
&& strcmp(cfg->module_conf, "dynlib subnetcache validator iterator") != 0
&& strcmp(cfg->module_conf, "dynlib respip subnetcache validator iterator") != 0
&& strcmp(cfg->module_conf, "subnetcache dynlib validator iterator") != 0
&& strcmp(cfg->module_conf, "respip subnetcache dynlib validator iterator") != 0
&& strcmp(cfg->module_conf, "subnetcache validator dynlib iterator") != 0
&& strcmp(cfg->module_conf, "respip subnetcache validator dynlib iterator") != 0
&& strcmp(cfg->module_conf, "dynlib ipsecmod iterator") != 0
&& strcmp(cfg->module_conf, "dynlib ipsecmod respip iterator") != 0
&& strcmp(cfg->module_conf, "ipsecmod dynlib iterator") != 0
&& strcmp(cfg->module_conf, "ipsecmod dynlib respip iterator") != 0
&& strcmp(cfg->module_conf, "ipsecmod validator iterator") != 0
&& strcmp(cfg->module_conf, "ipsecmod respip validator iterator") != 0
&& strcmp(cfg->module_conf, "dynlib ipsecmod validator iterator") != 0
&& strcmp(cfg->module_conf, "dynlib ipsecmod respip validator iterator") != 0
&& strcmp(cfg->module_conf, "ipsecmod dynlib validator iterator") != 0
&& strcmp(cfg->module_conf, "ipsecmod dynlib respip validator iterator") != 0
&& strcmp(cfg->module_conf, "ipsecmod validator dynlib iterator") != 0
&& strcmp(cfg->module_conf, "ipsecmod respip validator dynlib iterator") != 0
#endif
#ifdef USE_CACHEDB
&& strcmp(cfg->module_conf, "validator cachedb iterator") != 0
&& strcmp(cfg->module_conf, "respip validator cachedb iterator") != 0
&& strcmp(cfg->module_conf, "cachedb iterator") != 0
&& strcmp(cfg->module_conf, "respip cachedb iterator") != 0
&& strcmp(cfg->module_conf, "dns64 validator cachedb iterator") != 0
&& strcmp(cfg->module_conf, "dns64 cachedb iterator") != 0
#endif
#if defined(WITH_PYTHONMODULE) && defined(USE_CACHEDB)
&& strcmp(cfg->module_conf, "python dns64 cachedb iterator") != 0
&& strcmp(cfg->module_conf, "python dns64 validator cachedb iterator") != 0
&& strcmp(cfg->module_conf, "dns64 python cachedb iterator") != 0
&& strcmp(cfg->module_conf, "dns64 python validator cachedb iterator") != 0
&& strcmp(cfg->module_conf, "python cachedb iterator") != 0
&& strcmp(cfg->module_conf, "python respip cachedb iterator") != 0
&& strcmp(cfg->module_conf, "python validator cachedb iterator") != 0
&& strcmp(cfg->module_conf, "python respip validator cachedb iterator") != 0
&& strcmp(cfg->module_conf, "cachedb python iterator") != 0
&& strcmp(cfg->module_conf, "respip cachedb python iterator") != 0
&& strcmp(cfg->module_conf, "validator cachedb python iterator") != 0
&& strcmp(cfg->module_conf, "respip validator cachedb python iterator") != 0
&& strcmp(cfg->module_conf, "validator python cachedb iterator") != 0
&& strcmp(cfg->module_conf, "respip validator python cachedb iterator") != 0
#endif
#if defined(CLIENT_SUBNET) && defined(USE_CACHEDB)
&& strcmp(cfg->module_conf, "respip subnetcache validator cachedb iterator") != 0
&& strcmp(cfg->module_conf, "subnetcache validator cachedb iterator") != 0
#endif
#ifdef CLIENT_SUBNET
&& strcmp(cfg->module_conf, "subnetcache iterator") != 0
&& strcmp(cfg->module_conf, "respip subnetcache iterator") != 0
&& strcmp(cfg->module_conf, "subnetcache validator iterator") != 0
&& strcmp(cfg->module_conf, "respip subnetcache validator iterator") != 0
&& strcmp(cfg->module_conf, "dns64 subnetcache iterator") != 0
&& strcmp(cfg->module_conf, "dns64 subnetcache validator iterator") != 0
&& strcmp(cfg->module_conf, "dns64 subnetcache respip iterator") != 0
&& strcmp(cfg->module_conf, "dns64 subnetcache respip validator iterator") != 0
#endif
#if defined(WITH_PYTHONMODULE) && defined(CLIENT_SUBNET)
&& strcmp(cfg->module_conf, "python subnetcache iterator") != 0
&& strcmp(cfg->module_conf, "python respip subnetcache iterator") != 0
&& strcmp(cfg->module_conf, "subnetcache python iterator") != 0
&& strcmp(cfg->module_conf, "respip subnetcache python iterator") != 0
&& strcmp(cfg->module_conf, "python subnetcache validator iterator") != 0
&& strcmp(cfg->module_conf, "python respip subnetcache validator iterator") != 0
&& strcmp(cfg->module_conf, "subnetcache python validator iterator") != 0
&& strcmp(cfg->module_conf, "respip subnetcache python validator iterator") != 0
&& strcmp(cfg->module_conf, "subnetcache validator python iterator") != 0
&& strcmp(cfg->module_conf, "respip subnetcache validator python iterator") != 0
#endif
#ifdef USE_IPSECMOD
&& strcmp(cfg->module_conf, "ipsecmod iterator") != 0
&& strcmp(cfg->module_conf, "ipsecmod respip iterator") != 0
&& strcmp(cfg->module_conf, "ipsecmod validator iterator") != 0
&& strcmp(cfg->module_conf, "ipsecmod respip validator iterator") != 0
#endif
#if defined(WITH_PYTHONMODULE) && defined(USE_IPSECMOD)
&& strcmp(cfg->module_conf, "python ipsecmod iterator") != 0
&& strcmp(cfg->module_conf, "python ipsecmod respip iterator") != 0
&& strcmp(cfg->module_conf, "ipsecmod python iterator") != 0
&& strcmp(cfg->module_conf, "ipsecmod python respip iterator") != 0
&& strcmp(cfg->module_conf, "ipsecmod validator iterator") != 0
&& strcmp(cfg->module_conf, "ipsecmod respip validator iterator") != 0
&& strcmp(cfg->module_conf, "python ipsecmod validator iterator") != 0
&& strcmp(cfg->module_conf, "python ipsecmod respip validator iterator") != 0
&& strcmp(cfg->module_conf, "ipsecmod python validator iterator") != 0
&& strcmp(cfg->module_conf, "ipsecmod python respip validator iterator") != 0
&& strcmp(cfg->module_conf, "ipsecmod validator python iterator") != 0
&& strcmp(cfg->module_conf, "ipsecmod respip validator python iterator") != 0
#endif
#ifdef USE_IPSET
&& strcmp(cfg->module_conf, "validator ipset iterator") != 0
&& strcmp(cfg->module_conf, "validator ipset respip iterator") != 0
&& strcmp(cfg->module_conf, "ipset iterator") != 0
&& strcmp(cfg->module_conf, "ipset respip iterator") != 0
#endif
) {
fatal_exit("module conf '%s' is not known to work",
cfg->module_conf);
}
#ifdef HAVE_GETPWNAM
if(cfg->username && cfg->username[0]) {
if(getpwnam(cfg->username) == NULL)
fatal_exit("user '%s' does not exist.", cfg->username);
# ifdef HAVE_ENDPWENT
endpwent();
# endif
}
#endif
if(cfg->remote_control_enable && options_remote_is_address(cfg)
&& cfg->control_use_cert) {
check_chroot_string("server-key-file", &cfg->server_key_file,
cfg->chrootdir, cfg);
check_chroot_string("server-cert-file", &cfg->server_cert_file,
cfg->chrootdir, cfg);
if(!is_file(cfg->control_key_file))
fatal_exit("control-key-file: \"%s\" does not exist",
cfg->control_key_file);
if(!is_file(cfg->control_cert_file))
fatal_exit("control-cert-file: \"%s\" does not exist",
cfg->control_cert_file);
}
donotquerylocalhostcheck(cfg);
localzonechecks(cfg);
view_and_respipchecks(cfg);
#ifdef CLIENT_SUBNET
ecs_conf_checks(cfg);
#endif
}
/** check forwards */
static void
check_fwd(struct config_file* cfg)
{
struct iter_forwards* fwd = forwards_create();
if(!fwd || !forwards_apply_cfg(fwd, cfg)) {
fatal_exit("Could not set forward zones");
}
forwards_delete(fwd);
}
/** check hints */
static void
check_hints(struct config_file* cfg)
{
struct iter_hints* hints = hints_create();
if(!hints || !hints_apply_cfg(hints, cfg)) {
fatal_exit("Could not set root or stub hints");
}
hints_delete(hints);
}
/** check auth zones */
static void
check_auth(struct config_file* cfg)
{
int is_rpz = 0;
struct auth_zones* az = auth_zones_create();
if(!az || !auth_zones_apply_cfg(az, cfg, 0, &is_rpz, NULL, NULL)) {
fatal_exit("Could not setup authority zones");
}
auth_zones_delete(az);
}
/** check config file */
static void
checkconf(const char* cfgfile, const char* opt, int final)
{
char oldwd[4096];
struct config_file* cfg = config_create();
if(!cfg)
fatal_exit("out of memory");
oldwd[0] = 0;
if(!getcwd(oldwd, sizeof(oldwd))) {
log_err("cannot getcwd: %s", strerror(errno));
oldwd[0] = 0;
}
if(!config_read(cfg, cfgfile, NULL)) {
/* config_read prints messages to stderr */
config_delete(cfg);
exit(1);
}
if(oldwd[0] && chdir(oldwd) == -1)
log_err("cannot chdir(%s): %s", oldwd, strerror(errno));
if(opt) {
print_option(cfg, opt, final);
config_delete(cfg);
return;
}
morechecks(cfg);
check_mod(cfg, iter_get_funcblock());
check_mod(cfg, val_get_funcblock());
#ifdef WITH_PYTHONMODULE
if(strstr(cfg->module_conf, "python"))
check_mod(cfg, pythonmod_get_funcblock());
#endif
check_fwd(cfg);
check_hints(cfg);
check_auth(cfg);
printf("unbound-checkconf: no errors in %s\n", cfgfile);
config_delete(cfg);
}
/** getopt global, in case header files fail to declare it. */
extern int optind;
/** getopt global, in case header files fail to declare it. */
extern char* optarg;
/** Main routine for checkconf */
int main(int argc, char* argv[])
{
int c;
int final = 0;
const char* f;
const char* opt = NULL;
const char* cfgfile = CONFIGFILE;
checklock_start();
log_ident_set("unbound-checkconf");
log_init(NULL, 0, NULL);
#ifdef USE_WINSOCK
/* use registry config file in preference to compiletime location */
if(!(cfgfile=w_lookup_reg_str("Software\\Unbound", "ConfigFile")))
cfgfile = CONFIGFILE;
#endif /* USE_WINSOCK */
/* parse the options */
while( (c=getopt(argc, argv, "fho:")) != -1) {
switch(c) {
case 'f':
final = 1;
break;
case 'o':
opt = optarg;
break;
case '?':
case 'h':
default:
usage();
}
}
argc -= optind;
argv += optind;
if(argc != 0 && argc != 1)
usage();
if(argc == 1)
f = argv[0];
else f = cfgfile;
checkconf(f, opt, final);
checklock_stop();
return 0;
}
diff --git a/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.conf b/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.conf
new file mode 100644
index 000000000000..ff76cc37970c
--- /dev/null
+++ b/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.conf
@@ -0,0 +1,29 @@
+server:
+ verbosity: 4
+ interface: 127.0.0.1
+ port: @PORT@
+ use-syslog: no
+ directory: ""
+ pidfile: "unbound.pid"
+ chroot: ""
+ username: ""
+ module-config: "cachedb iterator"
+ do-not-query-localhost: no
+ qname-minimisation: no
+
+forward-zone:
+ name: "."
+ forward-addr: 127.0.0.1@@TOPORT@
+
+stub-zone:
+ name: "example.com"
+ stub-addr: 127.0.0.1@@TOPORT@
+
+remote-control:
+ control-enable: yes
+ control-interface: @CONTROL_PATH@/controlpipe.@CONTROL_PID@
+ control-use-cert: no
+
+cachedb:
+ backend: "testframe"
+ secret-seed: "testvalue"
diff --git a/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.dsc b/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.dsc
new file mode 100644
index 000000000000..9d267436edf6
--- /dev/null
+++ b/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.dsc
@@ -0,0 +1,16 @@
+BaseName: cachedb_no_store
+Version: 1.0
+Description: cachedb test the cachedb-no-store option
+CreationDate: Wed 11 Oct 11:00:00 CEST 2023
+Maintainer: dr. W.C.A. Wijngaards
+Category:
+Component:
+CmdDepends:
+Depends:
+Help:
+Pre: cachedb_no_store.pre
+Post: cachedb_no_store.post
+Test: cachedb_no_store.test
+AuxFiles:
+Passed:
+Failure:
diff --git a/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.post b/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.post
new file mode 100644
index 000000000000..901f01a8753d
--- /dev/null
+++ b/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.post
@@ -0,0 +1,20 @@
+# #-- cachedb_no_store.post --#
+# source the master var file when it's there
+[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
+# source the test var file when it's there
+[ -f .tpkg.var.test ] && source .tpkg.var.test
+#
+# do your teardown here
+PRE="../.."
+. ../common.sh
+
+echo "> cat logfiles"
+cat fwd.log
+if test -f fwd2.log; then cat fwd2.log; else echo "no fwd2.log"; fi
+if test -f fwd3.log; then cat fwd3.log; else echo "no fwd3.log"; fi
+if test -f fwd4.log; then cat fwd4.log; else echo "no fwd4.log"; fi
+cat unbound.log
+if test -f unbound2.log; then cat unbound2.log; else echo "no unbound2.log"; fi
+kill_pid $FWD_PID
+kill_pid `cat unbound.pid`
+rm -f $CONTROL_PATH/controlpipe.$CONTROL_PID
diff --git a/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.pre b/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.pre
new file mode 100644
index 000000000000..e59d3b8da759
--- /dev/null
+++ b/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.pre
@@ -0,0 +1,36 @@
+# #-- cachedb_no_store.pre--#
+# source the master var file when it's there
+[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
+# use .tpkg.var.test for in test variable passing
+[ -f .tpkg.var.test ] && source .tpkg.var.test
+
+PRE="../.."
+. ../common.sh
+if grep "define USE_CACHEDB 1" $PRE/config.h; then echo test enabled; else skip_test "test skipped"; fi
+
+get_random_port 2
+UNBOUND_PORT=$RND_PORT
+FWD_PORT=$(($RND_PORT + 1))
+echo "UNBOUND_PORT=$UNBOUND_PORT" >> .tpkg.var.test
+echo "FWD_PORT=$FWD_PORT" >> .tpkg.var.test
+
+# start forwarder
+get_ldns_testns
+$LDNS_TESTNS -p $FWD_PORT cachedb_no_store.testns >fwd.log 2>&1 &
+FWD_PID=$!
+echo "FWD_PID=$FWD_PID" >> .tpkg.var.test
+
+# make config file
+CONTROL_PATH=/tmp
+CONTROL_PID=$$
+sed -e 's/@PORT\@/'$UNBOUND_PORT'/' -e 's/@TOPORT\@/'$FWD_PORT'/' -e 's?@CONTROL_PATH\@?'$CONTROL_PATH'?' -e 's/@CONTROL_PID@/'$CONTROL_PID'/' < cachedb_no_store.conf > ub.conf
+# start unbound in the background
+$PRE/unbound -d -c ub.conf >unbound.log 2>&1 &
+UNBOUND_PID=$!
+echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test
+echo "CONTROL_PATH=$CONTROL_PATH" >> .tpkg.var.test
+echo "CONTROL_PID=$CONTROL_PID" >> .tpkg.var.test
+
+cat .tpkg.var.test
+wait_ldns_testns_up fwd.log
+wait_unbound_up unbound.log
diff --git a/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.servfail.testns b/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.servfail.testns
new file mode 100644
index 000000000000..b41abb0ff629
--- /dev/null
+++ b/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.servfail.testns
@@ -0,0 +1,8 @@
+ENTRY_BEGIN
+MATCH opcode
+ADJUST copy_id copy_query
+REPLY QR AA SERVFAIL
+SECTION QUESTION
+txt1.example.com. IN TXT
+SECTION ANSWER
+ENTRY_END
diff --git a/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.test b/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.test
new file mode 100644
index 000000000000..352026844776
--- /dev/null
+++ b/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.test
@@ -0,0 +1,132 @@
+# #-- cachedb_no_store.test --#
+# source the master var file when it's there
+[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
+# use .tpkg.var.test for in test variable passing
+[ -f .tpkg.var.test ] && source .tpkg.var.test
+
+PRE="../.."
+. ../common.sh
+
+# do the test
+get_ldns_testns
+
+# query for a text record that is stored by unbound's cache and cachedb
+# in the testframe cache.
+echo "> dig txt1.example.com."
+dig @localhost -p $UNBOUND_PORT txt1.example.com. TXT | tee outfile
+if grep "example text message" outfile; then
+ echo "OK"
+else
+ echo "Not OK"
+ exit 1
+fi
+
+# stop the forwarder with servfail, to check the answer came from the cache
+echo "> stop ldns-testns"
+kill_pid $FWD_PID
+echo "> start ldns-testns with servfails"
+$LDNS_TESTNS -p $FWD_PORT cachedb_no_store.servfail.testns >fwd2.log 2>&1 &
+FWD_PID=$!
+echo "FWD_PID=$FWD_PID" >> .tpkg.var.test
+wait_ldns_testns_up fwd2.log
+
+echo "> dig txt1.example.com. from unbound cache"
+dig @localhost -p $UNBOUND_PORT txt1.example.com. TXT | tee outfile
+if grep "example text message" outfile; then
+ echo "OK"
+else
+ echo "Not OK"
+ exit 1
+fi
+
+# clear the cache of unbound, but not cachedb testframe cache
+echo "> unbound-control flush"
+$PRE/unbound-control -c ub.conf flush_type txt1.example.com. TXT
+if test $? -ne 0; then
+ echo "wrong exit value."
+ exit 1
+else
+ echo "exit value: OK"
+fi
+
+echo "> dig txt1.example.com. from cachedb"
+dig @localhost -p $UNBOUND_PORT txt1.example.com. TXT | tee outfile
+if grep "example text message" outfile; then
+ echo "OK"
+else
+ echo "Not OK"
+ exit 1
+fi
+
+# start the forwarder again.
+echo "> stop ldns-testns"
+kill_pid $FWD_PID
+echo "> start ldns-testns"
+$LDNS_TESTNS -p $FWD_PORT cachedb_no_store.testns >fwd3.log 2>&1 &
+FWD_PID=$!
+echo "FWD_PID=$FWD_PID" >> .tpkg.var.test
+wait_ldns_testns_up fwd3.log
+
+# stop unbound to flush the cachedb cache
+echo "> stop unbound"
+kill_pid `cat unbound.pid`
+
+echo ""
+echo "> config unbound with cachedb-no-store: yes"
+echo "cachedb: cachedb-no-store: yes" >> ub.conf
+
+# start unbound again.
+echo "> start unbound"
+$PRE/unbound -d -c ub.conf >unbound2.log 2>&1 &
+UNBOUND_PID=$!
+echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test
+wait_unbound_up unbound2.log
+
+echo ""
+echo "> dig txt1.example.com."
+dig @localhost -p $UNBOUND_PORT txt1.example.com. TXT | tee outfile
+if grep "example text message" outfile; then
+ echo "OK"
+else
+ echo "Not OK"
+ exit 1
+fi
+
+# stop the forwarder with servfail, to check the answer came from the cache
+echo "> stop ldns-testns"
+kill_pid $FWD_PID
+echo "> start ldns-testns with servfails"
+$LDNS_TESTNS -p $FWD_PORT cachedb_no_store.servfail.testns >fwd4.log 2>&1 &
+FWD_PID=$!
+echo "FWD_PID=$FWD_PID" >> .tpkg.var.test
+wait_ldns_testns_up fwd4.log
+
+echo "> dig txt1.example.com. from unbound cache"
+dig @localhost -p $UNBOUND_PORT txt1.example.com. TXT | tee outfile
+if grep "example text message" outfile; then
+ echo "OK"
+else
+ echo "Not OK"
+ exit 1
+fi
+
+# clear the cache of unbound, but not cachedb testframe cache
+echo "> unbound-control flush"
+$PRE/unbound-control -c ub.conf flush_type txt1.example.com. TXT
+if test $? -ne 0; then
+ echo "wrong exit value."
+ exit 1
+else
+ echo "exit value: OK"
+fi
+
+echo "> dig txt1.example.com. from cachedb, but that has no message stored"
+dig @localhost -p $UNBOUND_PORT txt1.example.com. TXT | tee outfile
+if grep "SERVFAIL" outfile; then
+ echo "OK"
+else
+ echo "Not OK"
+ exit 1
+fi
+
+exit 0
diff --git a/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.testns b/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.testns
new file mode 100644
index 000000000000..282b224f82bd
--- /dev/null
+++ b/contrib/unbound/testdata/cachedb_no_store.tdir/cachedb_no_store.testns
@@ -0,0 +1,9 @@
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+txt1.example.com. IN TXT
+SECTION ANSWER
+txt1.example.com. IN TXT "example text message"
+ENTRY_END
diff --git a/contrib/unbound/testdata/disable_edns_do.rpl b/contrib/unbound/testdata/disable_edns_do.rpl
new file mode 100644
index 000000000000..82a16da062f1
--- /dev/null
+++ b/contrib/unbound/testdata/disable_edns_do.rpl
@@ -0,0 +1,164 @@
+; config options
+; The island of trust is at example.com
+server:
+ target-fetch-policy: "0 0 0 0 0"
+ qname-minimisation: "no"
+ trust-anchor-signaling: no
+ minimal-responses: no
+ disable-edns-do: yes
+
+stub-zone:
+ name: "."
+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Test lookup with disable-edns-do
+
+; K.ROOT-SERVERS.NET.
+RANGE_BEGIN 0 100
+ ADDRESS 193.0.14.129
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+. IN NS
+SECTION ANSWER
+. IN NS K.ROOT-SERVERS.NET.
+SECTION ADDITIONAL
+K.ROOT-SERVERS.NET. IN A 193.0.14.129
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION AUTHORITY
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+RANGE_END
+
+; a.gtld-servers.net.
+RANGE_BEGIN 0 100
+ ADDRESS 192.5.6.30
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+com. IN NS
+SECTION ANSWER
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+RANGE_END
+
+; ns.example.com.
+RANGE_BEGIN 0 100
+ ADDRESS 1.2.3.4
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN NS
+SECTION ANSWER
+example.com. IN NS ns.example.com.
+example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854}
+ENTRY_END
+
+; response to DNSKEY priming query
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN DNSKEY
+SECTION ANSWER
+example.com. 3600 IN DNSKEY 256 3 3 ALXLUsWqUrY3JYER3T4TBJII s70j+sDS/UT2QRp61SE7S3E EXopNXoFE73JLRmvpi/UrOO/Vz4Se 6wXv/CYCKjGw06U4WRgR YXcpEhJROyNapmdIKSx hOzfLVE1gqA0PweZR8d tY3aNQSRn3sPpwJr6Mi /PqQKAMMrZ9ckJpf1+b QMOOvxgzz2U1GS18b3y ZKcgTMEaJzd/GZYzi/B N2DzQ0MsrSwYXfsNLFO Bbs8PJMW4LYIxeeOe6rUgkWOF 7CC9Dh/dduQ1QrsJhmZAEFfd6ByYV+ ;{id = 2854 (zsk), size = 1688b}
+example.com. 3600 IN RRSIG DNSKEY 3 2 3600 20070926134802 20070829134802 2854 example.com. MCwCFG1yhRNtTEa3Eno2zhVVuy2EJX3wAhQeLyUp6+UXcpC5qGNu9tkrTEgPUg== ;{id = 2854}
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854}
+ENTRY_END
+
+; response to query of interest, when sent with EDNS DO
+ENTRY_BEGIN
+MATCH opcode qtype qname DO
+ADJUST copy_id
+REPLY QR AA DO NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.40
+ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCQMyTjn7WWwpwAR1LlVeLpRgZGuQIUCcJDEkwAuzytTDRlYK7nIMwH1CM= ;{id = 2854}
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+www.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFC99iE9K5y2WNgI0gFvBWaTi9wm6AhUAoUqOpDtG5Zct+Qr9F3mSdnbc6V4= ;{id = 2854}
+ENTRY_END
+
+; response to query of interest, when sent without DO
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.40
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+RANGE_END
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; recursion happens here.
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.40
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+SCENARIO_END
diff --git a/contrib/unbound/testdata/iter_ignore_empty.rpl b/contrib/unbound/testdata/iter_ignore_empty.rpl
index c70dd7e8df7b..4b2f695b8501 100644
--- a/contrib/unbound/testdata/iter_ignore_empty.rpl
+++ b/contrib/unbound/testdata/iter_ignore_empty.rpl
@@ -1,198 +1,248 @@
; config options
server:
target-fetch-policy: "0 0 0 0 0"
qname-minimisation: "no"
minimal-responses: no
stub-zone:
name: "."
stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
CONFIG_END
SCENARIO_BEGIN Test ignore of an empty response.
; K.ROOT-SERVERS.NET.
RANGE_BEGIN 0 100
ADDRESS 193.0.14.129
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR NOERROR
SECTION QUESTION
. IN NS
SECTION ANSWER
. IN NS K.ROOT-SERVERS.NET.
SECTION ADDITIONAL
K.ROOT-SERVERS.NET. IN A 193.0.14.129
ENTRY_END
ENTRY_BEGIN
MATCH opcode subdomain
ADJUST copy_id copy_query
REPLY QR NOERROR
SECTION QUESTION
com. IN NS
SECTION AUTHORITY
com. IN NS a.gtld-servers.net.
SECTION ADDITIONAL
a.gtld-servers.net. IN A 192.5.6.30
ENTRY_END
RANGE_END
; a.gtld-servers.net.
RANGE_BEGIN 0 100
ADDRESS 192.5.6.30
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR NOERROR
SECTION QUESTION
com. IN NS
SECTION ANSWER
com. IN NS a.gtld-servers.net.
SECTION ADDITIONAL
a.gtld-servers.net. IN A 192.5.6.30
ENTRY_END
ENTRY_BEGIN
MATCH opcode subdomain
ADJUST copy_id copy_query
REPLY QR NOERROR
SECTION QUESTION
example.com. IN NS
SECTION AUTHORITY
example.com. IN NS ns.example.com.
example.com. IN NS ns2.example2.com.
SECTION ADDITIONAL
ns.example.com. IN A 1.2.3.4
ENTRY_END
ENTRY_BEGIN
MATCH opcode subdomain
ADJUST copy_id copy_query
REPLY QR NOERROR
SECTION QUESTION
example2.com. IN NS
SECTION AUTHORITY
example2.com. IN NS ns2.example2.com.
SECTION ADDITIONAL
ns2.example2.com. IN A 1.2.3.5
ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+foo.com. IN NS
+SECTION AUTHORITY
+foo.com. IN NS ns.foo.com.
+SECTION ADDITIONAL
+ns.foo.com. IN A 1.2.3.5
+ENTRY_END
RANGE_END
; ns.example.com.
RANGE_BEGIN 0 100
ADDRESS 1.2.3.4
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
example.com. IN NS
SECTION ANSWER
example.com. IN NS ns.example.com.
example.com. IN NS ns2.example.net.
SECTION ADDITIONAL
ns.example.com. IN A 1.2.3.4
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
ns.example.com. IN A
SECTION ANSWER
ns.example.com. IN A 1.2.3.4
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
ns.example.com. IN AAAA
SECTION AUTHORITY
example.com. IN SOA ns root 4 14400 3600 604800 3600
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
SECTION AUTHORITY
SECTION ADDITIONAL
ENTRY_END
RANGE_END
; ns2.example2.com.
RANGE_BEGIN 0 100
ADDRESS 1.2.3.5
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
example2.com. IN NS
SECTION ANSWER
example2.com. IN NS ns2.example2.com.
SECTION ADDITIONAL
ns2.example2.com. IN A 1.2.3.5
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
ns2.example2.com. IN A
SECTION ANSWER
ns2.example2.com. IN A 1.2.3.5
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
ns2.example2.com. IN AAAA
SECTION AUTHORITY
example2.com. IN SOA ns2 root 4 14400 3600 604800 3600
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. IN A 10.20.30.40
ENTRY_END
+
+; foo.com
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+www.foo.com. IN A
+SECTION ANSWER
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+ns.foo.com. IN AAAA
+SECTION ANSWER
+SECTION AUTHORITY
+;foo.com. IN SOA ns2.foo.com root.foo.com 4 14400 3600 604800 3600
+ENTRY_END
RANGE_END
STEP 1 QUERY
ENTRY_BEGIN
REPLY RD
SECTION QUESTION
www.example.com. IN A
ENTRY_END
; recursion happens here.
STEP 10 CHECK_ANSWER
ENTRY_BEGIN
MATCH all
REPLY QR RD RA NOERROR
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. IN A 10.20.30.40
ENTRY_END
; wait for pending nameserver lookups.
STEP 20 TRAFFIC
+; Test that a nodata stays a nodata.
+STEP 30 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.foo.com. IN A
+ENTRY_END
+
+STEP 40 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.foo.com. IN A
+SECTION ANSWER
+ENTRY_END
+
SCENARIO_END
diff --git a/contrib/unbound/testdata/iter_scrub_rr_length.rpl b/contrib/unbound/testdata/iter_scrub_rr_length.rpl
new file mode 100644
index 000000000000..2ef73c2fe152
--- /dev/null
+++ b/contrib/unbound/testdata/iter_scrub_rr_length.rpl
@@ -0,0 +1,298 @@
+; config options
+server:
+ target-fetch-policy: "0 0 0 0 0"
+ qname-minimisation: "no"
+ minimal-responses: no
+ rrset-roundrobin: no
+ ede: yes
+ log-servfail: yes
+
+stub-zone:
+ name: "."
+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Test scrub of RRs of inappropriate length
+
+; K.ROOT-SERVERS.NET.
+RANGE_BEGIN 0 200
+ ADDRESS 193.0.14.129
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+. IN NS
+SECTION ANSWER
+. IN NS K.ROOT-SERVERS.NET.
+SECTION ADDITIONAL
+K.ROOT-SERVERS.NET. IN A 193.0.14.129
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION AUTHORITY
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+RANGE_END
+
+; a.gtld-servers.net.
+RANGE_BEGIN 0 200
+ ADDRESS 192.5.6.30
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+com. IN NS
+SECTION ANSWER
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+RANGE_END
+
+; ns.example.com.
+RANGE_BEGIN 0 200
+ ADDRESS 1.2.3.4
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN NS
+SECTION ANSWER
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.40
+www.example.com. IN A \# 3 030405
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+www.example.com. IN AAAA
+SECTION ANSWER
+www.example.com. IN AAAA 2001:db8::1234
+www.example.com. IN AAAA \# 48 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+broken1.example.com. IN A
+SECTION ANSWER
+broken1.example.com. IN A \# 3 030405
+broken1.example.com. IN A \# 3 030406
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+broken1.example.com. IN AAAA
+SECTION ANSWER
+broken1.example.com. IN AAAA \# 48 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F
+broken1.example.com. IN AAAA \# 48 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E30
+broken1.example.com. IN AAAA \# 48 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E31
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+broken2.example.com. IN A
+SECTION ANSWER
+broken2.example.com. IN A 1.2.3.4
+broken2.example.com. IN A \# 3 030405
+broken2.example.com. IN A 1.2.3.5
+broken2.example.com. IN A \# 3 030406
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A \# 3 030407
+ns.example.com. IN A 1.2.3.6
+ns.example.com. IN A \# 3 030408
+ns.example.com. IN A \# 3 030409
+ns.example.com. IN A 1.2.3.7
+ENTRY_END
+RANGE_END
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.40
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+STEP 20 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+www.example.com. IN AAAA
+ENTRY_END
+
+STEP 30 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+www.example.com. IN AAAA
+SECTION ANSWER
+www.example.com. IN AAAA 2001:db8::1234
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+STEP 40 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+broken1.example.com. IN A
+ENTRY_END
+
+STEP 50 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+broken1.example.com. IN A
+SECTION ANSWER
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+STEP 60 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+broken1.example.com. IN AAAA
+ENTRY_END
+
+STEP 70 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+broken1.example.com. IN AAAA
+SECTION ANSWER
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+
+STEP 80 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+broken2.example.com. IN A
+ENTRY_END
+
+STEP 90 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA NOERROR
+SECTION QUESTION
+broken2.example.com. IN A
+SECTION ANSWER
+broken2.example.com. IN A 1.2.3.4
+broken2.example.com. IN A 1.2.3.5
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.6
+ns.example.com. IN A 1.2.3.7
+ENTRY_END
+
+STEP 100 QUERY
+ENTRY_BEGIN
+REPLY RD CD DO
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+STEP 110 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ede=0
+REPLY QR RD CD RA DO NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.40
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.6
+ns.example.com. IN A 1.2.3.7
+ENTRY_END
+
+SCENARIO_END
diff --git a/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.conf b/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.conf
new file mode 100644
index 000000000000..befb4fbe97b3
--- /dev/null
+++ b/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.conf
@@ -0,0 +1,34 @@
+server:
+ verbosity: 7
+ # num-threads: 1
+ interface: 127.0.0.1
+ port: @PORT@
+ use-syslog: no
+ directory: ""
+ pidfile: "unbound.pid"
+ chroot: ""
+ username: ""
+ do-not-query-localhost: no
+ # for the test, so that DNSSEC verification works.
+ #val-override-date: 20230929090000
+ trust-anchor: ". DS 20326 8 2 E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D"
+
+remote-control:
+ control-enable: yes
+ control-interface: @CONTROL_PATH@/controlpipe.@CONTROL_PID@
+ control-use-cert: no
+
+# for the test, an upstream server in the test setup.
+stub-zone:
+ name: "."
+ stub-addr: 127.0.0.1@@TOPORT@
+
+# hyperlocal root zone
+auth-zone:
+ name: "."
+ fallback-enabled: yes
+ for-downstream: no
+ for-upstream: yes
+ zonefile: "root.zone"
+ zonemd-check: yes
+ zonemd-reject-absence: yes
diff --git a/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.dsc b/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.dsc
new file mode 100644
index 000000000000..8015ac2d13ad
--- /dev/null
+++ b/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.dsc
@@ -0,0 +1,16 @@
+BaseName: root_zonemd
+Version: 1.0
+Description: ZONEMD check for root zone
+CreationDate: Fri 29 Sep 09:00:00 CEST 2023
+Maintainer: dr. W.C.A. Wijngaards
+Category:
+Component:
+CmdDepends:
+Depends:
+Help:
+Pre: root_zonemd.pre
+Post: root_zonemd.post
+Test: root_zonemd.test
+AuxFiles:
+Passed:
+Failure:
diff --git a/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.post b/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.post
new file mode 100644
index 000000000000..a28599fafe7a
--- /dev/null
+++ b/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.post
@@ -0,0 +1,14 @@
+# #-- root_zonemd.post --#
+# source the master var file when it's there
+[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
+# source the test var file when it's there
+[ -f .tpkg.var.test ] && source .tpkg.var.test
+#
+# do your teardown here
+. ../common.sh
+echo "> cat logfiles"
+cat fwd.log
+cat unbound.log
+kill_pid $FWD_PID
+kill_pid $UNBOUND_PID
+rm -f $CONTROL_PATH/controlpipe.$CONTROL_PID
diff --git a/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.pre b/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.pre
new file mode 100644
index 000000000000..fe369bb20bbb
--- /dev/null
+++ b/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.pre
@@ -0,0 +1,50 @@
+# #-- root_zonemd.pre--#
+# source the master var file when it's there
+[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
+# use .tpkg.var.test for in test variable passing
+[ -f .tpkg.var.test ] && source .tpkg.var.test
+
+. ../common.sh
+
+# attempt to download the root zone
+from=k.root-servers.net
+dig @$from . AXFR > root.txt
+if test $? -ne 0; then
+ echo "could not fetch root zone"
+ skip_test "could not fetch root zone"
+fi
+grep " SOA " root.txt | head -1 > root.soa
+cat root.soa >> root.zone
+grep -v " SOA " root.txt >> root.zone
+echo "fetched root.zone"
+ls -l root.zone
+cat root.soa
+
+get_random_port 2
+UNBOUND_PORT=$RND_PORT
+FWD_PORT=$(($RND_PORT + 1))
+echo "UNBOUND_PORT=$UNBOUND_PORT" >> .tpkg.var.test
+echo "FWD_PORT=$FWD_PORT" >> .tpkg.var.test
+
+# start forwarder
+get_ldns_testns
+$LDNS_TESTNS -p $FWD_PORT root_zonemd.testns >fwd.log 2>&1 &
+FWD_PID=$!
+echo "FWD_PID=$FWD_PID" >> .tpkg.var.test
+
+# make config file
+CONTROL_PATH=/tmp
+CONTROL_PID=$$
+sed -e 's/@PORT\@/'$UNBOUND_PORT'/' -e 's/@TOPORT\@/'$FWD_PORT'/' -e 's?@CONTROL_PATH\@?'$CONTROL_PATH'?' -e 's/@CONTROL_PID@/'$CONTROL_PID'/' < root_zonemd.conf > ub.conf
+# start unbound in the background
+PRE="../.."
+$PRE/unbound -d -c ub.conf >unbound.log 2>&1 &
+UNBOUND_PID=$!
+echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test
+echo "CONTROL_PATH=$CONTROL_PATH" >> .tpkg.var.test
+echo "CONTROL_PID=$CONTROL_PID" >> .tpkg.var.test
+
+cat .tpkg.var.test
+wait_ldns_testns_up fwd.log
+wait_unbound_up unbound.log
+
diff --git a/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.test b/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.test
new file mode 100644
index 000000000000..da64ab6e9c2d
--- /dev/null
+++ b/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.test
@@ -0,0 +1,51 @@
+# #-- root_zonemd.test --#
+# source the master var file when it's there
+[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
+# use .tpkg.var.test for in test variable passing
+[ -f .tpkg.var.test ] && source .tpkg.var.test
+
+PRE="../.."
+# do the test
+echo "> dig www.example.com."
+dig @localhost -p $UNBOUND_PORT . SOA | tee outfile
+echo "> check answer"
+if grep root-servers outfile | grep "nstld.verisign-grs.com"; then
+ echo "OK"
+else
+ echo "Not OK"
+ exit 1
+fi
+
+echo "> unbound-control status"
+$PRE/unbound-control -c ub.conf status
+if test $? -ne 0; then
+ echo "wrong exit value."
+ exit 1
+else
+ echo "exit value: OK"
+fi
+
+# This is the output when an unsupported algorithm is used.
+if grep "auth zone . ZONEMD unsupported algorithm" unbound.log; then
+ echo "OK"
+else
+ echo "ZONEMD verification not OK"
+ exit 1
+fi
+
+echo "> unbound-control auth_zone_reload ."
+$PRE/unbound-control -c ub.conf auth_zone_reload . 2>&1 | tee outfile
+if test $? -ne 0; then
+ echo "wrong exit value."
+ exit 1
+fi
+# The output of the reload can be checked.
+#echo "> check unbound-control output"
+#if grep "example.com: ZONEMD verification successful" outfile; then
+ #echo "OK"
+#else
+ #echo "Not OK"
+ #exit 1
+#fi
+
+exit 0
diff --git a/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.testns b/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.testns
new file mode 100644
index 000000000000..d538f2215ecf
--- /dev/null
+++ b/contrib/unbound/testdata/root_zonemd.tdir/root_zonemd.testns
@@ -0,0 +1,9 @@
+# reply to everything
+ENTRY_BEGIN
+MATCH opcode
+ADJUST copy_id copy_query
+REPLY QR SERVFAIL
+SECTION QUESTION
+example.com. IN SOA
+SECTION ANSWER
+ENTRY_END
diff --git a/contrib/unbound/testdata/rpz_cached_cname.rpl b/contrib/unbound/testdata/rpz_cached_cname.rpl
new file mode 100644
index 000000000000..198b946310bf
--- /dev/null
+++ b/contrib/unbound/testdata/rpz_cached_cname.rpl
@@ -0,0 +1,122 @@
+; config options
+server:
+ module-config: "respip validator iterator"
+ target-fetch-policy: "0 0 0 0 0"
+ qname-minimisation: no
+ rrset-roundrobin: no
+ access-control: 192.0.0.0/8 allow
+
+rpz:
+ name: "rpz.example.com"
+ rpz-log: yes
+ rpz-log-name: "rpz.example.com"
+ zonefile:
+TEMPFILE_NAME rpz.example.com
+TEMPFILE_CONTENTS rpz.example.com
+rpz.example.com. 3600 IN SOA ns.rpz.example.com. hostmaster.rpz.example.com. 1 3600 900 86400 3600
+rpz.example.com. 3600 IN NS ns.rpz.example.net.
+a.foo.rpz.example.com. 120 IN A 10.99.99.99
+TEMPFILE_END
+
+stub-zone:
+ name: "."
+ stub-addr: 10.20.30.40
+
+CONFIG_END
+
+SCENARIO_BEGIN Test RPZ with cached CNAME to A record
+
+RANGE_BEGIN 0 100
+ ADDRESS 10.20.30.40
+
+ENTRY_BEGIN
+MATCH opcode qname qtype
+ADJUST copy_id
+REPLY QR NOERROR AA
+SECTION QUESTION
+. IN NS
+SECTION ANSWER
+. IN NS ns.
+SECTION ADDITIONAL
+ns. IN NS 10.20.30.40
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qname qtype
+ADJUST copy_id
+REPLY QR NOERROR AA
+SECTION QUESTION
+b.foo. IN A
+SECTION ANSWER
+b.foo. 30 CNAME a.foo.
+a.foo. 30 A 1.2.3.4
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qname qtype
+ADJUST copy_id
+REPLY QR NOERROR AA
+SECTION QUESTION
+a.foo. IN A
+SECTION ANSWER
+a.foo. A 1.2.3.4
+ENTRY_END
+
+RANGE_END
+
+STEP 10 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+a.foo. IN A
+ENTRY_END
+
+STEP 20 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA AA NOERROR
+SECTION QUESTION
+a.foo. IN A
+SECTION ANSWER
+a.foo. 120 A 10.99.99.99
+ENTRY_END
+
+STEP 30 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+b.foo. IN A
+ENTRY_END
+
+STEP 40 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA AA NOERROR
+SECTION QUESTION
+b.foo. IN A
+SECTION ANSWER
+b.foo. 30 CNAME a.foo.
+a.foo. 120 A 10.99.99.99
+ENTRY_END
+
+STEP 50 TIME_PASSES ELAPSE 3
+
+STEP 60 QUERY
+ENTRY_BEGIN
+REPLY RD
+SECTION QUESTION
+b.foo. IN A
+ENTRY_END
+
+STEP 70 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA AA NOERROR
+SECTION QUESTION
+b.foo. IN A
+SECTION ANSWER
+b.foo. 30 CNAME a.foo.
+a.foo. 120 A 10.99.99.99
+ENTRY_END
+
+SCENARIO_END
diff --git a/contrib/unbound/testdata/subnet_prezero.crpl b/contrib/unbound/testdata/subnet_prezero.crpl
new file mode 100644
index 000000000000..22cdfffb03b3
--- /dev/null
+++ b/contrib/unbound/testdata/subnet_prezero.crpl
@@ -0,0 +1,155 @@
+; subnet unit test
+server:
+ trust-anchor-signaling: no
+ send-client-subnet: 1.2.3.4
+ send-client-subnet: 1.2.3.5
+ target-fetch-policy: "0 0 0 0 0"
+ module-config: "subnetcache validator iterator"
+ qname-minimisation: no
+ minimal-responses: no
+
+stub-zone:
+ name: "example.com"
+ stub-addr: 1.2.3.4
+CONFIG_END
+
+SCENARIO_BEGIN Test subnetcache source prefix zero from client.
+; In RFC7871 section-7.1.2 (para. 2).
+; It says that the recursor must send no EDNS subnet or its own address
+; in the EDNS subnet to the upstream server. And use that answer for the
+; source prefix length zero query. That type of query is for privacy.
+; The authority server is then going to use the resolver's IP, if any, to
+; tailor the answer to the query source address.
+
+; ns.example.com
+RANGE_BEGIN 0 100
+ ADDRESS 1.2.3.4
+
+; reply with 0.0.0.0/0 in reply
+; For the test the answers for 0.0.0.0/0 queries are SERVFAIL, the normal
+; answers are NOERROR.
+ENTRY_BEGIN
+MATCH opcode qtype qname ednsdata
+ADJUST copy_id
+REPLY QR AA DO SERVFAIL
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN CNAME star.c10r.example.com.
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+ 00 08 00 04 ; OPCODE=subnet, optlen
+ 00 01 00 00 ; ip4, scope 0, source 0
+ ; 0.0.0.0/0
+HEX_EDNSDATA_END
+ENTRY_END
+
+; reply without subnet
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA DO NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN CNAME star.c10r.example.com.
+ENTRY_END
+
+; delegation answer for c10r.example.com, with subnet /0
+ENTRY_BEGIN
+MATCH opcode subdomain ednsdata
+ADJUST copy_id copy_query
+REPLY QR DO SERVFAIL
+SECTION QUESTION
+c10r.example.com. IN NS
+SECTION AUTHORITY
+c10r.example.com. IN NS ns.c10r.example.com.
+SECTION ADDITIONAL
+ns.c10r.example.com. IN A 1.2.3.5
+HEX_EDNSDATA_BEGIN
+ 00 08 00 04 ; OPCODE=subnet, optlen
+ 00 01 00 00 ; ip4, scope 0, source 0
+ ; 0.0.0.0/0
+HEX_EDNSDATA_END
+ENTRY_END
+
+; delegation answer for c10r.example.com, without subnet
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR DO NOERROR
+SECTION QUESTION
+c10r.example.com. IN NS
+SECTION AUTHORITY
+c10r.example.com. IN NS ns.c10r.example.com.
+SECTION ADDITIONAL
+ns.c10r.example.com. IN A 1.2.3.5
+ENTRY_END
+RANGE_END
+
+; ns.c10r.example.com
+RANGE_BEGIN 0 100
+ ADDRESS 1.2.3.5
+
+; reply with 0.0.0.0/0 in reply
+ENTRY_BEGIN
+MATCH opcode qtype qname ednsdata
+ADJUST copy_id
+REPLY QR AA DO SERVFAIL
+SECTION QUESTION
+star.c10r.example.com. IN A
+SECTION ANSWER
+star.c10r.example.com. IN A 1.2.3.6
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+ 00 08 00 04 ; OPCODE=subnet, optlen
+ 00 01 00 00 ; ip4, scope 0, source 0
+ ; 0.0.0.0/0
+HEX_EDNSDATA_END
+ENTRY_END
+
+; reply without subnet
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA DO NOERROR
+SECTION QUESTION
+star.c10r.example.com. IN A
+SECTION ANSWER
+star.c10r.example.com. IN A 1.2.3.6
+ENTRY_END
+RANGE_END
+
+; ask for www.example.com
+; server answers with CNAME to a delegation, that then
+; returns a /24 answer.
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+www.example.com. IN A
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+ 00 08 00 04 ; OPCODE=subnet, optlen
+ 00 01 00 00 ; ip4, scope 0, source 0
+ ; 0.0.0.0/0
+HEX_EDNSDATA_END
+ENTRY_END
+
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ednsdata
+REPLY QR RD RA DO NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN CNAME star.c10r.example.com.
+star.c10r.example.com. IN A 1.2.3.6
+SECTION ADDITIONAL
+HEX_EDNSDATA_BEGIN
+ 00 08 00 04 ; OPCODE=subnet, optlen
+ 00 01 00 00 ; ip4, scope 0, source 0
+ ; 0.0.0.0/0
+HEX_EDNSDATA_END
+ENTRY_END
+SCENARIO_END
diff --git a/contrib/unbound/testdata/val_scrub_rr_length.rpl b/contrib/unbound/testdata/val_scrub_rr_length.rpl
new file mode 100644
index 000000000000..0219b918e421
--- /dev/null
+++ b/contrib/unbound/testdata/val_scrub_rr_length.rpl
@@ -0,0 +1,164 @@
+; config options
+; The island of trust is at example.com
+server:
+ trust-anchor: "example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af"
+ val-override-date: "20070916134226"
+ target-fetch-policy: "0 0 0 0 0"
+ qname-minimisation: "no"
+ trust-anchor-signaling: no
+ minimal-responses: no
+ rrset-roundrobin: no
+ ede: yes
+ log-servfail: yes
+
+stub-zone:
+ name: "."
+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Test validator with scrub of RR for inappropriate length
+
+; K.ROOT-SERVERS.NET.
+RANGE_BEGIN 0 100
+ ADDRESS 193.0.14.129
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+. IN NS
+SECTION ANSWER
+. IN NS K.ROOT-SERVERS.NET.
+SECTION ADDITIONAL
+K.ROOT-SERVERS.NET. IN A 193.0.14.129
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION AUTHORITY
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+RANGE_END
+
+; a.gtld-servers.net.
+RANGE_BEGIN 0 100
+ ADDRESS 192.5.6.30
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+com. IN NS
+SECTION ANSWER
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+RANGE_END
+
+; ns.example.com.
+RANGE_BEGIN 0 100
+ ADDRESS 1.2.3.4
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN NS
+SECTION ANSWER
+example.com. IN NS ns.example.com.
+example.com. 3600 IN RRSIG NS 8 2 3600 20070926134150 20070829134150 55566 example.com. cHdLVCzujUQs6b67c1SmCX+/br4tgOg86Gj/R/x+PKUQmWHyeVwBSTlJuLOHbca3CQoyIQc+V2ilK6fjwjbY/dLk4uOlux8L+Zn7HsUXSOwJPIjsM3LuTa8CYDMvYhOP7KGR+vNpJVSsQ25pyDn6Rzsdl3E7DAf7uSkPV8VJwa8=
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ns.example.com. 3600 IN RRSIG A 8 3 3600 20070926134150 20070829134150 55566 example.com. PBwNifMNxTXlDorHX1neq1wUhWLmqk+PZ+PBZCI5BJAmakdgOXdLQiVqlKaErJyA/4uN+99fUf6/DqxwgxL8FIPdBkxMOTJaKrCFjEhL6qozTd3+DI6qFJPgTm1lrkpvb9W72MtK2vxAyT5I/bG2SWKdpzOaQXysbDb2hnxq3as=
+ENTRY_END
+
+; response to DNSKEY priming query
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN DNSKEY
+SECTION ANSWER
+example.com. IN DNSKEY 256 3 8 AwEAAdug/L739i0mgN2nuK/bhxu3wFn5Ud9nK2+XUmZQlPUEZUC5YZvm1rfMmEWTGBn87fFxEu/kjFZHJ55JLzqsbbpVHLbmKCTT2gYR2FV2WDKROGKuYbVkJIXdKAjJ0ONuK507NinYvlWXIoxHn22KAWOd9wKgSTNHBlmGkX+ts3hh ;{id = 55566 (zsk), size = 1024b}
+example.com. 3600 IN RRSIG DNSKEY 8 2 3600 20070926134150 20070829134150 55566 example.com. Ni7Q17l2dzKcAnHdU3Mycpdwo0I6qgGxRvBhBNI43xIUFHJpgKpbeMFxKvVTkbwHyMPMIuHmOaC82IBhOpGD10SExVh4erQhWS3Hvl+m4Cwl3WI9N+AW6CTB9yj+d4xzX3bHjjBt6MSk4bU8ABR7qIoAjgjY7zdtUDWQlaM+d18=
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+example.com. 3600 IN RRSIG NS 8 2 3600 20070926134150 20070829134150 55566 example.com. cHdLVCzujUQs6b67c1SmCX+/br4tgOg86Gj/R/x+PKUQmWHyeVwBSTlJuLOHbca3CQoyIQc+V2ilK6fjwjbY/dLk4uOlux8L+Zn7HsUXSOwJPIjsM3LuTa8CYDMvYhOP7KGR+vNpJVSsQ25pyDn6Rzsdl3E7DAf7uSkPV8VJwa8=
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ns.example.com. 3600 IN RRSIG A 8 3 3600 20070926134150 20070829134150 55566 example.com. PBwNifMNxTXlDorHX1neq1wUhWLmqk+PZ+PBZCI5BJAmakdgOXdLQiVqlKaErJyA/4uN+99fUf6/DqxwgxL8FIPdBkxMOTJaKrCFjEhL6qozTd3+DI6qFJPgTm1lrkpvb9W72MtK2vxAyT5I/bG2SWKdpzOaQXysbDb2hnxq3as=
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+ns.example.com. IN AAAA
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+example.com. 3600 IN RRSIG NS 8 2 3600 20070926134150 20070829134150 55566 example.com. cHdLVCzujUQs6b67c1SmCX+/br4tgOg86Gj/R/x+PKUQmWHyeVwBSTlJuLOHbca3CQoyIQc+V2ilK6fjwjbY/dLk4uOlux8L+Zn7HsUXSOwJPIjsM3LuTa8CYDMvYhOP7KGR+vNpJVSsQ25pyDn6Rzsdl3E7DAf7uSkPV8VJwa8=
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ns.example.com. 3600 IN RRSIG A 8 3 3600 20070926134150 20070829134150 55566 example.com. PBwNifMNxTXlDorHX1neq1wUhWLmqk+PZ+PBZCI5BJAmakdgOXdLQiVqlKaErJyA/4uN+99fUf6/DqxwgxL8FIPdBkxMOTJaKrCFjEhL6qozTd3+DI6qFJPgTm1lrkpvb9W72MtK2vxAyT5I/bG2SWKdpzOaQXysbDb2hnxq3as=
+ENTRY_END
+
+; response to query of interest
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+www.example.com. IN A 10.20.30.40
+www.example.com. IN A \# 5 0102030405
+; RRSIG includes the malformed record.
+www.example.com. 3600 IN RRSIG A 8 3 3600 20070926134150 20070829134150 55566 example.com. W4WFu9B81uRvp3Dj8uLIscypznKWuLuKrZqVg1on5/45/3/xyjHvj3TjTL3gruWFXPiQpldvOstXLZ5eN3OpqILdkVey0eqVATujpHwIruY6GWztVx5WptmFfK6E6zzshZ3RmAARqq/czQ+IZli2A9xixdY2H0o1dSU6gohEjjE=
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+example.com. 3600 IN RRSIG NS 8 2 3600 20070926134150 20070829134150 55566 example.com. cHdLVCzujUQs6b67c1SmCX+/br4tgOg86Gj/R/x+PKUQmWHyeVwBSTlJuLOHbca3CQoyIQc+V2ilK6fjwjbY/dLk4uOlux8L+Zn7HsUXSOwJPIjsM3LuTa8CYDMvYhOP7KGR+vNpJVSsQ25pyDn6Rzsdl3E7DAf7uSkPV8VJwa8=
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ns.example.com. 3600 IN RRSIG A 8 3 3600 20070926134150 20070829134150 55566 example.com. PBwNifMNxTXlDorHX1neq1wUhWLmqk+PZ+PBZCI5BJAmakdgOXdLQiVqlKaErJyA/4uN+99fUf6/DqxwgxL8FIPdBkxMOTJaKrCFjEhL6qozTd3+DI6qFJPgTm1lrkpvb9W72MtK2vxAyT5I/bG2SWKdpzOaQXysbDb2hnxq3as=
+ENTRY_END
+RANGE_END
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+www.example.com. IN A
+ENTRY_END
+
+; recursion happens here.
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all ede=0
+REPLY QR RD RA DO SERVFAIL
+SECTION QUESTION
+www.example.com. IN A
+SECTION ANSWER
+ENTRY_END
+
+SCENARIO_END
diff --git a/contrib/unbound/util/config_file.c b/contrib/unbound/util/config_file.c
index 71c6d245794f..5d3cdfb669af 100644
--- a/contrib/unbound/util/config_file.c
+++ b/contrib/unbound/util/config_file.c
@@ -1,2711 +1,2721 @@
/*
* util/config_file.c - reads and stores the config file for unbound.
*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file contains functions for the config file.
*/
#include "config.h"
#include <ctype.h>
#include <stdarg.h>
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#include "util/log.h"
#include "util/configyyrename.h"
#include "util/config_file.h"
#include "configparser.h"
#include "util/net_help.h"
#include "util/data/msgparse.h"
#include "util/module.h"
#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"
#include "sldns/parseutil.h"
#include "iterator/iterator.h"
#ifdef HAVE_GLOB_H
# include <glob.h>
#endif
#ifdef CLIENT_SUBNET
#include "edns-subnet/edns-subnet.h"
#endif
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
/** from cfg username, after daemonize setup performed */
uid_t cfg_uid = (uid_t)-1;
/** from cfg username, after daemonize setup performed */
gid_t cfg_gid = (gid_t)-1;
/** for debug allow small timeout values for fast rollovers */
int autr_permit_small_holddown = 0;
/** size (in bytes) of stream wait buffers max */
size_t stream_wait_max = 4 * 1024 * 1024;
size_t http2_query_buffer_max = 4 * 1024 * 1024;
size_t http2_response_buffer_max = 4 * 1024 * 1024;
/** global config during parsing */
struct config_parser_state* cfg_parser = 0;
/** init ports possible for use */
static void init_outgoing_availports(int* array, int num);
/** 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 = (struct config_file*)calloc(1, sizeof(struct config_file));
if(!cfg)
return NULL;
/* the defaults if no config is present */
cfg->verbosity = 1;
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->do_ip6 = 1;
cfg->do_udp = 1;
cfg->do_tcp = 1;
cfg->tcp_reuse_timeout = 60 * 1000; /* 60s in milisecs */
cfg->max_reuse_tcp_queries = 200;
cfg->tcp_upstream = 0;
cfg->udp_upstream_without_downstream = 0;
cfg->tcp_mss = 0;
cfg->outgoing_tcp_mss = 0;
cfg->tcp_idle_timeout = 30 * 1000; /* 30s in millisecs */
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->ssl_upstream = 0;
cfg->tls_cert_bundle = NULL;
cfg->tls_win_cert = 0;
cfg->tls_use_sni = 1;
cfg->https_port = UNBOUND_DNS_OVER_HTTPS_PORT;
if(!(cfg->http_endpoint = strdup("/dns-query"))) goto error_exit;
cfg->http_max_streams = 100;
cfg->http_query_buffer_size = 4*1024*1024;
cfg->http_response_buffer_size = 4*1024*1024;
cfg->http_nodelay = 1;
cfg->use_syslog = 1;
cfg->log_identity = NULL; /* changed later with argv[0] */
cfg->log_time_ascii = 0;
cfg->log_queries = 0;
cfg->log_replies = 0;
cfg->log_tag_queryreply = 0;
cfg->log_local_actions = 0;
cfg->log_servfail = 0;
#ifndef USE_WINSOCK
# ifdef USE_MINI_EVENT
/* select max 1024 sockets */
cfg->outgoing_num_ports = 960;
cfg->num_queries_per_thread = 512;
# else
/* libevent can use many sockets */
cfg->outgoing_num_ports = 4096;
cfg->num_queries_per_thread = 1024;
# endif
cfg->outgoing_num_tcp = 10;
cfg->incoming_num_tcp = 10;
#else
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;
#endif
cfg->stream_wait_size = 4 * 1024 * 1024;
cfg->edns_buffer_size = 1232; /* from DNS flagday recommendation */
cfg->msg_buffer_size = 65552; /* 64 k + a small margin */
cfg->msg_cache_size = 4 * 1024 * 1024;
cfg->msg_cache_slabs = 4;
cfg->jostle_time = 200;
cfg->rrset_cache_size = 4 * 1024 * 1024;
cfg->rrset_cache_slabs = 4;
cfg->host_ttl = 900;
cfg->bogus_ttl = 60;
cfg->min_ttl = 0;
cfg->max_ttl = 3600 * 24;
cfg->max_negative_ttl = 3600;
cfg->prefetch = 0;
cfg->prefetch_key = 0;
cfg->deny_any = 0;
cfg->infra_cache_slabs = 4;
cfg->infra_cache_numhosts = 10000;
cfg->infra_cache_min_rtt = 50;
cfg->infra_cache_max_rtt = 120000;
cfg->infra_keep_probing = 0;
cfg->delay_close = 0;
cfg->udp_connect = 1;
if(!(cfg->outgoing_avail_ports = (int*)calloc(65536, sizeof(int))))
goto error_exit;
init_outgoing_availports(cfg->outgoing_avail_ports, 65536);
if(!(cfg->username = strdup(UB_USERNAME))) goto error_exit;
#ifdef HAVE_CHROOT
if(!(cfg->chrootdir = strdup(CHROOT_DIR))) goto error_exit;
#endif
if(!(cfg->directory = strdup(RUN_DIR))) goto error_exit;
if(!(cfg->logfile = strdup(""))) goto error_exit;
if(!(cfg->pidfile = strdup(PIDFILE))) goto error_exit;
if(!(cfg->target_fetch_policy = strdup("3 2 1 0 0"))) goto error_exit;
cfg->fast_server_permil = 0;
cfg->fast_server_num = 3;
cfg->donotqueryaddrs = NULL;
cfg->donotquery_localhost = 1;
cfg->root_hints = NULL;
cfg->use_systemd = 0;
cfg->do_daemonize = 1;
cfg->if_automatic = 0;
cfg->if_automatic_ports = NULL;
cfg->so_rcvbuf = 0;
cfg->so_sndbuf = 0;
cfg->so_reuseport = REUSEPORT_DEFAULT;
cfg->ip_transparent = 0;
cfg->ip_freebind = 0;
cfg->ip_dscp = 0;
cfg->num_ifs = 0;
cfg->ifs = NULL;
cfg->num_out_ifs = 0;
cfg->out_ifs = NULL;
cfg->stubs = NULL;
cfg->forwards = NULL;
cfg->auths = NULL;
#ifdef CLIENT_SUBNET
cfg->client_subnet = NULL;
cfg->client_subnet_zone = NULL;
cfg->client_subnet_opcode = LDNS_EDNS_CLIENT_SUBNET;
cfg->client_subnet_always_forward = 0;
cfg->max_client_subnet_ipv4 = 24;
cfg->max_client_subnet_ipv6 = 56;
cfg->min_client_subnet_ipv4 = 0;
cfg->min_client_subnet_ipv6 = 0;
cfg->max_ecs_tree_size_ipv4 = 100;
cfg->max_ecs_tree_size_ipv6 = 100;
#endif
cfg->views = NULL;
cfg->acls = NULL;
cfg->tcp_connection_limits = NULL;
cfg->harden_short_bufsize = 1;
cfg->harden_large_queries = 0;
cfg->harden_glue = 1;
cfg->harden_dnssec_stripped = 1;
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->private_domain = NULL;
cfg->unwanted_threshold = 0;
cfg->hide_identity = 0;
cfg->hide_version = 0;
cfg->hide_trustanchor = 0;
cfg->hide_http_user_agent = 0;
cfg->identity = NULL;
cfg->version = NULL;
cfg->http_user_agent = NULL;
cfg->nsid_cfg_str = NULL;
cfg->nsid = NULL;
cfg->nsid_len = 0;
cfg->auto_trust_anchor_file_list = NULL;
cfg->trust_anchor_file_list = NULL;
cfg->trust_anchor_list = NULL;
cfg->trusted_keys_file_list = NULL;
cfg->trust_anchor_signaling = 1;
cfg->root_key_sentinel = 1;
cfg->domain_insecure = NULL;
cfg->val_date_override = 0;
cfg->val_sig_skew_min = 3600; /* at least daylight savings trouble */
cfg->val_sig_skew_max = 86400; /* at most timezone settings trouble */
cfg->val_max_restart = 5;
cfg->val_clean_additional = 1;
cfg->val_log_level = 0;
cfg->val_log_squelch = 0;
cfg->val_permissive_mode = 0;
cfg->aggressive_nsec = 1;
cfg->ignore_cd = 0;
+ cfg->disable_edns_do = 0;
cfg->serve_expired = 0;
cfg->serve_expired_ttl = 0;
cfg->serve_expired_ttl_reset = 0;
cfg->serve_expired_reply_ttl = 30;
cfg->serve_expired_client_timeout = 0;
cfg->ede_serve_expired = 0;
cfg->serve_original_ttl = 0;
cfg->zonemd_permissive_mode = 0;
cfg->add_holddown = 30*24*3600;
cfg->del_holddown = 30*24*3600;
cfg->keep_missing = 366*24*3600; /* one year plus a little leeway */
cfg->permit_small_holddown = 0;
cfg->key_cache_size = 4 * 1024 * 1024;
cfg->key_cache_slabs = 4;
cfg->neg_cache_size = 1 * 1024 * 1024;
cfg->local_zones = NULL;
cfg->local_zones_nodefault = NULL;
#ifdef USE_IPSET
cfg->local_zones_ipset = NULL;
#endif
cfg->local_zones_disable_default = 0;
cfg->local_data = NULL;
cfg->local_zone_overrides = NULL;
cfg->unblock_lan_zones = 0;
cfg->insecure_lan_zones = 0;
cfg->python_script = NULL;
cfg->dynlib_file = NULL;
cfg->remote_control_enable = 0;
cfg->control_ifs.first = NULL;
cfg->control_ifs.last = NULL;
cfg->control_port = UNBOUND_CONTROL_PORT;
cfg->control_use_cert = 1;
cfg->minimal_responses = 1;
cfg->rrset_roundrobin = 1;
cfg->unknown_server_time_limit = 376;
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")))
goto error_exit;
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")))
goto error_exit;
#ifdef CLIENT_SUBNET
if(!(cfg->module_conf = strdup("subnetcache validator iterator"))) goto error_exit;
#else
if(!(cfg->module_conf = strdup("validator iterator"))) goto error_exit;
#endif
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)))
goto error_exit;
#endif
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->ratelimit_slabs = 4;
cfg->ip_ratelimit_size = 4*1024*1024;
cfg->ratelimit_size = 4*1024*1024;
cfg->ratelimit_for_domain = NULL;
cfg->ratelimit_below_domain = NULL;
cfg->ip_ratelimit_factor = 10;
cfg->ratelimit_factor = 10;
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->shm_key = 11777;
cfg->edns_client_strings = NULL;
cfg->edns_client_string_opcode = 65001;
cfg->dnscrypt = 0;
cfg->dnscrypt_port = 0;
cfg->dnscrypt_provider = NULL;
cfg->dnscrypt_provider_cert = NULL;
cfg->dnscrypt_provider_cert_rotated = NULL;
cfg->dnscrypt_secret_key = NULL;
cfg->dnscrypt_shared_secret_cache_size = 4*1024*1024;
cfg->dnscrypt_shared_secret_cache_slabs = 4;
cfg->dnscrypt_nonce_cache_size = 4*1024*1024;
cfg->dnscrypt_nonce_cache_slabs = 4;
cfg->pad_responses = 1;
cfg->pad_responses_block_size = 468; /* from RFC8467 */
cfg->pad_queries = 1;
cfg->pad_queries_block_size = 128; /* from RFC8467 */
#ifdef USE_IPSECMOD
cfg->ipsecmod_enabled = 1;
cfg->ipsecmod_ignore_bogus = 0;
cfg->ipsecmod_hook = NULL;
cfg->ipsecmod_max_ttl = 3600;
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;
+ cfg->cachedb_no_store = 0;
#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;
+ cfg->redis_logical_db = 0;
#endif /* USE_REDIS */
#endif /* USE_CACHEDB */
#ifdef USE_IPSET
cfg->ipset_name_v4 = NULL;
cfg->ipset_name_v6 = NULL;
#endif
cfg->ede = 0;
return cfg;
error_exit:
config_delete(cfg);
return NULL;
}
struct config_file* config_create_forlib(void)
{
struct config_file* cfg = config_create();
if(!cfg) return NULL;
/* modifications for library use, less verbose, less memory */
free(cfg->chrootdir);
cfg->chrootdir = NULL;
cfg->verbosity = 0;
cfg->outgoing_num_ports = 16; /* in library use, this is 'reasonable'
and probably within the ulimit(maxfds) of the user */
cfg->outgoing_num_tcp = 2;
cfg->msg_cache_size = 1024*1024;
cfg->msg_cache_slabs = 1;
cfg->rrset_cache_size = 1024*1024;
cfg->rrset_cache_slabs = 1;
cfg->infra_cache_slabs = 1;
cfg->use_syslog = 0;
cfg->key_cache_size = 1024*1024;
cfg->key_cache_slabs = 1;
cfg->neg_cache_size = 100 * 1024;
cfg->donotquery_localhost = 0; /* allow, so that you can ask a
forward nameserver running on localhost */
cfg->val_log_level = 2; /* to fill why_bogus with */
cfg->val_log_squelch = 1;
cfg->minimal_responses = 0;
cfg->harden_short_bufsize = 1;
return cfg;
}
/** check that the value passed is >= 0 */
#define IS_NUMBER_OR_ZERO \
if(atoi(val) == 0 && strcmp(val, "0") != 0) return 0
/** check that the value passed is > 0 */
#define IS_NONZERO_NUMBER \
if(atoi(val) == 0) return 0
/** check that the value passed is not 0 and a power of 2 */
#define IS_POW2_NUMBER \
if(atoi(val) == 0 || !is_pow2((size_t)atoi(val))) return 0
/** check that the value passed is yes or no */
#define IS_YES_OR_NO \
if(strcmp(val, "yes") != 0 && strcmp(val, "no") != 0) return 0
/** put integer_or_zero into variable */
#define S_NUMBER_OR_ZERO(str, var) if(strcmp(opt, str) == 0) \
{ IS_NUMBER_OR_ZERO; cfg->var = atoi(val); }
/** put integer_nonzero into variable */
#define S_NUMBER_NONZERO(str, var) if(strcmp(opt, str) == 0) \
{ IS_NONZERO_NUMBER; cfg->var = atoi(val); }
/** put integer_or_zero into unsigned */
#define S_UNSIGNED_OR_ZERO(str, var) if(strcmp(opt, str) == 0) \
{ IS_NUMBER_OR_ZERO; cfg->var = (unsigned)atoi(val); }
/** put integer_or_zero into size_t */
#define S_SIZET_OR_ZERO(str, var) if(strcmp(opt, str) == 0) \
{ IS_NUMBER_OR_ZERO; cfg->var = (size_t)atoi(val); }
/** put integer_nonzero into size_t */
#define S_SIZET_NONZERO(str, var) if(strcmp(opt, str) == 0) \
{ IS_NONZERO_NUMBER; cfg->var = (size_t)atoi(val); }
/** put yesno into variable */
#define S_YNO(str, var) if(strcmp(opt, str) == 0) \
{ IS_YES_OR_NO; cfg->var = (strcmp(val, "yes") == 0); }
/** put memsize into variable */
#define S_MEMSIZE(str, var) if(strcmp(opt, str)==0) \
{ return cfg_parse_memsize(val, &cfg->var); }
/** put pow2 number into variable */
#define S_POW2(str, var) if(strcmp(opt, str)==0) \
{ IS_POW2_NUMBER; cfg->var = (size_t)atoi(val); }
/** put string into variable */
#define S_STR(str, var) if(strcmp(opt, str)==0) \
{ free(cfg->var); return (cfg->var = strdup(val)) != NULL; }
/** put string into strlist */
#define S_STRLIST(str, var) if(strcmp(opt, str)==0) \
{ return cfg_strlist_insert(&cfg->var, strdup(val)); }
/** put string into strlist if not present yet*/
#define S_STRLIST_UNIQ(str, var) if(strcmp(opt, str)==0) \
{ if(cfg_strlist_find(cfg->var, val)) { return 0;} \
return cfg_strlist_insert(&cfg->var, strdup(val)); }
/** append string to strlist */
#define S_STRLIST_APPEND(str, var) if(strcmp(opt, str)==0) \
{ return cfg_strlist_append(&cfg->var, strdup(val)); }
int config_set_option(struct config_file* cfg, const char* opt,
const char* val)
{
char buf[64];
if(!opt) return 0;
if(opt[strlen(opt)-1] != ':' && strlen(opt)+2<sizeof(buf)) {
snprintf(buf, sizeof(buf), "%s:", opt);
opt = buf;
}
S_NUMBER_OR_ZERO("verbosity:", verbosity)
else if(strcmp(opt, "statistics-interval:") == 0) {
if(strcmp(val, "0") == 0 || strcmp(val, "") == 0)
cfg->stat_interval = 0;
else if(atoi(val) == 0)
return 0;
else cfg->stat_interval = atoi(val);
} else if(strcmp(opt, "num-threads:") == 0) {
/* not supported, library must have 1 thread in bgworker */
return 0;
} else if(strcmp(opt, "outgoing-port-permit:") == 0) {
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,
cfg->outgoing_avail_ports, 65536);
} else if(strcmp(opt, "local-zone:") == 0) {
return cfg_parse_local_zone(cfg, val);
} else if(strcmp(opt, "val-override-date:") == 0) {
if(strcmp(val, "") == 0 || strcmp(val, "0") == 0) {
cfg->val_date_override = 0;
} else if(strlen(val) == 14) {
cfg->val_date_override = cfg_convert_timeval(val);
return cfg->val_date_override != 0;
} else {
if(atoi(val) == 0) return 0;
cfg->val_date_override = (uint32_t)atoi(val);
}
} 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) {
cfg->use_syslog = 0;
free(cfg->logfile);
return (cfg->logfile = strdup(val)) != NULL;
}
else if(strcmp(opt, "log-time-ascii:") == 0)
{ IS_YES_OR_NO; cfg->log_time_ascii = (strcmp(val, "yes") == 0);
log_set_time_asc(cfg->log_time_ascii); }
else S_SIZET_NONZERO("max-udp-size:", max_udp_size)
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_YNO("do-ip4:", do_ip4)
else S_YNO("do-ip6:", do_ip6)
else S_YNO("do-udp:", do_udp)
else S_YNO("do-tcp:", do_tcp)
else S_YNO("prefer-ip4:", prefer_ip4)
else S_YNO("prefer-ip6:", prefer_ip6)
else S_YNO("tcp-upstream:", tcp_upstream)
else S_YNO("udp-upstream-without-downstream:",
udp_upstream_without_downstream)
else S_NUMBER_NONZERO("tcp-mss:", tcp_mss)
else S_NUMBER_NONZERO("outgoing-tcp-mss:", outgoing_tcp_mss)
else S_NUMBER_NONZERO("tcp-auth-query-timeout:", tcp_auth_query_timeout)
else S_NUMBER_NONZERO("tcp-idle-timeout:", tcp_idle_timeout)
else S_NUMBER_NONZERO("max-reuse-tcp-queries:", max_reuse_tcp_queries)
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_STR("tls-service-key:", ssl_service_key)
else S_STR("ssl-service-pem:", ssl_service_pem)
else S_STR("tls-service-pem:", ssl_service_pem)
else S_NUMBER_NONZERO("ssl-port:", ssl_port)
else S_NUMBER_NONZERO("tls-port:", ssl_port)
else S_STR("ssl-cert-bundle:", tls_cert_bundle)
else S_STR("tls-cert-bundle:", tls_cert_bundle)
else S_YNO("tls-win-cert:", tls_win_cert)
else S_YNO("tls-system-cert:", tls_win_cert)
else S_STRLIST("additional-ssl-port:", tls_additional_port)
else S_STRLIST("additional-tls-port:", tls_additional_port)
else S_STRLIST("tls-additional-ports:", tls_additional_port)
else S_STRLIST("tls-additional-port:", tls_additional_port)
else S_STRLIST_APPEND("tls-session-ticket-keys:", tls_session_ticket_keys)
else S_STR("tls-ciphers:", tls_ciphers)
else S_STR("tls-ciphersuites:", tls_ciphersuites)
else S_YNO("tls-use-sni:", tls_use_sni)
else S_NUMBER_NONZERO("https-port:", https_port)
else S_STR("http-endpoint:", http_endpoint)
else S_NUMBER_NONZERO("http-max-streams:", http_max_streams)
else S_MEMSIZE("http-query-buffer-size:", http_query_buffer_size)
else S_MEMSIZE("http-response-buffer-size:", http_response_buffer_size)
else S_YNO("http-nodelay:", http_nodelay)
else S_YNO("http-notls-downstream:", http_notls_downstream)
else S_YNO("interface-automatic:", if_automatic)
else S_STR("interface-automatic-ports:", if_automatic_ports)
else S_YNO("use-systemd:", use_systemd)
else S_YNO("do-daemonize:", do_daemonize)
else S_NUMBER_NONZERO("port:", port)
else S_NUMBER_NONZERO("outgoing-range:", outgoing_num_ports)
else S_SIZET_OR_ZERO("outgoing-num-tcp:", outgoing_num_tcp)
else S_SIZET_OR_ZERO("incoming-num-tcp:", incoming_num_tcp)
else S_MEMSIZE("stream-wait-size:", stream_wait_size)
else S_SIZET_NONZERO("edns-buffer-size:", edns_buffer_size)
else S_SIZET_NONZERO("msg-buffer-size:", msg_buffer_size)
else S_MEMSIZE("msg-cache-size:", msg_cache_size)
else S_POW2("msg-cache-slabs:", msg_cache_slabs)
else S_SIZET_NONZERO("num-queries-per-thread:",num_queries_per_thread)
else S_SIZET_OR_ZERO("jostle-timeout:", jostle_time)
else S_MEMSIZE("so-rcvbuf:", so_rcvbuf)
else S_MEMSIZE("so-sndbuf:", so_sndbuf)
else S_YNO("so-reuseport:", so_reuseport)
else S_YNO("ip-transparent:", ip_transparent)
else S_YNO("ip-freebind:", ip_freebind)
else S_NUMBER_OR_ZERO("ip-dscp:", ip_dscp)
else S_MEMSIZE("rrset-cache-size:", rrset_cache_size)
else S_POW2("rrset-cache-slabs:", rrset_cache_slabs)
else S_YNO("prefetch:", prefetch)
else S_YNO("prefetch-key:", prefetch_key)
else S_YNO("deny-any:", deny_any)
else if(strcmp(opt, "cache-max-ttl:") == 0)
{ IS_NUMBER_OR_ZERO; cfg->max_ttl = atoi(val); MAX_TTL=(time_t)cfg->max_ttl;}
else if(strcmp(opt, "cache-max-negative-ttl:") == 0)
{ IS_NUMBER_OR_ZERO; cfg->max_negative_ttl = atoi(val); MAX_NEG_TTL=(time_t)cfg->max_negative_ttl;}
else if(strcmp(opt, "cache-min-ttl:") == 0)
{ IS_NUMBER_OR_ZERO; cfg->min_ttl = atoi(val); MIN_TTL=(time_t)cfg->min_ttl;}
else if(strcmp(opt, "infra-cache-min-rtt:") == 0) {
IS_NUMBER_OR_ZERO; cfg->infra_cache_min_rtt = atoi(val);
RTT_MIN_TIMEOUT=cfg->infra_cache_min_rtt;
}
else if(strcmp(opt, "infra-cache-max-rtt:") == 0) {
IS_NUMBER_OR_ZERO; cfg->infra_cache_max_rtt = atoi(val);
RTT_MAX_TIMEOUT=cfg->infra_cache_max_rtt;
USEFUL_SERVER_TOP_TIMEOUT = RTT_MAX_TIMEOUT;
BLACKLIST_PENALTY = USEFUL_SERVER_TOP_TIMEOUT*4;
}
else S_YNO("infra-keep-probing:", infra_keep_probing)
else S_NUMBER_OR_ZERO("infra-host-ttl:", host_ttl)
else S_POW2("infra-cache-slabs:", infra_cache_slabs)
else S_SIZET_NONZERO("infra-cache-numhosts:", infra_cache_numhosts)
else S_NUMBER_OR_ZERO("delay-close:", delay_close)
else S_YNO("udp-connect:", udp_connect)
else S_STR("chroot:", chrootdir)
else S_STR("username:", username)
else S_STR("directory:", directory)
else S_STR("pidfile:", pidfile)
else S_YNO("hide-identity:", hide_identity)
else S_YNO("hide-version:", hide_version)
else S_YNO("hide-trustanchor:", hide_trustanchor)
else S_YNO("hide-http-user-agent:", hide_http_user_agent)
else S_STR("identity:", identity)
else S_STR("version:", version)
else S_STR("http-user-agent:", http_user_agent)
else if(strcmp(opt, "nsid:") == 0) {
free(cfg->nsid_cfg_str);
if (!(cfg->nsid_cfg_str = strdup(val)))
return 0;
/* Empty string is just validly unsetting nsid */
if (*val == 0) {
free(cfg->nsid);
cfg->nsid = NULL;
cfg->nsid_len = 0;
return 1;
}
cfg->nsid = cfg_parse_nsid(val, &cfg->nsid_len);
return cfg->nsid != NULL;
}
else S_STRLIST("root-hints:", root_hints)
else S_STR("target-fetch-policy:", target_fetch_policy)
else S_YNO("harden-glue:", harden_glue)
else S_YNO("harden-short-bufsize:", harden_short_bufsize)
else S_YNO("harden-large-queries:", harden_large_queries)
else S_YNO("harden-dnssec-stripped:", harden_dnssec_stripped)
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 S_STRLIST("private-address:", private_address)
else S_STRLIST("private-domain:", private_domain)
else S_YNO("do-not-query-localhost:", donotquery_localhost)
else S_STRLIST("do-not-query-address:", donotqueryaddrs)
else S_STRLIST("auto-trust-anchor-file:", auto_trust_anchor_file_list)
else S_STRLIST("trust-anchor-file:", trust_anchor_file_list)
else S_STRLIST("trust-anchor:", trust_anchor_list)
else S_STRLIST("trusted-keys-file:", trusted_keys_file_list)
else S_YNO("trust-anchor-signaling:", trust_anchor_signaling)
else S_YNO("root-key-sentinel:", root_key_sentinel)
else S_STRLIST("domain-insecure:", domain_insecure)
else S_NUMBER_OR_ZERO("val-bogus-ttl:", bogus_ttl)
else S_YNO("val-clean-additional:", val_clean_additional)
else S_NUMBER_OR_ZERO("val-log-level:", val_log_level)
else S_YNO("val-log-squelch:", val_log_squelch)
else S_YNO("log-queries:", log_queries)
else S_YNO("log-replies:", log_replies)
else S_YNO("log-tag-queryreply:", log_tag_queryreply)
else S_YNO("log-local-actions:", log_local_actions)
else S_YNO("log-servfail:", log_servfail)
else S_YNO("val-permissive-mode:", val_permissive_mode)
else S_YNO("aggressive-nsec:", aggressive_nsec)
else S_YNO("ignore-cd-flag:", ignore_cd)
+ else S_YNO("disable-edns-do:", disable_edns_do)
else if(strcmp(opt, "serve-expired:") == 0)
{ IS_YES_OR_NO; cfg->serve_expired = (strcmp(val, "yes") == 0);
SERVE_EXPIRED = cfg->serve_expired; }
else if(strcmp(opt, "serve-expired-ttl:") == 0)
{ IS_NUMBER_OR_ZERO; cfg->serve_expired_ttl = atoi(val); SERVE_EXPIRED_TTL=(time_t)cfg->serve_expired_ttl;}
else S_YNO("serve-expired-ttl-reset:", serve_expired_ttl_reset)
else if(strcmp(opt, "serve-expired-reply-ttl:") == 0)
{ IS_NUMBER_OR_ZERO; cfg->serve_expired_reply_ttl = atoi(val); SERVE_EXPIRED_REPLY_TTL=(time_t)cfg->serve_expired_reply_ttl;}
else S_NUMBER_OR_ZERO("serve-expired-client-timeout:", serve_expired_client_timeout)
else S_YNO("ede:", ede)
else S_YNO("ede-serve-expired:", ede_serve_expired)
else S_YNO("serve-original-ttl:", serve_original_ttl)
else S_STR("val-nsec3-keysize-iterations:", val_nsec3_key_iterations)
else S_YNO("zonemd-permissive-mode:", zonemd_permissive_mode)
else S_UNSIGNED_OR_ZERO("add-holddown:", add_holddown)
else S_UNSIGNED_OR_ZERO("del-holddown:", del_holddown)
else S_UNSIGNED_OR_ZERO("keep-missing:", keep_missing)
else if(strcmp(opt, "permit-small-holddown:") == 0)
{ IS_YES_OR_NO; cfg->permit_small_holddown = (strcmp(val, "yes") == 0);
autr_permit_small_holddown = cfg->permit_small_holddown; }
else S_MEMSIZE("key-cache-size:", key_cache_size)
else S_POW2("key-cache-slabs:", key_cache_slabs)
else S_MEMSIZE("neg-cache-size:", neg_cache_size)
else S_YNO("minimal-responses:", minimal_responses)
else S_YNO("rrset-roundrobin:", rrset_roundrobin)
else S_NUMBER_OR_ZERO("unknown-server-time-limit:", unknown_server_time_limit)
else S_STRLIST("local-data:", local_data)
else S_YNO("unblock-lan-zones:", unblock_lan_zones)
else S_YNO("insecure-lan-zones:", insecure_lan_zones)
else S_YNO("control-enable:", remote_control_enable)
else S_STRLIST_APPEND("control-interface:", control_ifs)
else S_NUMBER_NONZERO("control-port:", control_port)
else S_STR("server-key-file:", server_key_file)
else S_STR("server-cert-file:", server_cert_file)
else S_STR("control-key-file:", control_key_file)
else S_STR("control-cert-file:", control_cert_file)
else S_STR("module-config:", module_conf)
else S_STRLIST("python-script:", python_script)
else S_STRLIST("dynlib-file:", dynlib_file)
else S_YNO("disable-dnssec-lame-check:", disable_dnssec_lame_check)
#ifdef CLIENT_SUBNET
/* Can't set max subnet prefix here, since that value is used when
* generating the address tree. */
/* No client-subnet-always-forward here, module registration depends on
* this option. */
#endif
#ifdef USE_DNSTAP
else S_YNO("dnstap-enable:", dnstap)
else S_YNO("dnstap-bidirectional:", dnstap_bidirectional)
else S_STR("dnstap-socket-path:", dnstap_socket_path)
else S_STR("dnstap-ip:", dnstap_ip)
else S_YNO("dnstap-tls:", dnstap_tls)
else S_STR("dnstap-tls-server-name:", dnstap_tls_server_name)
else S_STR("dnstap-tls-cert-bundle:", dnstap_tls_cert_bundle)
else S_STR("dnstap-tls-client-key-file:", dnstap_tls_client_key_file)
else S_STR("dnstap-tls-client-cert-file:",
dnstap_tls_client_cert_file)
else S_YNO("dnstap-send-identity:", dnstap_send_identity)
else S_YNO("dnstap-send-version:", dnstap_send_version)
else S_STR("dnstap-identity:", dnstap_identity)
else S_STR("dnstap-version:", dnstap_version)
else S_YNO("dnstap-log-resolver-query-messages:",
dnstap_log_resolver_query_messages)
else S_YNO("dnstap-log-resolver-response-messages:",
dnstap_log_resolver_response_messages)
else S_YNO("dnstap-log-client-query-messages:",
dnstap_log_client_query_messages)
else S_YNO("dnstap-log-client-response-messages:",
dnstap_log_client_response_messages)
else S_YNO("dnstap-log-forwarder-query-messages:",
dnstap_log_forwarder_query_messages)
else S_YNO("dnstap-log-forwarder-response-messages:",
dnstap_log_forwarder_response_messages)
#endif
#ifdef USE_DNSCRYPT
else S_YNO("dnscrypt-enable:", dnscrypt)
else S_NUMBER_NONZERO("dnscrypt-port:", dnscrypt_port)
else S_STR("dnscrypt-provider:", dnscrypt_provider)
else S_STRLIST_UNIQ("dnscrypt-provider-cert:", dnscrypt_provider_cert)
else S_STRLIST("dnscrypt-provider-cert-rotated:", dnscrypt_provider_cert_rotated)
else S_STRLIST_UNIQ("dnscrypt-secret-key:", dnscrypt_secret_key)
else S_MEMSIZE("dnscrypt-shared-secret-cache-size:",
dnscrypt_shared_secret_cache_size)
else S_POW2("dnscrypt-shared-secret-cache-slabs:",
dnscrypt_shared_secret_cache_slabs)
else S_MEMSIZE("dnscrypt-nonce-cache-size:",
dnscrypt_nonce_cache_size)
else S_POW2("dnscrypt-nonce-cache-slabs:",
dnscrypt_nonce_cache_slabs)
#endif
else if(strcmp(opt, "ip-ratelimit-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 if(strcmp(opt, "ratelimit:") == 0) {
IS_NUMBER_OR_ZERO; cfg->ratelimit = atoi(val);
infra_dp_ratelimit=cfg->ratelimit;
}
else S_MEMSIZE("ip-ratelimit-size:", ip_ratelimit_size)
else S_MEMSIZE("ratelimit-size:", ratelimit_size)
else S_POW2("ip-ratelimit-slabs:", ip_ratelimit_slabs)
else S_POW2("ratelimit-slabs:", ratelimit_slabs)
else S_NUMBER_OR_ZERO("ip-ratelimit-factor:", ip_ratelimit_factor)
else S_NUMBER_OR_ZERO("ratelimit-factor:", ratelimit_factor)
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)
else S_YNO("qname-minimisation-strict:", qname_minimisation_strict)
else S_YNO("pad-responses:", pad_responses)
else S_SIZET_NONZERO("pad-responses-block-size:", pad_responses_block_size)
else S_YNO("pad-queries:", pad_queries)
else S_SIZET_NONZERO("pad-queries-block-size:", pad_queries_block_size)
else S_STRLIST("proxy-protocol-port:", proxy_protocol_port)
#ifdef USE_IPSECMOD
else S_YNO("ipsecmod-enabled:", ipsecmod_enabled)
else S_YNO("ipsecmod-ignore-bogus:", ipsecmod_ignore_bogus)
else if(strcmp(opt, "ipsecmod-max-ttl:") == 0)
{ IS_NUMBER_OR_ZERO; cfg->ipsecmod_max_ttl = atoi(val); }
else S_YNO("ipsecmod-strict:", ipsecmod_strict)
#endif
+#ifdef USE_CACHEDB
+ else S_YNO("cachedb-no-store:", cachedb_no_store)
+#endif /* USE_CACHEDB */
else if(strcmp(opt, "define-tag:") ==0) {
return config_add_tag(cfg, val);
/* val_sig_skew_min, max and val_max_restart are copied into val_env
* during init so this does not update val_env with set_option */
} else if(strcmp(opt, "val-sig-skew-min:") == 0)
{ IS_NUMBER_OR_ZERO; cfg->val_sig_skew_min = (int32_t)atoi(val); }
else if(strcmp(opt, "val-sig-skew-max:") == 0)
{ IS_NUMBER_OR_ZERO; cfg->val_sig_skew_max = (int32_t)atoi(val); }
else if(strcmp(opt, "val-max-restart:") == 0)
{ 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**)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) {
memmove(oi, cfg->out_ifs, cfg->num_out_ifs*sizeof(char*));
free(cfg->out_ifs);
}
oi[cfg->num_out_ifs++] = d;
cfg->out_ifs = oi;
} else {
/* unknown or unsupported (from the set_option interface):
* interface, outgoing-interface, access-control,
* stub-zone, name, stub-addr, stub-host, stub-prime
* forward-first, stub-first, forward-ssl-upstream,
* stub-ssl-upstream, forward-zone, auth-zone
* name, forward-addr, forward-host,
* ratelimit-for-domain, ratelimit-below-domain,
* local-zone-tag, access-control-view, interface-*,
* send-client-subnet, client-subnet-always-forward,
* max-client-subnet-ipv4, max-client-subnet-ipv6,
* min-client-subnet-ipv4, min-client-subnet-ipv6,
* max-ecs-tree-size-ipv4, max-ecs-tree-size-ipv6, ipsecmod_hook,
* ipsecmod_whitelist. */
return 0;
}
return 1;
}
void config_print_func(char* line, void* arg)
{
FILE* f = (FILE*)arg;
(void)fprintf(f, "%s\n", line);
}
/** collate func arg */
struct config_collate_arg {
/** list of result items */
struct config_strlist_head list;
/** if a malloc error occurred, 0 is OK */
int status;
};
void config_collate_func(char* line, void* arg)
{
struct config_collate_arg* m = (struct config_collate_arg*)arg;
if(m->status)
return;
if(!cfg_strlist_append(&m->list, strdup(line)))
m->status = 1;
}
int config_get_option_list(struct config_file* cfg, const char* opt,
struct config_strlist** list)
{
struct config_collate_arg m;
memset(&m, 0, sizeof(m));
*list = NULL;
if(!config_get_option(cfg, opt, config_collate_func, &m))
return 1;
if(m.status) {
config_delstrlist(m.list.first);
return 2;
}
*list = m.list.first;
return 0;
}
int
config_get_option_collate(struct config_file* cfg, const char* opt, char** str)
{
struct config_strlist* list = NULL;
int r;
*str = NULL;
if((r = config_get_option_list(cfg, opt, &list)) != 0)
return r;
*str = config_collate_cat(list);
config_delstrlist(list);
if(!*str) return 2;
return 0;
}
char*
config_collate_cat(struct config_strlist* list)
{
size_t total = 0, left;
struct config_strlist* s;
char *r, *w;
if(!list) /* no elements */
return strdup("");
if(list->next == NULL) /* one element , no newline at end. */
return strdup(list->str);
/* count total length */
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);
if(!r)
return NULL;
w = r;
for(s=list; s; s=s->next) {
size_t this = strlen(s->str);
if(this+2 > left) { /* sanity check */
free(r);
return NULL;
}
snprintf(w, left, "%s\n", s->str);
this = strlen(w);
w += this;
left -= this;
}
return r;
}
/** compare and print decimal option */
#define O_DEC(opt, str, var) if(strcmp(opt, str)==0) \
{snprintf(buf, len, "%d", (int)cfg->var); \
func(buf, arg);}
/** compare and print unsigned option */
#define O_UNS(opt, str, var) if(strcmp(opt, str)==0) \
{snprintf(buf, len, "%u", (unsigned)cfg->var); \
func(buf, arg);}
/** compare and print yesno option */
#define O_YNO(opt, str, var) if(strcmp(opt, str)==0) \
{func(cfg->var?"yes":"no", arg);}
/** compare and print string option */
#define O_STR(opt, str, var) if(strcmp(opt, str)==0) \
{func(cfg->var?cfg->var:"", arg);}
/** compare and print array option */
#define O_IFC(opt, str, num, arr) if(strcmp(opt, str)==0) \
{int i; for(i=0; i<cfg->num; i++) func(cfg->arr[i], arg);}
/** compare and print memorysize option */
#define O_MEM(opt, str, var) if(strcmp(opt, str)==0) { \
if(cfg->var > 1024*1024*1024) { \
size_t f=cfg->var/(size_t)1000000, b=cfg->var%(size_t)1000000; \
snprintf(buf, len, "%u%6.6u", (unsigned)f, (unsigned)b); \
} else snprintf(buf, len, "%u", (unsigned)cfg->var); \
func(buf, arg);}
/** compare and print list option */
#define O_LST(opt, name, lst) if(strcmp(opt, name)==0) { \
struct config_strlist* p = cfg->lst; \
for(p = cfg->lst; p; p = p->next) \
func(p->str, arg); \
}
/** compare and print list option */
#define O_LS2(opt, name, lst) if(strcmp(opt, name)==0) { \
struct config_str2list* p = cfg->lst; \
for(p = cfg->lst; p; p = p->next) { \
snprintf(buf, len, "%s %s", p->str, p->str2); \
func(buf, arg); \
} \
}
/** compare and print list option */
#define O_LS3(opt, name, lst) if(strcmp(opt, name)==0) { \
struct config_str3list* p = cfg->lst; \
for(p = cfg->lst; p; p = p->next) { \
snprintf(buf, len, "%s %s %s", p->str, p->str2, p->str3); \
func(buf, arg); \
} \
}
/** compare and print taglist option */
#define O_LTG(opt, name, lst) if(strcmp(opt, name)==0) { \
char* tmpstr = NULL; \
struct config_strbytelist *p = cfg->lst; \
for(p = cfg->lst; p; p = p->next) {\
tmpstr = config_taglist2str(cfg, p->str2, p->str2len); \
if(tmpstr) {\
snprintf(buf, len, "%s %s", p->str, tmpstr); \
func(buf, arg); \
free(tmpstr); \
} \
} \
}
int
config_get_option(struct config_file* cfg, const char* opt,
void (*func)(char*,void*), void* arg)
{
char buf[1024], nopt[64];
size_t len = sizeof(buf);
if(!opt) return 0;
if(opt && opt[strlen(opt)-1] == ':' && strlen(opt)<sizeof(nopt)) {
memmove(nopt, opt, strlen(opt));
nopt[strlen(opt)-1] = 0;
opt = nopt;
}
fptr_ok(fptr_whitelist_print_func(func));
O_DEC(opt, "verbosity", verbosity)
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_STR(opt, "log-identity", log_identity)
else O_YNO(opt, "log-time-ascii", log_time_ascii)
else O_DEC(opt, "num-threads", num_threads)
else O_IFC(opt, "interface", num_ifs, ifs)
else O_IFC(opt, "outgoing-interface", num_out_ifs, out_ifs)
else O_YNO(opt, "interface-automatic", if_automatic)
else O_STR(opt, "interface-automatic-ports", if_automatic_ports)
else O_DEC(opt, "port", port)
else O_DEC(opt, "outgoing-range", outgoing_num_ports)
else O_DEC(opt, "outgoing-num-tcp", outgoing_num_tcp)
else O_DEC(opt, "incoming-num-tcp", incoming_num_tcp)
else O_MEM(opt, "stream-wait-size", stream_wait_size)
else O_DEC(opt, "edns-buffer-size", edns_buffer_size)
else O_DEC(opt, "msg-buffer-size", msg_buffer_size)
else O_MEM(opt, "msg-cache-size", msg_cache_size)
else O_DEC(opt, "msg-cache-slabs", msg_cache_slabs)
else O_DEC(opt, "num-queries-per-thread", num_queries_per_thread)
else O_UNS(opt, "jostle-timeout", jostle_time)
else O_MEM(opt, "so-rcvbuf", so_rcvbuf)
else O_MEM(opt, "so-sndbuf", so_sndbuf)
else O_YNO(opt, "so-reuseport", so_reuseport)
else O_YNO(opt, "ip-transparent", ip_transparent)
else O_YNO(opt, "ip-freebind", ip_freebind)
else O_DEC(opt, "ip-dscp", ip_dscp)
else O_MEM(opt, "rrset-cache-size", rrset_cache_size)
else O_DEC(opt, "rrset-cache-slabs", rrset_cache_slabs)
else O_YNO(opt, "prefetch-key", prefetch_key)
else O_YNO(opt, "prefetch", prefetch)
else O_YNO(opt, "deny-any", deny_any)
else O_DEC(opt, "cache-max-ttl", max_ttl)
else O_DEC(opt, "cache-max-negative-ttl", max_negative_ttl)
else O_DEC(opt, "cache-min-ttl", min_ttl)
else O_DEC(opt, "infra-host-ttl", host_ttl)
else O_DEC(opt, "infra-cache-slabs", infra_cache_slabs)
else O_DEC(opt, "infra-cache-min-rtt", infra_cache_min_rtt)
else O_UNS(opt, "infra-cache-max-rtt", infra_cache_max_rtt)
else O_YNO(opt, "infra-keep-probing", infra_keep_probing)
else O_MEM(opt, "infra-cache-numhosts", infra_cache_numhosts)
else O_UNS(opt, "delay-close", delay_close)
else O_YNO(opt, "udp-connect", udp_connect)
else O_YNO(opt, "do-ip4", do_ip4)
else O_YNO(opt, "do-ip6", do_ip6)
else O_YNO(opt, "do-udp", do_udp)
else O_YNO(opt, "do-tcp", do_tcp)
else O_YNO(opt, "prefer-ip4", prefer_ip4)
else O_YNO(opt, "prefer-ip6", prefer_ip6)
else O_YNO(opt, "tcp-upstream", tcp_upstream)
else O_YNO(opt, "udp-upstream-without-downstream", udp_upstream_without_downstream)
else O_DEC(opt, "tcp-mss", tcp_mss)
else O_DEC(opt, "outgoing-tcp-mss", outgoing_tcp_mss)
else O_DEC(opt, "tcp-auth-query-timeout", tcp_auth_query_timeout)
else O_DEC(opt, "tcp-idle-timeout", tcp_idle_timeout)
else O_DEC(opt, "max-reuse-tcp-queries", max_reuse_tcp_queries)
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_STR(opt, "tls-service-key", ssl_service_key)
else O_STR(opt, "ssl-service-pem", ssl_service_pem)
else O_STR(opt, "tls-service-pem", ssl_service_pem)
else O_DEC(opt, "ssl-port", ssl_port)
else O_DEC(opt, "tls-port", ssl_port)
else O_STR(opt, "ssl-cert-bundle", tls_cert_bundle)
else O_STR(opt, "tls-cert-bundle", tls_cert_bundle)
else O_YNO(opt, "tls-win-cert", tls_win_cert)
else O_YNO(opt, "tls-system-cert", tls_win_cert)
else O_LST(opt, "additional-ssl-port", tls_additional_port)
else O_LST(opt, "additional-tls-port", tls_additional_port)
else O_LST(opt, "tls-additional-ports", tls_additional_port)
else O_LST(opt, "tls-additional-port", tls_additional_port)
else O_LST(opt, "tls-session-ticket-keys", tls_session_ticket_keys.first)
else O_STR(opt, "tls-ciphers", tls_ciphers)
else O_STR(opt, "tls-ciphersuites", tls_ciphersuites)
else O_YNO(opt, "tls-use-sni", tls_use_sni)
else O_DEC(opt, "https-port", https_port)
else O_STR(opt, "http-endpoint", http_endpoint)
else O_UNS(opt, "http-max-streams", http_max_streams)
else O_MEM(opt, "http-query-buffer-size", http_query_buffer_size)
else O_MEM(opt, "http-response-buffer-size", http_response_buffer_size)
else O_YNO(opt, "http-nodelay", http_nodelay)
else O_YNO(opt, "http-notls-downstream", http_notls_downstream)
else O_YNO(opt, "use-systemd", use_systemd)
else O_YNO(opt, "do-daemonize", do_daemonize)
else O_STR(opt, "chroot", chrootdir)
else O_STR(opt, "username", username)
else O_STR(opt, "directory", directory)
else O_STR(opt, "logfile", logfile)
else O_YNO(opt, "log-queries", log_queries)
else O_YNO(opt, "log-replies", log_replies)
else O_YNO(opt, "log-tag-queryreply", log_tag_queryreply)
else O_YNO(opt, "log-local-actions", log_local_actions)
else O_YNO(opt, "log-servfail", log_servfail)
else O_STR(opt, "pidfile", pidfile)
else O_YNO(opt, "hide-identity", hide_identity)
else O_YNO(opt, "hide-version", hide_version)
else O_YNO(opt, "hide-trustanchor", hide_trustanchor)
else O_YNO(opt, "hide-http-user-agent", hide_http_user_agent)
else O_STR(opt, "identity", identity)
else O_STR(opt, "version", version)
else O_STR(opt, "http-user-agent", http_user_agent)
else O_STR(opt, "nsid", nsid_cfg_str)
else O_STR(opt, "target-fetch-policy", target_fetch_policy)
else O_YNO(opt, "harden-short-bufsize", harden_short_bufsize)
else O_YNO(opt, "harden-large-queries", harden_large_queries)
else O_YNO(opt, "harden-glue", harden_glue)
else O_YNO(opt, "harden-dnssec-stripped", harden_dnssec_stripped)
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_YNO(opt, "do-not-query-localhost", donotquery_localhost)
else O_STR(opt, "module-config", module_conf)
else O_DEC(opt, "val-bogus-ttl", bogus_ttl)
else O_YNO(opt, "val-clean-additional", val_clean_additional)
else O_DEC(opt, "val-log-level", val_log_level)
else O_YNO(opt, "val-permissive-mode", val_permissive_mode)
else O_YNO(opt, "aggressive-nsec", aggressive_nsec)
else O_YNO(opt, "ignore-cd-flag", ignore_cd)
+ else O_YNO(opt, "disable-edns-do", disable_edns_do)
else O_YNO(opt, "serve-expired", serve_expired)
else O_DEC(opt, "serve-expired-ttl", serve_expired_ttl)
else O_YNO(opt, "serve-expired-ttl-reset", serve_expired_ttl_reset)
else O_DEC(opt, "serve-expired-reply-ttl", serve_expired_reply_ttl)
else O_DEC(opt, "serve-expired-client-timeout", serve_expired_client_timeout)
else O_YNO(opt, "ede", ede)
else O_YNO(opt, "ede-serve-expired", ede_serve_expired)
else O_YNO(opt, "serve-original-ttl", serve_original_ttl)
else O_STR(opt, "val-nsec3-keysize-iterations",val_nsec3_key_iterations)
else O_YNO(opt, "zonemd-permissive-mode", zonemd_permissive_mode)
else O_UNS(opt, "add-holddown", add_holddown)
else O_UNS(opt, "del-holddown", del_holddown)
else O_UNS(opt, "keep-missing", keep_missing)
else O_YNO(opt, "permit-small-holddown", permit_small_holddown)
else O_MEM(opt, "key-cache-size", key_cache_size)
else O_DEC(opt, "key-cache-slabs", key_cache_slabs)
else O_MEM(opt, "neg-cache-size", neg_cache_size)
else O_YNO(opt, "control-enable", remote_control_enable)
else O_DEC(opt, "control-port", control_port)
else O_STR(opt, "server-key-file", server_key_file)
else O_STR(opt, "server-cert-file", server_cert_file)
else O_STR(opt, "control-key-file", control_key_file)
else O_STR(opt, "control-cert-file", control_cert_file)
else O_LST(opt, "root-hints", root_hints)
else O_LS2(opt, "access-control", acls)
else O_LS2(opt, "tcp-connection-limit", tcp_connection_limits)
else O_LST(opt, "do-not-query-address", donotqueryaddrs)
else O_LST(opt, "private-address", private_address)
else O_LST(opt, "private-domain", private_domain)
else O_LST(opt, "auto-trust-anchor-file", auto_trust_anchor_file_list)
else O_LST(opt, "trust-anchor-file", trust_anchor_file_list)
else O_LST(opt, "trust-anchor", trust_anchor_list)
else O_LST(opt, "trusted-keys-file", trusted_keys_file_list)
else O_YNO(opt, "trust-anchor-signaling", trust_anchor_signaling)
else O_YNO(opt, "root-key-sentinel", root_key_sentinel)
else O_LST(opt, "control-interface", control_ifs.first)
else O_LST(opt, "domain-insecure", domain_insecure)
else O_UNS(opt, "val-override-date", val_date_override)
else O_YNO(opt, "minimal-responses", minimal_responses)
else O_YNO(opt, "rrset-roundrobin", rrset_roundrobin)
else O_DEC(opt, "unknown-server-time-limit", unknown_server_time_limit)
#ifdef CLIENT_SUBNET
else O_LST(opt, "send-client-subnet", client_subnet)
else O_LST(opt, "client-subnet-zone", client_subnet_zone)
else O_DEC(opt, "max-client-subnet-ipv4", max_client_subnet_ipv4)
else O_DEC(opt, "max-client-subnet-ipv6", max_client_subnet_ipv6)
else O_DEC(opt, "min-client-subnet-ipv4", min_client_subnet_ipv4)
else O_DEC(opt, "min-client-subnet-ipv6", min_client_subnet_ipv6)
else O_DEC(opt, "max-ecs-tree-size-ipv4", max_ecs_tree_size_ipv4)
else O_DEC(opt, "max-ecs-tree-size-ipv6", max_ecs_tree_size_ipv6)
else O_YNO(opt, "client-subnet-always-forward:",
client_subnet_always_forward)
#endif
#ifdef USE_DNSTAP
else O_YNO(opt, "dnstap-enable", dnstap)
else O_YNO(opt, "dnstap-bidirectional", dnstap_bidirectional)
else O_STR(opt, "dnstap-socket-path", dnstap_socket_path)
else O_STR(opt, "dnstap-ip", dnstap_ip)
else O_YNO(opt, "dnstap-tls", dnstap_tls)
else O_STR(opt, "dnstap-tls-server-name", dnstap_tls_server_name)
else O_STR(opt, "dnstap-tls-cert-bundle", dnstap_tls_cert_bundle)
else O_STR(opt, "dnstap-tls-client-key-file",
dnstap_tls_client_key_file)
else O_STR(opt, "dnstap-tls-client-cert-file",
dnstap_tls_client_cert_file)
else O_YNO(opt, "dnstap-send-identity", dnstap_send_identity)
else O_YNO(opt, "dnstap-send-version", dnstap_send_version)
else O_STR(opt, "dnstap-identity", dnstap_identity)
else O_STR(opt, "dnstap-version", dnstap_version)
else O_YNO(opt, "dnstap-log-resolver-query-messages",
dnstap_log_resolver_query_messages)
else O_YNO(opt, "dnstap-log-resolver-response-messages",
dnstap_log_resolver_response_messages)
else O_YNO(opt, "dnstap-log-client-query-messages",
dnstap_log_client_query_messages)
else O_YNO(opt, "dnstap-log-client-response-messages",
dnstap_log_client_response_messages)
else O_YNO(opt, "dnstap-log-forwarder-query-messages",
dnstap_log_forwarder_query_messages)
else O_YNO(opt, "dnstap-log-forwarder-response-messages",
dnstap_log_forwarder_response_messages)
#endif
#ifdef USE_DNSCRYPT
else O_YNO(opt, "dnscrypt-enable", dnscrypt)
else O_DEC(opt, "dnscrypt-port", dnscrypt_port)
else O_STR(opt, "dnscrypt-provider", dnscrypt_provider)
else O_LST(opt, "dnscrypt-provider-cert", dnscrypt_provider_cert)
else O_LST(opt, "dnscrypt-provider-cert-rotated", dnscrypt_provider_cert_rotated)
else O_LST(opt, "dnscrypt-secret-key", dnscrypt_secret_key)
else O_MEM(opt, "dnscrypt-shared-secret-cache-size",
dnscrypt_shared_secret_cache_size)
else O_DEC(opt, "dnscrypt-shared-secret-cache-slabs",
dnscrypt_shared_secret_cache_slabs)
else O_MEM(opt, "dnscrypt-nonce-cache-size",
dnscrypt_nonce_cache_size)
else O_DEC(opt, "dnscrypt-nonce-cache-slabs",
dnscrypt_nonce_cache_slabs)
#endif
else O_YNO(opt, "unblock-lan-zones", unblock_lan_zones)
else O_YNO(opt, "insecure-lan-zones", insecure_lan_zones)
else O_DEC(opt, "max-udp-size", max_udp_size)
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_MEM(opt, "ratelimit-size", ratelimit_size)
else O_DEC(opt, "ip-ratelimit-slabs", ip_ratelimit_slabs)
else O_DEC(opt, "ratelimit-slabs", ratelimit_slabs)
else O_LS2(opt, "ratelimit-for-domain", ratelimit_for_domain)
else O_LS2(opt, "ratelimit-below-domain", ratelimit_below_domain)
else O_DEC(opt, "ip-ratelimit-factor", ip_ratelimit_factor)
else O_DEC(opt, "ratelimit-factor", ratelimit_factor)
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)
else O_DEC(opt, "val-sig-skew-max", val_sig_skew_max)
else O_DEC(opt, "val-max-restart", val_max_restart)
else O_YNO(opt, "qname-minimisation", qname_minimisation)
else O_YNO(opt, "qname-minimisation-strict", qname_minimisation_strict)
else O_IFC(opt, "define-tag", num_tags, tagname)
else O_LTG(opt, "local-zone-tag", local_zone_tags)
else O_LTG(opt, "access-control-tag", acl_tags)
else O_LTG(opt, "response-ip-tag", respip_tags)
else O_LS3(opt, "local-zone-override", local_zone_overrides)
else O_LS3(opt, "access-control-tag-action", acl_tag_actions)
else O_LS3(opt, "access-control-tag-data", acl_tag_datas)
else O_LS2(opt, "access-control-view", acl_view)
else O_LS2(opt, "interface-action", interface_actions)
else O_LTG(opt, "interface-tag", interface_tags)
else O_LS3(opt, "interface-tag-action", interface_tag_actions)
else O_LS3(opt, "interface-tag-data", interface_tag_datas)
else O_LS2(opt, "interface-view", interface_view)
else O_YNO(opt, "pad-responses", pad_responses)
else O_DEC(opt, "pad-responses-block-size", pad_responses_block_size)
else O_YNO(opt, "pad-queries", pad_queries)
else O_DEC(opt, "pad-queries-block-size", pad_queries_block_size)
else O_LS2(opt, "edns-client-strings", edns_client_strings)
else O_LST(opt, "proxy-protocol-port", proxy_protocol_port)
#ifdef USE_IPSECMOD
else O_YNO(opt, "ipsecmod-enabled", ipsecmod_enabled)
else O_YNO(opt, "ipsecmod-ignore-bogus", ipsecmod_ignore_bogus)
else O_STR(opt, "ipsecmod-hook", ipsecmod_hook)
else O_DEC(opt, "ipsecmod-max-ttl", ipsecmod_max_ttl)
else O_LST(opt, "ipsecmod-whitelist", ipsecmod_whitelist)
else O_YNO(opt, "ipsecmod-strict", ipsecmod_strict)
#endif
#ifdef USE_CACHEDB
else O_STR(opt, "backend", cachedb_backend)
else O_STR(opt, "secret-seed", cachedb_secret)
+ else O_YNO(opt, "cachedb-no-store", cachedb_no_store)
#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)
+ else O_DEC(opt, "redis-logical-db", redis_logical_db)
#endif /* USE_REDIS */
#endif /* USE_CACHEDB */
#ifdef USE_IPSET
else O_STR(opt, "name-v4", ipset_name_v4)
else O_STR(opt, "name-v6", ipset_name_v6)
#endif
/* not here:
* outgoing-permit, outgoing-avoid - have list of ports
* local-zone - zones and nodefault variables
* local-data - see below
* local-data-ptr - converted to local-data entries
* stub-zone, name, stub-addr, stub-host, stub-prime
* forward-zone, name, forward-addr, forward-host
*/
else return 0;
return 1;
}
/** initialize the global cfg_parser object */
static void
create_cfg_parser(struct config_file* cfg, char* filename, const char* chroot)
{
static struct config_parser_state st;
cfg_parser = &st;
cfg_parser->filename = filename;
cfg_parser->line = 1;
cfg_parser->errors = 0;
cfg_parser->cfg = cfg;
cfg_parser->chroot = chroot;
cfg_parser->started_toplevel = 0;
init_cfg_parse();
}
int
config_read(struct config_file* cfg, const char* filename, const char* chroot)
{
FILE *in;
char *fname = (char*)filename;
#ifdef HAVE_GLOB
glob_t g;
size_t i;
int r, flags;
#endif
if(!fname)
return 1;
/* check for wildcards */
#ifdef HAVE_GLOB
if(!(!strchr(fname, '*') && !strchr(fname, '?') && !strchr(fname, '[') &&
!strchr(fname, '{') && !strchr(fname, '~'))) {
verbose(VERB_QUERY, "wildcard found, processing %s", fname);
flags = 0
#ifdef GLOB_ERR
| GLOB_ERR
#endif
#ifdef GLOB_NOSORT
| GLOB_NOSORT
#endif
#ifdef GLOB_BRACE
| GLOB_BRACE
#endif
#ifdef GLOB_TILDE
| GLOB_TILDE
#endif
;
memset(&g, 0, sizeof(g));
r = glob(fname, flags, NULL, &g);
if(r) {
/* some error */
globfree(&g);
if(r == GLOB_NOMATCH) {
verbose(VERB_QUERY, "include: "
"no matches for %s", fname);
return 1;
} else if(r == GLOB_NOSPACE) {
log_err("include: %s: "
"fnametern out of memory", fname);
} else if(r == GLOB_ABORTED) {
log_err("wildcard include: %s: expansion "
"aborted (%s)", fname, strerror(errno));
} else {
log_err("wildcard include: %s: expansion "
"failed (%s)", fname, strerror(errno));
}
/* ignore globs that yield no files */
return 1;
}
/* process files found, if any */
for(i=0; i<(size_t)g.gl_pathc; i++) {
if(!config_read(cfg, g.gl_pathv[i], chroot)) {
log_err("error reading wildcard "
"include: %s", g.gl_pathv[i]);
globfree(&g);
return 0;
}
}
globfree(&g);
return 1;
}
#endif /* HAVE_GLOB */
in = fopen(fname, "r");
if(!in) {
log_err("Could not open %s: %s", fname, strerror(errno));
return 0;
}
create_cfg_parser(cfg, fname, chroot);
ub_c_in = in;
ub_c_parse();
fclose(in);
if(!cfg->dnscrypt) cfg->dnscrypt_port = 0;
if(cfg_parser->errors != 0) {
fprintf(stderr, "read %s failed: %d errors in configuration file\n",
fname, cfg_parser->errors);
errno=EINVAL;
return 0;
}
return 1;
}
struct config_stub* cfg_stub_find(struct config_stub*** pp, const char* nm)
{
struct config_stub* p = *(*pp);
while(p) {
if(strcmp(p->name, nm) == 0)
return p;
(*pp) = &p->next;
p = p->next;
}
return NULL;
}
void
config_delstrlist(struct config_strlist* p)
{
struct config_strlist *np;
while(p) {
np = p->next;
free(p->str);
free(p);
p = np;
}
}
void
config_deldblstrlist(struct config_str2list* p)
{
struct config_str2list *np;
while(p) {
np = p->next;
free(p->str);
free(p->str2);
free(p);
p = np;
}
}
void
config_deltrplstrlist(struct config_str3list* p)
{
struct config_str3list *np;
while(p) {
np = p->next;
free(p->str);
free(p->str2);
free(p->str3);
free(p);
p = np;
}
}
void
config_delauth(struct config_auth* p)
{
if(!p) return;
free(p->name);
config_delstrlist(p->masters);
config_delstrlist(p->urls);
config_delstrlist(p->allow_notify);
free(p->zonefile);
free(p->rpz_taglist);
free(p->rpz_action_override);
free(p->rpz_cname);
free(p->rpz_log_name);
free(p);
}
void
config_delauths(struct config_auth* p)
{
struct config_auth* np;
while(p) {
np = p->next;
config_delauth(p);
p = np;
}
}
void
config_delstub(struct config_stub* p)
{
if(!p) return;
free(p->name);
config_delstrlist(p->hosts);
config_delstrlist(p->addrs);
free(p);
}
void
config_delstubs(struct config_stub* p)
{
struct config_stub* np;
while(p) {
np = p->next;
config_delstub(p);
p = np;
}
}
void
config_delview(struct config_view* p)
{
if(!p) return;
free(p->name);
config_deldblstrlist(p->local_zones);
config_delstrlist(p->local_zones_nodefault);
#ifdef USE_IPSET
config_delstrlist(p->local_zones_ipset);
#endif
config_delstrlist(p->local_data);
free(p);
}
void
config_delviews(struct config_view* p)
{
struct config_view* np;
while(p) {
np = p->next;
config_delview(p);
p = np;
}
}
void
config_del_strarray(char** array, int num)
{
int i;
if(!array)
return;
for(i=0; i<num; i++) {
free(array[i]);
}
free(array);
}
void
config_del_strbytelist(struct config_strbytelist* p)
{
struct config_strbytelist* np;
while(p) {
np = p->next;
free(p->str);
free(p->str2);
free(p);
p = np;
}
}
void
config_delete(struct config_file* cfg)
{
if(!cfg) return;
free(cfg->username);
free(cfg->chrootdir);
free(cfg->directory);
free(cfg->logfile);
free(cfg->pidfile);
free(cfg->if_automatic_ports);
free(cfg->target_fetch_policy);
free(cfg->ssl_service_key);
free(cfg->ssl_service_pem);
free(cfg->tls_cert_bundle);
config_delstrlist(cfg->tls_additional_port);
config_delstrlist(cfg->tls_session_ticket_keys.first);
free(cfg->tls_ciphers);
free(cfg->tls_ciphersuites);
free(cfg->http_endpoint);
if(cfg->log_identity) {
log_ident_revert_to_default();
free(cfg->log_identity);
}
config_del_strarray(cfg->ifs, cfg->num_ifs);
config_del_strarray(cfg->out_ifs, cfg->num_out_ifs);
config_delstubs(cfg->stubs);
config_delstubs(cfg->forwards);
config_delauths(cfg->auths);
config_delviews(cfg->views);
config_delstrlist(cfg->donotqueryaddrs);
config_delstrlist(cfg->root_hints);
#ifdef CLIENT_SUBNET
config_delstrlist(cfg->client_subnet);
config_delstrlist(cfg->client_subnet_zone);
#endif
free(cfg->identity);
free(cfg->version);
free(cfg->http_user_agent);
free(cfg->nsid_cfg_str);
free(cfg->nsid);
free(cfg->module_conf);
free(cfg->outgoing_avail_ports);
config_delstrlist(cfg->caps_whitelist);
config_delstrlist(cfg->private_address);
config_delstrlist(cfg->private_domain);
config_delstrlist(cfg->auto_trust_anchor_file_list);
config_delstrlist(cfg->trust_anchor_file_list);
config_delstrlist(cfg->trusted_keys_file_list);
config_delstrlist(cfg->trust_anchor_list);
config_delstrlist(cfg->domain_insecure);
config_deldblstrlist(cfg->acls);
config_deldblstrlist(cfg->tcp_connection_limits);
free(cfg->val_nsec3_key_iterations);
config_deldblstrlist(cfg->local_zones);
config_delstrlist(cfg->local_zones_nodefault);
#ifdef USE_IPSET
config_delstrlist(cfg->local_zones_ipset);
#endif
config_delstrlist(cfg->local_data);
config_deltrplstrlist(cfg->local_zone_overrides);
config_del_strarray(cfg->tagname, cfg->num_tags);
config_del_strbytelist(cfg->local_zone_tags);
config_del_strbytelist(cfg->respip_tags);
config_deldblstrlist(cfg->acl_view);
config_del_strbytelist(cfg->acl_tags);
config_deltrplstrlist(cfg->acl_tag_actions);
config_deltrplstrlist(cfg->acl_tag_datas);
config_deldblstrlist(cfg->interface_actions);
config_deldblstrlist(cfg->interface_view);
config_del_strbytelist(cfg->interface_tags);
config_deltrplstrlist(cfg->interface_tag_actions);
config_deltrplstrlist(cfg->interface_tag_datas);
config_delstrlist(cfg->control_ifs.first);
free(cfg->server_key_file);
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->dnstap_ip);
free(cfg->dnstap_tls_server_name);
free(cfg->dnstap_tls_cert_bundle);
free(cfg->dnstap_tls_client_key_file);
free(cfg->dnstap_tls_client_cert_file);
free(cfg->dnstap_identity);
free(cfg->dnstap_version);
config_deldblstrlist(cfg->ratelimit_for_domain);
config_deldblstrlist(cfg->ratelimit_below_domain);
config_delstrlist(cfg->python_script);
config_delstrlist(cfg->dynlib_file);
config_deldblstrlist(cfg->edns_client_strings);
config_delstrlist(cfg->proxy_protocol_port);
#ifdef USE_IPSECMOD
free(cfg->ipsecmod_hook);
config_delstrlist(cfg->ipsecmod_whitelist);
#endif
#ifdef USE_CACHEDB
free(cfg->cachedb_backend);
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->ipset_name_v4);
free(cfg->ipset_name_v6);
#endif
free(cfg);
}
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 */
const int iana_assigned[] = {
#include "util/iana_ports.inc"
-1 }; /* end marker to put behind trailing comma */
int i;
/* do not use <1024, that could be trouble with the system, privs */
for(i=1024; i<num; i++) {
a[i] = i;
}
/* create empty spot at 49152 to keep ephemeral ports available
* to other programs */
for(i=49152; i<49152+256; i++)
a[i] = 0;
/* pick out all the IANA assigned ports */
for(i=0; iana_assigned[i]!=-1; i++) {
if(iana_assigned[i] < num)
a[iana_assigned[i]] = 0;
}
}
int
cfg_mark_ports(const char* str, int allow, int* avail, int num)
{
char* mid = strchr(str, '-');
#ifdef DISABLE_EXPLICIT_PORT_RANDOMISATION
log_warn("Explicit port randomisation disabled, ignoring "
"outgoing-port-permit and outgoing-port-avoid configuration "
"options");
#endif
if(!mid) {
int port = atoi(str);
if(port == 0 && strcmp(str, "0") != 0) {
log_err("cannot parse port number '%s'", str);
return 0;
}
if(port < num)
avail[port] = (allow?port:0);
} else {
int i, low, high = atoi(mid+1);
char buf[16];
if(high == 0 && strcmp(mid+1, "0") != 0) {
log_err("cannot parse port number '%s'", mid+1);
return 0;
}
if( (int)(mid-str)+1 >= (int)sizeof(buf) ) {
log_err("cannot parse port number '%s'", str);
return 0;
}
if(mid > str)
memcpy(buf, str, (size_t)(mid-str));
buf[mid-str] = 0;
low = atoi(buf);
if(low == 0 && strcmp(buf, "0") != 0) {
log_err("cannot parse port number '%s'", buf);
return 0;
}
for(i=low; i<=high; i++) {
if(i < num)
avail[i] = (allow?i:0);
}
return 1;
}
return 1;
}
int
cfg_scan_ports(int* avail, int num)
{
int i;
int count = 0;
for(i=0; i<num; i++) {
if(avail[i])
count++;
}
return count;
}
int cfg_condense_ports(struct config_file* cfg, int** avail)
{
int num = cfg_scan_ports(cfg->outgoing_avail_ports, 65536);
int i, at = 0;
*avail = NULL;
if(num == 0)
return 0;
*avail = (int*)reallocarray(NULL, (size_t)num, sizeof(int));
if(!*avail)
return 0;
for(i=0; i<65536; i++) {
if(cfg->outgoing_avail_ports[i])
(*avail)[at++] = cfg->outgoing_avail_ports[i];
}
log_assert(at == num);
return num;
}
void cfg_apply_local_port_policy(struct config_file* cfg, int num) {
(void)cfg;
(void)num;
#ifdef USE_LINUX_IP_LOCAL_PORT_RANGE
{
int i = 0;
FILE* range_fd;
if ((range_fd = fopen(LINUX_IP_LOCAL_PORT_RANGE_PATH, "r")) != NULL) {
int min_port = 0;
int max_port = num - 1;
if (fscanf(range_fd, "%d %d", &min_port, &max_port) == 2) {
for(i=0; i<min_port; i++) {
cfg->outgoing_avail_ports[i] = 0;
}
for(i=max_port+1; i<num; i++) {
cfg->outgoing_avail_ports[i] = 0;
}
} else {
log_err("unexpected port range in %s",
LINUX_IP_LOCAL_PORT_RANGE_PATH);
}
fclose(range_fd);
} else {
log_err("failed to read from file: %s (%s)",
LINUX_IP_LOCAL_PORT_RANGE_PATH,
strerror(errno));
}
}
#endif
}
/** print error with file and line number */
static void ub_c_error_va_list(const char *fmt, va_list args)
{
cfg_parser->errors++;
fprintf(stderr, "%s:%d: error: ", cfg_parser->filename,
cfg_parser->line);
vfprintf(stderr, fmt, args);
fprintf(stderr, "\n");
}
/** print error with file and line number */
void ub_c_error_msg(const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
ub_c_error_va_list(fmt, args);
va_end(args);
}
void ub_c_error(const char *str)
{
cfg_parser->errors++;
if(strcmp(str, "syntax error")==0 && cfg_parser->started_toplevel ==0)
str = "syntax error, is there no section start after an "
"include-toplevel directive perhaps.";
fprintf(stderr, "%s:%d: error: %s\n", cfg_parser->filename,
cfg_parser->line, str);
}
int ub_c_wrap(void)
{
return 1;
}
int cfg_strlist_append(struct config_strlist_head* list, char* item)
{
struct config_strlist *s;
if(!item || !list) {
free(item);
return 0;
}
s = (struct config_strlist*)calloc(1, sizeof(struct config_strlist));
if(!s) {
free(item);
return 0;
}
s->str = item;
s->next = NULL;
if(list->last)
list->last->next = s;
else
list->first = s;
list->last = s;
return 1;
}
int
cfg_region_strlist_insert(struct regional* region,
struct config_strlist** head, char* item)
{
struct config_strlist *s;
if(!item || !head)
return 0;
s = (struct config_strlist*)regional_alloc_zero(region,
sizeof(struct config_strlist));
if(!s)
return 0;
s->str = item;
s->next = *head;
*head = s;
return 1;
}
struct config_strlist*
cfg_strlist_find(struct config_strlist* head, const char *item)
{
struct config_strlist *s = head;
if(!head){
return NULL;
}
while(s) {
if(strcmp(s->str, item) == 0) {
return s;
}
s = s->next;
}
return NULL;
}
int
cfg_strlist_insert(struct config_strlist** head, char* item)
{
struct config_strlist *s;
if(!item || !head) {
free(item);
return 0;
}
s = (struct config_strlist*)calloc(1, sizeof(struct config_strlist));
if(!s) {
free(item);
return 0;
}
s->str = item;
s->next = *head;
*head = s;
return 1;
}
int
cfg_strlist_append_ex(struct config_strlist** head, char* item)
{
struct config_strlist *s;
if(!item || !head)
return 0;
s = (struct config_strlist*)calloc(1, sizeof(struct config_strlist));
if(!s)
return 0;
s->str = item;
s->next = NULL;
if (*head==NULL) {
*head = s;
} else {
struct config_strlist *last = *head;
while (last->next!=NULL) {
last = last->next;
}
last->next = s;
}
return 1;
}
int
cfg_str2list_insert(struct config_str2list** head, char* item, char* i2)
{
struct config_str2list *s;
if(!item || !i2 || !head) {
free(item);
free(i2);
return 0;
}
s = (struct config_str2list*)calloc(1, sizeof(struct config_str2list));
if(!s) {
free(item);
free(i2);
return 0;
}
s->str = item;
s->str2 = i2;
s->next = *head;
*head = s;
return 1;
}
int
cfg_str3list_insert(struct config_str3list** head, char* item, char* i2,
char* i3)
{
struct config_str3list *s;
if(!item || !i2 || !i3 || !head)
return 0;
s = (struct config_str3list*)calloc(1, sizeof(struct config_str3list));
if(!s)
return 0;
s->str = item;
s->str2 = i2;
s->str3 = i3;
s->next = *head;
*head = s;
return 1;
}
int
cfg_strbytelist_insert(struct config_strbytelist** head, char* item,
uint8_t* i2, size_t i2len)
{
struct config_strbytelist* s;
if(!item || !i2 || !head)
return 0;
s = (struct config_strbytelist*)calloc(1, sizeof(*s));
if(!s)
return 0;
s->str = item;
s->str2 = i2;
s->str2len = i2len;
s->next = *head;
*head = s;
return 1;
}
time_t
cfg_convert_timeval(const char* str)
{
time_t t;
struct tm tm;
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,
&tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6)
return 0;
tm.tm_year -= 1900;
tm.tm_mon--;
/* Check values */
if (tm.tm_year < 70) return 0;
if (tm.tm_mon < 0 || tm.tm_mon > 11) return 0;
if (tm.tm_mday < 1 || tm.tm_mday > 31) return 0;
if (tm.tm_hour < 0 || tm.tm_hour > 23) return 0;
if (tm.tm_min < 0 || tm.tm_min > 59) return 0;
if (tm.tm_sec < 0 || tm.tm_sec > 59) return 0;
/* call ldns conversion function */
t = sldns_mktime_from_utc(&tm);
return t;
}
int
cfg_count_numbers(const char* s)
{
/* format ::= (sp num)+ sp */
/* num ::= [-](0-9)+ */
/* sp ::= (space|tab)* */
int num = 0;
while(*s) {
while(*s && isspace((unsigned char)*s))
s++;
if(!*s) /* end of string */
break;
if(*s == '-')
s++;
if(!*s) /* only - not allowed */
return 0;
if(!isdigit((unsigned char)*s)) /* bad character */
return 0;
while(*s && isdigit((unsigned char)*s))
s++;
num++;
}
return num;
}
/** all digit number */
static int isalldigit(const char* str, size_t l)
{
size_t i;
for(i=0; i<l; i++)
if(!isdigit((unsigned char)str[i]))
return 0;
return 1;
}
int
cfg_parse_memsize(const char* str, size_t* res)
{
size_t len;
size_t mult = 1;
if(!str || (len=(size_t)strlen(str)) == 0) {
log_err("not a size: '%s'", str);
return 0;
}
if(isalldigit(str, len)) {
*res = (size_t)atol(str);
return 1;
}
/* check appended num */
while(len>0 && str[len-1]==' ')
len--;
if(len > 1 && str[len-1] == 'b')
len--;
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')
mult = 1024*1024;
else if(len > 1 && tolower((unsigned char)str[len-1]) == 'k')
mult = 1024;
else if(len > 0 && isdigit((unsigned char)str[len-1]))
mult = 1;
else {
log_err("unknown size specifier: '%s'", str);
return 0;
}
while(len>1 && str[len-2]==' ')
len--;
if(!isalldigit(str, len-1)) {
log_err("unknown size specifier: '%s'", str);
return 0;
}
*res = ((size_t)atol(str)) * mult;
return 1;
}
int
find_tag_id(struct config_file* cfg, const char* tag)
{
int i;
for(i=0; i<cfg->num_tags; i++) {
if(strcmp(cfg->tagname[i], tag) == 0)
return i;
}
return -1;
}
int
config_add_tag(struct config_file* cfg, const char* tag)
{
char** newarray;
char* newtag;
if(find_tag_id(cfg, tag) != -1)
return 1; /* nothing to do */
newarray = (char**)malloc(sizeof(char*)*(cfg->num_tags+1));
if(!newarray)
return 0;
newtag = strdup(tag);
if(!newtag) {
free(newarray);
return 0;
}
if(cfg->tagname) {
memcpy(newarray, cfg->tagname, sizeof(char*)*cfg->num_tags);
free(cfg->tagname);
}
newarray[cfg->num_tags++] = newtag;
cfg->tagname = newarray;
return 1;
}
/** set a bit in a bit array */
static void
cfg_set_bit(uint8_t* bitlist, size_t len, int id)
{
int pos = id/8;
log_assert((size_t)pos < len);
(void)len;
bitlist[pos] |= 1<<(id%8);
}
uint8_t* config_parse_taglist(struct config_file* cfg, char* str,
size_t* listlen)
{
uint8_t* taglist = NULL;
size_t len = 0;
char* p, *s;
/* allocate */
if(cfg->num_tags == 0) {
log_err("parse taglist, but no tags defined");
return 0;
}
len = (size_t)(cfg->num_tags+7)/8;
taglist = calloc(1, len);
if(!taglist) {
log_err("out of memory");
return 0;
}
/* parse */
s = str;
while((p=strsep(&s, " \t\n")) != NULL) {
if(*p) {
int id = find_tag_id(cfg, p);
/* set this bit in the bitlist */
if(id == -1) {
log_err("unknown tag: %s", p);
free(taglist);
return 0;
}
cfg_set_bit(taglist, len, id);
}
}
*listlen = len;
return taglist;
}
uint8_t* cfg_parse_nsid(const char* str, uint16_t* nsid_len)
{
uint8_t* nsid = NULL;
if (strncasecmp(str, "ascii_", 6) == 0) {
if ((nsid = (uint8_t *)strdup(str + 6)))
*nsid_len = strlen(str + 6);
} else if (strlen(str) % 2) {
; /* hex string has even number of characters */
}
else if (*str && (nsid = calloc(1, strlen(str) / 2))) {
const char *ch;
uint8_t *dp;
for ( ch = str, dp = nsid
; isxdigit(ch[0]) && isxdigit(ch[1])
; ch += 2, dp++) {
*dp = (uint8_t)sldns_hexdigit_to_int(ch[0]) * 16;
*dp += (uint8_t)sldns_hexdigit_to_int(ch[1]);
}
if (*ch) {
free(nsid);
nsid = NULL;
} else
*nsid_len = strlen(str) / 2;
}
return nsid;
}
char* config_taglist2str(struct config_file* cfg, uint8_t* taglist,
size_t taglen)
{
char buf[10240];
size_t i, j, len = 0;
buf[0] = 0;
for(i=0; i<taglen; i++) {
if(taglist[i] == 0)
continue;
for(j=0; j<8; j++) {
if((taglist[i] & (1<<j)) != 0) {
size_t id = i*8 + j;
snprintf(buf+len, sizeof(buf)-len, "%s%s",
(len==0?"":" "), cfg->tagname[id]);
len += strlen(buf+len);
}
}
}
return strdup(buf);
}
int taglist_intersect(uint8_t* list1, size_t list1len, const uint8_t* list2,
size_t list2len)
{
size_t i;
if(!list1 || !list2)
return 0;
for(i=0; i<list1len && i<list2len; i++) {
if((list1[i] & list2[i]) != 0)
return 1;
}
return 0;
}
void
config_apply(struct config_file* config)
{
MAX_TTL = (time_t)config->max_ttl;
MIN_TTL = (time_t)config->min_ttl;
SERVE_EXPIRED = config->serve_expired;
SERVE_EXPIRED_TTL = (time_t)config->serve_expired_ttl;
SERVE_EXPIRED_REPLY_TTL = (time_t)config->serve_expired_reply_ttl;
SERVE_ORIGINAL_TTL = config->serve_original_ttl;
MAX_NEG_TTL = (time_t)config->max_negative_ttl;
RTT_MIN_TIMEOUT = config->infra_cache_min_rtt;
RTT_MAX_TIMEOUT = config->infra_cache_max_rtt;
EDNS_ADVERTISED_SIZE = (uint16_t)config->edns_buffer_size;
MINIMAL_RESPONSES = config->minimal_responses;
RRSET_ROUNDROBIN = config->rrset_roundrobin;
LOG_TAG_QUERYREPLY = config->log_tag_queryreply;
UNKNOWN_SERVER_NICENESS = config->unknown_server_time_limit;
USEFUL_SERVER_TOP_TIMEOUT = RTT_MAX_TIMEOUT;
BLACKLIST_PENALTY = USEFUL_SERVER_TOP_TIMEOUT*4;
log_set_time_asc(config->log_time_ascii);
autr_permit_small_holddown = config->permit_small_holddown;
stream_wait_max = config->stream_wait_size;
http2_query_buffer_max = config->http_query_buffer_size;
http2_response_buffer_max = config->http_response_buffer_size;
}
void config_lookup_uid(struct config_file* cfg)
{
#ifdef HAVE_GETPWNAM
/* translate username into uid and gid */
if(cfg->username && cfg->username[0]) {
struct passwd *pwd;
if((pwd = getpwnam(cfg->username)) != NULL) {
cfg_uid = pwd->pw_uid;
cfg_gid = pwd->pw_gid;
}
}
#else
(void)cfg;
#endif
}
/**
* Calculate string length of full pathname in original filesys
* @param fname: the path name to convert.
* Must not be null or empty.
* @param cfg: config struct for chroot and chdir (if set).
* @param use_chdir: if false, only chroot is applied.
* @return length of string.
* remember to allocate one more for 0 at end in mallocs.
*/
static size_t
strlen_after_chroot(const char* fname, struct config_file* cfg, int use_chdir)
{
size_t len = 0;
int slashit = 0;
if(cfg->chrootdir && cfg->chrootdir[0] &&
strncmp(cfg->chrootdir, fname, strlen(cfg->chrootdir)) == 0) {
/* already full pathname, return it */
return strlen(fname);
}
/* chroot */
if(cfg->chrootdir && cfg->chrootdir[0]) {
/* start with chrootdir */
len += strlen(cfg->chrootdir);
slashit = 1;
}
/* chdir */
#ifdef UB_ON_WINDOWS
if(fname[0] != 0 && fname[1] == ':') {
/* full path, no chdir */
} else
#endif
if(fname[0] == '/' || !use_chdir) {
/* full path, no chdir */
} else if(cfg->directory && cfg->directory[0]) {
/* prepend chdir */
if(slashit && cfg->directory[0] != '/')
len++;
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);
slashit = 1;
}
/* fname */
if(slashit && fname[0] != '/')
len++;
len += strlen(fname);
return len;
}
char*
fname_after_chroot(const char* fname, struct config_file* cfg, int use_chdir)
{
size_t len = strlen_after_chroot(fname, cfg, use_chdir)+1;
int slashit = 0;
char* buf = (char*)malloc(len);
if(!buf)
return NULL;
buf[0] = 0;
/* is fname already in chroot ? */
if(cfg->chrootdir && cfg->chrootdir[0] &&
strncmp(cfg->chrootdir, fname, strlen(cfg->chrootdir)) == 0) {
/* already full pathname, return it */
(void)strlcpy(buf, fname, len);
buf[len-1] = 0;
return buf;
}
/* chroot */
if(cfg->chrootdir && cfg->chrootdir[0]) {
/* start with chrootdir */
(void)strlcpy(buf, cfg->chrootdir, len);
slashit = 1;
}
#ifdef UB_ON_WINDOWS
if(fname[0] != 0 && fname[1] == ':') {
/* full path, no chdir */
} else
#endif
/* chdir */
if(fname[0] == '/' || !use_chdir) {
/* full path, no chdir */
} else if(cfg->directory && cfg->directory[0]) {
/* prepend chdir */
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,
strlen(cfg->chrootdir)) == 0)
(void)strlcat(buf, cfg->directory+strlen(cfg->chrootdir),
len);
else (void)strlcat(buf, cfg->directory, len);
slashit = 1;
}
/* fname */
if(slashit && fname[0] != '/')
(void)strlcat(buf, "/", len);
(void)strlcat(buf, fname, len);
buf[len-1] = 0;
return buf;
}
/** return next space character in string */
static char* next_space_pos(const char* str)
{
char* sp = strchr(str, ' ');
char* tab = strchr(str, '\t');
if(!tab && !sp)
return NULL;
if(!sp) return tab;
if(!tab) return sp;
return (sp<tab)?sp:tab;
}
/** return last space character in string */
static char* last_space_pos(const char* str)
{
char* sp = strrchr(str, ' ');
char* tab = strrchr(str, '\t');
if(!tab && !sp)
return NULL;
if(!sp) return tab;
if(!tab) return sp;
return (sp>tab)?sp:tab;
}
int
cfg_parse_local_zone(struct config_file* cfg, const char* val)
{
const char *type, *name_end, *name;
char buf[256];
/* parse it as: [zone_name] [between stuff] [zone_type] */
name = val;
while(*name && isspace((unsigned char)*name))
name++;
if(!*name) {
log_err("syntax error: too short: %s", val);
return 0;
}
name_end = next_space_pos(name);
if(!name_end || !*name_end) {
log_err("syntax error: expected zone type: %s", val);
return 0;
}
if (name_end - name > 255) {
log_err("syntax error: bad zone name: %s", val);
return 0;
}
(void)strlcpy(buf, name, sizeof(buf));
buf[name_end-name] = '\0';
type = last_space_pos(name_end);
while(type && *type && isspace((unsigned char)*type))
type++;
if(!type || !*type) {
log_err("syntax error: expected zone type: %s", val);
return 0;
}
if(strcmp(type, "nodefault")==0) {
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,
strdup(name));
#endif
} else {
return cfg_str2list_insert(&cfg->local_zones, strdup(buf),
strdup(type));
}
}
char* cfg_ptr_reverse(char* str)
{
char* ip, *ip_end;
char* name;
char* result;
char buf[1024];
struct sockaddr_storage addr;
socklen_t addrlen;
/* parse it as: [IP] [between stuff] [name] */
ip = str;
while(*ip && isspace((unsigned char)*ip))
ip++;
if(!*ip) {
log_err("syntax error: too short: %s", str);
return NULL;
}
ip_end = next_space_pos(ip);
if(!ip_end || !*ip_end) {
log_err("syntax error: expected name: %s", str);
return NULL;
}
name = last_space_pos(ip_end);
if(!name || !*name) {
log_err("syntax error: expected name: %s", str);
return NULL;
}
sscanf(ip, "%100s", buf);
buf[sizeof(buf)-1]=0;
if(!ipstrtoaddr(buf, UNBOUND_DNS_PORT, &addr, &addrlen)) {
log_err("syntax error: cannot parse address: %s", str);
return NULL;
}
/* reverse IPv4:
* ddd.ddd.ddd.ddd.in-addr-arpa.
* IPv6: (h.){32}.ip6.arpa. */
if(addr_is_ip6(&addr, addrlen)) {
uint8_t ad[16];
const char* hex = "0123456789abcdef";
char *p = buf;
int i;
memmove(ad, &((struct sockaddr_in6*)&addr)->sin6_addr,
sizeof(ad));
for(i=15; i>=0; i--) {
uint8_t b = ad[i];
*p++ = hex[ (b&0x0f) ];
*p++ = '.';
*p++ = hex[ (b&0xf0) >> 4 ];
*p++ = '.';
}
snprintf(buf+16*4, sizeof(buf)-16*4, "ip6.arpa. ");
} else {
uint8_t ad[4];
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],
(unsigned)ad[1], (unsigned)ad[0]);
}
/* printed the reverse address, now the between goop and name on end */
while(*ip_end && isspace((unsigned char)*ip_end))
ip_end++;
if(name>ip_end) {
snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%.*s",
(int)(name-ip_end), ip_end);
}
snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), " PTR %s", name);
result = strdup(buf);
if(!result) {
log_err("out of memory parsing %s", str);
return NULL;
}
return result;
}
#ifdef UB_ON_WINDOWS
char*
w_lookup_reg_str(const char* key, const char* name)
{
HKEY hk = NULL;
DWORD type = 0;
BYTE buf[1024];
DWORD len = (DWORD)sizeof(buf);
LONG ret;
char* result = NULL;
ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, KEY_READ, &hk);
if(ret == ERROR_FILE_NOT_FOUND)
return NULL; /* key does not exist */
else if(ret != ERROR_SUCCESS) {
log_err("RegOpenKeyEx failed");
return NULL;
}
ret = RegQueryValueEx(hk, (LPCTSTR)name, 0, &type, buf, &len);
if(RegCloseKey(hk))
log_err("RegCloseKey");
if(ret == ERROR_FILE_NOT_FOUND)
return NULL; /* name does not exist */
else if(ret != ERROR_SUCCESS) {
log_err("RegQueryValueEx failed");
return NULL;
}
if(type == REG_SZ || type == REG_MULTI_SZ || type == REG_EXPAND_SZ) {
buf[sizeof(buf)-1] = 0;
buf[sizeof(buf)-2] = 0; /* for multi_sz */
result = strdup((char*)buf);
if(!result) log_err("out of memory");
}
return result;
}
void w_config_adjust_directory(struct config_file* cfg)
{
if(cfg->directory && cfg->directory[0]) {
TCHAR dirbuf[2*MAX_PATH+4];
if(strcmp(cfg->directory, "%EXECUTABLE%") == 0) {
/* get executable path, and if that contains
* directories, snip off the filename part */
dirbuf[0] = 0;
if(!GetModuleFileName(NULL, dirbuf, MAX_PATH))
log_err("could not GetModuleFileName");
if(strrchr(dirbuf, '\\')) {
(strrchr(dirbuf, '\\'))[0] = 0;
} else log_err("GetModuleFileName had no path");
if(dirbuf[0]) {
/* adjust directory for later lookups to work*/
free(cfg->directory);
cfg->directory = memdup(dirbuf, strlen(dirbuf)+1);
}
}
}
}
#endif /* UB_ON_WINDOWS */
int options_remote_is_address(struct config_file* cfg)
{
if(!cfg->remote_control_enable) return 0;
if(!cfg->control_ifs.first) return 1;
if(!cfg->control_ifs.first->str) return 1;
if(cfg->control_ifs.first->str[0] == 0) return 1;
return (cfg->control_ifs.first->str[0] != '/');
}
/** see if interface is https, its port number == the https port number */
int
if_is_https(const char* ifname, const char* port, int https_port)
{
char* p = strchr(ifname, '@');
if(!p && atoi(port) == https_port)
return 1;
if(p && atoi(p+1) == https_port)
return 1;
return 0;
}
/** see if config contains https turned on */
int cfg_has_https(struct config_file* cfg)
{
int i;
char portbuf[32];
snprintf(portbuf, sizeof(portbuf), "%d", cfg->port);
for(i = 0; i<cfg->num_ifs; i++) {
if(if_is_https(cfg->ifs[i], portbuf, cfg->https_port))
return 1;
}
return 0;
}
/** see if interface is PROXYv2, its port number == the proxy port number */
int
if_is_pp2(const char* ifname, const char* port,
struct config_strlist* proxy_protocol_port)
{
struct config_strlist* s;
char* p = strchr(ifname, '@');
for(s = proxy_protocol_port; s; s = s->next) {
if(p && atoi(p+1) == atoi(s->str))
return 1;
if(!p && atoi(port) == atoi(s->str))
return 1;
}
return 0;
}
/** see if interface is DNSCRYPT, its port number == the dnscrypt port number */
int
if_is_dnscrypt(const char* ifname, const char* port, int dnscrypt_port)
{
#ifdef USE_DNSCRYPT
return ((strchr(ifname, '@') &&
atoi(strchr(ifname, '@')+1) == dnscrypt_port) ||
(!strchr(ifname, '@') && atoi(port) == dnscrypt_port));
#else
(void)ifname;
(void)port;
(void)dnscrypt_port;
return 0;
#endif
}
diff --git a/contrib/unbound/util/config_file.h b/contrib/unbound/util/config_file.h
index 452f3c6a78fb..ad22b8330e36 100644
--- a/contrib/unbound/util/config_file.h
+++ b/contrib/unbound/util/config_file.h
@@ -1,1368 +1,1374 @@
/*
* util/config_file.h - reads and stores the config file for unbound.
*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file contains functions for the config file.
*/
#ifndef UTIL_CONFIG_FILE_H
#define UTIL_CONFIG_FILE_H
#include "sldns/rrdef.h"
struct config_stub;
struct config_auth;
struct config_view;
struct config_strlist;
struct config_str2list;
struct config_str3list;
struct config_strbytelist;
struct module_qstate;
struct sock_list;
struct ub_packed_rrset_key;
struct regional;
/** List head for strlist processing, used for append operation. */
struct config_strlist_head {
/** first in list of text items */
struct config_strlist* first;
/** last in list of text items */
struct config_strlist* last;
};
/**
* The configuration options.
* Strings are malloced.
*/
struct config_file {
/** verbosity level as specified in the config file */
int verbosity;
/** statistics interval (in seconds) */
int stat_interval;
/** if false, statistics values are reset after printing them */
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;
/** port on which queries are answered. */
int port;
/** do ip4 query support. */
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 prefer_ip6;
/** do udp query support. */
int do_udp;
/** do tcp query support. */
int do_tcp;
/** max number of queries on a reuse connection. */
size_t max_reuse_tcp_queries;
/** timeout for REUSE entries in milliseconds. */
int tcp_reuse_timeout;
/** timeout in milliseconds for TCP queries to auth servers. */
int tcp_auth_query_timeout;
/** tcp upstream queries (no UDP upstream queries) */
int tcp_upstream;
/** udp upstream enabled when no UDP downstream is enabled (do_udp no)*/
int udp_upstream_without_downstream;
/** maximum segment size of tcp socket which queries are answered */
int tcp_mss;
/** maximum segment size of tcp socket for outgoing queries */
int outgoing_tcp_mss;
/** tcp idle timeout, in msec */
int tcp_idle_timeout;
/** do edns tcp keepalive */
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;
/** private key file for dnstcp-ssl service (enabled if not NULL) */
char* ssl_service_key;
/** public key file for dnstcp-ssl service */
char* ssl_service_pem;
/** port on which to provide ssl service */
int ssl_port;
/** if outgoing tcp connections use SSL */
int ssl_upstream;
/** cert bundle for outgoing connections */
char* tls_cert_bundle;
/** should the system certificate store get added to the cert bundle */
int tls_win_cert;
/** additional tls ports */
struct config_strlist* tls_additional_port;
/** secret key used to encrypt and decrypt TLS session ticket */
struct config_strlist_head tls_session_ticket_keys;
/** TLS ciphers */
char* tls_ciphers;
/** TLS chiphersuites (TLSv1.3) */
char* tls_ciphersuites;
/** if SNI is to be used */
int tls_use_sni;
/** port on which to provide DNS over HTTPS service */
int https_port;
/** endpoint for HTTP service */
char* http_endpoint;
/** MAX_CONCURRENT_STREAMS HTTP/2 setting */
uint32_t http_max_streams;
/** maximum size of all HTTP2 query buffers combined. */
size_t http_query_buffer_size;
/** maximum size of all HTTP2 response buffers combined. */
size_t http_response_buffer_size;
/** set TCP_NODELAY option for http sockets */
int http_nodelay;
/** Disable TLS for http sockets downstream */
int http_notls_downstream;
/** outgoing port range number of ports (per thread) */
int outgoing_num_ports;
/** number of outgoing tcp buffers per (per thread) */
size_t outgoing_num_tcp;
/** number of incoming tcp buffers per (per thread) */
size_t incoming_num_tcp;
/** allowed udp port numbers, array with 0 if not allowed */
int* outgoing_avail_ports;
/** EDNS buffer size to use */
size_t edns_buffer_size;
/** size of the stream wait buffers, max */
size_t stream_wait_size;
/** number of bytes buffer size for DNS messages */
size_t msg_buffer_size;
/** size of the message cache */
size_t msg_cache_size;
/** slabs in the message cache. */
size_t msg_cache_slabs;
/** number of queries every thread can service */
size_t num_queries_per_thread;
/** number of msec to wait before items can be jostled out */
size_t jostle_time;
/** size of the rrset cache */
size_t rrset_cache_size;
/** slabs in the rrset cache */
size_t rrset_cache_slabs;
/** host cache ttl in seconds */
int host_ttl;
/** number of slabs in the infra host cache */
size_t infra_cache_slabs;
/** max number of hosts in the infra cache */
size_t infra_cache_numhosts;
/** min value for infra cache rtt (min retransmit timeout) */
int infra_cache_min_rtt;
/** max value for infra cache rtt (max retransmit timeout) */
int infra_cache_max_rtt;
/** keep probing hosts that are down */
int infra_keep_probing;
/** delay close of udp-timeouted ports, if 0 no delayclose. in msec */
int delay_close;
/** udp_connect enable uses UDP connect to mitigate ICMP side channel */
int udp_connect;
/** the target fetch policy for the iterator */
char* target_fetch_policy;
/** percent*10, how many times in 1000 to pick from the fastest
* destinations */
int fast_server_permil;
/** number of fastest server to select from */
size_t fast_server_num;
/** automatic interface for incoming messages. Uses ipv6 remapping,
* and recvmsg/sendmsg ancillary data to detect interfaces, boolean */
int if_automatic;
/** extra ports to open if if_automatic enabled, or NULL for default */
char* if_automatic_ports;
/** SO_RCVBUF size to set on port 53 UDP socket */
size_t so_rcvbuf;
/** SO_SNDBUF size to set on port 53 UDP socket */
size_t so_sndbuf;
/** SO_REUSEPORT requested on port 53 sockets */
int so_reuseport;
/** IP_TRANSPARENT socket option requested on port 53 sockets */
int ip_transparent;
/** IP_FREEBIND socket option request on port 53 sockets */
int ip_freebind;
/** IP_TOS socket option requested on port 53 sockets */
int ip_dscp;
/** number of interfaces to open. If 0 default all interfaces. */
int num_ifs;
/** interface description strings (IP addresses) */
char **ifs;
/** number of outgoing interfaces to open.
* If 0 default all interfaces. */
int num_out_ifs;
/** outgoing interface description strings (IP addresses) */
char **out_ifs;
/** the root hints */
struct config_strlist* root_hints;
/** the stub definitions, linked list */
struct config_stub* stubs;
/** the forward zone definitions, linked list */
struct config_stub* forwards;
/** the auth zone definitions, linked list */
struct config_auth* auths;
/** the views definitions, linked list */
struct config_view* views;
/** list of donotquery addresses, linked list */
struct config_strlist* donotqueryaddrs;
#ifdef CLIENT_SUBNET
/** 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 */
struct config_strlist* client_subnet_zone;
/** opcode assigned by IANA for edns0-client-subnet option */
uint16_t client_subnet_opcode;
/** Do not check whitelist if incoming query contains an ECS record */
int client_subnet_always_forward;
/** Subnet length we are willing to give up privacy for */
uint8_t max_client_subnet_ipv4;
uint8_t max_client_subnet_ipv6;
/** Minimum subnet length we are willing to answer */
uint8_t min_client_subnet_ipv4;
uint8_t min_client_subnet_ipv6;
/** Max number of nodes in the ECS radix tree */
uint32_t max_ecs_tree_size_ipv4;
uint32_t max_ecs_tree_size_ipv6;
#endif
/** list of access control entries, linked list */
struct config_str2list* acls;
/** use default localhost donotqueryaddr entries */
int donotquery_localhost;
/** list of tcp connection limitss, linked list */
struct config_str2list* tcp_connection_limits;
/** harden against very small edns buffer sizes */
int harden_short_bufsize;
/** harden against very large query sizes */
int harden_large_queries;
/** harden against spoofed glue (out of zone data) */
int harden_glue;
/** harden against receiving no DNSSEC data for trust anchor */
int harden_dnssec_stripped;
/** harden against queries that fall under known nxdomain names */
int harden_below_nxdomain;
/** harden the referral path, query for NS,A,AAAA and validate */
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 */
struct config_strlist* caps_whitelist;
/** strip away these private addrs from answers, no DNS Rebinding */
struct config_strlist* private_address;
/** allow domain (and subdomains) to use private address space */
struct config_strlist* private_domain;
/** what threshold for unwanted action. */
size_t unwanted_threshold;
/** the number of seconds maximal TTL used for RRsets and messages */
int max_ttl;
/** the number of seconds minimum TTL used for RRsets and messages */
int min_ttl;
/** the number of seconds maximal negative TTL for SOA in auth */
int max_negative_ttl;
/** if prefetching of messages should be performed. */
int prefetch;
/** if prefetching of DNSKEYs should be performed. */
int prefetch_key;
/** deny queries of type ANY with an empty answer */
int deny_any;
/** chrootdir, if not "" or chroot will be done */
char* chrootdir;
/** username to change to, if not "". */
char* username;
/** working directory */
char* directory;
/** filename to log to. */
char* logfile;
/** pidfile to write pid to. */
char* pidfile;
/** should log messages be sent to syslogd */
int use_syslog;
/** log timestamp in ascii UTC */
int log_time_ascii;
/** log queries with one line per query */
int log_queries;
/** log replies with one line per reply */
int log_replies;
/** tag log_queries and log_replies for filtering */
int log_tag_queryreply;
/** log every local-zone hit **/
int log_local_actions;
/** log servfails with a reason */
int log_servfail;
/** log identity to report */
char* log_identity;
/** do not report identity (id.server, hostname.bind) */
int hide_identity;
/** do not report version (version.server, version.bind) */
int hide_version;
/** do not report trustanchor (trustanchor.unbound) */
int hide_trustanchor;
/** do not report the User-Agent HTTP header */
int hide_http_user_agent;
/** identity, hostname is returned if "". */
char* identity;
/** version, package version returned if "". */
char* version;
/** User-Agent for HTTP header */
char* http_user_agent;
/** nsid */
char *nsid_cfg_str;
uint8_t *nsid;
uint16_t nsid_len;
/** 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 */
struct config_strlist* trust_anchor_list;
/** files with 5011 autotrust tracked keys */
struct config_strlist* auto_trust_anchor_file_list;
/** files with trusted DNSKEYs in named.conf format, list */
struct config_strlist* trusted_keys_file_list;
/** insecure domain list */
struct config_strlist* domain_insecure;
/** send key tag query */
int trust_anchor_signaling;
/** enable root key sentinel */
int root_key_sentinel;
/** if not 0, this value is the validation date for RRSIGs */
int32_t val_date_override;
/** the minimum for signature clock skew */
int32_t val_sig_skew_min;
/** the maximum for signature clock skew */
int32_t val_sig_skew_max;
/** 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;
/** should validator clean additional section for secure msgs */
int val_clean_additional;
/** log bogus messages by the validator */
int val_log_level;
/** squelch val_log_level to log - this is library goes to callback */
int val_log_squelch;
/** should validator allow bogus messages to go through */
int val_permissive_mode;
/** use cached NSEC records to synthesise (negative) answers */
int aggressive_nsec;
/** ignore the CD flag in incoming queries and refuse them bogus data */
int ignore_cd;
+ /** disable EDNS DO flag in outgoing requests */
+ int disable_edns_do;
/** serve expired entries and prefetch them */
int serve_expired;
/** serve expired entries until TTL after expiration */
int serve_expired_ttl;
/** reset serve expired TTL after failed update attempt */
int serve_expired_ttl_reset;
/** TTL for the serve expired replies */
int serve_expired_reply_ttl;
/** serve expired entries only after trying to update the entries and this
* timeout (in milliseconds) is reached */
int serve_expired_client_timeout;
/** serve EDE code 3 - Stale Answer (RFC8914) for expired entries */
int ede_serve_expired;
/** serve original TTLs rather than decrementing ones */
int serve_original_ttl;
/** nsec3 maximum iterations per key size, string */
char* val_nsec3_key_iterations;
/** if zonemd failures are permitted, only logged */
int zonemd_permissive_mode;
/** autotrust add holddown time, in seconds */
unsigned int add_holddown;
/** autotrust del holddown time, in seconds */
unsigned int del_holddown;
/** autotrust keep_missing time, in seconds. 0 is forever. */
unsigned int keep_missing;
/** permit small holddown values, allowing 5011 rollover very fast */
int permit_small_holddown;
/** size of the key cache */
size_t key_cache_size;
/** slabs in the key cache. */
size_t key_cache_slabs;
/** size of the neg cache */
size_t neg_cache_size;
/** local zones config */
struct config_str2list* local_zones;
/** local zones nodefault list */
struct config_strlist* local_zones_nodefault;
#ifdef USE_IPSET
/** local zones ipset list */
struct config_strlist* local_zones_ipset;
#endif
/** do not add any default local zone */
int local_zones_disable_default;
/** local data RRs configured */
struct config_strlist* local_data;
/** local zone override types per netblock */
struct config_str3list* local_zone_overrides;
/** unblock lan zones (reverse lookups for AS112 zones) */
int unblock_lan_zones;
/** insecure lan zones (don't validate AS112 zones) */
int insecure_lan_zones;
/** list of zonename, tagbitlist */
struct config_strbytelist* local_zone_tags;
/** list of aclname, tagbitlist */
struct config_strbytelist* acl_tags;
/** list of aclname, tagname, localzonetype */
struct config_str3list* acl_tag_actions;
/** list of aclname, tagname, redirectdata */
struct config_str3list* acl_tag_datas;
/** list of aclname, view*/
struct config_str2list* acl_view;
/** list of interface action entries, linked list */
struct config_str2list* interface_actions;
/** list of interface, tagbitlist */
struct config_strbytelist* interface_tags;
/** list of interface, tagname, localzonetype */
struct config_str3list* interface_tag_actions;
/** list of interface, tagname, redirectdata */
struct config_str3list* interface_tag_datas;
/** list of interface, view*/
struct config_str2list* interface_view;
/** list of IP-netblock, tagbitlist */
struct config_strbytelist* respip_tags;
/** list of response-driven access control entries, linked list */
struct config_str2list* respip_actions;
/** RRs configured for response-driven access controls */
struct config_str2list* respip_data;
/** tag list, array with tagname[i] is malloced string */
char** tagname;
/** number of items in the taglist */
int num_tags;
/** remote control section. enable toggle. */
int remote_control_enable;
/** the interfaces the remote control should listen on */
struct config_strlist_head control_ifs;
/** if the use-cert option is set */
int control_use_cert;
/** port number for the control port */
int control_port;
/** private key file for server */
char* server_key_file;
/** certificate file for server */
char* server_cert_file;
/** private key file for unbound-control */
char* control_key_file;
/** certificate file for unbound-control */
char* control_cert_file;
/** Python script file */
struct config_strlist* python_script;
/** Dynamic library file */
struct config_strlist* dynlib_file;
/** Use systemd socket activation. */
int use_systemd;
/** daemonize, i.e. fork into the background. */
int do_daemonize;
/* minimal response when positive answer */
int minimal_responses;
/* RRSet roundrobin */
int rrset_roundrobin;
/* wait time for unknown server in msec */
int unknown_server_time_limit;
/* maximum UDP response size */
size_t max_udp_size;
/* DNS64 prefix */
char* dns64_prefix;
/* Synthetize all AAAA record despite the presence of an authoritative one */
int dns64_synthall;
/** 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 */
int dnstap_bidirectional;
/** dnstap socket path */
char* dnstap_socket_path;
/** dnstap IP */
char* dnstap_ip;
/** dnstap TLS enable */
int dnstap_tls;
/** dnstap tls server authentication name */
char* dnstap_tls_server_name;
/** dnstap server cert bundle */
char* dnstap_tls_cert_bundle;
/** dnstap client key for client authentication */
char* dnstap_tls_client_key_file;
/** dnstap client cert for client authentication */
char* dnstap_tls_client_cert_file;
/** true to send "identity" via dnstap */
int dnstap_send_identity;
/** true to send "version" via dnstap */
int dnstap_send_version;
/** dnstap "identity", hostname is used if "". */
char* dnstap_identity;
/** dnstap "version", package version is used if "". */
char* dnstap_version;
/** true to log dnstap RESOLVER_QUERY message events */
int dnstap_log_resolver_query_messages;
/** true to log dnstap RESOLVER_RESPONSE message events */
int dnstap_log_resolver_response_messages;
/** true to log dnstap CLIENT_QUERY message events */
int dnstap_log_client_query_messages;
/** true to log dnstap CLIENT_RESPONSE message events */
int dnstap_log_client_response_messages;
/** true to log dnstap FORWARDER_QUERY message events */
int dnstap_log_forwarder_query_messages;
/** true to log dnstap FORWARDER_RESPONSE message events */
int dnstap_log_forwarder_response_messages;
/** true to disable DNSSEC lameness check in iterator */
int disable_dnssec_lame_check;
/** 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 */
size_t ip_ratelimit_size;
/** ip_ratelimit factor, 0 blocks all, 10 allows 1/10 of traffic */
int ip_ratelimit_factor;
/** ratelimit backoff, when on, if the limit is reached it is
* considered an attack and it backs off until 'demand' decreases over
* the RATE_WINDOW. */
int ip_ratelimit_backoff;
/** ratelimit for domains. 0 is off, otherwise qps (unless overridden) */
int ratelimit;
/** number of slabs for ratelimit cache */
size_t ratelimit_slabs;
/** memory size in bytes for ratelimit cache */
size_t ratelimit_size;
/** ratelimits for domain (exact match) */
struct config_str2list* ratelimit_for_domain;
/** ratelimits below domain */
struct config_str2list* ratelimit_below_domain;
/** ratelimit factor, 0 blocks all, 10 allows 1/10 of traffic */
int ratelimit_factor;
/** ratelimit backoff, when on, if the limit is reached it is
* considered an attack and it backs off until 'demand' decreases over
* the RATE_WINDOW. */
int ratelimit_backoff;
/** 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.
* Do not apply fallback */
int qname_minimisation_strict;
/** SHM data - true if shm is enabled */
int shm_enable;
/** SHM data - key for the shm */
int shm_key;
/** list of EDNS client string entries, linked list */
struct config_str2list* edns_client_strings;
/** EDNS opcode to use for EDNS client strings */
uint16_t edns_client_string_opcode;
/** DNSCrypt */
/** true to enable dnscrypt */
int dnscrypt;
/** port on which to provide dnscrypt service */
int dnscrypt_port;
/** provider name 2.dnscrypt-cert.example.com */
char* dnscrypt_provider;
/** dnscrypt secret keys 1.key */
struct config_strlist* dnscrypt_secret_key;
/** dnscrypt provider certs 1.cert */
struct config_strlist* dnscrypt_provider_cert;
/** dnscrypt provider certs 1.cert which have been rotated and should not be
* advertised through DNS's providername TXT record but are required to be
* able to handle existing traffic using the old cert. */
struct config_strlist* dnscrypt_provider_cert_rotated;
/** memory size in bytes for dnscrypt shared secrets cache */
size_t dnscrypt_shared_secret_cache_size;
/** number of slabs for dnscrypt shared secrets cache */
size_t dnscrypt_shared_secret_cache_slabs;
/** memory size in bytes for dnscrypt nonces cache */
size_t dnscrypt_nonce_cache_size;
/** number of slabs for dnscrypt nonces cache */
size_t dnscrypt_nonce_cache_slabs;
/** EDNS padding according to RFC7830 and RFC8467 */
/** true to enable padding of responses (default: on) */
int pad_responses;
/** block size with which to pad encrypted responses (default: 468) */
size_t pad_responses_block_size;
/** true to enable padding of queries (default: on) */
int pad_queries;
/** block size with which to pad encrypted queries (default: 128) */
size_t pad_queries_block_size;
/** IPsec module */
#ifdef USE_IPSECMOD
/** false to bypass the IPsec module */
int ipsecmod_enabled;
/** whitelisted domains for ipsecmod */
struct config_strlist* ipsecmod_whitelist;
/** path to external hook */
char* ipsecmod_hook;
/** true to proceed even with a bogus IPSECKEY */
int ipsecmod_ignore_bogus;
/** max TTL for the A/AAAA records that call the hook */
int ipsecmod_max_ttl;
/** false to proceed even when ipsecmod_hook fails */
int ipsecmod_strict;
#endif
/* cachedb module */
#ifdef USE_CACHEDB
/** backend DB name */
char* cachedb_backend;
/** secret seed for hash key calculation */
char* cachedb_secret;
+ /** cachedb that does not store, but only reads from database, if on */
+ int cachedb_no_store;
#ifdef USE_REDIS
/** redis server's IP address or host name */
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;
+ /** set the redis logical database upon connection */
+ int redis_logical_db;
#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
char* ipset_name_v4;
char* ipset_name_v6;
#endif
/** respond with Extended DNS Errors (RFC8914) */
int ede;
};
/** from cfg username, after daemonize setup performed */
extern uid_t cfg_uid;
/** from cfg username, after daemonize setup performed */
extern gid_t cfg_gid;
/** debug and enable small timeouts */
extern int autr_permit_small_holddown;
/** size (in bytes) of stream wait buffers max */
extern size_t stream_wait_max;
/** size (in bytes) of all total HTTP2 query buffers max */
extern size_t http2_query_buffer_max;
/** size (in bytes) of all total HTTP2 response buffers max */
extern size_t http2_response_buffer_max;
/**
* Stub config options
*/
struct config_stub {
/** next in list */
struct config_stub* next;
/** domain name (in text) of the stub apex domain */
char* name;
/** list of stub nameserver hosts (domain name) */
struct config_strlist* hosts;
/** list of stub nameserver addresses (IP address) */
struct config_strlist* addrs;
/** if stub-prime is set */
int isprime;
/** if forward-first is set (failover to without if fails) */
int isfirst;
/** use tcp for queries to this stub */
int tcp_upstream;
/** use SSL for queries to this stub */
int ssl_upstream;
/*** no cache */
int no_cache;
};
/**
* Auth config options
*/
struct config_auth {
/** next in list */
struct config_auth* next;
/** domain name (in text) of the auth apex domain */
char* name;
/** list of masters */
struct config_strlist* masters;
/** list of urls */
struct config_strlist* urls;
/** list of allow-notify */
struct config_strlist* allow_notify;
/** zonefile (or NULL) */
char* zonefile;
/** provide downstream answers */
int for_downstream;
/** provide upstream answers */
int for_upstream;
/** fallback to recursion to authorities if zone expired and other
* reasons perhaps (like, query bogus) */
int fallback_enabled;
/** this zone is used to create local-zone policies */
int isrpz;
/** rpz tags (or NULL) */
uint8_t* rpz_taglist;
/** length of the taglist (in bytes) */
size_t rpz_taglistlen;
/** Override RPZ action for this zone, regardless of zone content */
char* rpz_action_override;
/** Log when this RPZ policy is applied */
int rpz_log;
/** Display this name in the log when RPZ policy is applied */
char* rpz_log_name;
/** Always reply with this CNAME target if the cname override action is
* used */
char* rpz_cname;
/** signal nxdomain block with unset RA */
int rpz_signal_nxdomain_ra;
/** Check ZONEMD records for this zone */
int zonemd_check;
/** Reject absence of ZONEMD records, zone must have one */
int zonemd_reject_absence;
};
/**
* View config options
*/
struct config_view {
/** next in list */
struct config_view* next;
/** view name */
char* name;
/** local zones */
struct config_str2list* local_zones;
/** local data RRs */
struct config_strlist* local_data;
/** local zones nodefault list */
struct config_strlist* local_zones_nodefault;
#ifdef USE_IPSET
/** local zones ipset list */
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 */
int isfirst;
/** predefined actions for particular IP address responses */
struct config_str2list* respip_actions;
/** data complementing the 'redirect' response IP actions */
struct config_str2list* respip_data;
};
/**
* List of strings for config options
*/
struct config_strlist {
/** next item in list */
struct config_strlist* next;
/** config option string */
char* str;
};
/**
* List of two strings for config options
*/
struct config_str2list {
/** next item in list */
struct config_str2list* next;
/** first string */
char* str;
/** second string */
char* str2;
};
/**
* List of three strings for config options
*/
struct config_str3list {
/** next item in list */
struct config_str3list* next;
/** first string */
char* str;
/** second string */
char* str2;
/** third string */
char* str3;
};
/**
* List of string, bytestring for config options
*/
struct config_strbytelist {
/** next item in list */
struct config_strbytelist* next;
/** first string */
char* str;
/** second bytestring */
uint8_t* str2;
size_t str2len;
};
/**
* Create config file structure. Filled with default values.
* @return: the new structure or NULL on memory error.
*/
struct config_file* config_create(void);
/**
* Create config file structure for library use. Filled with default values.
* @return: the new structure or NULL on memory error.
*/
struct config_file* config_create_forlib(void);
/**
* Read the config file from the specified filename.
* @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
* file not found.
*/
int config_read(struct config_file* config, const char* filename,
const char* chroot);
/**
* Destroy the config file structure.
* @param config: to delete.
*/
void config_delete(struct config_file* config);
/**
* Apply config to global constants; this routine is called in single thread.
* @param config: to apply. Side effect: global constants change.
*/
void config_apply(struct config_file* config);
/**
* Find username, sets cfg_uid and cfg_gid.
* @param config: the config structure.
*/
void config_lookup_uid(struct config_file* config);
/**
* Set the given keyword to the given value.
* @param config: where to store config
* @param option: option name, including the ':' character.
* @param value: value, this string is copied if needed, or parsed.
* The caller owns the value string.
* @return 0 on error (malloc or syntax error).
*/
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 :.
* 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,
void (*func)(char*,void*), void* arg);
/**
* Get an option and return strlist
* @param cfg: config file
* @param opt: option name.
* @param list: list is returned here. malloced, caller must free it.
* @return 0=OK, 1=syntax error, 2=malloc failed.
*/
int config_get_option_list(struct config_file* cfg, const char* opt,
struct config_strlist** list);
/**
* Get an option and collate results into string
* @param cfg: config file
* @param opt: option name.
* @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,
char** str);
/**
* function to print to a file, use as func with config_get_option.
* @param line: text to print. \n appended.
* @param arg: pass a FILE*, like stdout.
*/
void config_print_func(char* line, void* arg);
/**
* function to collate the text strings into a strlist_head.
* @param line: text to append.
* @param arg: pass a strlist_head structure. zeroed on start.
*/
void config_collate_func(char* line, void* arg);
/**
* take a strlist_head list and return a malloc string. separated with newline.
* @param list: strlist first to collate. zeroes return "".
* @return NULL on malloc failure. Or if malloc failure happened in strlist.
*/
char* config_collate_cat(struct config_strlist* list);
/**
* Append text at end of list.
* @param list: list head. zeroed at start.
* @param item: new item. malloced by caller. if NULL the insertion fails.
* @return true on success.
* on fail the item is free()ed.
*/
int cfg_strlist_append(struct config_strlist_head* list, char* item);
/**
* Searches the end of a string list and appends the given text.
* @param head: pointer to strlist head variable.
* @param item: new item. malloced by caller. if NULL the insertion fails.
* @return true on success.
*/
int cfg_strlist_append_ex(struct config_strlist** head, char* item);
/**
* Find string in strlist.
* @param head: pointer to strlist head variable.
* @param item: the item to search for.
* @return: the element in the list when found, NULL otherwise.
*/
struct config_strlist* cfg_strlist_find(struct config_strlist* head,
const char* item);
/**
* Insert string into strlist.
* @param head: pointer to strlist head variable.
* @param item: new item. malloced by caller. If NULL the insertion fails.
* @return: true on success.
* on fail, the item is free()d.
*/
int cfg_strlist_insert(struct config_strlist** head, char* item);
/** insert with region for allocation. */
int cfg_region_strlist_insert(struct regional* region,
struct config_strlist** head, char* item);
/**
* Insert string into str2list.
* @param head: pointer to str2list head variable.
* @param item: new item. malloced by caller. If NULL the insertion fails.
* @param i2: 2nd string, malloced by caller. If NULL the insertion fails.
* @return: true on success.
* on fail, the item and i2 are free()d.
*/
int cfg_str2list_insert(struct config_str2list** head, char* item, char* i2);
/**
* Insert string into str3list.
* @param head: pointer to str3list head variable.
* @param item: new item. malloced by caller. If NULL the insertion fails.
* @param i2: 2nd string, malloced by caller. If NULL the insertion fails.
* @param i3: 3rd string, malloced by caller. If NULL the insertion fails.
* @return: true on success.
*/
int cfg_str3list_insert(struct config_str3list** head, char* item, char* i2,
char* i3);
/**
* Insert string into strbytelist.
* @param head: pointer to strbytelist head variable.
* @param item: new item. malloced by caller. If NULL the insertion fails.
* @param i2: 2nd string, malloced by caller. If NULL the insertion fails.
* @param i2len: length of the i2 bytestring.
* @return: true on success.
*/
int cfg_strbytelist_insert(struct config_strbytelist** head, char* item,
uint8_t* i2, size_t i2len);
/**
* Find stub in config list, also returns prevptr (for deletion).
* @param pp: call routine with pointer to a pointer to the start of the list,
* if the stub is found, on exit, the value contains a pointer to the
* next pointer that points to the found element (or to the list start
* pointer if it is the first element).
* @param nm: name of stub to find.
* @return: pointer to config_stub if found, or NULL if not found.
*/
struct config_stub* cfg_stub_find(struct config_stub*** pp, const char* nm);
/**
* Delete items in config string list.
* @param list: list.
*/
void config_delstrlist(struct config_strlist* list);
/**
* Delete items in config double string list.
* @param list: list.
*/
void config_deldblstrlist(struct config_str2list* list);
/**
* Delete items in config triple string list.
* @param list: list.
*/
void config_deltrplstrlist(struct config_str3list* list);
/** delete string array */
void config_del_strarray(char** array, int num);
/** delete stringbytelist */
void config_del_strbytelist(struct config_strbytelist* list);
/**
* Delete a stub item
* @param p: stub item
*/
void config_delstub(struct config_stub* p);
/**
* Delete items in config stub list.
* @param list: list.
*/
void config_delstubs(struct config_stub* list);
/**
* Delete an auth item
* @param p: auth item
*/
void config_delauth(struct config_auth* p);
/**
* Delete items in config auth list.
* @param list: list.
*/
void config_delauths(struct config_auth* list);
/**
* Delete a view item
* @param p: view item
*/
void config_delview(struct config_view* p);
/**
* Delete items in config view list.
* @param list: list.
*/
void config_delviews(struct config_view* list);
/** check if config for remote control turns on IP-address interface
* with certificates or a named pipe without certificates. */
int options_remote_is_address(struct config_file* cfg);
/**
* Convert 14digit to time value
* @param str: string of 14 digits
* @return time value or 0 for error.
*/
time_t cfg_convert_timeval(const char* str);
/**
* Count number of values in the string.
* format ::= (sp num)+ sp
* num ::= [-](0-9)+
* sp ::= (space|tab)*
*
* @param str: string
* @return: 0 on parse error, or empty string, else
* number of integer values in the string.
*/
int cfg_count_numbers(const char* str);
/**
* Convert a 'nice' memory or file size into a bytecount
* From '100k' to 102400. and so on. Understands kKmMgG.
* 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
* is logged).
*/
int cfg_parse_memsize(const char* str, size_t* res);
/**
* Parse nsid from string into binary nsid. nsid is either a hexadecimal
* string or an ascii string prepended with ascii_ in which case the
* characters after ascii_ are simply copied.
* @param str: the string to parse.
* @param nsid_len: returns length of nsid in bytes.
* @return malloced bytes or NULL on parse error or malloc failure.
*/
uint8_t* cfg_parse_nsid(const char* str, uint16_t* nsid_len);
/**
* Add a tag name to the config. It is added at the end with a new ID value.
* @param cfg: the config structure.
* @param tag: string (which is copied) with the name.
* @return: false on alloc failure.
*/
int config_add_tag(struct config_file* cfg, const char* tag);
/**
* Find tag ID in the tag list.
* @param cfg: the config structure.
* @param tag: string with tag name to search for.
* @return: 0..(num_tags-1) with tag ID, or -1 if tagname is not found.
*/
int find_tag_id(struct config_file* cfg, const char* tag);
/**
* 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 listlen: returns length of in bytes.
* @return malloced bytes with a bitlist of the tags. or NULL on parse error
* or malloc failure.
*/
uint8_t* config_parse_taglist(struct config_file* cfg, char* str,
size_t* listlen);
/**
* convert tag bitlist to a malloced string with tag names. For debug output.
* @param cfg: the config structure (with tagnames)
* @param taglist: the tag bitlist.
* @param len: length of the tag bitlist.
* @return malloced string or NULL.
*/
char* config_taglist2str(struct config_file* cfg, uint8_t* taglist,
size_t len);
/**
* see if two taglists intersect (have tags in common).
* @param list1: first tag bitlist.
* @param list1len: length in bytes of first list.
* @param list2: second tag bitlist.
* @param list2len: length in bytes of second list.
* @return true if there are tags in common, 0 if not.
*/
int taglist_intersect(uint8_t* list1, size_t list1len, const uint8_t* list2,
size_t list2len);
/**
* Parse local-zone directive into two strings and register it in the config.
* @param cfg: to put it in.
* @param val: argument strings to local-zone, "example.com nodefault".
* @return: false on failure
*/
int cfg_parse_local_zone(struct config_file* cfg, const char* val);
/**
* Mark "number" or "low-high" as available or not in ports array.
* @param str: string in input
* @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
* is logged).
*/
int cfg_mark_ports(const char* str, int allow, int* avail, int num);
/**
* Get a condensed list of ports returned. allocated.
* @param cfg: config file.
* @param avail: the available ports array is returned here.
* @return: number of ports in array or 0 on error.
*/
int cfg_condense_ports(struct config_file* cfg, int** avail);
/**
* Apply system specific port range policy.
* @param cfg: config file.
* @param num: size of the array (65536).
*/
void cfg_apply_local_port_policy(struct config_file* cfg, int num);
/**
* Scan ports available
* @param avail: the array from cfg.
* @param num: size of the array (65536).
* @return the number of ports available for use.
*/
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.
* @param cfg: config struct for chroot and chdir (if set).
* @param use_chdir: if false, only chroot is applied.
* @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,
int use_chdir);
/**
* Convert a ptr shorthand into a full reverse-notation PTR record.
* @param str: input string, "IP name"
* @return: malloced string "reversed-ip-name PTR name"
*/
char* cfg_ptr_reverse(char* str);
/**
* Used during options parsing
*/
struct config_parser_state {
/** name of file being parser */
char* filename;
/** line number in the file, starts at 1 */
int line;
/** number of errors encountered */
int errors;
/** the result of parsing is stored here. */
struct config_file* cfg;
/** the current chroot dir (or NULL if none) */
const char* chroot;
/** if we are started in a toplevel, or not, after a force_toplevel */
int started_toplevel;
};
/** global config parser object used during config parsing */
extern struct config_parser_state* cfg_parser;
/** init lex state */
void init_cfg_parse(void);
/** lex in file */
extern FILE* ub_c_in;
/** lex out file */
extern FILE* ub_c_out;
/** the yacc lex generated parse function */
int ub_c_parse(void);
/** the lexer function */
int ub_c_lex(void);
/** wrap function */
int ub_c_wrap(void);
/** parsing helpers: print error with file and line numbers */
void ub_c_error(const char* msg);
/** parsing helpers: print error with file and line numbers */
void ub_c_error_msg(const char* fmt, ...) ATTR_FORMAT(printf, 1, 2);
#ifdef UB_ON_WINDOWS
/**
* Obtain registry string (if it exists).
* @param key: key string
* @param name: name of value to fetch.
* @return malloced string with the result or NULL if it did not
* exist on an error (logged with log_err) was encountered.
*/
char* w_lookup_reg_str(const char* key, const char* name);
/** Modify directory in options for module file name */
void w_config_adjust_directory(struct config_file* cfg);
#endif /* UB_ON_WINDOWS */
/** debug option for unit tests. */
extern int fake_dsa, fake_sha1;
/** see if interface is https, its port number == the https port number */
int if_is_https(const char* ifname, const char* port, int https_port);
/**
* Return true if the config contains settings that enable https.
* @param cfg: config information.
* @return true if https ports are used for server.
*/
int cfg_has_https(struct config_file* cfg);
/** see if interface is PROXYv2, its port number == the proxy port number */
int if_is_pp2(const char* ifname, const char* port,
struct config_strlist* proxy_protocol_port);
/** see if interface is DNSCRYPT, its port number == the dnscrypt port number */
int if_is_dnscrypt(const char* ifname, const char* port, int dnscrypt_port);
#ifdef USE_LINUX_IP_LOCAL_PORT_RANGE
#define LINUX_IP_LOCAL_PORT_RANGE_PATH "/proc/sys/net/ipv4/ip_local_port_range"
#endif
#endif /* UTIL_CONFIG_FILE_H */
diff --git a/contrib/unbound/util/configlexer.lex b/contrib/unbound/util/configlexer.lex
index 5b0775ad1980..2265d51b64ef 100644
--- a/contrib/unbound/util/configlexer.lex
+++ b/contrib/unbound/util/configlexer.lex
@@ -1,711 +1,714 @@
%{
/*
* configlexer.lex - lexical analyzer for unbound config file
*
* Copyright (c) 2001-2006, NLnet Labs. All rights reserved
*
* See LICENSE for the license.
*
*/
#include "config.h"
/* because flex keeps having sign-unsigned compare problems that are unfixed*/
#if defined(__clang__)||(defined(__GNUC__)&&((__GNUC__ >4)||(defined(__GNUC_MINOR__)&&(__GNUC__ ==4)&&(__GNUC_MINOR__ >=2))))
#pragma GCC diagnostic ignored "-Wsign-compare"
#endif
#include <ctype.h>
#include <strings.h>
#ifdef HAVE_GLOB_H
# include <glob.h>
#endif
#include "util/config_file.h"
#include "configparser.h"
void ub_c_error(const char *message);
#if 0
#define LEXOUT(s) printf s /* used ONLY when debugging */
#else
#define LEXOUT(s)
#endif
/** avoid warning in about fwrite return value */
#define ECHO ub_c_error_msg("syntax error at text: %s", ub_c_text)
/** A parser variable, this is a statement in the config file which is
* of the form variable: value1 value2 ... nargs is the number of values. */
#define YDVAR(nargs, var) \
num_args=(nargs); \
LEXOUT(("v(%s%d) ", ub_c_text, num_args)); \
if(num_args > 0) { BEGIN(val); } \
return (var);
struct inc_state {
char* filename;
int line;
YY_BUFFER_STATE buffer;
struct inc_state* next;
int inc_toplevel;
};
static struct inc_state* config_include_stack = NULL;
static int inc_depth = 0;
static int inc_prev = 0;
static int num_args = 0;
static int inc_toplevel = 0;
void init_cfg_parse(void)
{
config_include_stack = NULL;
inc_depth = 0;
inc_prev = 0;
num_args = 0;
inc_toplevel = 0;
}
static void config_start_include(const char* filename, int toplevel)
{
FILE *input;
struct inc_state* s;
char* nm;
if(inc_depth+1 > 100000) {
ub_c_error_msg("too many include files");
return;
}
if(*filename == '\0') {
ub_c_error_msg("empty include file name");
return;
}
s = (struct inc_state*)malloc(sizeof(*s));
if(!s) {
ub_c_error_msg("include %s: malloc failure", filename);
return;
}
if(cfg_parser->chroot && strncmp(filename, cfg_parser->chroot,
strlen(cfg_parser->chroot)) == 0) {
filename += strlen(cfg_parser->chroot);
}
nm = strdup(filename);
if(!nm) {
ub_c_error_msg("include %s: strdup failure", filename);
free(s);
return;
}
input = fopen(filename, "r");
if(!input) {
ub_c_error_msg("cannot open include file '%s': %s",
filename, strerror(errno));
free(s);
free(nm);
return;
}
LEXOUT(("switch_to_include_file(%s)\n", filename));
inc_depth++;
s->filename = cfg_parser->filename;
s->line = cfg_parser->line;
s->buffer = YY_CURRENT_BUFFER;
s->inc_toplevel = inc_toplevel;
s->next = config_include_stack;
config_include_stack = s;
cfg_parser->filename = nm;
cfg_parser->line = 1;
inc_toplevel = toplevel;
yy_switch_to_buffer(yy_create_buffer(input, YY_BUF_SIZE));
}
static void config_start_include_glob(const char* filename, int toplevel)
{
/* check for wildcards */
#ifdef HAVE_GLOB
glob_t g;
int i, r, flags;
if(!(!strchr(filename, '*') && !strchr(filename, '?') && !strchr(filename, '[') &&
!strchr(filename, '{') && !strchr(filename, '~'))) {
flags = 0
#ifdef GLOB_ERR
| GLOB_ERR
#endif
/* do not set GLOB_NOSORT so the results are sorted
and in a predictable order. */
#ifdef GLOB_BRACE
| GLOB_BRACE
#endif
#ifdef GLOB_TILDE
| GLOB_TILDE
#endif
;
memset(&g, 0, sizeof(g));
if(cfg_parser->chroot && strncmp(filename, cfg_parser->chroot,
strlen(cfg_parser->chroot)) == 0) {
filename += strlen(cfg_parser->chroot);
}
r = glob(filename, flags, NULL, &g);
if(r) {
/* some error */
globfree(&g);
if(r == GLOB_NOMATCH)
return; /* no matches for pattern */
config_start_include(filename, toplevel); /* let original deal with it */
return;
}
/* process files found, if any */
for(i=(int)g.gl_pathc-1; i>=0; i--) {
config_start_include(g.gl_pathv[i], toplevel);
}
globfree(&g);
return;
}
#endif /* HAVE_GLOB */
config_start_include(filename, toplevel);
}
static void config_end_include(void)
{
struct inc_state* s = config_include_stack;
--inc_depth;
if(!s) return;
free(cfg_parser->filename);
cfg_parser->filename = s->filename;
cfg_parser->line = s->line;
yy_delete_buffer(YY_CURRENT_BUFFER);
yy_switch_to_buffer(s->buffer);
config_include_stack = s->next;
inc_toplevel = s->inc_toplevel;
free(s);
}
#ifndef yy_set_bol /* compat definition, for flex 2.4.6 */
#define yy_set_bol(at_bol) \
{ \
if ( ! yy_current_buffer ) \
yy_current_buffer = yy_create_buffer( ub_c_in, YY_BUF_SIZE ); \
yy_current_buffer->yy_ch_buf[0] = ((at_bol)?'\n':' '); \
}
#endif
%}
%option noinput
%option nounput
%{
#ifndef YY_NO_UNPUT
#define YY_NO_UNPUT 1
#endif
#ifndef YY_NO_INPUT
#define YY_NO_INPUT 1
#endif
%}
SPACE [ \t]
LETTER [a-zA-Z]
UNQUOTEDLETTER [^\'\"\n\r \t\\]|\\.
UNQUOTEDLETTER_NOCOLON [^\:\'\"\n\r \t\\]|\\.
NEWLINE [\r\n]
COMMENT \#
COLON \:
DQANY [^\"\n\r\\]|\\.
SQANY [^\'\n\r\\]|\\.
%x quotedstring singlequotedstr include include_quoted val include_toplevel include_toplevel_quoted
%%
<INITIAL,val>{SPACE}* {
LEXOUT(("SP ")); /* ignore */ }
<INITIAL,val>{SPACE}*{COMMENT}.* {
/* note that flex makes the longest match and '.' is any but not nl */
LEXOUT(("comment(%s) ", ub_c_text)); /* ignore */ }
server{COLON} { YDVAR(0, VAR_SERVER) }
qname-minimisation{COLON} { YDVAR(1, VAR_QNAME_MINIMISATION) }
qname-minimisation-strict{COLON} { YDVAR(1, VAR_QNAME_MINIMISATION_STRICT) }
num-threads{COLON} { YDVAR(1, VAR_NUM_THREADS) }
verbosity{COLON} { YDVAR(1, VAR_VERBOSITY) }
port{COLON} { YDVAR(1, VAR_PORT) }
outgoing-range{COLON} { YDVAR(1, VAR_OUTGOING_RANGE) }
outgoing-port-permit{COLON} { YDVAR(1, VAR_OUTGOING_PORT_PERMIT) }
outgoing-port-avoid{COLON} { YDVAR(1, VAR_OUTGOING_PORT_AVOID) }
outgoing-num-tcp{COLON} { YDVAR(1, VAR_OUTGOING_NUM_TCP) }
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) }
do-tcp{COLON} { YDVAR(1, VAR_DO_TCP) }
tcp-upstream{COLON} { YDVAR(1, VAR_TCP_UPSTREAM) }
tcp-mss{COLON} { YDVAR(1, VAR_TCP_MSS) }
outgoing-tcp-mss{COLON} { YDVAR(1, VAR_OUTGOING_TCP_MSS) }
tcp-idle-timeout{COLON} { YDVAR(1, VAR_TCP_IDLE_TIMEOUT) }
max-reuse-tcp-queries{COLON} { YDVAR(1, VAR_MAX_REUSE_TCP_QUERIES) }
tcp-reuse-timeout{COLON} { YDVAR(1, VAR_TCP_REUSE_TIMEOUT) }
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) }
tls-service-key{COLON} { YDVAR(1, VAR_SSL_SERVICE_KEY) }
ssl-service-pem{COLON} { YDVAR(1, VAR_SSL_SERVICE_PEM) }
tls-service-pem{COLON} { YDVAR(1, VAR_SSL_SERVICE_PEM) }
ssl-port{COLON} { YDVAR(1, VAR_SSL_PORT) }
tls-port{COLON} { YDVAR(1, VAR_SSL_PORT) }
ssl-cert-bundle{COLON} { YDVAR(1, VAR_TLS_CERT_BUNDLE) }
tls-cert-bundle{COLON} { YDVAR(1, VAR_TLS_CERT_BUNDLE) }
tls-win-cert{COLON} { YDVAR(1, VAR_TLS_WIN_CERT) }
tls-system-cert{COLON} { YDVAR(1, VAR_TLS_WIN_CERT) }
additional-ssl-port{COLON} { YDVAR(1, VAR_TLS_ADDITIONAL_PORT) }
additional-tls-port{COLON} { YDVAR(1, VAR_TLS_ADDITIONAL_PORT) }
tls-additional-ports{COLON} { YDVAR(1, VAR_TLS_ADDITIONAL_PORT) }
tls-additional-port{COLON} { YDVAR(1, VAR_TLS_ADDITIONAL_PORT) }
tls-session-ticket-keys{COLON} { YDVAR(1, VAR_TLS_SESSION_TICKET_KEYS) }
tls-ciphers{COLON} { YDVAR(1, VAR_TLS_CIPHERS) }
tls-ciphersuites{COLON} { YDVAR(1, VAR_TLS_CIPHERSUITES) }
tls-use-sni{COLON} { YDVAR(1, VAR_TLS_USE_SNI) }
https-port{COLON} { YDVAR(1, VAR_HTTPS_PORT) }
http-endpoint{COLON} { YDVAR(1, VAR_HTTP_ENDPOINT) }
http-max-streams{COLON} { YDVAR(1, VAR_HTTP_MAX_STREAMS) }
http-query-buffer-size{COLON} { YDVAR(1, VAR_HTTP_QUERY_BUFFER_SIZE) }
http-response-buffer-size{COLON} { YDVAR(1, VAR_HTTP_RESPONSE_BUFFER_SIZE) }
http-nodelay{COLON} { YDVAR(1, VAR_HTTP_NODELAY) }
http-notls-downstream{COLON} { YDVAR(1, VAR_HTTP_NOTLS_DOWNSTREAM) }
use-systemd{COLON} { YDVAR(1, VAR_USE_SYSTEMD) }
do-daemonize{COLON} { YDVAR(1, VAR_DO_DAEMONIZE) }
interface{COLON} { YDVAR(1, VAR_INTERFACE) }
ip-address{COLON} { YDVAR(1, VAR_INTERFACE) }
outgoing-interface{COLON} { YDVAR(1, VAR_OUTGOING_INTERFACE) }
interface-automatic{COLON} { YDVAR(1, VAR_INTERFACE_AUTOMATIC) }
interface-automatic-ports{COLON} { YDVAR(1, VAR_INTERFACE_AUTOMATIC_PORTS) }
so-rcvbuf{COLON} { YDVAR(1, VAR_SO_RCVBUF) }
so-sndbuf{COLON} { YDVAR(1, VAR_SO_SNDBUF) }
so-reuseport{COLON} { YDVAR(1, VAR_SO_REUSEPORT) }
ip-transparent{COLON} { YDVAR(1, VAR_IP_TRANSPARENT) }
ip-freebind{COLON} { YDVAR(1, VAR_IP_FREEBIND) }
ip-dscp{COLON} { YDVAR(1, VAR_IP_DSCP) }
chroot{COLON} { YDVAR(1, VAR_CHROOT) }
username{COLON} { YDVAR(1, VAR_USERNAME) }
directory{COLON} { YDVAR(1, VAR_DIRECTORY) }
logfile{COLON} { YDVAR(1, VAR_LOGFILE) }
pidfile{COLON} { YDVAR(1, VAR_PIDFILE) }
root-hints{COLON} { YDVAR(1, VAR_ROOT_HINTS) }
stream-wait-size{COLON} { YDVAR(1, VAR_STREAM_WAIT_SIZE) }
edns-buffer-size{COLON} { YDVAR(1, VAR_EDNS_BUFFER_SIZE) }
msg-buffer-size{COLON} { YDVAR(1, VAR_MSG_BUFFER_SIZE) }
msg-cache-size{COLON} { YDVAR(1, VAR_MSG_CACHE_SIZE) }
msg-cache-slabs{COLON} { YDVAR(1, VAR_MSG_CACHE_SLABS) }
rrset-cache-size{COLON} { YDVAR(1, VAR_RRSET_CACHE_SIZE) }
rrset-cache-slabs{COLON} { YDVAR(1, VAR_RRSET_CACHE_SLABS) }
cache-max-ttl{COLON} { YDVAR(1, VAR_CACHE_MAX_TTL) }
cache-max-negative-ttl{COLON} { YDVAR(1, VAR_CACHE_MAX_NEGATIVE_TTL) }
cache-min-ttl{COLON} { YDVAR(1, VAR_CACHE_MIN_TTL) }
infra-host-ttl{COLON} { YDVAR(1, VAR_INFRA_HOST_TTL) }
infra-lame-ttl{COLON} { YDVAR(1, VAR_INFRA_LAME_TTL) }
infra-cache-slabs{COLON} { YDVAR(1, VAR_INFRA_CACHE_SLABS) }
infra-cache-numhosts{COLON} { YDVAR(1, VAR_INFRA_CACHE_NUMHOSTS) }
infra-cache-lame-size{COLON} { YDVAR(1, VAR_INFRA_CACHE_LAME_SIZE) }
infra-cache-min-rtt{COLON} { YDVAR(1, VAR_INFRA_CACHE_MIN_RTT) }
infra-cache-max-rtt{COLON} { YDVAR(1, VAR_INFRA_CACHE_MAX_RTT) }
infra-keep-probing{COLON} { YDVAR(1, VAR_INFRA_KEEP_PROBING) }
num-queries-per-thread{COLON} { YDVAR(1, VAR_NUM_QUERIES_PER_THREAD) }
jostle-timeout{COLON} { YDVAR(1, VAR_JOSTLE_TIMEOUT) }
delay-close{COLON} { YDVAR(1, VAR_DELAY_CLOSE) }
udp-connect{COLON} { YDVAR(1, VAR_UDP_CONNECT) }
target-fetch-policy{COLON} { YDVAR(1, VAR_TARGET_FETCH_POLICY) }
harden-short-bufsize{COLON} { YDVAR(1, VAR_HARDEN_SHORT_BUFSIZE) }
harden-large-queries{COLON} { YDVAR(1, VAR_HARDEN_LARGE_QUERIES) }
harden-glue{COLON} { YDVAR(1, VAR_HARDEN_GLUE) }
harden-dnssec-stripped{COLON} { YDVAR(1, VAR_HARDEN_DNSSEC_STRIPPED) }
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) }
unwanted-reply-threshold{COLON} { YDVAR(1, VAR_UNWANTED_REPLY_THRESHOLD) }
private-address{COLON} { YDVAR(1, VAR_PRIVATE_ADDRESS) }
private-domain{COLON} { YDVAR(1, VAR_PRIVATE_DOMAIN) }
prefetch-key{COLON} { YDVAR(1, VAR_PREFETCH_KEY) }
prefetch{COLON} { YDVAR(1, VAR_PREFETCH) }
deny-any{COLON} { YDVAR(1, VAR_DENY_ANY) }
stub-zone{COLON} { YDVAR(0, VAR_STUB_ZONE) }
name{COLON} { YDVAR(1, VAR_NAME) }
stub-addr{COLON} { YDVAR(1, VAR_STUB_ADDR) }
stub-host{COLON} { YDVAR(1, VAR_STUB_HOST) }
stub-prime{COLON} { YDVAR(1, VAR_STUB_PRIME) }
stub-first{COLON} { YDVAR(1, VAR_STUB_FIRST) }
stub-no-cache{COLON} { YDVAR(1, VAR_STUB_NO_CACHE) }
stub-ssl-upstream{COLON} { YDVAR(1, VAR_STUB_SSL_UPSTREAM) }
stub-tls-upstream{COLON} { YDVAR(1, VAR_STUB_SSL_UPSTREAM) }
stub-tcp-upstream{COLON} { YDVAR(1, VAR_STUB_TCP_UPSTREAM) }
forward-zone{COLON} { YDVAR(0, VAR_FORWARD_ZONE) }
forward-addr{COLON} { YDVAR(1, VAR_FORWARD_ADDR) }
forward-host{COLON} { YDVAR(1, VAR_FORWARD_HOST) }
forward-first{COLON} { YDVAR(1, VAR_FORWARD_FIRST) }
forward-no-cache{COLON} { YDVAR(1, VAR_FORWARD_NO_CACHE) }
forward-ssl-upstream{COLON} { YDVAR(1, VAR_FORWARD_SSL_UPSTREAM) }
forward-tls-upstream{COLON} { YDVAR(1, VAR_FORWARD_SSL_UPSTREAM) }
forward-tcp-upstream{COLON} { YDVAR(1, VAR_FORWARD_TCP_UPSTREAM) }
auth-zone{COLON} { YDVAR(0, VAR_AUTH_ZONE) }
rpz{COLON} { YDVAR(0, VAR_RPZ) }
tags{COLON} { YDVAR(1, VAR_TAGS) }
rpz-action-override{COLON} { YDVAR(1, VAR_RPZ_ACTION_OVERRIDE) }
rpz-cname-override{COLON} { YDVAR(1, VAR_RPZ_CNAME_OVERRIDE) }
rpz-log{COLON} { YDVAR(1, VAR_RPZ_LOG) }
rpz-log-name{COLON} { YDVAR(1, VAR_RPZ_LOG_NAME) }
rpz-signal-nxdomain-ra{COLON} { YDVAR(1, VAR_RPZ_SIGNAL_NXDOMAIN_RA) }
zonefile{COLON} { YDVAR(1, VAR_ZONEFILE) }
master{COLON} { YDVAR(1, VAR_MASTER) }
primary{COLON} { YDVAR(1, VAR_MASTER) }
url{COLON} { YDVAR(1, VAR_URL) }
allow-notify{COLON} { YDVAR(1, VAR_ALLOW_NOTIFY) }
for-downstream{COLON} { YDVAR(1, VAR_FOR_DOWNSTREAM) }
for-upstream{COLON} { YDVAR(1, VAR_FOR_UPSTREAM) }
fallback-enabled{COLON} { YDVAR(1, VAR_FALLBACK_ENABLED) }
view{COLON} { YDVAR(0, VAR_VIEW) }
view-first{COLON} { YDVAR(1, VAR_VIEW_FIRST) }
do-not-query-address{COLON} { YDVAR(1, VAR_DO_NOT_QUERY_ADDRESS) }
do-not-query-localhost{COLON} { YDVAR(1, VAR_DO_NOT_QUERY_LOCALHOST) }
access-control{COLON} { YDVAR(2, VAR_ACCESS_CONTROL) }
interface-action{COLON} { YDVAR(2, VAR_INTERFACE_ACTION) }
send-client-subnet{COLON} { YDVAR(1, VAR_SEND_CLIENT_SUBNET) }
client-subnet-zone{COLON} { YDVAR(1, VAR_CLIENT_SUBNET_ZONE) }
client-subnet-always-forward{COLON} { YDVAR(1, VAR_CLIENT_SUBNET_ALWAYS_FORWARD) }
client-subnet-opcode{COLON} { YDVAR(1, VAR_CLIENT_SUBNET_OPCODE) }
max-client-subnet-ipv4{COLON} { YDVAR(1, VAR_MAX_CLIENT_SUBNET_IPV4) }
max-client-subnet-ipv6{COLON} { YDVAR(1, VAR_MAX_CLIENT_SUBNET_IPV6) }
min-client-subnet-ipv4{COLON} { YDVAR(1, VAR_MIN_CLIENT_SUBNET_IPV4) }
min-client-subnet-ipv6{COLON} { YDVAR(1, VAR_MIN_CLIENT_SUBNET_IPV6) }
max-ecs-tree-size-ipv4{COLON} { YDVAR(1, VAR_MAX_ECS_TREE_SIZE_IPV4) }
max-ecs-tree-size-ipv6{COLON} { YDVAR(1, VAR_MAX_ECS_TREE_SIZE_IPV6) }
hide-identity{COLON} { YDVAR(1, VAR_HIDE_IDENTITY) }
hide-version{COLON} { YDVAR(1, VAR_HIDE_VERSION) }
hide-trustanchor{COLON} { YDVAR(1, VAR_HIDE_TRUSTANCHOR) }
hide-http-user-agent{COLON} { YDVAR(1, VAR_HIDE_HTTP_USER_AGENT) }
identity{COLON} { YDVAR(1, VAR_IDENTITY) }
version{COLON} { YDVAR(1, VAR_VERSION) }
http-user-agent{COLON} { YDVAR(1, VAR_HTTP_USER_AGENT) }
module-config{COLON} { YDVAR(1, VAR_MODULE_CONF) }
dlv-anchor{COLON} { YDVAR(1, VAR_DLV_ANCHOR) }
dlv-anchor-file{COLON} { YDVAR(1, VAR_DLV_ANCHOR_FILE) }
trust-anchor-file{COLON} { YDVAR(1, VAR_TRUST_ANCHOR_FILE) }
auto-trust-anchor-file{COLON} { YDVAR(1, VAR_AUTO_TRUST_ANCHOR_FILE) }
trusted-keys-file{COLON} { YDVAR(1, VAR_TRUSTED_KEYS_FILE) }
trust-anchor{COLON} { YDVAR(1, VAR_TRUST_ANCHOR) }
trust-anchor-signaling{COLON} { YDVAR(1, VAR_TRUST_ANCHOR_SIGNALING) }
root-key-sentinel{COLON} { YDVAR(1, VAR_ROOT_KEY_SENTINEL) }
val-override-date{COLON} { YDVAR(1, VAR_VAL_OVERRIDE_DATE) }
val-sig-skew-min{COLON} { YDVAR(1, VAR_VAL_SIG_SKEW_MIN) }
val-sig-skew-max{COLON} { YDVAR(1, VAR_VAL_SIG_SKEW_MAX) }
val-max-restart{COLON} { YDVAR(1, VAR_VAL_MAX_RESTART) }
val-bogus-ttl{COLON} { YDVAR(1, VAR_BOGUS_TTL) }
val-clean-additional{COLON} { YDVAR(1, VAR_VAL_CLEAN_ADDITIONAL) }
val-permissive-mode{COLON} { YDVAR(1, VAR_VAL_PERMISSIVE_MODE) }
aggressive-nsec{COLON} { YDVAR(1, VAR_AGGRESSIVE_NSEC) }
ignore-cd-flag{COLON} { YDVAR(1, VAR_IGNORE_CD_FLAG) }
+disable-edns-do{COLON} { YDVAR(1, VAR_DISABLE_EDNS_DO) }
serve-expired{COLON} { YDVAR(1, VAR_SERVE_EXPIRED) }
serve-expired-ttl{COLON} { YDVAR(1, VAR_SERVE_EXPIRED_TTL) }
serve-expired-ttl-reset{COLON} { YDVAR(1, VAR_SERVE_EXPIRED_TTL_RESET) }
serve-expired-reply-ttl{COLON} { YDVAR(1, VAR_SERVE_EXPIRED_REPLY_TTL) }
serve-expired-client-timeout{COLON} { YDVAR(1, VAR_SERVE_EXPIRED_CLIENT_TIMEOUT) }
ede-serve-expired{COLON} { YDVAR(1, VAR_EDE_SERVE_EXPIRED) }
serve-original-ttl{COLON} { YDVAR(1, VAR_SERVE_ORIGINAL_TTL) }
fake-dsa{COLON} { YDVAR(1, VAR_FAKE_DSA) }
fake-sha1{COLON} { YDVAR(1, VAR_FAKE_SHA1) }
val-log-level{COLON} { YDVAR(1, VAR_VAL_LOG_LEVEL) }
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} {
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) }
zonemd-reject-absence{COLON} { YDVAR(1, VAR_ZONEMD_REJECT_ABSENCE) }
add-holddown{COLON} { YDVAR(1, VAR_ADD_HOLDDOWN) }
del-holddown{COLON} { YDVAR(1, VAR_DEL_HOLDDOWN) }
keep-missing{COLON} { YDVAR(1, VAR_KEEP_MISSING) }
permit-small-holddown{COLON} { YDVAR(1, VAR_PERMIT_SMALL_HOLDDOWN) }
use-syslog{COLON} { YDVAR(1, VAR_USE_SYSLOG) }
log-identity{COLON} { YDVAR(1, VAR_LOG_IDENTITY) }
log-time-ascii{COLON} { YDVAR(1, VAR_LOG_TIME_ASCII) }
log-queries{COLON} { YDVAR(1, VAR_LOG_QUERIES) }
log-replies{COLON} { YDVAR(1, VAR_LOG_REPLIES) }
log-tag-queryreply{COLON} { YDVAR(1, VAR_LOG_TAG_QUERYREPLY) }
log-local-actions{COLON} { YDVAR(1, VAR_LOG_LOCAL_ACTIONS) }
log-servfail{COLON} { YDVAR(1, VAR_LOG_SERVFAIL) }
local-zone{COLON} { YDVAR(2, VAR_LOCAL_ZONE) }
local-data{COLON} { YDVAR(1, VAR_LOCAL_DATA) }
local-data-ptr{COLON} { YDVAR(1, VAR_LOCAL_DATA_PTR) }
unblock-lan-zones{COLON} { YDVAR(1, VAR_UNBLOCK_LAN_ZONES) }
insecure-lan-zones{COLON} { YDVAR(1, VAR_INSECURE_LAN_ZONES) }
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) }
control-enable{COLON} { YDVAR(1, VAR_CONTROL_ENABLE) }
control-interface{COLON} { YDVAR(1, VAR_CONTROL_INTERFACE) }
control-port{COLON} { YDVAR(1, VAR_CONTROL_PORT) }
control-use-cert{COLON} { YDVAR(1, VAR_CONTROL_USE_CERT) }
server-key-file{COLON} { YDVAR(1, VAR_SERVER_KEY_FILE) }
server-cert-file{COLON} { YDVAR(1, VAR_SERVER_CERT_FILE) }
control-key-file{COLON} { YDVAR(1, VAR_CONTROL_KEY_FILE) }
control-cert-file{COLON} { YDVAR(1, VAR_CONTROL_CERT_FILE) }
python-script{COLON} { YDVAR(1, VAR_PYTHON_SCRIPT) }
python{COLON} { YDVAR(0, VAR_PYTHON) }
dynlib-file{COLON} { YDVAR(1, VAR_DYNLIB_FILE) }
dynlib{COLON} { YDVAR(0, VAR_DYNLIB) }
domain-insecure{COLON} { YDVAR(1, VAR_DOMAIN_INSECURE) }
minimal-responses{COLON} { YDVAR(1, VAR_MINIMAL_RESPONSES) }
rrset-roundrobin{COLON} { YDVAR(1, VAR_RRSET_ROUNDROBIN) }
unknown-server-time-limit{COLON} { YDVAR(1, VAR_UNKNOWN_SERVER_TIME_LIMIT) }
max-udp-size{COLON} { YDVAR(1, VAR_MAX_UDP_SIZE) }
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) }
access-control-tag-action{COLON} { YDVAR(3, VAR_ACCESS_CONTROL_TAG_ACTION) }
access-control-tag-data{COLON} { YDVAR(3, VAR_ACCESS_CONTROL_TAG_DATA) }
access-control-view{COLON} { YDVAR(2, VAR_ACCESS_CONTROL_VIEW) }
interface-tag{COLON} { YDVAR(2, VAR_INTERFACE_TAG) }
interface-tag-action{COLON} { YDVAR(3, VAR_INTERFACE_TAG_ACTION) }
interface-tag-data{COLON} { YDVAR(3, VAR_INTERFACE_TAG_DATA) }
interface-view{COLON} { YDVAR(2, VAR_INTERFACE_VIEW) }
local-zone-override{COLON} { YDVAR(3, VAR_LOCAL_ZONE_OVERRIDE) }
dnstap{COLON} { YDVAR(0, VAR_DNSTAP) }
dnstap-enable{COLON} { YDVAR(1, VAR_DNSTAP_ENABLE) }
dnstap-bidirectional{COLON} { YDVAR(1, VAR_DNSTAP_BIDIRECTIONAL) }
dnstap-socket-path{COLON} { YDVAR(1, VAR_DNSTAP_SOCKET_PATH) }
dnstap-ip{COLON} { YDVAR(1, VAR_DNSTAP_IP) }
dnstap-tls{COLON} { YDVAR(1, VAR_DNSTAP_TLS) }
dnstap-tls-server-name{COLON} { YDVAR(1, VAR_DNSTAP_TLS_SERVER_NAME) }
dnstap-tls-cert-bundle{COLON} { YDVAR(1, VAR_DNSTAP_TLS_CERT_BUNDLE) }
dnstap-tls-client-key-file{COLON} {
YDVAR(1, VAR_DNSTAP_TLS_CLIENT_KEY_FILE) }
dnstap-tls-client-cert-file{COLON} {
YDVAR(1, VAR_DNSTAP_TLS_CLIENT_CERT_FILE) }
dnstap-send-identity{COLON} { YDVAR(1, VAR_DNSTAP_SEND_IDENTITY) }
dnstap-send-version{COLON} { YDVAR(1, VAR_DNSTAP_SEND_VERSION) }
dnstap-identity{COLON} { YDVAR(1, VAR_DNSTAP_IDENTITY) }
dnstap-version{COLON} { YDVAR(1, VAR_DNSTAP_VERSION) }
dnstap-log-resolver-query-messages{COLON} {
YDVAR(1, VAR_DNSTAP_LOG_RESOLVER_QUERY_MESSAGES) }
dnstap-log-resolver-response-messages{COLON} {
YDVAR(1, VAR_DNSTAP_LOG_RESOLVER_RESPONSE_MESSAGES) }
dnstap-log-client-query-messages{COLON} {
YDVAR(1, VAR_DNSTAP_LOG_CLIENT_QUERY_MESSAGES) }
dnstap-log-client-response-messages{COLON} {
YDVAR(1, VAR_DNSTAP_LOG_CLIENT_RESPONSE_MESSAGES) }
dnstap-log-forwarder-query-messages{COLON} {
YDVAR(1, VAR_DNSTAP_LOG_FORWARDER_QUERY_MESSAGES) }
dnstap-log-forwarder-response-messages{COLON} {
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-size{COLON} { YDVAR(1, VAR_IP_RATELIMIT_SIZE) }
ratelimit-size{COLON} { YDVAR(1, VAR_RATELIMIT_SIZE) }
ratelimit-for-domain{COLON} { YDVAR(2, VAR_RATELIMIT_FOR_DOMAIN) }
ratelimit-below-domain{COLON} { YDVAR(2, VAR_RATELIMIT_BELOW_DOMAIN) }
ip-ratelimit-factor{COLON} { YDVAR(1, VAR_IP_RATELIMIT_FACTOR) }
ratelimit-factor{COLON} { YDVAR(1, VAR_RATELIMIT_FACTOR) }
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) }
low-rtt-permil{COLON} { YDVAR(1, VAR_FAST_SERVER_PERMIL) }
fast-server-permil{COLON} { YDVAR(1, VAR_FAST_SERVER_PERMIL) }
response-ip-tag{COLON} { YDVAR(2, VAR_RESPONSE_IP_TAG) }
response-ip{COLON} { YDVAR(2, VAR_RESPONSE_IP) }
response-ip-data{COLON} { YDVAR(2, VAR_RESPONSE_IP_DATA) }
dnscrypt{COLON} { YDVAR(0, VAR_DNSCRYPT) }
dnscrypt-enable{COLON} { YDVAR(1, VAR_DNSCRYPT_ENABLE) }
dnscrypt-port{COLON} { YDVAR(1, VAR_DNSCRYPT_PORT) }
dnscrypt-provider{COLON} { YDVAR(1, VAR_DNSCRYPT_PROVIDER) }
dnscrypt-secret-key{COLON} { YDVAR(1, VAR_DNSCRYPT_SECRET_KEY) }
dnscrypt-provider-cert{COLON} { YDVAR(1, VAR_DNSCRYPT_PROVIDER_CERT) }
dnscrypt-provider-cert-rotated{COLON} { YDVAR(1, VAR_DNSCRYPT_PROVIDER_CERT_ROTATED) }
dnscrypt-shared-secret-cache-size{COLON} {
YDVAR(1, VAR_DNSCRYPT_SHARED_SECRET_CACHE_SIZE) }
dnscrypt-shared-secret-cache-slabs{COLON} {
YDVAR(1, VAR_DNSCRYPT_SHARED_SECRET_CACHE_SLABS) }
dnscrypt-nonce-cache-size{COLON} { YDVAR(1, VAR_DNSCRYPT_NONCE_CACHE_SIZE) }
dnscrypt-nonce-cache-slabs{COLON} { YDVAR(1, VAR_DNSCRYPT_NONCE_CACHE_SLABS) }
pad-responses{COLON} { YDVAR(1, VAR_PAD_RESPONSES) }
pad-responses-block-size{COLON} { YDVAR(1, VAR_PAD_RESPONSES_BLOCK_SIZE) }
pad-queries{COLON} { YDVAR(1, VAR_PAD_QUERIES) }
pad-queries-block-size{COLON} { YDVAR(1, VAR_PAD_QUERIES_BLOCK_SIZE) }
ipsecmod-enabled{COLON} { YDVAR(1, VAR_IPSECMOD_ENABLED) }
ipsecmod-ignore-bogus{COLON} { YDVAR(1, VAR_IPSECMOD_IGNORE_BOGUS) }
ipsecmod-hook{COLON} { YDVAR(1, VAR_IPSECMOD_HOOK) }
ipsecmod-max-ttl{COLON} { YDVAR(1, VAR_IPSECMOD_MAX_TTL) }
ipsecmod-whitelist{COLON} { YDVAR(1, VAR_IPSECMOD_WHITELIST) }
ipsecmod-allow{COLON} { YDVAR(1, VAR_IPSECMOD_WHITELIST) }
ipsecmod-strict{COLON} { YDVAR(1, VAR_IPSECMOD_STRICT) }
cachedb{COLON} { YDVAR(0, VAR_CACHEDB) }
backend{COLON} { YDVAR(1, VAR_CACHEDB_BACKEND) }
secret-seed{COLON} { YDVAR(1, VAR_CACHEDB_SECRETSEED) }
+cachedb-no-store{COLON} { YDVAR(1, VAR_CACHEDB_NO_STORE) }
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) }
+redis-logical-db{COLON} { YDVAR(1, VAR_CACHEDB_REDISLOGICALDB) }
ipset{COLON} { YDVAR(0, VAR_IPSET) }
name-v4{COLON} { YDVAR(1, VAR_IPSET_NAME_V4) }
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 ) }
ede{COLON} { YDVAR(1, VAR_EDE ) }
proxy-protocol-port{COLON} { YDVAR(1, VAR_PROXY_PROTOCOL_PORT) }
<INITIAL,val>{NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++; }
/* Quoted strings. Strip leading and ending quotes */
<val>\" { BEGIN(quotedstring); LEXOUT(("QS ")); }
<quotedstring><<EOF>> {
ub_c_error("EOF inside quoted string");
if(--num_args == 0) { BEGIN(INITIAL); }
else { BEGIN(val); }
}
<quotedstring>{DQANY}* { LEXOUT(("STR(%s) ", ub_c_text)); yymore(); }
<quotedstring>{NEWLINE} { ub_c_error("newline inside quoted string, no end \"");
cfg_parser->line++; BEGIN(INITIAL); }
<quotedstring>\" {
LEXOUT(("QE "));
if(--num_args == 0) { BEGIN(INITIAL); }
else { BEGIN(val); }
ub_c_text[ub_c_leng - 1] = '\0';
ub_c_lval.str = strdup(ub_c_text);
if(!ub_c_lval.str)
ub_c_error("out of memory");
return STRING_ARG;
}
/* Single Quoted strings. Strip leading and ending quotes */
<val>\' { BEGIN(singlequotedstr); LEXOUT(("SQS ")); }
<singlequotedstr><<EOF>> {
ub_c_error("EOF inside quoted string");
if(--num_args == 0) { BEGIN(INITIAL); }
else { BEGIN(val); }
}
<singlequotedstr>{SQANY}* { LEXOUT(("STR(%s) ", ub_c_text)); yymore(); }
<singlequotedstr>{NEWLINE} { ub_c_error("newline inside quoted string, no end '");
cfg_parser->line++; BEGIN(INITIAL); }
<singlequotedstr>\' {
LEXOUT(("SQE "));
if(--num_args == 0) { BEGIN(INITIAL); }
else { BEGIN(val); }
ub_c_text[ub_c_leng - 1] = '\0';
ub_c_lval.str = strdup(ub_c_text);
if(!ub_c_lval.str)
ub_c_error("out of memory");
return STRING_ARG;
}
/* include: directive */
<INITIAL,val>include{COLON} {
LEXOUT(("v(%s) ", ub_c_text)); inc_prev = YYSTATE; BEGIN(include); }
<include><<EOF>> {
ub_c_error("EOF inside include directive");
BEGIN(inc_prev);
}
<include>{SPACE}* { LEXOUT(("ISP ")); /* ignore */ }
<include>{NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++;}
<include>\" { LEXOUT(("IQS ")); BEGIN(include_quoted); }
<include>{UNQUOTEDLETTER}* {
LEXOUT(("Iunquotedstr(%s) ", ub_c_text));
config_start_include_glob(ub_c_text, 0);
BEGIN(inc_prev);
}
<include_quoted><<EOF>> {
ub_c_error("EOF inside quoted string");
BEGIN(inc_prev);
}
<include_quoted>{DQANY}* { LEXOUT(("ISTR(%s) ", ub_c_text)); yymore(); }
<include_quoted>{NEWLINE} { ub_c_error("newline before \" in include name");
cfg_parser->line++; BEGIN(inc_prev); }
<include_quoted>\" {
LEXOUT(("IQE "));
ub_c_text[ub_c_leng - 1] = '\0';
config_start_include_glob(ub_c_text,0);
BEGIN(inc_prev);
}
<INITIAL,val><<EOF>> {
LEXOUT(("LEXEOF "));
yy_set_bol(1); /* Set beginning of line, so "^" rules match. */
if (!config_include_stack) {
yyterminate();
} else {
fclose(yyin);
int prev_toplevel = inc_toplevel;
fclose(ub_c_in);
config_end_include();
if(prev_toplevel) return (VAR_FORCE_TOPLEVEL);
}
}
/* include-toplevel: directive */
<INITIAL,val>include-toplevel{COLON} {
LEXOUT(("v(%s) ", ub_c_text)); inc_prev = YYSTATE; BEGIN(include_toplevel);
}
<include_toplevel><<EOF>> {
ub_c_error("EOF inside include_toplevel directive");
BEGIN(inc_prev);
}
<include_toplevel>{SPACE}* { LEXOUT(("ITSP ")); /* ignore */ }
<include_toplevel>{NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++; }
<include_toplevel>\" { LEXOUT(("ITQS ")); BEGIN(include_toplevel_quoted); }
<include_toplevel>{UNQUOTEDLETTER}* {
LEXOUT(("ITunquotedstr(%s) ", ub_c_text));
config_start_include_glob(ub_c_text, 1);
BEGIN(inc_prev);
return (VAR_FORCE_TOPLEVEL);
}
<include_toplevel_quoted><<EOF>> {
ub_c_error("EOF inside quoted string");
BEGIN(inc_prev);
}
<include_toplevel_quoted>{DQANY}* { LEXOUT(("ITSTR(%s) ", ub_c_text)); yymore(); }
<include_toplevel_quoted>{NEWLINE} {
ub_c_error("newline before \" in include name");
cfg_parser->line++; BEGIN(inc_prev);
}
<include_toplevel_quoted>\" {
LEXOUT(("ITQE "));
ub_c_text[yyleng - 1] = '\0';
config_start_include_glob(ub_c_text, 1);
BEGIN(inc_prev);
return (VAR_FORCE_TOPLEVEL);
}
<val>{UNQUOTEDLETTER}* { LEXOUT(("unquotedstr(%s) ", ub_c_text));
if(--num_args == 0) { BEGIN(INITIAL); }
ub_c_lval.str = strdup(ub_c_text); return STRING_ARG; }
{UNQUOTEDLETTER_NOCOLON}* {
ub_c_error_msg("unknown keyword '%s'", ub_c_text);
}
<*>. {
ub_c_error_msg("stray '%s'", ub_c_text);
}
%%
diff --git a/contrib/unbound/util/configparser.y b/contrib/unbound/util/configparser.y
index d8f25a67ebbf..da5d6608f1d5 100644
--- a/contrib/unbound/util/configparser.y
+++ b/contrib/unbound/util/configparser.y
@@ -1,3919 +1,3958 @@
/*
* configparser.y -- yacc grammar for unbound configuration files
*
* Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
%{
#include "config.h"
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#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);
static void validate_respip_action(const char* action);
static void validate_acl_action(const char* action);
/* these need to be global, otherwise they cannot be used inside yacc */
extern struct config_parser_state* cfg_parser;
#if 0
#define OUTYY(s) printf s /* used ONLY when debugging */
#else
#define OUTYY(s)
#endif
%}
%union {
char* str;
};
%token SPACE LETTER NEWLINE COMMENT COLON ANY ZONESTR
%token <str> STRING_ARG
%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_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_INFRA_HOST_TTL VAR_INFRA_LAME_TTL VAR_INFRA_CACHE_SLABS
%token VAR_INFRA_CACHE_NUMHOSTS VAR_INFRA_CACHE_LAME_SIZE VAR_NAME
%token VAR_STUB_ZONE VAR_STUB_HOST VAR_STUB_ADDR VAR_TARGET_FETCH_POLICY
%token VAR_HARDEN_SHORT_BUFSIZE VAR_HARDEN_LARGE_QUERIES
%token VAR_FORWARD_ZONE VAR_FORWARD_HOST VAR_FORWARD_ADDR
%token VAR_DO_NOT_QUERY_ADDRESS VAR_HIDE_IDENTITY VAR_HIDE_VERSION
%token VAR_IDENTITY VAR_VERSION VAR_HARDEN_GLUE VAR_MODULE_CONF
%token VAR_TRUST_ANCHOR_FILE VAR_TRUST_ANCHOR VAR_VAL_OVERRIDE_DATE
%token VAR_BOGUS_TTL VAR_VAL_CLEAN_ADDITIONAL VAR_VAL_PERMISSIVE_MODE
%token VAR_INCOMING_NUM_TCP VAR_MSG_BUFFER_SIZE VAR_KEY_CACHE_SIZE
%token VAR_KEY_CACHE_SLABS VAR_TRUSTED_KEYS_FILE
%token VAR_VAL_NSEC3_KEYSIZE_ITERATIONS VAR_USE_SYSLOG
%token VAR_OUTGOING_INTERFACE VAR_ROOT_HINTS VAR_DO_NOT_QUERY_LOCALHOST
%token VAR_CACHE_MAX_TTL VAR_HARDEN_DNSSEC_STRIPPED VAR_ACCESS_CONTROL
%token VAR_LOCAL_ZONE VAR_LOCAL_DATA VAR_INTERFACE_AUTOMATIC
%token VAR_STATISTICS_INTERVAL VAR_DO_DAEMONIZE VAR_USE_CAPS_FOR_ID
%token VAR_STATISTICS_CUMULATIVE VAR_OUTGOING_PORT_PERMIT
%token VAR_OUTGOING_PORT_AVOID VAR_DLV_ANCHOR_FILE VAR_DLV_ANCHOR
%token VAR_NEG_CACHE_SIZE VAR_HARDEN_REFERRAL_PATH VAR_PRIVATE_ADDRESS
%token VAR_PRIVATE_DOMAIN VAR_REMOTE_CONTROL VAR_CONTROL_ENABLE
%token VAR_CONTROL_INTERFACE VAR_CONTROL_PORT VAR_SERVER_KEY_FILE
%token VAR_SERVER_CERT_FILE VAR_CONTROL_KEY_FILE VAR_CONTROL_CERT_FILE
%token VAR_CONTROL_USE_CERT VAR_TCP_REUSE_TIMEOUT VAR_MAX_REUSE_TCP_QUERIES
%token VAR_EXTENDED_STATISTICS VAR_LOCAL_DATA_PTR VAR_JOSTLE_TIMEOUT
%token VAR_STUB_PRIME VAR_UNWANTED_REPLY_THRESHOLD VAR_LOG_TIME_ASCII
%token VAR_DOMAIN_INSECURE VAR_PYTHON VAR_PYTHON_SCRIPT VAR_VAL_SIG_SKEW_MIN
%token VAR_VAL_SIG_SKEW_MAX VAR_VAL_MAX_RESTART VAR_CACHE_MIN_TTL
%token VAR_VAL_LOG_LEVEL VAR_AUTO_TRUST_ANCHOR_FILE VAR_KEEP_MISSING
%token VAR_ADD_HOLDDOWN VAR_DEL_HOLDDOWN VAR_SO_RCVBUF VAR_EDNS_BUFFER_SIZE
%token VAR_PREFETCH VAR_PREFETCH_KEY VAR_SO_SNDBUF VAR_SO_REUSEPORT
%token VAR_HARDEN_BELOW_NXDOMAIN VAR_IGNORE_CD_FLAG VAR_LOG_QUERIES
%token VAR_LOG_REPLIES VAR_LOG_LOCAL_ACTIONS VAR_TCP_UPSTREAM
%token VAR_SSL_UPSTREAM VAR_TCP_AUTH_QUERY_TIMEOUT VAR_SSL_SERVICE_KEY
%token VAR_SSL_SERVICE_PEM VAR_SSL_PORT VAR_FORWARD_FIRST
%token VAR_STUB_SSL_UPSTREAM VAR_FORWARD_SSL_UPSTREAM VAR_TLS_CERT_BUNDLE
%token VAR_STUB_TCP_UPSTREAM VAR_FORWARD_TCP_UPSTREAM
%token VAR_HTTPS_PORT VAR_HTTP_ENDPOINT VAR_HTTP_MAX_STREAMS
%token VAR_HTTP_QUERY_BUFFER_SIZE VAR_HTTP_RESPONSE_BUFFER_SIZE
%token VAR_HTTP_NODELAY VAR_HTTP_NOTLS_DOWNSTREAM
%token VAR_STUB_FIRST VAR_MINIMAL_RESPONSES VAR_RRSET_ROUNDROBIN
%token VAR_MAX_UDP_SIZE VAR_DELAY_CLOSE VAR_UDP_CONNECT
%token VAR_UNBLOCK_LAN_ZONES VAR_INSECURE_LAN_ZONES
%token VAR_INFRA_CACHE_MIN_RTT VAR_INFRA_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_DNSTAP_SEND_IDENTITY VAR_DNSTAP_SEND_VERSION VAR_DNSTAP_BIDIRECTIONAL
%token VAR_DNSTAP_IDENTITY VAR_DNSTAP_VERSION
%token VAR_DNSTAP_LOG_RESOLVER_QUERY_MESSAGES
%token VAR_DNSTAP_LOG_RESOLVER_RESPONSE_MESSAGES
%token VAR_DNSTAP_LOG_CLIENT_QUERY_MESSAGES
%token VAR_DNSTAP_LOG_CLIENT_RESPONSE_MESSAGES
%token VAR_DNSTAP_LOG_FORWARDER_QUERY_MESSAGES
%token VAR_DNSTAP_LOG_FORWARDER_RESPONSE_MESSAGES
%token VAR_RESPONSE_IP_TAG VAR_RESPONSE_IP VAR_RESPONSE_IP_DATA
%token VAR_HARDEN_ALGO_DOWNGRADE VAR_IP_TRANSPARENT
%token VAR_IP_DSCP
%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 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_SEND_CLIENT_SUBNET VAR_CLIENT_SUBNET_ZONE
%token VAR_CLIENT_SUBNET_ALWAYS_FORWARD VAR_CLIENT_SUBNET_OPCODE
%token VAR_MAX_CLIENT_SUBNET_IPV4 VAR_MAX_CLIENT_SUBNET_IPV6
%token VAR_MIN_CLIENT_SUBNET_IPV4 VAR_MIN_CLIENT_SUBNET_IPV6
%token VAR_MAX_ECS_TREE_SIZE_IPV4 VAR_MAX_ECS_TREE_SIZE_IPV6
%token VAR_CAPS_WHITELIST VAR_CACHE_MAX_NEGATIVE_TTL VAR_PERMIT_SMALL_HOLDDOWN
%token VAR_QNAME_MINIMISATION VAR_QNAME_MINIMISATION_STRICT VAR_IP_FREEBIND
%token VAR_DEFINE_TAG VAR_LOCAL_ZONE_TAG VAR_ACCESS_CONTROL_TAG
%token VAR_LOCAL_ZONE_OVERRIDE VAR_ACCESS_CONTROL_TAG_ACTION
%token VAR_ACCESS_CONTROL_TAG_DATA VAR_VIEW VAR_ACCESS_CONTROL_VIEW
%token VAR_VIEW_FIRST VAR_SERVE_EXPIRED VAR_SERVE_EXPIRED_TTL
%token VAR_SERVE_EXPIRED_TTL_RESET VAR_SERVE_EXPIRED_REPLY_TTL
%token VAR_SERVE_EXPIRED_CLIENT_TIMEOUT VAR_EDE_SERVE_EXPIRED
%token VAR_SERVE_ORIGINAL_TTL VAR_FAKE_DSA
%token VAR_FAKE_SHA1 VAR_LOG_IDENTITY VAR_HIDE_TRUSTANCHOR
%token VAR_HIDE_HTTP_USER_AGENT VAR_HTTP_USER_AGENT
%token VAR_TRUST_ANCHOR_SIGNALING VAR_AGGRESSIVE_NSEC VAR_USE_SYSTEMD
%token VAR_SHM_ENABLE VAR_SHM_KEY VAR_ROOT_KEY_SENTINEL
%token VAR_DNSCRYPT VAR_DNSCRYPT_ENABLE VAR_DNSCRYPT_PORT VAR_DNSCRYPT_PROVIDER
%token VAR_DNSCRYPT_SECRET_KEY VAR_DNSCRYPT_PROVIDER_CERT
%token VAR_DNSCRYPT_PROVIDER_CERT_ROTATED
%token VAR_DNSCRYPT_SHARED_SECRET_CACHE_SIZE
%token VAR_DNSCRYPT_SHARED_SECRET_CACHE_SLABS
%token VAR_DNSCRYPT_NONCE_CACHE_SIZE
%token VAR_DNSCRYPT_NONCE_CACHE_SLABS
%token VAR_PAD_RESPONSES VAR_PAD_RESPONSES_BLOCK_SIZE
%token VAR_PAD_QUERIES VAR_PAD_QUERIES_BLOCK_SIZE
%token VAR_IPSECMOD_ENABLED VAR_IPSECMOD_HOOK VAR_IPSECMOD_IGNORE_BOGUS
%token VAR_IPSECMOD_MAX_TTL VAR_IPSECMOD_WHITELIST VAR_IPSECMOD_STRICT
%token VAR_CACHEDB VAR_CACHEDB_BACKEND VAR_CACHEDB_SECRETSEED
%token VAR_CACHEDB_REDISHOST VAR_CACHEDB_REDISPORT VAR_CACHEDB_REDISTIMEOUT
%token VAR_CACHEDB_REDISEXPIRERECORDS VAR_CACHEDB_REDISPATH VAR_CACHEDB_REDISPASSWORD
+%token VAR_CACHEDB_REDISLOGICALDB
%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_IPSET VAR_IPSET_NAME_V4 VAR_IPSET_NAME_V6
%token VAR_TLS_SESSION_TICKET_KEYS VAR_RPZ VAR_TAGS VAR_RPZ_ACTION_OVERRIDE
%token VAR_RPZ_CNAME_OVERRIDE VAR_RPZ_LOG VAR_RPZ_LOG_NAME
%token VAR_DYNLIB VAR_DYNLIB_FILE VAR_EDNS_CLIENT_STRING
%token VAR_EDNS_CLIENT_STRING_OPCODE VAR_NSID
%token VAR_ZONEMD_PERMISSIVE_MODE VAR_ZONEMD_CHECK VAR_ZONEMD_REJECT_ABSENCE
%token VAR_RPZ_SIGNAL_NXDOMAIN_RA 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 VAR_STATISTICS_INHIBIT_ZERO
-%token VAR_HARDEN_UNKNOWN_ADDITIONAL
+%token VAR_HARDEN_UNKNOWN_ADDITIONAL VAR_DISABLE_EDNS_DO VAR_CACHEDB_NO_STORE
%%
toplevelvars: /* empty */ | toplevelvars toplevelvar ;
toplevelvar: serverstart contents_server | stubstart contents_stub |
forwardstart contents_forward | pythonstart contents_py |
rcstart contents_rc | dtstart contents_dt | viewstart contents_view |
dnscstart contents_dnsc | cachedbstart contents_cachedb |
ipsetstart contents_ipset | authstart contents_auth |
rpzstart contents_rpz | dynlibstart contents_dl |
force_toplevel
;
force_toplevel: VAR_FORCE_TOPLEVEL
{
OUTYY(("\nP(force-toplevel)\n"));
cfg_parser->started_toplevel = 0;
}
;
/* server: declaration */
serverstart: VAR_SERVER
{
OUTYY(("\nP(server:)\n"));
cfg_parser->started_toplevel = 1;
}
;
contents_server: contents_server content_server
| ;
content_server: server_num_threads | server_verbosity | server_port |
server_outgoing_range | server_do_ip4 |
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_num_queries_per_thread | server_rrset_cache_size |
server_rrset_cache_slabs | server_outgoing_num_tcp |
server_infra_host_ttl | server_infra_lame_ttl |
server_infra_cache_slabs | server_infra_cache_numhosts |
server_infra_cache_lame_size | server_target_fetch_policy |
server_harden_short_bufsize | server_harden_large_queries |
server_do_not_query_address | server_hide_identity |
server_hide_version | server_identity | server_version |
server_hide_http_user_agent | server_http_user_agent |
server_harden_glue | server_module_conf | server_trust_anchor_file |
server_trust_anchor | server_val_override_date | server_bogus_ttl |
server_val_clean_additional | server_val_permissive_mode |
server_incoming_num_tcp | server_msg_buffer_size |
server_key_cache_size | server_key_cache_slabs |
server_trusted_keys_file | server_val_nsec3_keysize_iterations |
server_use_syslog | server_outgoing_interface | server_root_hints |
server_do_not_query_localhost | server_cache_max_ttl |
server_harden_dnssec_stripped | server_access_control |
server_local_zone | server_local_data | server_interface_automatic |
server_statistics_interval | server_do_daemonize |
server_use_caps_for_id | server_statistics_cumulative |
server_outgoing_port_permit | server_outgoing_port_avoid |
server_dlv_anchor_file | server_dlv_anchor | server_neg_cache_size |
server_harden_referral_path | server_private_address |
server_private_domain | server_extended_statistics |
server_local_data_ptr | server_jostle_timeout |
server_unwanted_reply_threshold | server_log_time_ascii |
server_domain_insecure | server_val_sig_skew_min |
server_val_sig_skew_max | server_val_max_restart |
server_cache_min_ttl | server_val_log_level |
server_auto_trust_anchor_file | server_add_holddown |
server_del_holddown | server_keep_missing | server_so_rcvbuf |
server_edns_buffer_size | server_prefetch | server_prefetch_key |
server_so_sndbuf | server_harden_below_nxdomain | server_ignore_cd_flag |
server_log_queries | server_log_replies | server_tcp_upstream | server_ssl_upstream |
server_log_local_actions |
server_ssl_service_key | server_ssl_service_pem | server_ssl_port |
server_https_port | server_http_endpoint | server_http_max_streams |
server_http_query_buffer_size | server_http_response_buffer_size |
server_http_nodelay | server_http_notls_downstream |
server_minimal_responses | server_rrset_roundrobin | server_max_udp_size |
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_ip_ratelimit_slabs | server_ratelimit_slabs |
server_ip_ratelimit_size | server_ratelimit_size |
server_ratelimit_for_domain |
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_min_client_subnet_ipv4 | server_min_client_subnet_ipv6 |
server_max_ecs_tree_size_ipv4 | server_max_ecs_tree_size_ipv6 |
server_caps_whitelist | server_cache_max_negative_ttl |
server_permit_small_holddown | server_qname_minimisation |
server_ip_freebind | server_define_tag | server_local_zone_tag |
server_disable_dnssec_lame_check | server_access_control_tag |
server_local_zone_override | server_access_control_tag_action |
server_access_control_tag_data | server_access_control_view |
server_interface_action | server_interface_view | server_interface_tag |
server_interface_tag_action | server_interface_tag_data |
server_qname_minimisation_strict |
server_pad_responses | server_pad_responses_block_size |
server_pad_queries | server_pad_queries_block_size |
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_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_hide_trustanchor | server_trust_anchor_signaling |
server_root_key_sentinel |
server_ipsecmod_enabled | server_ipsecmod_hook |
server_ipsecmod_ignore_bogus | server_ipsecmod_max_ttl |
server_ipsecmod_whitelist | server_ipsecmod_strict |
server_udp_upstream_without_downstream | server_aggressive_nsec |
server_tls_cert_bundle | server_tls_additional_port | server_low_rtt |
server_fast_server_permil | server_fast_server_num | server_tls_win_cert |
server_tcp_connection_limit | server_log_servfail | server_deny_any |
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_statistics_inhibit_zero |
- server_harden_unknown_additional
+ server_harden_unknown_additional | server_disable_edns_do
;
stubstart: VAR_STUB_ZONE
{
struct config_stub* s;
OUTYY(("\nP(stub_zone:)\n"));
cfg_parser->started_toplevel = 1;
s = (struct config_stub*)calloc(1, sizeof(struct config_stub));
if(s) {
s->next = cfg_parser->cfg->stubs;
cfg_parser->cfg->stubs = s;
} else {
yyerror("out of memory");
}
}
;
contents_stub: contents_stub content_stub
| ;
content_stub: stub_name | stub_host | stub_addr | stub_prime | stub_first |
stub_no_cache | stub_ssl_upstream | stub_tcp_upstream
;
forwardstart: VAR_FORWARD_ZONE
{
struct config_stub* s;
OUTYY(("\nP(forward_zone:)\n"));
cfg_parser->started_toplevel = 1;
s = (struct config_stub*)calloc(1, sizeof(struct config_stub));
if(s) {
s->next = cfg_parser->cfg->forwards;
cfg_parser->cfg->forwards = s;
} else {
yyerror("out of memory");
}
}
;
contents_forward: contents_forward content_forward
| ;
content_forward: forward_name | forward_host | forward_addr | forward_first |
forward_no_cache | forward_ssl_upstream | forward_tcp_upstream
;
viewstart: VAR_VIEW
{
struct config_view* s;
OUTYY(("\nP(view:)\n"));
cfg_parser->started_toplevel = 1;
s = (struct config_view*)calloc(1, sizeof(struct config_view));
if(s) {
s->next = cfg_parser->cfg->views;
if(s->next && !s->next->name)
yyerror("view without name");
cfg_parser->cfg->views = s;
} else {
yyerror("out of memory");
}
}
;
contents_view: contents_view content_view
| ;
content_view: view_name | view_local_zone | view_local_data | view_first |
view_response_ip | view_response_ip_data | view_local_data_ptr
;
authstart: VAR_AUTH_ZONE
{
struct config_auth* s;
OUTYY(("\nP(auth_zone:)\n"));
cfg_parser->started_toplevel = 1;
s = (struct config_auth*)calloc(1, sizeof(struct config_auth));
if(s) {
s->next = cfg_parser->cfg->auths;
cfg_parser->cfg->auths = s;
/* defaults for auth zone */
s->for_downstream = 1;
s->for_upstream = 1;
s->fallback_enabled = 0;
s->zonemd_check = 0;
s->zonemd_reject_absence = 0;
s->isrpz = 0;
} else {
yyerror("out of memory");
}
}
;
contents_auth: contents_auth content_auth
| ;
content_auth: auth_name | auth_zonefile | auth_master | auth_url |
auth_for_downstream | auth_for_upstream | auth_fallback_enabled |
auth_allow_notify | auth_zonemd_check | auth_zonemd_reject_absence
;
rpz_tag: VAR_TAGS STRING_ARG
{
uint8_t* bitlist;
size_t len = 0;
OUTYY(("P(server_local_zone_tag:%s)\n", $2));
bitlist = config_parse_taglist(cfg_parser->cfg, $2,
&len);
free($2);
if(!bitlist) {
yyerror("could not parse tags, (define-tag them first)");
}
if(bitlist) {
cfg_parser->cfg->auths->rpz_taglist = bitlist;
cfg_parser->cfg->auths->rpz_taglistlen = len;
}
}
;
rpz_action_override: VAR_RPZ_ACTION_OVERRIDE STRING_ARG
{
OUTYY(("P(rpz_action_override:%s)\n", $2));
if(strcmp($2, "nxdomain")!=0 && strcmp($2, "nodata")!=0 &&
strcmp($2, "passthru")!=0 && strcmp($2, "drop")!=0 &&
strcmp($2, "cname")!=0 && strcmp($2, "disabled")!=0) {
yyerror("rpz-action-override action: expected nxdomain, "
"nodata, passthru, drop, cname or disabled");
free($2);
cfg_parser->cfg->auths->rpz_action_override = NULL;
}
else {
cfg_parser->cfg->auths->rpz_action_override = $2;
}
}
;
rpz_cname_override: VAR_RPZ_CNAME_OVERRIDE STRING_ARG
{
OUTYY(("P(rpz_cname_override:%s)\n", $2));
free(cfg_parser->cfg->auths->rpz_cname);
cfg_parser->cfg->auths->rpz_cname = $2;
}
;
rpz_log: VAR_RPZ_LOG STRING_ARG
{
OUTYY(("P(rpz_log:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->auths->rpz_log = (strcmp($2, "yes")==0);
free($2);
}
;
rpz_log_name: VAR_RPZ_LOG_NAME STRING_ARG
{
OUTYY(("P(rpz_log_name:%s)\n", $2));
free(cfg_parser->cfg->auths->rpz_log_name);
cfg_parser->cfg->auths->rpz_log_name = $2;
}
;
rpz_signal_nxdomain_ra: VAR_RPZ_SIGNAL_NXDOMAIN_RA STRING_ARG
{
OUTYY(("P(rpz_signal_nxdomain_ra:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->auths->rpz_signal_nxdomain_ra = (strcmp($2, "yes")==0);
free($2);
}
;
rpzstart: VAR_RPZ
{
struct config_auth* s;
OUTYY(("\nP(rpz:)\n"));
cfg_parser->started_toplevel = 1;
s = (struct config_auth*)calloc(1, sizeof(struct config_auth));
if(s) {
s->next = cfg_parser->cfg->auths;
cfg_parser->cfg->auths = s;
/* defaults for RPZ auth zone */
s->for_downstream = 0;
s->for_upstream = 0;
s->fallback_enabled = 0;
s->isrpz = 1;
} else {
yyerror("out of memory");
}
}
;
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 |
rpz_log | rpz_log_name | rpz_signal_nxdomain_ra | auth_for_downstream
;
server_num_threads: VAR_NUM_THREADS STRING_ARG
{
OUTYY(("P(server_num_threads:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->num_threads = atoi($2);
free($2);
}
;
server_verbosity: VAR_VERBOSITY STRING_ARG
{
OUTYY(("P(server_verbosity:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->verbosity = atoi($2);
free($2);
}
;
server_statistics_interval: VAR_STATISTICS_INTERVAL STRING_ARG
{
OUTYY(("P(server_statistics_interval:%s)\n", $2));
if(strcmp($2, "") == 0 || strcmp($2, "0") == 0)
cfg_parser->cfg->stat_interval = 0;
else if(atoi($2) == 0)
yyerror("number expected");
else cfg_parser->cfg->stat_interval = atoi($2);
free($2);
}
;
server_statistics_cumulative: VAR_STATISTICS_CUMULATIVE STRING_ARG
{
OUTYY(("P(server_statistics_cumulative:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->stat_cumulative = (strcmp($2, "yes")==0);
free($2);
}
;
server_extended_statistics: VAR_EXTENDED_STATISTICS STRING_ARG
{
OUTYY(("P(server_extended_statistics:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->stat_extended = (strcmp($2, "yes")==0);
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));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->shm_enable = (strcmp($2, "yes")==0);
free($2);
}
;
server_shm_key: VAR_SHM_KEY STRING_ARG
{
OUTYY(("P(server_shm_key:%s)\n", $2));
if(strcmp($2, "") == 0 || strcmp($2, "0") == 0)
cfg_parser->cfg->shm_key = 0;
else if(atoi($2) == 0)
yyerror("number expected");
else cfg_parser->cfg->shm_key = atoi($2);
free($2);
}
;
server_port: VAR_PORT STRING_ARG
{
OUTYY(("P(server_port:%s)\n", $2));
if(atoi($2) == 0)
yyerror("port number expected");
else cfg_parser->cfg->port = atoi($2);
free($2);
}
;
server_send_client_subnet: VAR_SEND_CLIENT_SUBNET STRING_ARG
{
#ifdef CLIENT_SUBNET
OUTYY(("P(server_send_client_subnet:%s)\n", $2));
if(!cfg_strlist_insert(&cfg_parser->cfg->client_subnet, $2))
fatal_exit("out of memory adding client-subnet");
#else
OUTYY(("P(Compiled without edns subnet option, ignoring)\n"));
free($2);
#endif
}
;
server_client_subnet_zone: VAR_CLIENT_SUBNET_ZONE STRING_ARG
{
#ifdef CLIENT_SUBNET
OUTYY(("P(server_client_subnet_zone:%s)\n", $2));
if(!cfg_strlist_insert(&cfg_parser->cfg->client_subnet_zone,
$2))
fatal_exit("out of memory adding client-subnet-zone");
#else
OUTYY(("P(Compiled without edns subnet option, ignoring)\n"));
free($2);
#endif
}
;
server_client_subnet_always_forward:
VAR_CLIENT_SUBNET_ALWAYS_FORWARD STRING_ARG
{
#ifdef CLIENT_SUBNET
OUTYY(("P(server_client_subnet_always_forward:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else
cfg_parser->cfg->client_subnet_always_forward =
(strcmp($2, "yes")==0);
#else
OUTYY(("P(Compiled without edns subnet option, ignoring)\n"));
#endif
free($2);
}
;
server_client_subnet_opcode: VAR_CLIENT_SUBNET_OPCODE STRING_ARG
{
#ifdef CLIENT_SUBNET
OUTYY(("P(client_subnet_opcode:%s)\n", $2));
OUTYY(("P(Deprecated option, ignoring)\n"));
#else
OUTYY(("P(Compiled without edns subnet option, ignoring)\n"));
#endif
free($2);
}
;
server_max_client_subnet_ipv4: VAR_MAX_CLIENT_SUBNET_IPV4 STRING_ARG
{
#ifdef CLIENT_SUBNET
OUTYY(("P(max_client_subnet_ipv4:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("IPv4 subnet length expected");
else if (atoi($2) > 32)
cfg_parser->cfg->max_client_subnet_ipv4 = 32;
else if (atoi($2) < 0)
cfg_parser->cfg->max_client_subnet_ipv4 = 0;
else cfg_parser->cfg->max_client_subnet_ipv4 = (uint8_t)atoi($2);
#else
OUTYY(("P(Compiled without edns subnet option, ignoring)\n"));
#endif
free($2);
}
;
server_max_client_subnet_ipv6: VAR_MAX_CLIENT_SUBNET_IPV6 STRING_ARG
{
#ifdef CLIENT_SUBNET
OUTYY(("P(max_client_subnet_ipv6:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("Ipv6 subnet length expected");
else if (atoi($2) > 128)
cfg_parser->cfg->max_client_subnet_ipv6 = 128;
else if (atoi($2) < 0)
cfg_parser->cfg->max_client_subnet_ipv6 = 0;
else cfg_parser->cfg->max_client_subnet_ipv6 = (uint8_t)atoi($2);
#else
OUTYY(("P(Compiled without edns subnet option, ignoring)\n"));
#endif
free($2);
}
;
server_min_client_subnet_ipv4: VAR_MIN_CLIENT_SUBNET_IPV4 STRING_ARG
{
#ifdef CLIENT_SUBNET
OUTYY(("P(min_client_subnet_ipv4:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("IPv4 subnet length expected");
else if (atoi($2) > 32)
cfg_parser->cfg->min_client_subnet_ipv4 = 32;
else if (atoi($2) < 0)
cfg_parser->cfg->min_client_subnet_ipv4 = 0;
else cfg_parser->cfg->min_client_subnet_ipv4 = (uint8_t)atoi($2);
#else
OUTYY(("P(Compiled without edns subnet option, ignoring)\n"));
#endif
free($2);
}
;
server_min_client_subnet_ipv6: VAR_MIN_CLIENT_SUBNET_IPV6 STRING_ARG
{
#ifdef CLIENT_SUBNET
OUTYY(("P(min_client_subnet_ipv6:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("Ipv6 subnet length expected");
else if (atoi($2) > 128)
cfg_parser->cfg->min_client_subnet_ipv6 = 128;
else if (atoi($2) < 0)
cfg_parser->cfg->min_client_subnet_ipv6 = 0;
else cfg_parser->cfg->min_client_subnet_ipv6 = (uint8_t)atoi($2);
#else
OUTYY(("P(Compiled without edns subnet option, ignoring)\n"));
#endif
free($2);
}
;
server_max_ecs_tree_size_ipv4: VAR_MAX_ECS_TREE_SIZE_IPV4 STRING_ARG
{
#ifdef CLIENT_SUBNET
OUTYY(("P(max_ecs_tree_size_ipv4:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("IPv4 ECS tree size expected");
else if (atoi($2) < 0)
cfg_parser->cfg->max_ecs_tree_size_ipv4 = 0;
else cfg_parser->cfg->max_ecs_tree_size_ipv4 = (uint32_t)atoi($2);
#else
OUTYY(("P(Compiled without edns subnet option, ignoring)\n"));
#endif
free($2);
}
;
server_max_ecs_tree_size_ipv6: VAR_MAX_ECS_TREE_SIZE_IPV6 STRING_ARG
{
#ifdef CLIENT_SUBNET
OUTYY(("P(max_ecs_tree_size_ipv6:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("IPv6 ECS tree size expected");
else if (atoi($2) < 0)
cfg_parser->cfg->max_ecs_tree_size_ipv6 = 0;
else cfg_parser->cfg->max_ecs_tree_size_ipv6 = (uint32_t)atoi($2);
#else
OUTYY(("P(Compiled without edns subnet option, ignoring)\n"));
#endif
free($2);
}
;
server_interface: VAR_INTERFACE STRING_ARG
{
OUTYY(("P(server_interface:%s)\n", $2));
if(cfg_parser->cfg->num_ifs == 0)
cfg_parser->cfg->ifs = calloc(1, sizeof(char*));
else cfg_parser->cfg->ifs = realloc(cfg_parser->cfg->ifs,
(cfg_parser->cfg->num_ifs+1)*sizeof(char*));
if(!cfg_parser->cfg->ifs)
yyerror("out of memory");
else
cfg_parser->cfg->ifs[cfg_parser->cfg->num_ifs++] = $2;
}
;
server_outgoing_interface: VAR_OUTGOING_INTERFACE STRING_ARG
{
OUTYY(("P(server_outgoing_interface:%s)\n", $2));
if(cfg_parser->cfg->num_out_ifs == 0)
cfg_parser->cfg->out_ifs = calloc(1, sizeof(char*));
else cfg_parser->cfg->out_ifs = realloc(
cfg_parser->cfg->out_ifs,
(cfg_parser->cfg->num_out_ifs+1)*sizeof(char*));
if(!cfg_parser->cfg->out_ifs)
yyerror("out of memory");
else
cfg_parser->cfg->out_ifs[
cfg_parser->cfg->num_out_ifs++] = $2;
}
;
server_outgoing_range: VAR_OUTGOING_RANGE STRING_ARG
{
OUTYY(("P(server_outgoing_range:%s)\n", $2));
if(atoi($2) == 0)
yyerror("number expected");
else cfg_parser->cfg->outgoing_num_ports = atoi($2);
free($2);
}
;
server_outgoing_port_permit: VAR_OUTGOING_PORT_PERMIT STRING_ARG
{
OUTYY(("P(server_outgoing_port_permit:%s)\n", $2));
if(!cfg_mark_ports($2, 1,
cfg_parser->cfg->outgoing_avail_ports, 65536))
yyerror("port number or range (\"low-high\") expected");
free($2);
}
;
server_outgoing_port_avoid: VAR_OUTGOING_PORT_AVOID STRING_ARG
{
OUTYY(("P(server_outgoing_port_avoid:%s)\n", $2));
if(!cfg_mark_ports($2, 0,
cfg_parser->cfg->outgoing_avail_ports, 65536))
yyerror("port number or range (\"low-high\") expected");
free($2);
}
;
server_outgoing_num_tcp: VAR_OUTGOING_NUM_TCP STRING_ARG
{
OUTYY(("P(server_outgoing_num_tcp:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->outgoing_num_tcp = atoi($2);
free($2);
}
;
server_incoming_num_tcp: VAR_INCOMING_NUM_TCP STRING_ARG
{
OUTYY(("P(server_incoming_num_tcp:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->incoming_num_tcp = atoi($2);
free($2);
}
;
server_interface_automatic: VAR_INTERFACE_AUTOMATIC STRING_ARG
{
OUTYY(("P(server_interface_automatic:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->if_automatic = (strcmp($2, "yes")==0);
free($2);
}
;
server_interface_automatic_ports: VAR_INTERFACE_AUTOMATIC_PORTS STRING_ARG
{
OUTYY(("P(server_interface_automatic_ports:%s)\n", $2));
free(cfg_parser->cfg->if_automatic_ports);
cfg_parser->cfg->if_automatic_ports = $2;
}
;
server_do_ip4: VAR_DO_IP4 STRING_ARG
{
OUTYY(("P(server_do_ip4:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->do_ip4 = (strcmp($2, "yes")==0);
free($2);
}
;
server_do_ip6: VAR_DO_IP6 STRING_ARG
{
OUTYY(("P(server_do_ip6:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->do_ip6 = (strcmp($2, "yes")==0);
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));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->do_udp = (strcmp($2, "yes")==0);
free($2);
}
;
server_do_tcp: VAR_DO_TCP STRING_ARG
{
OUTYY(("P(server_do_tcp:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->do_tcp = (strcmp($2, "yes")==0);
free($2);
}
;
server_prefer_ip4: VAR_PREFER_IP4 STRING_ARG
{
OUTYY(("P(server_prefer_ip4:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->prefer_ip4 = (strcmp($2, "yes")==0);
free($2);
}
;
server_prefer_ip6: VAR_PREFER_IP6 STRING_ARG
{
OUTYY(("P(server_prefer_ip6:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->prefer_ip6 = (strcmp($2, "yes")==0);
free($2);
}
;
server_tcp_mss: VAR_TCP_MSS STRING_ARG
{
OUTYY(("P(server_tcp_mss:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->tcp_mss = atoi($2);
free($2);
}
;
server_outgoing_tcp_mss: VAR_OUTGOING_TCP_MSS STRING_ARG
{
OUTYY(("P(server_outgoing_tcp_mss:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->outgoing_tcp_mss = atoi($2);
free($2);
}
;
server_tcp_idle_timeout: VAR_TCP_IDLE_TIMEOUT STRING_ARG
{
OUTYY(("P(server_tcp_idle_timeout:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else if (atoi($2) > 120000)
cfg_parser->cfg->tcp_idle_timeout = 120000;
else if (atoi($2) < 1)
cfg_parser->cfg->tcp_idle_timeout = 1;
else cfg_parser->cfg->tcp_idle_timeout = atoi($2);
free($2);
}
;
server_max_reuse_tcp_queries: VAR_MAX_REUSE_TCP_QUERIES STRING_ARG
{
OUTYY(("P(server_max_reuse_tcp_queries:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else if (atoi($2) < 1)
cfg_parser->cfg->max_reuse_tcp_queries = 0;
else cfg_parser->cfg->max_reuse_tcp_queries = atoi($2);
free($2);
}
;
server_tcp_reuse_timeout: VAR_TCP_REUSE_TIMEOUT STRING_ARG
{
OUTYY(("P(server_tcp_reuse_timeout:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else if (atoi($2) < 1)
cfg_parser->cfg->tcp_reuse_timeout = 0;
else cfg_parser->cfg->tcp_reuse_timeout = atoi($2);
free($2);
}
;
server_tcp_auth_query_timeout: VAR_TCP_AUTH_QUERY_TIMEOUT STRING_ARG
{
OUTYY(("P(server_tcp_auth_query_timeout:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else if (atoi($2) < 1)
cfg_parser->cfg->tcp_auth_query_timeout = 0;
else cfg_parser->cfg->tcp_auth_query_timeout = atoi($2);
free($2);
}
;
server_tcp_keepalive: VAR_EDNS_TCP_KEEPALIVE STRING_ARG
{
OUTYY(("P(server_tcp_keepalive:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->do_tcp_keepalive = (strcmp($2, "yes")==0);
free($2);
}
;
server_tcp_keepalive_timeout: VAR_EDNS_TCP_KEEPALIVE_TIMEOUT STRING_ARG
{
OUTYY(("P(server_tcp_keepalive_timeout:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else if (atoi($2) > 6553500)
cfg_parser->cfg->tcp_keepalive_timeout = 6553500;
else if (atoi($2) < 1)
cfg_parser->cfg->tcp_keepalive_timeout = 0;
else cfg_parser->cfg->tcp_keepalive_timeout = atoi($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));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->tcp_upstream = (strcmp($2, "yes")==0);
free($2);
}
;
server_udp_upstream_without_downstream: VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM STRING_ARG
{
OUTYY(("P(server_udp_upstream_without_downstream:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->udp_upstream_without_downstream = (strcmp($2, "yes")==0);
free($2);
}
;
server_ssl_upstream: VAR_SSL_UPSTREAM STRING_ARG
{
OUTYY(("P(server_ssl_upstream:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->ssl_upstream = (strcmp($2, "yes")==0);
free($2);
}
;
server_ssl_service_key: VAR_SSL_SERVICE_KEY STRING_ARG
{
OUTYY(("P(server_ssl_service_key:%s)\n", $2));
free(cfg_parser->cfg->ssl_service_key);
cfg_parser->cfg->ssl_service_key = $2;
}
;
server_ssl_service_pem: VAR_SSL_SERVICE_PEM STRING_ARG
{
OUTYY(("P(server_ssl_service_pem:%s)\n", $2));
free(cfg_parser->cfg->ssl_service_pem);
cfg_parser->cfg->ssl_service_pem = $2;
}
;
server_ssl_port: VAR_SSL_PORT STRING_ARG
{
OUTYY(("P(server_ssl_port:%s)\n", $2));
if(atoi($2) == 0)
yyerror("port number expected");
else cfg_parser->cfg->ssl_port = atoi($2);
free($2);
}
;
server_tls_cert_bundle: VAR_TLS_CERT_BUNDLE STRING_ARG
{
OUTYY(("P(server_tls_cert_bundle:%s)\n", $2));
free(cfg_parser->cfg->tls_cert_bundle);
cfg_parser->cfg->tls_cert_bundle = $2;
}
;
server_tls_win_cert: VAR_TLS_WIN_CERT STRING_ARG
{
OUTYY(("P(server_tls_win_cert:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->tls_win_cert = (strcmp($2, "yes")==0);
free($2);
}
;
server_tls_additional_port: VAR_TLS_ADDITIONAL_PORT STRING_ARG
{
OUTYY(("P(server_tls_additional_port:%s)\n", $2));
if(!cfg_strlist_insert(&cfg_parser->cfg->tls_additional_port,
$2))
yyerror("out of memory");
}
;
server_tls_ciphers: VAR_TLS_CIPHERS STRING_ARG
{
OUTYY(("P(server_tls_ciphers:%s)\n", $2));
free(cfg_parser->cfg->tls_ciphers);
cfg_parser->cfg->tls_ciphers = $2;
}
;
server_tls_ciphersuites: VAR_TLS_CIPHERSUITES STRING_ARG
{
OUTYY(("P(server_tls_ciphersuites:%s)\n", $2));
free(cfg_parser->cfg->tls_ciphersuites);
cfg_parser->cfg->tls_ciphersuites = $2;
}
;
server_tls_session_ticket_keys: VAR_TLS_SESSION_TICKET_KEYS STRING_ARG
{
OUTYY(("P(server_tls_session_ticket_keys:%s)\n", $2));
if(!cfg_strlist_append(&cfg_parser->cfg->tls_session_ticket_keys,
$2))
yyerror("out of memory");
}
;
server_tls_use_sni: VAR_TLS_USE_SNI STRING_ARG
{
OUTYY(("P(server_tls_use_sni:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->tls_use_sni = (strcmp($2, "yes")==0);
free($2);
}
;
server_https_port: VAR_HTTPS_PORT STRING_ARG
{
OUTYY(("P(server_https_port:%s)\n", $2));
if(atoi($2) == 0)
yyerror("port number expected");
else cfg_parser->cfg->https_port = atoi($2);
free($2);
};
server_http_endpoint: VAR_HTTP_ENDPOINT STRING_ARG
{
OUTYY(("P(server_http_endpoint:%s)\n", $2));
free(cfg_parser->cfg->http_endpoint);
if($2 && $2[0] != '/') {
cfg_parser->cfg->http_endpoint = malloc(strlen($2)+2);
if(!cfg_parser->cfg->http_endpoint)
yyerror("out of memory");
cfg_parser->cfg->http_endpoint[0] = '/';
memmove(cfg_parser->cfg->http_endpoint+1, $2,
strlen($2)+1);
free($2);
} else {
cfg_parser->cfg->http_endpoint = $2;
}
};
server_http_max_streams: VAR_HTTP_MAX_STREAMS STRING_ARG
{
OUTYY(("P(server_http_max_streams:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->http_max_streams = atoi($2);
free($2);
};
server_http_query_buffer_size: VAR_HTTP_QUERY_BUFFER_SIZE STRING_ARG
{
OUTYY(("P(server_http_query_buffer_size:%s)\n", $2));
if(!cfg_parse_memsize($2,
&cfg_parser->cfg->http_query_buffer_size))
yyerror("memory size expected");
free($2);
};
server_http_response_buffer_size: VAR_HTTP_RESPONSE_BUFFER_SIZE STRING_ARG
{
OUTYY(("P(server_http_response_buffer_size:%s)\n", $2));
if(!cfg_parse_memsize($2,
&cfg_parser->cfg->http_response_buffer_size))
yyerror("memory size expected");
free($2);
};
server_http_nodelay: VAR_HTTP_NODELAY STRING_ARG
{
OUTYY(("P(server_http_nodelay:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
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));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->http_notls_downstream = (strcmp($2, "yes")==0);
free($2);
};
server_use_systemd: VAR_USE_SYSTEMD STRING_ARG
{
OUTYY(("P(server_use_systemd:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->use_systemd = (strcmp($2, "yes")==0);
free($2);
}
;
server_do_daemonize: VAR_DO_DAEMONIZE STRING_ARG
{
OUTYY(("P(server_do_daemonize:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->do_daemonize = (strcmp($2, "yes")==0);
free($2);
}
;
server_use_syslog: VAR_USE_SYSLOG STRING_ARG
{
OUTYY(("P(server_use_syslog:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->use_syslog = (strcmp($2, "yes")==0);
#if !defined(HAVE_SYSLOG_H) && !defined(UB_ON_WINDOWS)
if(strcmp($2, "yes") == 0)
yyerror("no syslog services are available. "
"(reconfigure and compile to add)");
#endif
free($2);
}
;
server_log_time_ascii: VAR_LOG_TIME_ASCII STRING_ARG
{
OUTYY(("P(server_log_time_ascii:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->log_time_ascii = (strcmp($2, "yes")==0);
free($2);
}
;
server_log_queries: VAR_LOG_QUERIES STRING_ARG
{
OUTYY(("P(server_log_queries:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->log_queries = (strcmp($2, "yes")==0);
free($2);
}
;
server_log_replies: VAR_LOG_REPLIES STRING_ARG
{
OUTYY(("P(server_log_replies:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->log_replies = (strcmp($2, "yes")==0);
free($2);
}
;
server_log_tag_queryreply: VAR_LOG_TAG_QUERYREPLY STRING_ARG
{
OUTYY(("P(server_log_tag_queryreply:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->log_tag_queryreply = (strcmp($2, "yes")==0);
free($2);
}
;
server_log_servfail: VAR_LOG_SERVFAIL STRING_ARG
{
OUTYY(("P(server_log_servfail:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->log_servfail = (strcmp($2, "yes")==0);
free($2);
}
;
server_log_local_actions: VAR_LOG_LOCAL_ACTIONS STRING_ARG
{
OUTYY(("P(server_log_local_actions:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->log_local_actions = (strcmp($2, "yes")==0);
free($2);
}
;
server_chroot: VAR_CHROOT STRING_ARG
{
OUTYY(("P(server_chroot:%s)\n", $2));
free(cfg_parser->cfg->chrootdir);
cfg_parser->cfg->chrootdir = $2;
}
;
server_username: VAR_USERNAME STRING_ARG
{
OUTYY(("P(server_username:%s)\n", $2));
free(cfg_parser->cfg->username);
cfg_parser->cfg->username = $2;
}
;
server_directory: VAR_DIRECTORY STRING_ARG
{
OUTYY(("P(server_directory:%s)\n", $2));
free(cfg_parser->cfg->directory);
cfg_parser->cfg->directory = $2;
/* change there right away for includes relative to this */
if($2[0]) {
char* d;
#ifdef UB_ON_WINDOWS
w_config_adjust_directory(cfg_parser->cfg);
#endif
d = cfg_parser->cfg->directory;
/* adjust directory if we have already chroot,
* like, we reread after sighup */
if(cfg_parser->chroot && cfg_parser->chroot[0] &&
strncmp(d, cfg_parser->chroot, strlen(
cfg_parser->chroot)) == 0)
d += strlen(cfg_parser->chroot);
if(d[0]) {
if(chdir(d))
log_err("cannot chdir to directory: %s (%s)",
d, strerror(errno));
}
}
}
;
server_logfile: VAR_LOGFILE STRING_ARG
{
OUTYY(("P(server_logfile:%s)\n", $2));
free(cfg_parser->cfg->logfile);
cfg_parser->cfg->logfile = $2;
cfg_parser->cfg->use_syslog = 0;
}
;
server_pidfile: VAR_PIDFILE STRING_ARG
{
OUTYY(("P(server_pidfile:%s)\n", $2));
free(cfg_parser->cfg->pidfile);
cfg_parser->cfg->pidfile = $2;
}
;
server_root_hints: VAR_ROOT_HINTS STRING_ARG
{
OUTYY(("P(server_root_hints:%s)\n", $2));
if(!cfg_strlist_insert(&cfg_parser->cfg->root_hints, $2))
yyerror("out of memory");
}
;
server_dlv_anchor_file: VAR_DLV_ANCHOR_FILE STRING_ARG
{
OUTYY(("P(server_dlv_anchor_file:%s)\n", $2));
log_warn("option dlv-anchor-file ignored: DLV is decommissioned");
free($2);
}
;
server_dlv_anchor: VAR_DLV_ANCHOR STRING_ARG
{
OUTYY(("P(server_dlv_anchor:%s)\n", $2));
log_warn("option dlv-anchor ignored: DLV is decommissioned");
free($2);
}
;
server_auto_trust_anchor_file: VAR_AUTO_TRUST_ANCHOR_FILE STRING_ARG
{
OUTYY(("P(server_auto_trust_anchor_file:%s)\n", $2));
if(!cfg_strlist_insert(&cfg_parser->cfg->
auto_trust_anchor_file_list, $2))
yyerror("out of memory");
}
;
server_trust_anchor_file: VAR_TRUST_ANCHOR_FILE STRING_ARG
{
OUTYY(("P(server_trust_anchor_file:%s)\n", $2));
if(!cfg_strlist_insert(&cfg_parser->cfg->
trust_anchor_file_list, $2))
yyerror("out of memory");
}
;
server_trusted_keys_file: VAR_TRUSTED_KEYS_FILE STRING_ARG
{
OUTYY(("P(server_trusted_keys_file:%s)\n", $2));
if(!cfg_strlist_insert(&cfg_parser->cfg->
trusted_keys_file_list, $2))
yyerror("out of memory");
}
;
server_trust_anchor: VAR_TRUST_ANCHOR STRING_ARG
{
OUTYY(("P(server_trust_anchor:%s)\n", $2));
if(!cfg_strlist_insert(&cfg_parser->cfg->trust_anchor_list, $2))
yyerror("out of memory");
}
;
server_trust_anchor_signaling: VAR_TRUST_ANCHOR_SIGNALING STRING_ARG
{
OUTYY(("P(server_trust_anchor_signaling:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else
cfg_parser->cfg->trust_anchor_signaling =
(strcmp($2, "yes")==0);
free($2);
}
;
server_root_key_sentinel: VAR_ROOT_KEY_SENTINEL STRING_ARG
{
OUTYY(("P(server_root_key_sentinel:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else
cfg_parser->cfg->root_key_sentinel =
(strcmp($2, "yes")==0);
free($2);
}
;
server_domain_insecure: VAR_DOMAIN_INSECURE STRING_ARG
{
OUTYY(("P(server_domain_insecure:%s)\n", $2));
if(!cfg_strlist_insert(&cfg_parser->cfg->domain_insecure, $2))
yyerror("out of memory");
}
;
server_hide_identity: VAR_HIDE_IDENTITY STRING_ARG
{
OUTYY(("P(server_hide_identity:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->hide_identity = (strcmp($2, "yes")==0);
free($2);
}
;
server_hide_version: VAR_HIDE_VERSION STRING_ARG
{
OUTYY(("P(server_hide_version:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->hide_version = (strcmp($2, "yes")==0);
free($2);
}
;
server_hide_trustanchor: VAR_HIDE_TRUSTANCHOR STRING_ARG
{
OUTYY(("P(server_hide_trustanchor:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->hide_trustanchor = (strcmp($2, "yes")==0);
free($2);
}
;
server_hide_http_user_agent: VAR_HIDE_HTTP_USER_AGENT STRING_ARG
{
OUTYY(("P(server_hide_user_agent:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->hide_http_user_agent = (strcmp($2, "yes")==0);
free($2);
}
;
server_identity: VAR_IDENTITY STRING_ARG
{
OUTYY(("P(server_identity:%s)\n", $2));
free(cfg_parser->cfg->identity);
cfg_parser->cfg->identity = $2;
}
;
server_version: VAR_VERSION STRING_ARG
{
OUTYY(("P(server_version:%s)\n", $2));
free(cfg_parser->cfg->version);
cfg_parser->cfg->version = $2;
}
;
server_http_user_agent: VAR_HTTP_USER_AGENT STRING_ARG
{
OUTYY(("P(server_http_user_agent:%s)\n", $2));
free(cfg_parser->cfg->http_user_agent);
cfg_parser->cfg->http_user_agent = $2;
}
;
server_nsid: VAR_NSID STRING_ARG
{
OUTYY(("P(server_nsid:%s)\n", $2));
free(cfg_parser->cfg->nsid_cfg_str);
cfg_parser->cfg->nsid_cfg_str = $2;
free(cfg_parser->cfg->nsid);
cfg_parser->cfg->nsid = NULL;
cfg_parser->cfg->nsid_len = 0;
if (*$2 == 0)
; /* pass; empty string is not setting nsid */
else if (!(cfg_parser->cfg->nsid = cfg_parse_nsid(
$2, &cfg_parser->cfg->nsid_len)))
yyerror("the NSID must be either a hex string or an "
"ascii character string prepended with ascii_.");
}
;
server_so_rcvbuf: VAR_SO_RCVBUF STRING_ARG
{
OUTYY(("P(server_so_rcvbuf:%s)\n", $2));
if(!cfg_parse_memsize($2, &cfg_parser->cfg->so_rcvbuf))
yyerror("buffer size expected");
free($2);
}
;
server_so_sndbuf: VAR_SO_SNDBUF STRING_ARG
{
OUTYY(("P(server_so_sndbuf:%s)\n", $2));
if(!cfg_parse_memsize($2, &cfg_parser->cfg->so_sndbuf))
yyerror("buffer size expected");
free($2);
}
;
server_so_reuseport: VAR_SO_REUSEPORT STRING_ARG
{
OUTYY(("P(server_so_reuseport:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->so_reuseport =
(strcmp($2, "yes")==0);
free($2);
}
;
server_ip_transparent: VAR_IP_TRANSPARENT STRING_ARG
{
OUTYY(("P(server_ip_transparent:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->ip_transparent =
(strcmp($2, "yes")==0);
free($2);
}
;
server_ip_freebind: VAR_IP_FREEBIND STRING_ARG
{
OUTYY(("P(server_ip_freebind:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->ip_freebind =
(strcmp($2, "yes")==0);
free($2);
}
;
server_ip_dscp: VAR_IP_DSCP STRING_ARG
{
OUTYY(("P(server_ip_dscp:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else if (atoi($2) > 63)
yyerror("value too large (max 63)");
else if (atoi($2) < 0)
yyerror("value too small (min 0)");
else
cfg_parser->cfg->ip_dscp = atoi($2);
free($2);
}
;
server_stream_wait_size: VAR_STREAM_WAIT_SIZE STRING_ARG
{
OUTYY(("P(server_stream_wait_size:%s)\n", $2));
if(!cfg_parse_memsize($2, &cfg_parser->cfg->stream_wait_size))
yyerror("memory size expected");
free($2);
}
;
server_edns_buffer_size: VAR_EDNS_BUFFER_SIZE STRING_ARG
{
OUTYY(("P(server_edns_buffer_size:%s)\n", $2));
if(atoi($2) == 0)
yyerror("number expected");
else if (atoi($2) < 12)
yyerror("edns buffer size too small");
else if (atoi($2) > 65535)
cfg_parser->cfg->edns_buffer_size = 65535;
else cfg_parser->cfg->edns_buffer_size = atoi($2);
free($2);
}
;
server_msg_buffer_size: VAR_MSG_BUFFER_SIZE STRING_ARG
{
OUTYY(("P(server_msg_buffer_size:%s)\n", $2));
if(atoi($2) == 0)
yyerror("number expected");
else if (atoi($2) < 4096)
yyerror("message buffer size too small (use 4096)");
else cfg_parser->cfg->msg_buffer_size = atoi($2);
free($2);
}
;
server_msg_cache_size: VAR_MSG_CACHE_SIZE STRING_ARG
{
OUTYY(("P(server_msg_cache_size:%s)\n", $2));
if(!cfg_parse_memsize($2, &cfg_parser->cfg->msg_cache_size))
yyerror("memory size expected");
free($2);
}
;
server_msg_cache_slabs: VAR_MSG_CACHE_SLABS STRING_ARG
{
OUTYY(("P(server_msg_cache_slabs:%s)\n", $2));
if(atoi($2) == 0) {
yyerror("number expected");
} else {
cfg_parser->cfg->msg_cache_slabs = atoi($2);
if(!is_pow2(cfg_parser->cfg->msg_cache_slabs))
yyerror("must be a power of 2");
}
free($2);
}
;
server_num_queries_per_thread: VAR_NUM_QUERIES_PER_THREAD STRING_ARG
{
OUTYY(("P(server_num_queries_per_thread:%s)\n", $2));
if(atoi($2) == 0)
yyerror("number expected");
else cfg_parser->cfg->num_queries_per_thread = atoi($2);
free($2);
}
;
server_jostle_timeout: VAR_JOSTLE_TIMEOUT STRING_ARG
{
OUTYY(("P(server_jostle_timeout:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->jostle_time = atoi($2);
free($2);
}
;
server_delay_close: VAR_DELAY_CLOSE STRING_ARG
{
OUTYY(("P(server_delay_close:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->delay_close = atoi($2);
free($2);
}
;
server_udp_connect: VAR_UDP_CONNECT STRING_ARG
{
OUTYY(("P(server_udp_connect:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->udp_connect = (strcmp($2, "yes")==0);
free($2);
}
;
server_unblock_lan_zones: VAR_UNBLOCK_LAN_ZONES STRING_ARG
{
OUTYY(("P(server_unblock_lan_zones:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->unblock_lan_zones =
(strcmp($2, "yes")==0);
free($2);
}
;
server_insecure_lan_zones: VAR_INSECURE_LAN_ZONES STRING_ARG
{
OUTYY(("P(server_insecure_lan_zones:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->insecure_lan_zones =
(strcmp($2, "yes")==0);
free($2);
}
;
server_rrset_cache_size: VAR_RRSET_CACHE_SIZE STRING_ARG
{
OUTYY(("P(server_rrset_cache_size:%s)\n", $2));
if(!cfg_parse_memsize($2, &cfg_parser->cfg->rrset_cache_size))
yyerror("memory size expected");
free($2);
}
;
server_rrset_cache_slabs: VAR_RRSET_CACHE_SLABS STRING_ARG
{
OUTYY(("P(server_rrset_cache_slabs:%s)\n", $2));
if(atoi($2) == 0) {
yyerror("number expected");
} else {
cfg_parser->cfg->rrset_cache_slabs = atoi($2);
if(!is_pow2(cfg_parser->cfg->rrset_cache_slabs))
yyerror("must be a power of 2");
}
free($2);
}
;
server_infra_host_ttl: VAR_INFRA_HOST_TTL STRING_ARG
{
OUTYY(("P(server_infra_host_ttl:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->host_ttl = atoi($2);
free($2);
}
;
server_infra_lame_ttl: VAR_INFRA_LAME_TTL STRING_ARG
{
OUTYY(("P(server_infra_lame_ttl:%s)\n", $2));
verbose(VERB_DETAIL, "ignored infra-lame-ttl: %s (option "
"removed, use infra-host-ttl)", $2);
free($2);
}
;
server_infra_cache_numhosts: VAR_INFRA_CACHE_NUMHOSTS STRING_ARG
{
OUTYY(("P(server_infra_cache_numhosts:%s)\n", $2));
if(atoi($2) == 0)
yyerror("number expected");
else cfg_parser->cfg->infra_cache_numhosts = atoi($2);
free($2);
}
;
server_infra_cache_lame_size: VAR_INFRA_CACHE_LAME_SIZE STRING_ARG
{
OUTYY(("P(server_infra_cache_lame_size:%s)\n", $2));
verbose(VERB_DETAIL, "ignored infra-cache-lame-size: %s "
"(option removed, use infra-cache-numhosts)", $2);
free($2);
}
;
server_infra_cache_slabs: VAR_INFRA_CACHE_SLABS STRING_ARG
{
OUTYY(("P(server_infra_cache_slabs:%s)\n", $2));
if(atoi($2) == 0) {
yyerror("number expected");
} else {
cfg_parser->cfg->infra_cache_slabs = atoi($2);
if(!is_pow2(cfg_parser->cfg->infra_cache_slabs))
yyerror("must be a power of 2");
}
free($2);
}
;
server_infra_cache_min_rtt: VAR_INFRA_CACHE_MIN_RTT STRING_ARG
{
OUTYY(("P(server_infra_cache_min_rtt:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->infra_cache_min_rtt = atoi($2);
free($2);
}
;
server_infra_cache_max_rtt: VAR_INFRA_CACHE_MAX_RTT STRING_ARG
{
OUTYY(("P(server_infra_cache_max_rtt:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->infra_cache_max_rtt = atoi($2);
free($2);
}
;
server_infra_keep_probing: VAR_INFRA_KEEP_PROBING STRING_ARG
{
OUTYY(("P(server_infra_keep_probing:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->infra_keep_probing =
(strcmp($2, "yes")==0);
free($2);
}
;
server_target_fetch_policy: VAR_TARGET_FETCH_POLICY STRING_ARG
{
OUTYY(("P(server_target_fetch_policy:%s)\n", $2));
free(cfg_parser->cfg->target_fetch_policy);
cfg_parser->cfg->target_fetch_policy = $2;
}
;
server_harden_short_bufsize: VAR_HARDEN_SHORT_BUFSIZE STRING_ARG
{
OUTYY(("P(server_harden_short_bufsize:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->harden_short_bufsize =
(strcmp($2, "yes")==0);
free($2);
}
;
server_harden_large_queries: VAR_HARDEN_LARGE_QUERIES STRING_ARG
{
OUTYY(("P(server_harden_large_queries:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->harden_large_queries =
(strcmp($2, "yes")==0);
free($2);
}
;
server_harden_glue: VAR_HARDEN_GLUE STRING_ARG
{
OUTYY(("P(server_harden_glue:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->harden_glue =
(strcmp($2, "yes")==0);
free($2);
}
;
server_harden_dnssec_stripped: VAR_HARDEN_DNSSEC_STRIPPED STRING_ARG
{
OUTYY(("P(server_harden_dnssec_stripped:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->harden_dnssec_stripped =
(strcmp($2, "yes")==0);
free($2);
}
;
server_harden_below_nxdomain: VAR_HARDEN_BELOW_NXDOMAIN STRING_ARG
{
OUTYY(("P(server_harden_below_nxdomain:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->harden_below_nxdomain =
(strcmp($2, "yes")==0);
free($2);
}
;
server_harden_referral_path: VAR_HARDEN_REFERRAL_PATH STRING_ARG
{
OUTYY(("P(server_harden_referral_path:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->harden_referral_path =
(strcmp($2, "yes")==0);
free($2);
}
;
server_harden_algo_downgrade: VAR_HARDEN_ALGO_DOWNGRADE STRING_ARG
{
OUTYY(("P(server_harden_algo_downgrade:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->harden_algo_downgrade =
(strcmp($2, "yes")==0);
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));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->use_caps_bits_for_id =
(strcmp($2, "yes")==0);
free($2);
}
;
server_caps_whitelist: VAR_CAPS_WHITELIST STRING_ARG
{
OUTYY(("P(server_caps_whitelist:%s)\n", $2));
if(!cfg_strlist_insert(&cfg_parser->cfg->caps_whitelist, $2))
yyerror("out of memory");
}
;
server_private_address: VAR_PRIVATE_ADDRESS STRING_ARG
{
OUTYY(("P(server_private_address:%s)\n", $2));
if(!cfg_strlist_insert(&cfg_parser->cfg->private_address, $2))
yyerror("out of memory");
}
;
server_private_domain: VAR_PRIVATE_DOMAIN STRING_ARG
{
OUTYY(("P(server_private_domain:%s)\n", $2));
if(!cfg_strlist_insert(&cfg_parser->cfg->private_domain, $2))
yyerror("out of memory");
}
;
server_prefetch: VAR_PREFETCH STRING_ARG
{
OUTYY(("P(server_prefetch:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->prefetch = (strcmp($2, "yes")==0);
free($2);
}
;
server_prefetch_key: VAR_PREFETCH_KEY STRING_ARG
{
OUTYY(("P(server_prefetch_key:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->prefetch_key = (strcmp($2, "yes")==0);
free($2);
}
;
server_deny_any: VAR_DENY_ANY STRING_ARG
{
OUTYY(("P(server_deny_any:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->deny_any = (strcmp($2, "yes")==0);
free($2);
}
;
server_unwanted_reply_threshold: VAR_UNWANTED_REPLY_THRESHOLD STRING_ARG
{
OUTYY(("P(server_unwanted_reply_threshold:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->unwanted_threshold = atoi($2);
free($2);
}
;
server_do_not_query_address: VAR_DO_NOT_QUERY_ADDRESS STRING_ARG
{
OUTYY(("P(server_do_not_query_address:%s)\n", $2));
if(!cfg_strlist_insert(&cfg_parser->cfg->donotqueryaddrs, $2))
yyerror("out of memory");
}
;
server_do_not_query_localhost: VAR_DO_NOT_QUERY_LOCALHOST STRING_ARG
{
OUTYY(("P(server_do_not_query_localhost:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->donotquery_localhost =
(strcmp($2, "yes")==0);
free($2);
}
;
server_access_control: VAR_ACCESS_CONTROL STRING_ARG STRING_ARG
{
OUTYY(("P(server_access_control:%s %s)\n", $2, $3));
validate_acl_action($3);
if(!cfg_str2list_insert(&cfg_parser->cfg->acls, $2, $3))
fatal_exit("out of memory adding acl");
}
;
server_interface_action: VAR_INTERFACE_ACTION STRING_ARG STRING_ARG
{
OUTYY(("P(server_interface_action:%s %s)\n", $2, $3));
validate_acl_action($3);
if(!cfg_str2list_insert(
&cfg_parser->cfg->interface_actions, $2, $3))
fatal_exit("out of memory adding acl");
}
;
server_module_conf: VAR_MODULE_CONF STRING_ARG
{
OUTYY(("P(server_module_conf:%s)\n", $2));
free(cfg_parser->cfg->module_conf);
cfg_parser->cfg->module_conf = $2;
}
;
server_val_override_date: VAR_VAL_OVERRIDE_DATE STRING_ARG
{
OUTYY(("P(server_val_override_date:%s)\n", $2));
if(*$2 == '\0' || strcmp($2, "0") == 0) {
cfg_parser->cfg->val_date_override = 0;
} else if(strlen($2) == 14) {
cfg_parser->cfg->val_date_override =
cfg_convert_timeval($2);
if(!cfg_parser->cfg->val_date_override)
yyerror("bad date/time specification");
} else {
if(atoi($2) == 0)
yyerror("number expected");
cfg_parser->cfg->val_date_override = atoi($2);
}
free($2);
}
;
server_val_sig_skew_min: VAR_VAL_SIG_SKEW_MIN STRING_ARG
{
OUTYY(("P(server_val_sig_skew_min:%s)\n", $2));
if(*$2 == '\0' || strcmp($2, "0") == 0) {
cfg_parser->cfg->val_sig_skew_min = 0;
} else {
cfg_parser->cfg->val_sig_skew_min = atoi($2);
if(!cfg_parser->cfg->val_sig_skew_min)
yyerror("number expected");
}
free($2);
}
;
server_val_sig_skew_max: VAR_VAL_SIG_SKEW_MAX STRING_ARG
{
OUTYY(("P(server_val_sig_skew_max:%s)\n", $2));
if(*$2 == '\0' || strcmp($2, "0") == 0) {
cfg_parser->cfg->val_sig_skew_max = 0;
} else {
cfg_parser->cfg->val_sig_skew_max = atoi($2);
if(!cfg_parser->cfg->val_sig_skew_max)
yyerror("number expected");
}
free($2);
}
;
server_val_max_restart: VAR_VAL_MAX_RESTART STRING_ARG
{
OUTYY(("P(server_val_max_restart:%s)\n", $2));
if(*$2 == '\0' || strcmp($2, "0") == 0) {
cfg_parser->cfg->val_max_restart = 0;
} else {
cfg_parser->cfg->val_max_restart = atoi($2);
if(!cfg_parser->cfg->val_max_restart)
yyerror("number expected");
}
free($2);
}
;
server_cache_max_ttl: VAR_CACHE_MAX_TTL STRING_ARG
{
OUTYY(("P(server_cache_max_ttl:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->max_ttl = atoi($2);
free($2);
}
;
server_cache_max_negative_ttl: VAR_CACHE_MAX_NEGATIVE_TTL STRING_ARG
{
OUTYY(("P(server_cache_max_negative_ttl:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->max_negative_ttl = atoi($2);
free($2);
}
;
server_cache_min_ttl: VAR_CACHE_MIN_TTL STRING_ARG
{
OUTYY(("P(server_cache_min_ttl:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->min_ttl = atoi($2);
free($2);
}
;
server_bogus_ttl: VAR_BOGUS_TTL STRING_ARG
{
OUTYY(("P(server_bogus_ttl:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->bogus_ttl = atoi($2);
free($2);
}
;
server_val_clean_additional: VAR_VAL_CLEAN_ADDITIONAL STRING_ARG
{
OUTYY(("P(server_val_clean_additional:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->val_clean_additional =
(strcmp($2, "yes")==0);
free($2);
}
;
server_val_permissive_mode: VAR_VAL_PERMISSIVE_MODE STRING_ARG
{
OUTYY(("P(server_val_permissive_mode:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->val_permissive_mode =
(strcmp($2, "yes")==0);
free($2);
}
;
server_aggressive_nsec: VAR_AGGRESSIVE_NSEC STRING_ARG
{
OUTYY(("P(server_aggressive_nsec:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else
cfg_parser->cfg->aggressive_nsec =
(strcmp($2, "yes")==0);
free($2);
}
;
server_ignore_cd_flag: VAR_IGNORE_CD_FLAG STRING_ARG
{
OUTYY(("P(server_ignore_cd_flag:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->ignore_cd = (strcmp($2, "yes")==0);
free($2);
}
;
+server_disable_edns_do: VAR_DISABLE_EDNS_DO STRING_ARG
+ {
+ OUTYY(("P(server_disable_edns_do:%s)\n", $2));
+ if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+ yyerror("expected yes or no.");
+ else cfg_parser->cfg->disable_edns_do = (strcmp($2, "yes")==0);
+ free($2);
+ }
+ ;
server_serve_expired: VAR_SERVE_EXPIRED STRING_ARG
{
OUTYY(("P(server_serve_expired:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->serve_expired = (strcmp($2, "yes")==0);
free($2);
}
;
server_serve_expired_ttl: VAR_SERVE_EXPIRED_TTL STRING_ARG
{
OUTYY(("P(server_serve_expired_ttl:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->serve_expired_ttl = atoi($2);
free($2);
}
;
server_serve_expired_ttl_reset: VAR_SERVE_EXPIRED_TTL_RESET STRING_ARG
{
OUTYY(("P(server_serve_expired_ttl_reset:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->serve_expired_ttl_reset = (strcmp($2, "yes")==0);
free($2);
}
;
server_serve_expired_reply_ttl: VAR_SERVE_EXPIRED_REPLY_TTL STRING_ARG
{
OUTYY(("P(server_serve_expired_reply_ttl:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->serve_expired_reply_ttl = atoi($2);
free($2);
}
;
server_serve_expired_client_timeout: VAR_SERVE_EXPIRED_CLIENT_TIMEOUT STRING_ARG
{
OUTYY(("P(server_serve_expired_client_timeout:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->serve_expired_client_timeout = atoi($2);
free($2);
}
;
server_ede_serve_expired: VAR_EDE_SERVE_EXPIRED STRING_ARG
{
OUTYY(("P(server_ede_serve_expired:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->ede_serve_expired = (strcmp($2, "yes")==0);
free($2);
}
;
server_serve_original_ttl: VAR_SERVE_ORIGINAL_TTL STRING_ARG
{
OUTYY(("P(server_serve_original_ttl:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->serve_original_ttl = (strcmp($2, "yes")==0);
free($2);
}
;
server_fake_dsa: VAR_FAKE_DSA STRING_ARG
{
OUTYY(("P(server_fake_dsa:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
#if defined(HAVE_SSL) || defined(HAVE_NETTLE)
else fake_dsa = (strcmp($2, "yes")==0);
if(fake_dsa)
log_warn("test option fake_dsa is enabled");
#endif
free($2);
}
;
server_fake_sha1: VAR_FAKE_SHA1 STRING_ARG
{
OUTYY(("P(server_fake_sha1:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
#if defined(HAVE_SSL) || defined(HAVE_NETTLE)
else fake_sha1 = (strcmp($2, "yes")==0);
if(fake_sha1)
log_warn("test option fake_sha1 is enabled");
#endif
free($2);
}
;
server_val_log_level: VAR_VAL_LOG_LEVEL STRING_ARG
{
OUTYY(("P(server_val_log_level:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->val_log_level = atoi($2);
free($2);
}
;
server_val_nsec3_keysize_iterations: VAR_VAL_NSEC3_KEYSIZE_ITERATIONS STRING_ARG
{
OUTYY(("P(server_val_nsec3_keysize_iterations:%s)\n", $2));
free(cfg_parser->cfg->val_nsec3_key_iterations);
cfg_parser->cfg->val_nsec3_key_iterations = $2;
}
;
server_zonemd_permissive_mode: VAR_ZONEMD_PERMISSIVE_MODE STRING_ARG
{
OUTYY(("P(server_zonemd_permissive_mode:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->zonemd_permissive_mode = (strcmp($2, "yes")==0);
free($2);
}
;
server_add_holddown: VAR_ADD_HOLDDOWN STRING_ARG
{
OUTYY(("P(server_add_holddown:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->add_holddown = atoi($2);
free($2);
}
;
server_del_holddown: VAR_DEL_HOLDDOWN STRING_ARG
{
OUTYY(("P(server_del_holddown:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->del_holddown = atoi($2);
free($2);
}
;
server_keep_missing: VAR_KEEP_MISSING STRING_ARG
{
OUTYY(("P(server_keep_missing:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->keep_missing = atoi($2);
free($2);
}
;
server_permit_small_holddown: VAR_PERMIT_SMALL_HOLDDOWN STRING_ARG
{
OUTYY(("P(server_permit_small_holddown:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->permit_small_holddown =
(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));
if(!cfg_parse_memsize($2, &cfg_parser->cfg->key_cache_size))
yyerror("memory size expected");
free($2);
}
;
server_key_cache_slabs: VAR_KEY_CACHE_SLABS STRING_ARG
{
OUTYY(("P(server_key_cache_slabs:%s)\n", $2));
if(atoi($2) == 0) {
yyerror("number expected");
} else {
cfg_parser->cfg->key_cache_slabs = atoi($2);
if(!is_pow2(cfg_parser->cfg->key_cache_slabs))
yyerror("must be a power of 2");
}
free($2);
}
;
server_neg_cache_size: VAR_NEG_CACHE_SIZE STRING_ARG
{
OUTYY(("P(server_neg_cache_size:%s)\n", $2));
if(!cfg_parse_memsize($2, &cfg_parser->cfg->neg_cache_size))
yyerror("memory size expected");
free($2);
}
;
server_local_zone: VAR_LOCAL_ZONE STRING_ARG STRING_ARG
{
OUTYY(("P(server_local_zone:%s %s)\n", $2, $3));
if(strcmp($3, "static")!=0 && strcmp($3, "deny")!=0 &&
strcmp($3, "refuse")!=0 && strcmp($3, "redirect")!=0 &&
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
&& strcmp($3, "always_deny")!=0
&& strcmp($3, "always_null")!=0
&& strcmp($3, "noview")!=0
&& strcmp($3, "inform")!=0 && strcmp($3, "inform_deny")!=0
&& strcmp($3, "inform_redirect") != 0
&& strcmp($3, "ipset") != 0) {
yyerror("local-zone type: expected static, deny, "
"refuse, redirect, transparent, "
"typetransparent, inform, inform_deny, "
"inform_redirect, always_transparent, block_a,"
"always_refuse, always_nxdomain, "
"always_nodata, always_deny, always_null, "
"noview, nodefault or ipset");
free($2);
free($3);
} else if(strcmp($3, "nodefault")==0) {
if(!cfg_strlist_insert(&cfg_parser->cfg->
local_zones_nodefault, $2))
fatal_exit("out of memory adding local-zone");
free($3);
#ifdef USE_IPSET
} else if(strcmp($3, "ipset")==0) {
size_t len = strlen($2);
/* Make sure to add the trailing dot.
* These are str compared to domain names. */
if($2[len-1] != '.') {
if(!($2 = realloc($2, len+2))) {
fatal_exit("out of memory adding local-zone");
}
$2[len] = '.';
$2[len+1] = 0;
}
if(!cfg_strlist_insert(&cfg_parser->cfg->
local_zones_ipset, $2))
fatal_exit("out of memory adding local-zone");
free($3);
#endif
} else {
if(!cfg_str2list_insert(&cfg_parser->cfg->local_zones,
$2, $3))
fatal_exit("out of memory adding local-zone");
}
}
;
server_local_data: VAR_LOCAL_DATA STRING_ARG
{
OUTYY(("P(server_local_data:%s)\n", $2));
if(!cfg_strlist_insert(&cfg_parser->cfg->local_data, $2))
fatal_exit("out of memory adding local-data");
}
;
server_local_data_ptr: VAR_LOCAL_DATA_PTR STRING_ARG
{
char* ptr;
OUTYY(("P(server_local_data_ptr:%s)\n", $2));
ptr = cfg_ptr_reverse($2);
free($2);
if(ptr) {
if(!cfg_strlist_insert(&cfg_parser->cfg->
local_data, ptr))
fatal_exit("out of memory adding local-data");
} else {
yyerror("local-data-ptr could not be reversed");
}
}
;
server_minimal_responses: VAR_MINIMAL_RESPONSES STRING_ARG
{
OUTYY(("P(server_minimal_responses:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->minimal_responses =
(strcmp($2, "yes")==0);
free($2);
}
;
server_rrset_roundrobin: VAR_RRSET_ROUNDROBIN STRING_ARG
{
OUTYY(("P(server_rrset_roundrobin:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->rrset_roundrobin =
(strcmp($2, "yes")==0);
free($2);
}
;
server_unknown_server_time_limit: VAR_UNKNOWN_SERVER_TIME_LIMIT STRING_ARG
{
OUTYY(("P(server_unknown_server_time_limit:%s)\n", $2));
cfg_parser->cfg->unknown_server_time_limit = atoi($2);
free($2);
}
;
server_max_udp_size: VAR_MAX_UDP_SIZE STRING_ARG
{
OUTYY(("P(server_max_udp_size:%s)\n", $2));
cfg_parser->cfg->max_udp_size = atoi($2);
free($2);
}
;
server_dns64_prefix: VAR_DNS64_PREFIX STRING_ARG
{
OUTYY(("P(dns64_prefix:%s)\n", $2));
free(cfg_parser->cfg->dns64_prefix);
cfg_parser->cfg->dns64_prefix = $2;
}
;
server_dns64_synthall: VAR_DNS64_SYNTHALL STRING_ARG
{
OUTYY(("P(server_dns64_synthall:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->dns64_synthall = (strcmp($2, "yes")==0);
free($2);
}
;
server_dns64_ignore_aaaa: VAR_DNS64_IGNORE_AAAA STRING_ARG
{
OUTYY(("P(dns64_ignore_aaaa:%s)\n", $2));
if(!cfg_strlist_insert(&cfg_parser->cfg->dns64_ignore_aaaa,
$2))
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;
OUTYY(("P(server_define_tag:%s)\n", $2));
while((p=strsep(&s, " \t\n")) != NULL) {
if(*p) {
if(!config_add_tag(cfg_parser->cfg, p))
yyerror("could not define-tag, "
"out of memory");
}
}
free($2);
}
;
server_local_zone_tag: VAR_LOCAL_ZONE_TAG STRING_ARG STRING_ARG
{
size_t len = 0;
uint8_t* bitlist = config_parse_taglist(cfg_parser->cfg, $3,
&len);
free($3);
OUTYY(("P(server_local_zone_tag:%s)\n", $2));
if(!bitlist) {
yyerror("could not parse tags, (define-tag them first)");
free($2);
}
if(bitlist) {
if(!cfg_strbytelist_insert(
&cfg_parser->cfg->local_zone_tags,
$2, bitlist, len)) {
yyerror("out of memory");
free($2);
}
}
}
;
server_access_control_tag: VAR_ACCESS_CONTROL_TAG STRING_ARG STRING_ARG
{
size_t len = 0;
uint8_t* bitlist = config_parse_taglist(cfg_parser->cfg, $3,
&len);
free($3);
OUTYY(("P(server_access_control_tag:%s)\n", $2));
if(!bitlist) {
yyerror("could not parse tags, (define-tag them first)");
free($2);
}
if(bitlist) {
if(!cfg_strbytelist_insert(
&cfg_parser->cfg->acl_tags,
$2, bitlist, len)) {
yyerror("out of memory");
free($2);
}
}
}
;
server_access_control_tag_action: VAR_ACCESS_CONTROL_TAG_ACTION STRING_ARG STRING_ARG STRING_ARG
{
OUTYY(("P(server_access_control_tag_action:%s %s %s)\n", $2, $3, $4));
if(!cfg_str3list_insert(&cfg_parser->cfg->acl_tag_actions,
$2, $3, $4)) {
yyerror("out of memory");
free($2);
free($3);
free($4);
}
}
;
server_access_control_tag_data: VAR_ACCESS_CONTROL_TAG_DATA STRING_ARG STRING_ARG STRING_ARG
{
OUTYY(("P(server_access_control_tag_data:%s %s %s)\n", $2, $3, $4));
if(!cfg_str3list_insert(&cfg_parser->cfg->acl_tag_datas,
$2, $3, $4)) {
yyerror("out of memory");
free($2);
free($3);
free($4);
}
}
;
server_local_zone_override: VAR_LOCAL_ZONE_OVERRIDE STRING_ARG STRING_ARG STRING_ARG
{
OUTYY(("P(server_local_zone_override:%s %s %s)\n", $2, $3, $4));
if(!cfg_str3list_insert(&cfg_parser->cfg->local_zone_overrides,
$2, $3, $4)) {
yyerror("out of memory");
free($2);
free($3);
free($4);
}
}
;
server_access_control_view: VAR_ACCESS_CONTROL_VIEW STRING_ARG STRING_ARG
{
OUTYY(("P(server_access_control_view:%s %s)\n", $2, $3));
if(!cfg_str2list_insert(&cfg_parser->cfg->acl_view,
$2, $3)) {
yyerror("out of memory");
}
}
;
server_interface_tag: VAR_INTERFACE_TAG STRING_ARG STRING_ARG
{
size_t len = 0;
uint8_t* bitlist = config_parse_taglist(cfg_parser->cfg, $3,
&len);
free($3);
OUTYY(("P(server_interface_tag:%s)\n", $2));
if(!bitlist) {
yyerror("could not parse tags, (define-tag them first)");
free($2);
}
if(bitlist) {
if(!cfg_strbytelist_insert(
&cfg_parser->cfg->interface_tags,
$2, bitlist, len)) {
yyerror("out of memory");
free($2);
}
}
}
;
server_interface_tag_action: VAR_INTERFACE_TAG_ACTION STRING_ARG STRING_ARG STRING_ARG
{
OUTYY(("P(server_interface_tag_action:%s %s %s)\n", $2, $3, $4));
if(!cfg_str3list_insert(&cfg_parser->cfg->interface_tag_actions,
$2, $3, $4)) {
yyerror("out of memory");
free($2);
free($3);
free($4);
}
}
;
server_interface_tag_data: VAR_INTERFACE_TAG_DATA STRING_ARG STRING_ARG STRING_ARG
{
OUTYY(("P(server_interface_tag_data:%s %s %s)\n", $2, $3, $4));
if(!cfg_str3list_insert(&cfg_parser->cfg->interface_tag_datas,
$2, $3, $4)) {
yyerror("out of memory");
free($2);
free($3);
free($4);
}
}
;
server_interface_view: VAR_INTERFACE_VIEW STRING_ARG STRING_ARG
{
OUTYY(("P(server_interface_view:%s %s)\n", $2, $3));
if(!cfg_str2list_insert(&cfg_parser->cfg->interface_view,
$2, $3)) {
yyerror("out of memory");
}
}
;
server_response_ip_tag: VAR_RESPONSE_IP_TAG STRING_ARG STRING_ARG
{
size_t len = 0;
uint8_t* bitlist = config_parse_taglist(cfg_parser->cfg, $3,
&len);
free($3);
OUTYY(("P(response_ip_tag:%s)\n", $2));
if(!bitlist) {
yyerror("could not parse tags, (define-tag them first)");
free($2);
}
if(bitlist) {
if(!cfg_strbytelist_insert(
&cfg_parser->cfg->respip_tags,
$2, bitlist, len)) {
yyerror("out of memory");
free($2);
}
}
}
;
server_ip_ratelimit: VAR_IP_RATELIMIT STRING_ARG
{
OUTYY(("P(server_ip_ratelimit:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->ip_ratelimit = atoi($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));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->ratelimit = atoi($2);
free($2);
}
;
server_ip_ratelimit_size: VAR_IP_RATELIMIT_SIZE STRING_ARG
{
OUTYY(("P(server_ip_ratelimit_size:%s)\n", $2));
if(!cfg_parse_memsize($2, &cfg_parser->cfg->ip_ratelimit_size))
yyerror("memory size expected");
free($2);
}
;
server_ratelimit_size: VAR_RATELIMIT_SIZE STRING_ARG
{
OUTYY(("P(server_ratelimit_size:%s)\n", $2));
if(!cfg_parse_memsize($2, &cfg_parser->cfg->ratelimit_size))
yyerror("memory size expected");
free($2);
}
;
server_ip_ratelimit_slabs: VAR_IP_RATELIMIT_SLABS STRING_ARG
{
OUTYY(("P(server_ip_ratelimit_slabs:%s)\n", $2));
if(atoi($2) == 0) {
yyerror("number expected");
} else {
cfg_parser->cfg->ip_ratelimit_slabs = atoi($2);
if(!is_pow2(cfg_parser->cfg->ip_ratelimit_slabs))
yyerror("must be a power of 2");
}
free($2);
}
;
server_ratelimit_slabs: VAR_RATELIMIT_SLABS STRING_ARG
{
OUTYY(("P(server_ratelimit_slabs:%s)\n", $2));
if(atoi($2) == 0) {
yyerror("number expected");
} else {
cfg_parser->cfg->ratelimit_slabs = atoi($2);
if(!is_pow2(cfg_parser->cfg->ratelimit_slabs))
yyerror("must be a power of 2");
}
free($2);
}
;
server_ratelimit_for_domain: VAR_RATELIMIT_FOR_DOMAIN STRING_ARG STRING_ARG
{
OUTYY(("P(server_ratelimit_for_domain:%s %s)\n", $2, $3));
if(atoi($3) == 0 && strcmp($3, "0") != 0) {
yyerror("number expected");
free($2);
free($3);
} else {
if(!cfg_str2list_insert(&cfg_parser->cfg->
ratelimit_for_domain, $2, $3))
fatal_exit("out of memory adding "
"ratelimit-for-domain");
}
}
;
server_ratelimit_below_domain: VAR_RATELIMIT_BELOW_DOMAIN STRING_ARG STRING_ARG
{
OUTYY(("P(server_ratelimit_below_domain:%s %s)\n", $2, $3));
if(atoi($3) == 0 && strcmp($3, "0") != 0) {
yyerror("number expected");
free($2);
free($3);
} else {
if(!cfg_str2list_insert(&cfg_parser->cfg->
ratelimit_below_domain, $2, $3))
fatal_exit("out of memory adding "
"ratelimit-below-domain");
}
}
;
server_ip_ratelimit_factor: VAR_IP_RATELIMIT_FACTOR STRING_ARG
{
OUTYY(("P(server_ip_ratelimit_factor:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->ip_ratelimit_factor = atoi($2);
free($2);
}
;
server_ratelimit_factor: VAR_RATELIMIT_FACTOR STRING_ARG
{
OUTYY(("P(server_ratelimit_factor:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->ratelimit_factor = atoi($2);
free($2);
}
;
server_ip_ratelimit_backoff: VAR_IP_RATELIMIT_BACKOFF STRING_ARG
{
OUTYY(("P(server_ip_ratelimit_backoff:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->ip_ratelimit_backoff =
(strcmp($2, "yes")==0);
free($2);
}
;
server_ratelimit_backoff: VAR_RATELIMIT_BACKOFF STRING_ARG
{
OUTYY(("P(server_ratelimit_backoff:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->ratelimit_backoff =
(strcmp($2, "yes")==0);
free($2);
}
;
server_outbound_msg_retry: VAR_OUTBOUND_MSG_RETRY STRING_ARG
{
OUTYY(("P(server_outbound_msg_retry:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->outbound_msg_retry = atoi($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"));
free($2);
}
;
server_fast_server_num: VAR_FAST_SERVER_NUM STRING_ARG
{
OUTYY(("P(server_fast_server_num:%s)\n", $2));
if(atoi($2) <= 0)
yyerror("number expected");
else cfg_parser->cfg->fast_server_num = atoi($2);
free($2);
}
;
server_fast_server_permil: VAR_FAST_SERVER_PERMIL STRING_ARG
{
OUTYY(("P(server_fast_server_permil:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->fast_server_permil = atoi($2);
free($2);
}
;
server_qname_minimisation: VAR_QNAME_MINIMISATION STRING_ARG
{
OUTYY(("P(server_qname_minimisation:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->qname_minimisation =
(strcmp($2, "yes")==0);
free($2);
}
;
server_qname_minimisation_strict: VAR_QNAME_MINIMISATION_STRICT STRING_ARG
{
OUTYY(("P(server_qname_minimisation_strict:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->qname_minimisation_strict =
(strcmp($2, "yes")==0);
free($2);
}
;
server_pad_responses: VAR_PAD_RESPONSES STRING_ARG
{
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 =
(strcmp($2, "yes")==0);
free($2);
}
;
server_pad_responses_block_size: VAR_PAD_RESPONSES_BLOCK_SIZE STRING_ARG
{
OUTYY(("P(server_pad_responses_block_size:%s)\n", $2));
if(atoi($2) == 0)
yyerror("number expected");
else cfg_parser->cfg->pad_responses_block_size = atoi($2);
free($2);
}
;
server_pad_queries: VAR_PAD_QUERIES STRING_ARG
{
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 =
(strcmp($2, "yes")==0);
free($2);
}
;
server_pad_queries_block_size: VAR_PAD_QUERIES_BLOCK_SIZE STRING_ARG
{
OUTYY(("P(server_pad_queries_block_size:%s)\n", $2));
if(atoi($2) == 0)
yyerror("number expected");
else cfg_parser->cfg->pad_queries_block_size = atoi($2);
free($2);
}
;
server_ipsecmod_enabled: VAR_IPSECMOD_ENABLED STRING_ARG
{
#ifdef USE_IPSECMOD
OUTYY(("P(server_ipsecmod_enabled:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->ipsecmod_enabled = (strcmp($2, "yes")==0);
#else
OUTYY(("P(Compiled without IPsec module, ignoring)\n"));
#endif
free($2);
}
;
server_ipsecmod_ignore_bogus: VAR_IPSECMOD_IGNORE_BOGUS STRING_ARG
{
#ifdef USE_IPSECMOD
OUTYY(("P(server_ipsecmod_ignore_bogus:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->ipsecmod_ignore_bogus = (strcmp($2, "yes")==0);
#else
OUTYY(("P(Compiled without IPsec module, ignoring)\n"));
#endif
free($2);
}
;
server_ipsecmod_hook: VAR_IPSECMOD_HOOK STRING_ARG
{
#ifdef USE_IPSECMOD
OUTYY(("P(server_ipsecmod_hook:%s)\n", $2));
free(cfg_parser->cfg->ipsecmod_hook);
cfg_parser->cfg->ipsecmod_hook = $2;
#else
OUTYY(("P(Compiled without IPsec module, ignoring)\n"));
free($2);
#endif
}
;
server_ipsecmod_max_ttl: VAR_IPSECMOD_MAX_TTL STRING_ARG
{
#ifdef USE_IPSECMOD
OUTYY(("P(server_ipsecmod_max_ttl:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->ipsecmod_max_ttl = atoi($2);
free($2);
#else
OUTYY(("P(Compiled without IPsec module, ignoring)\n"));
free($2);
#endif
}
;
server_ipsecmod_whitelist: VAR_IPSECMOD_WHITELIST STRING_ARG
{
#ifdef USE_IPSECMOD
OUTYY(("P(server_ipsecmod_whitelist:%s)\n", $2));
if(!cfg_strlist_insert(&cfg_parser->cfg->ipsecmod_whitelist, $2))
yyerror("out of memory");
#else
OUTYY(("P(Compiled without IPsec module, ignoring)\n"));
free($2);
#endif
}
;
server_ipsecmod_strict: VAR_IPSECMOD_STRICT STRING_ARG
{
#ifdef USE_IPSECMOD
OUTYY(("P(server_ipsecmod_strict:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->ipsecmod_strict = (strcmp($2, "yes")==0);
free($2);
#else
OUTYY(("P(Compiled without IPsec module, ignoring)\n"));
free($2);
#endif
}
;
server_edns_client_string: VAR_EDNS_CLIENT_STRING STRING_ARG STRING_ARG
{
OUTYY(("P(server_edns_client_string:%s %s)\n", $2, $3));
if(!cfg_str2list_insert(
&cfg_parser->cfg->edns_client_strings, $2, $3))
fatal_exit("out of memory adding "
"edns-client-string");
}
;
server_edns_client_string_opcode: VAR_EDNS_CLIENT_STRING_OPCODE STRING_ARG
{
OUTYY(("P(edns_client_string_opcode:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("option code expected");
else if(atoi($2) > 65535 || atoi($2) < 0)
yyerror("option code must be in interval [0, 65535]");
else cfg_parser->cfg->edns_client_string_opcode = atoi($2);
free($2);
}
;
server_ede: VAR_EDE STRING_ARG
{
OUTYY(("P(server_ede:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->ede = (strcmp($2, "yes")==0);
free($2);
}
;
server_proxy_protocol_port: VAR_PROXY_PROTOCOL_PORT STRING_ARG
{
OUTYY(("P(server_proxy_protocol_port:%s)\n", $2));
if(!cfg_strlist_insert(&cfg_parser->cfg->proxy_protocol_port, $2))
yyerror("out of memory");
}
;
stub_name: VAR_NAME STRING_ARG
{
OUTYY(("P(name:%s)\n", $2));
if(cfg_parser->cfg->stubs->name)
yyerror("stub name override, there must be one name "
"for one stub-zone");
free(cfg_parser->cfg->stubs->name);
cfg_parser->cfg->stubs->name = $2;
}
;
stub_host: VAR_STUB_HOST STRING_ARG
{
OUTYY(("P(stub-host:%s)\n", $2));
if(!cfg_strlist_insert(&cfg_parser->cfg->stubs->hosts, $2))
yyerror("out of memory");
}
;
stub_addr: VAR_STUB_ADDR STRING_ARG
{
OUTYY(("P(stub-addr:%s)\n", $2));
if(!cfg_strlist_insert(&cfg_parser->cfg->stubs->addrs, $2))
yyerror("out of memory");
}
;
stub_first: VAR_STUB_FIRST STRING_ARG
{
OUTYY(("P(stub-first:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->stubs->isfirst=(strcmp($2, "yes")==0);
free($2);
}
;
stub_no_cache: VAR_STUB_NO_CACHE STRING_ARG
{
OUTYY(("P(stub-no-cache:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->stubs->no_cache=(strcmp($2, "yes")==0);
free($2);
}
;
stub_ssl_upstream: VAR_STUB_SSL_UPSTREAM STRING_ARG
{
OUTYY(("P(stub-ssl-upstream:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->stubs->ssl_upstream =
(strcmp($2, "yes")==0);
free($2);
}
;
stub_tcp_upstream: VAR_STUB_TCP_UPSTREAM STRING_ARG
{
OUTYY(("P(stub-tcp-upstream:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->stubs->tcp_upstream =
(strcmp($2, "yes")==0);
free($2);
}
;
stub_prime: VAR_STUB_PRIME STRING_ARG
{
OUTYY(("P(stub-prime:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->stubs->isprime =
(strcmp($2, "yes")==0);
free($2);
}
;
forward_name: VAR_NAME STRING_ARG
{
OUTYY(("P(name:%s)\n", $2));
if(cfg_parser->cfg->forwards->name)
yyerror("forward name override, there must be one "
"name for one forward-zone");
free(cfg_parser->cfg->forwards->name);
cfg_parser->cfg->forwards->name = $2;
}
;
forward_host: VAR_FORWARD_HOST STRING_ARG
{
OUTYY(("P(forward-host:%s)\n", $2));
if(!cfg_strlist_insert(&cfg_parser->cfg->forwards->hosts, $2))
yyerror("out of memory");
}
;
forward_addr: VAR_FORWARD_ADDR STRING_ARG
{
OUTYY(("P(forward-addr:%s)\n", $2));
if(!cfg_strlist_insert(&cfg_parser->cfg->forwards->addrs, $2))
yyerror("out of memory");
}
;
forward_first: VAR_FORWARD_FIRST STRING_ARG
{
OUTYY(("P(forward-first:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->forwards->isfirst=(strcmp($2, "yes")==0);
free($2);
}
;
forward_no_cache: VAR_FORWARD_NO_CACHE STRING_ARG
{
OUTYY(("P(forward-no-cache:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->forwards->no_cache=(strcmp($2, "yes")==0);
free($2);
}
;
forward_ssl_upstream: VAR_FORWARD_SSL_UPSTREAM STRING_ARG
{
OUTYY(("P(forward-ssl-upstream:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->forwards->ssl_upstream =
(strcmp($2, "yes")==0);
free($2);
}
;
forward_tcp_upstream: VAR_FORWARD_TCP_UPSTREAM STRING_ARG
{
OUTYY(("P(forward-tcp-upstream:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->forwards->tcp_upstream =
(strcmp($2, "yes")==0);
free($2);
}
;
auth_name: VAR_NAME STRING_ARG
{
OUTYY(("P(name:%s)\n", $2));
if(cfg_parser->cfg->auths->name)
yyerror("auth name override, there must be one name "
"for one auth-zone");
free(cfg_parser->cfg->auths->name);
cfg_parser->cfg->auths->name = $2;
}
;
auth_zonefile: VAR_ZONEFILE STRING_ARG
{
OUTYY(("P(zonefile:%s)\n", $2));
free(cfg_parser->cfg->auths->zonefile);
cfg_parser->cfg->auths->zonefile = $2;
}
;
auth_master: VAR_MASTER STRING_ARG
{
OUTYY(("P(master:%s)\n", $2));
if(!cfg_strlist_insert(&cfg_parser->cfg->auths->masters, $2))
yyerror("out of memory");
}
;
auth_url: VAR_URL STRING_ARG
{
OUTYY(("P(url:%s)\n", $2));
if(!cfg_strlist_insert(&cfg_parser->cfg->auths->urls, $2))
yyerror("out of memory");
}
;
auth_allow_notify: VAR_ALLOW_NOTIFY STRING_ARG
{
OUTYY(("P(allow-notify:%s)\n", $2));
if(!cfg_strlist_insert(&cfg_parser->cfg->auths->allow_notify,
$2))
yyerror("out of memory");
}
;
auth_zonemd_check: VAR_ZONEMD_CHECK STRING_ARG
{
OUTYY(("P(zonemd-check:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->auths->zonemd_check =
(strcmp($2, "yes")==0);
free($2);
}
;
auth_zonemd_reject_absence: VAR_ZONEMD_REJECT_ABSENCE STRING_ARG
{
OUTYY(("P(zonemd-reject-absence:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->auths->zonemd_reject_absence =
(strcmp($2, "yes")==0);
free($2);
}
;
auth_for_downstream: VAR_FOR_DOWNSTREAM STRING_ARG
{
OUTYY(("P(for-downstream:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->auths->for_downstream =
(strcmp($2, "yes")==0);
free($2);
}
;
auth_for_upstream: VAR_FOR_UPSTREAM STRING_ARG
{
OUTYY(("P(for-upstream:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->auths->for_upstream =
(strcmp($2, "yes")==0);
free($2);
}
;
auth_fallback_enabled: VAR_FALLBACK_ENABLED STRING_ARG
{
OUTYY(("P(fallback-enabled:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->auths->fallback_enabled =
(strcmp($2, "yes")==0);
free($2);
}
;
view_name: VAR_NAME STRING_ARG
{
OUTYY(("P(name:%s)\n", $2));
if(cfg_parser->cfg->views->name)
yyerror("view name override, there must be one "
"name for one view");
free(cfg_parser->cfg->views->name);
cfg_parser->cfg->views->name = $2;
}
;
view_local_zone: VAR_LOCAL_ZONE STRING_ARG STRING_ARG
{
OUTYY(("P(view_local_zone:%s %s)\n", $2, $3));
if(strcmp($3, "static")!=0 && strcmp($3, "deny")!=0 &&
strcmp($3, "refuse")!=0 && strcmp($3, "redirect")!=0 &&
strcmp($3, "transparent")!=0 && strcmp($3, "nodefault")!=0
&& strcmp($3, "typetransparent")!=0
&& strcmp($3, "always_transparent")!=0
&& strcmp($3, "always_refuse")!=0
&& strcmp($3, "always_nxdomain")!=0
&& strcmp($3, "always_nodata")!=0
&& strcmp($3, "always_deny")!=0
&& strcmp($3, "always_null")!=0
&& strcmp($3, "noview")!=0
&& strcmp($3, "inform")!=0 && strcmp($3, "inform_deny")!=0
&& strcmp($3, "inform_redirect") != 0
&& strcmp($3, "ipset") != 0) {
yyerror("local-zone type: expected static, deny, "
"refuse, redirect, transparent, "
"typetransparent, inform, inform_deny, "
"inform_redirect, always_transparent, "
"always_refuse, always_nxdomain, "
"always_nodata, always_deny, always_null, "
"noview, nodefault or ipset");
free($2);
free($3);
} else if(strcmp($3, "nodefault")==0) {
if(!cfg_strlist_insert(&cfg_parser->cfg->views->
local_zones_nodefault, $2))
fatal_exit("out of memory adding local-zone");
free($3);
#ifdef USE_IPSET
} else if(strcmp($3, "ipset")==0) {
size_t len = strlen($2);
/* Make sure to add the trailing dot.
* These are str compared to domain names. */
if($2[len-1] != '.') {
if(!($2 = realloc($2, len+2))) {
fatal_exit("out of memory adding local-zone");
}
$2[len] = '.';
$2[len+1] = 0;
}
if(!cfg_strlist_insert(&cfg_parser->cfg->views->
local_zones_ipset, $2))
fatal_exit("out of memory adding local-zone");
free($3);
#endif
} else {
if(!cfg_str2list_insert(
&cfg_parser->cfg->views->local_zones,
$2, $3))
fatal_exit("out of memory adding local-zone");
}
}
;
view_response_ip: VAR_RESPONSE_IP STRING_ARG STRING_ARG
{
OUTYY(("P(view_response_ip:%s %s)\n", $2, $3));
validate_respip_action($3);
if(!cfg_str2list_insert(
&cfg_parser->cfg->views->respip_actions, $2, $3))
fatal_exit("out of memory adding per-view "
"response-ip action");
}
;
view_response_ip_data: VAR_RESPONSE_IP_DATA STRING_ARG STRING_ARG
{
OUTYY(("P(view_response_ip_data:%s)\n", $2));
if(!cfg_str2list_insert(
&cfg_parser->cfg->views->respip_data, $2, $3))
fatal_exit("out of memory adding response-ip-data");
}
;
view_local_data: VAR_LOCAL_DATA STRING_ARG
{
OUTYY(("P(view_local_data:%s)\n", $2));
if(!cfg_strlist_insert(&cfg_parser->cfg->views->local_data, $2)) {
fatal_exit("out of memory adding local-data");
}
}
;
view_local_data_ptr: VAR_LOCAL_DATA_PTR STRING_ARG
{
char* ptr;
OUTYY(("P(view_local_data_ptr:%s)\n", $2));
ptr = cfg_ptr_reverse($2);
free($2);
if(ptr) {
if(!cfg_strlist_insert(&cfg_parser->cfg->views->
local_data, ptr))
fatal_exit("out of memory adding local-data");
} else {
yyerror("local-data-ptr could not be reversed");
}
}
;
view_first: VAR_VIEW_FIRST STRING_ARG
{
OUTYY(("P(view-first:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->views->isfirst=(strcmp($2, "yes")==0);
free($2);
}
;
rcstart: VAR_REMOTE_CONTROL
{
OUTYY(("\nP(remote-control:)\n"));
cfg_parser->started_toplevel = 1;
}
;
contents_rc: contents_rc content_rc
| ;
content_rc: rc_control_enable | rc_control_interface | rc_control_port |
rc_server_key_file | rc_server_cert_file | rc_control_key_file |
rc_control_cert_file | rc_control_use_cert
;
rc_control_enable: VAR_CONTROL_ENABLE STRING_ARG
{
OUTYY(("P(control_enable:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->remote_control_enable =
(strcmp($2, "yes")==0);
free($2);
}
;
rc_control_port: VAR_CONTROL_PORT STRING_ARG
{
OUTYY(("P(control_port:%s)\n", $2));
if(atoi($2) == 0)
yyerror("control port number expected");
else cfg_parser->cfg->control_port = atoi($2);
free($2);
}
;
rc_control_interface: VAR_CONTROL_INTERFACE STRING_ARG
{
OUTYY(("P(control_interface:%s)\n", $2));
if(!cfg_strlist_append(&cfg_parser->cfg->control_ifs, $2))
yyerror("out of memory");
}
;
rc_control_use_cert: VAR_CONTROL_USE_CERT STRING_ARG
{
OUTYY(("P(control_use_cert:%s)\n", $2));
cfg_parser->cfg->control_use_cert = (strcmp($2, "yes")==0);
free($2);
}
;
rc_server_key_file: VAR_SERVER_KEY_FILE STRING_ARG
{
OUTYY(("P(rc_server_key_file:%s)\n", $2));
free(cfg_parser->cfg->server_key_file);
cfg_parser->cfg->server_key_file = $2;
}
;
rc_server_cert_file: VAR_SERVER_CERT_FILE STRING_ARG
{
OUTYY(("P(rc_server_cert_file:%s)\n", $2));
free(cfg_parser->cfg->server_cert_file);
cfg_parser->cfg->server_cert_file = $2;
}
;
rc_control_key_file: VAR_CONTROL_KEY_FILE STRING_ARG
{
OUTYY(("P(rc_control_key_file:%s)\n", $2));
free(cfg_parser->cfg->control_key_file);
cfg_parser->cfg->control_key_file = $2;
}
;
rc_control_cert_file: VAR_CONTROL_CERT_FILE STRING_ARG
{
OUTYY(("P(rc_control_cert_file:%s)\n", $2));
free(cfg_parser->cfg->control_cert_file);
cfg_parser->cfg->control_cert_file = $2;
}
;
dtstart: VAR_DNSTAP
{
OUTYY(("\nP(dnstap:)\n"));
cfg_parser->started_toplevel = 1;
}
;
contents_dt: contents_dt content_dt
| ;
content_dt: dt_dnstap_enable | dt_dnstap_socket_path | dt_dnstap_bidirectional |
dt_dnstap_ip | dt_dnstap_tls | dt_dnstap_tls_server_name |
dt_dnstap_tls_cert_bundle |
dt_dnstap_tls_client_key_file | dt_dnstap_tls_client_cert_file |
dt_dnstap_send_identity | dt_dnstap_send_version |
dt_dnstap_identity | dt_dnstap_version |
dt_dnstap_log_resolver_query_messages |
dt_dnstap_log_resolver_response_messages |
dt_dnstap_log_client_query_messages |
dt_dnstap_log_client_response_messages |
dt_dnstap_log_forwarder_query_messages |
dt_dnstap_log_forwarder_response_messages
;
dt_dnstap_enable: VAR_DNSTAP_ENABLE STRING_ARG
{
OUTYY(("P(dt_dnstap_enable:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->dnstap = (strcmp($2, "yes")==0);
free($2);
}
;
dt_dnstap_bidirectional: VAR_DNSTAP_BIDIRECTIONAL STRING_ARG
{
OUTYY(("P(dt_dnstap_bidirectional:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->dnstap_bidirectional =
(strcmp($2, "yes")==0);
free($2);
}
;
dt_dnstap_socket_path: VAR_DNSTAP_SOCKET_PATH STRING_ARG
{
OUTYY(("P(dt_dnstap_socket_path:%s)\n", $2));
free(cfg_parser->cfg->dnstap_socket_path);
cfg_parser->cfg->dnstap_socket_path = $2;
}
;
dt_dnstap_ip: VAR_DNSTAP_IP STRING_ARG
{
OUTYY(("P(dt_dnstap_ip:%s)\n", $2));
free(cfg_parser->cfg->dnstap_ip);
cfg_parser->cfg->dnstap_ip = $2;
}
;
dt_dnstap_tls: VAR_DNSTAP_TLS STRING_ARG
{
OUTYY(("P(dt_dnstap_tls:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->dnstap_tls = (strcmp($2, "yes")==0);
free($2);
}
;
dt_dnstap_tls_server_name: VAR_DNSTAP_TLS_SERVER_NAME STRING_ARG
{
OUTYY(("P(dt_dnstap_tls_server_name:%s)\n", $2));
free(cfg_parser->cfg->dnstap_tls_server_name);
cfg_parser->cfg->dnstap_tls_server_name = $2;
}
;
dt_dnstap_tls_cert_bundle: VAR_DNSTAP_TLS_CERT_BUNDLE STRING_ARG
{
OUTYY(("P(dt_dnstap_tls_cert_bundle:%s)\n", $2));
free(cfg_parser->cfg->dnstap_tls_cert_bundle);
cfg_parser->cfg->dnstap_tls_cert_bundle = $2;
}
;
dt_dnstap_tls_client_key_file: VAR_DNSTAP_TLS_CLIENT_KEY_FILE STRING_ARG
{
OUTYY(("P(dt_dnstap_tls_client_key_file:%s)\n", $2));
free(cfg_parser->cfg->dnstap_tls_client_key_file);
cfg_parser->cfg->dnstap_tls_client_key_file = $2;
}
;
dt_dnstap_tls_client_cert_file: VAR_DNSTAP_TLS_CLIENT_CERT_FILE STRING_ARG
{
OUTYY(("P(dt_dnstap_tls_client_cert_file:%s)\n", $2));
free(cfg_parser->cfg->dnstap_tls_client_cert_file);
cfg_parser->cfg->dnstap_tls_client_cert_file = $2;
}
;
dt_dnstap_send_identity: VAR_DNSTAP_SEND_IDENTITY STRING_ARG
{
OUTYY(("P(dt_dnstap_send_identity:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->dnstap_send_identity = (strcmp($2, "yes")==0);
free($2);
}
;
dt_dnstap_send_version: VAR_DNSTAP_SEND_VERSION STRING_ARG
{
OUTYY(("P(dt_dnstap_send_version:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->dnstap_send_version = (strcmp($2, "yes")==0);
free($2);
}
;
dt_dnstap_identity: VAR_DNSTAP_IDENTITY STRING_ARG
{
OUTYY(("P(dt_dnstap_identity:%s)\n", $2));
free(cfg_parser->cfg->dnstap_identity);
cfg_parser->cfg->dnstap_identity = $2;
}
;
dt_dnstap_version: VAR_DNSTAP_VERSION STRING_ARG
{
OUTYY(("P(dt_dnstap_version:%s)\n", $2));
free(cfg_parser->cfg->dnstap_version);
cfg_parser->cfg->dnstap_version = $2;
}
;
dt_dnstap_log_resolver_query_messages: VAR_DNSTAP_LOG_RESOLVER_QUERY_MESSAGES STRING_ARG
{
OUTYY(("P(dt_dnstap_log_resolver_query_messages:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->dnstap_log_resolver_query_messages =
(strcmp($2, "yes")==0);
free($2);
}
;
dt_dnstap_log_resolver_response_messages: VAR_DNSTAP_LOG_RESOLVER_RESPONSE_MESSAGES STRING_ARG
{
OUTYY(("P(dt_dnstap_log_resolver_response_messages:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->dnstap_log_resolver_response_messages =
(strcmp($2, "yes")==0);
free($2);
}
;
dt_dnstap_log_client_query_messages: VAR_DNSTAP_LOG_CLIENT_QUERY_MESSAGES STRING_ARG
{
OUTYY(("P(dt_dnstap_log_client_query_messages:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->dnstap_log_client_query_messages =
(strcmp($2, "yes")==0);
free($2);
}
;
dt_dnstap_log_client_response_messages: VAR_DNSTAP_LOG_CLIENT_RESPONSE_MESSAGES STRING_ARG
{
OUTYY(("P(dt_dnstap_log_client_response_messages:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->dnstap_log_client_response_messages =
(strcmp($2, "yes")==0);
free($2);
}
;
dt_dnstap_log_forwarder_query_messages: VAR_DNSTAP_LOG_FORWARDER_QUERY_MESSAGES STRING_ARG
{
OUTYY(("P(dt_dnstap_log_forwarder_query_messages:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->dnstap_log_forwarder_query_messages =
(strcmp($2, "yes")==0);
free($2);
}
;
dt_dnstap_log_forwarder_response_messages: VAR_DNSTAP_LOG_FORWARDER_RESPONSE_MESSAGES STRING_ARG
{
OUTYY(("P(dt_dnstap_log_forwarder_response_messages:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->dnstap_log_forwarder_response_messages =
(strcmp($2, "yes")==0);
free($2);
}
;
pythonstart: VAR_PYTHON
{
OUTYY(("\nP(python:)\n"));
cfg_parser->started_toplevel = 1;
}
;
contents_py: contents_py content_py
| ;
content_py: py_script
;
py_script: VAR_PYTHON_SCRIPT STRING_ARG
{
OUTYY(("P(python-script:%s)\n", $2));
if(!cfg_strlist_append_ex(&cfg_parser->cfg->python_script, $2))
yyerror("out of memory");
}
;
dynlibstart: VAR_DYNLIB
{
OUTYY(("\nP(dynlib:)\n"));
cfg_parser->started_toplevel = 1;
}
;
contents_dl: contents_dl content_dl
| ;
content_dl: dl_file
;
dl_file: VAR_DYNLIB_FILE STRING_ARG
{
OUTYY(("P(dynlib-file:%s)\n", $2));
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));
if (strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->disable_dnssec_lame_check =
(strcmp($2, "yes")==0);
free($2);
}
;
server_log_identity: VAR_LOG_IDENTITY STRING_ARG
{
OUTYY(("P(server_log_identity:%s)\n", $2));
free(cfg_parser->cfg->log_identity);
cfg_parser->cfg->log_identity = $2;
}
;
server_response_ip: VAR_RESPONSE_IP STRING_ARG STRING_ARG
{
OUTYY(("P(server_response_ip:%s %s)\n", $2, $3));
validate_respip_action($3);
if(!cfg_str2list_insert(&cfg_parser->cfg->respip_actions,
$2, $3))
fatal_exit("out of memory adding response-ip");
}
;
server_response_ip_data: VAR_RESPONSE_IP_DATA STRING_ARG STRING_ARG
{
OUTYY(("P(server_response_ip_data:%s)\n", $2));
if(!cfg_str2list_insert(&cfg_parser->cfg->respip_data,
$2, $3))
fatal_exit("out of memory adding response-ip-data");
}
;
dnscstart: VAR_DNSCRYPT
{
OUTYY(("\nP(dnscrypt:)\n"));
cfg_parser->started_toplevel = 1;
}
;
contents_dnsc: contents_dnsc content_dnsc
| ;
content_dnsc:
dnsc_dnscrypt_enable | dnsc_dnscrypt_port | dnsc_dnscrypt_provider |
dnsc_dnscrypt_secret_key | dnsc_dnscrypt_provider_cert |
dnsc_dnscrypt_provider_cert_rotated |
dnsc_dnscrypt_shared_secret_cache_size |
dnsc_dnscrypt_shared_secret_cache_slabs |
dnsc_dnscrypt_nonce_cache_size |
dnsc_dnscrypt_nonce_cache_slabs
;
dnsc_dnscrypt_enable: VAR_DNSCRYPT_ENABLE STRING_ARG
{
OUTYY(("P(dnsc_dnscrypt_enable:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->dnscrypt = (strcmp($2, "yes")==0);
free($2);
}
;
dnsc_dnscrypt_port: VAR_DNSCRYPT_PORT STRING_ARG
{
OUTYY(("P(dnsc_dnscrypt_port:%s)\n", $2));
if(atoi($2) == 0)
yyerror("port number expected");
else cfg_parser->cfg->dnscrypt_port = atoi($2);
free($2);
}
;
dnsc_dnscrypt_provider: VAR_DNSCRYPT_PROVIDER STRING_ARG
{
OUTYY(("P(dnsc_dnscrypt_provider:%s)\n", $2));
free(cfg_parser->cfg->dnscrypt_provider);
cfg_parser->cfg->dnscrypt_provider = $2;
}
;
dnsc_dnscrypt_provider_cert: VAR_DNSCRYPT_PROVIDER_CERT STRING_ARG
{
OUTYY(("P(dnsc_dnscrypt_provider_cert:%s)\n", $2));
if(cfg_strlist_find(cfg_parser->cfg->dnscrypt_provider_cert, $2))
log_warn("dnscrypt-provider-cert %s is a duplicate", $2);
if(!cfg_strlist_insert(&cfg_parser->cfg->dnscrypt_provider_cert, $2))
fatal_exit("out of memory adding dnscrypt-provider-cert");
}
;
dnsc_dnscrypt_provider_cert_rotated: VAR_DNSCRYPT_PROVIDER_CERT_ROTATED STRING_ARG
{
OUTYY(("P(dnsc_dnscrypt_provider_cert_rotated:%s)\n", $2));
if(!cfg_strlist_insert(&cfg_parser->cfg->dnscrypt_provider_cert_rotated, $2))
fatal_exit("out of memory adding dnscrypt-provider-cert-rotated");
}
;
dnsc_dnscrypt_secret_key: VAR_DNSCRYPT_SECRET_KEY STRING_ARG
{
OUTYY(("P(dnsc_dnscrypt_secret_key:%s)\n", $2));
if(cfg_strlist_find(cfg_parser->cfg->dnscrypt_secret_key, $2))
log_warn("dnscrypt-secret-key: %s is a duplicate", $2);
if(!cfg_strlist_insert(&cfg_parser->cfg->dnscrypt_secret_key, $2))
fatal_exit("out of memory adding dnscrypt-secret-key");
}
;
dnsc_dnscrypt_shared_secret_cache_size: VAR_DNSCRYPT_SHARED_SECRET_CACHE_SIZE STRING_ARG
{
OUTYY(("P(dnscrypt_shared_secret_cache_size:%s)\n", $2));
if(!cfg_parse_memsize($2, &cfg_parser->cfg->dnscrypt_shared_secret_cache_size))
yyerror("memory size expected");
free($2);
}
;
dnsc_dnscrypt_shared_secret_cache_slabs: VAR_DNSCRYPT_SHARED_SECRET_CACHE_SLABS STRING_ARG
{
OUTYY(("P(dnscrypt_shared_secret_cache_slabs:%s)\n", $2));
if(atoi($2) == 0) {
yyerror("number expected");
} else {
cfg_parser->cfg->dnscrypt_shared_secret_cache_slabs = atoi($2);
if(!is_pow2(cfg_parser->cfg->dnscrypt_shared_secret_cache_slabs))
yyerror("must be a power of 2");
}
free($2);
}
;
dnsc_dnscrypt_nonce_cache_size: VAR_DNSCRYPT_NONCE_CACHE_SIZE STRING_ARG
{
OUTYY(("P(dnscrypt_nonce_cache_size:%s)\n", $2));
if(!cfg_parse_memsize($2, &cfg_parser->cfg->dnscrypt_nonce_cache_size))
yyerror("memory size expected");
free($2);
}
;
dnsc_dnscrypt_nonce_cache_slabs: VAR_DNSCRYPT_NONCE_CACHE_SLABS STRING_ARG
{
OUTYY(("P(dnscrypt_nonce_cache_slabs:%s)\n", $2));
if(atoi($2) == 0) {
yyerror("number expected");
} else {
cfg_parser->cfg->dnscrypt_nonce_cache_slabs = atoi($2);
if(!is_pow2(cfg_parser->cfg->dnscrypt_nonce_cache_slabs))
yyerror("must be a power of 2");
}
free($2);
}
;
cachedbstart: VAR_CACHEDB
{
OUTYY(("\nP(cachedb:)\n"));
cfg_parser->started_toplevel = 1;
}
;
contents_cachedb: contents_cachedb content_cachedb
| ;
content_cachedb: cachedb_backend_name | cachedb_secret_seed |
redis_server_host | redis_server_port | redis_timeout |
- redis_expire_records | redis_server_path | redis_server_password
+ redis_expire_records | redis_server_path | redis_server_password |
+ cachedb_no_store | redis_logical_db
;
cachedb_backend_name: VAR_CACHEDB_BACKEND STRING_ARG
{
#ifdef USE_CACHEDB
OUTYY(("P(backend:%s)\n", $2));
free(cfg_parser->cfg->cachedb_backend);
cfg_parser->cfg->cachedb_backend = $2;
#else
OUTYY(("P(Compiled without cachedb, ignoring)\n"));
free($2);
#endif
}
;
cachedb_secret_seed: VAR_CACHEDB_SECRETSEED STRING_ARG
{
#ifdef USE_CACHEDB
OUTYY(("P(secret-seed:%s)\n", $2));
free(cfg_parser->cfg->cachedb_secret);
cfg_parser->cfg->cachedb_secret = $2;
#else
OUTYY(("P(Compiled without cachedb, ignoring)\n"));
free($2);
#endif
}
;
+cachedb_no_store: VAR_CACHEDB_NO_STORE STRING_ARG
+ {
+ #ifdef USE_CACHEDB
+ OUTYY(("P(cachedb_no_store:%s)\n", $2));
+ if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+ yyerror("expected yes or no.");
+ else cfg_parser->cfg->cachedb_no_store = (strcmp($2, "yes")==0);
+ #else
+ OUTYY(("P(Compiled without cachedb, ignoring)\n"));
+ #endif
+ free($2);
+ }
+ ;
redis_server_host: VAR_CACHEDB_REDISHOST STRING_ARG
{
#if defined(USE_CACHEDB) && defined(USE_REDIS)
OUTYY(("P(redis_server_host:%s)\n", $2));
free(cfg_parser->cfg->redis_server_host);
cfg_parser->cfg->redis_server_host = $2;
#else
OUTYY(("P(Compiled without cachedb or redis, ignoring)\n"));
free($2);
#endif
}
;
redis_server_port: VAR_CACHEDB_REDISPORT STRING_ARG
{
#if defined(USE_CACHEDB) && defined(USE_REDIS)
int port;
OUTYY(("P(redis_server_port:%s)\n", $2));
port = atoi($2);
if(port == 0 || port < 0 || port > 65535)
yyerror("valid redis server port number expected");
else cfg_parser->cfg->redis_server_port = port;
#else
OUTYY(("P(Compiled without cachedb or redis, ignoring)\n"));
#endif
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)
OUTYY(("P(redis_timeout:%s)\n", $2));
if(atoi($2) == 0)
yyerror("redis timeout value expected");
else cfg_parser->cfg->redis_timeout = atoi($2);
#else
OUTYY(("P(Compiled without cachedb or redis, ignoring)\n"));
#endif
free($2);
}
;
redis_expire_records: VAR_CACHEDB_REDISEXPIRERECORDS STRING_ARG
{
#if defined(USE_CACHEDB) && defined(USE_REDIS)
OUTYY(("P(redis_expire_records:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->redis_expire_records = (strcmp($2, "yes")==0);
#else
OUTYY(("P(Compiled without cachedb or redis, ignoring)\n"));
#endif
free($2);
}
;
+redis_logical_db: VAR_CACHEDB_REDISLOGICALDB STRING_ARG
+ {
+ #if defined(USE_CACHEDB) && defined(USE_REDIS)
+ int db;
+ OUTYY(("P(redis_logical_db:%s)\n", $2));
+ db = atoi($2);
+ if((db == 0 && strcmp($2, "0") != 0) || db < 0)
+ yyerror("valid redis logical database index expected");
+ else cfg_parser->cfg->redis_logical_db = db;
+ #else
+ OUTYY(("P(Compiled without cachedb or redis, ignoring)\n"));
+ #endif
+ free($2);
+ }
+ ;
server_tcp_connection_limit: VAR_TCP_CONNECTION_LIMIT STRING_ARG STRING_ARG
{
OUTYY(("P(server_tcp_connection_limit:%s %s)\n", $2, $3));
if (atoi($3) < 0)
yyerror("positive number expected");
else {
if(!cfg_str2list_insert(&cfg_parser->cfg->tcp_connection_limits, $2, $3))
fatal_exit("out of memory adding tcp connection limit");
}
}
;
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"));
cfg_parser->started_toplevel = 1;
}
;
contents_ipset: contents_ipset content_ipset
| ;
content_ipset: ipset_name_v4 | ipset_name_v6
;
ipset_name_v4: VAR_IPSET_NAME_V4 STRING_ARG
{
#ifdef USE_IPSET
OUTYY(("P(name-v4:%s)\n", $2));
if(cfg_parser->cfg->ipset_name_v4)
yyerror("ipset name v4 override, there must be one "
"name for ip v4");
free(cfg_parser->cfg->ipset_name_v4);
cfg_parser->cfg->ipset_name_v4 = $2;
#else
OUTYY(("P(Compiled without ipset, ignoring)\n"));
free($2);
#endif
}
;
ipset_name_v6: VAR_IPSET_NAME_V6 STRING_ARG
{
#ifdef USE_IPSET
OUTYY(("P(name-v6:%s)\n", $2));
if(cfg_parser->cfg->ipset_name_v6)
yyerror("ipset name v6 override, there must be one "
"name for ip v6");
free(cfg_parser->cfg->ipset_name_v6);
cfg_parser->cfg->ipset_name_v6 = $2;
#else
OUTYY(("P(Compiled without ipset, ignoring)\n"));
free($2);
#endif
}
;
%%
/* parse helper routines could be here */
static void
validate_respip_action(const char* action)
{
if(strcmp(action, "deny")!=0 &&
strcmp(action, "redirect")!=0 &&
strcmp(action, "inform")!=0 &&
strcmp(action, "inform_deny")!=0 &&
strcmp(action, "always_transparent")!=0 &&
strcmp(action, "always_refuse")!=0 &&
strcmp(action, "always_nxdomain")!=0)
{
yyerror("response-ip action: expected deny, redirect, "
"inform, inform_deny, always_transparent, "
"always_refuse or always_nxdomain");
}
}
static void
validate_acl_action(const char* action)
{
if(strcmp(action, "deny")!=0 &&
strcmp(action, "refuse")!=0 &&
strcmp(action, "deny_non_local")!=0 &&
strcmp(action, "refuse_non_local")!=0 &&
strcmp(action, "allow_setrd")!=0 &&
strcmp(action, "allow")!=0 &&
strcmp(action, "allow_snoop")!=0 &&
strcmp(action, "allow_cookie")!=0)
{
yyerror("expected deny, refuse, deny_non_local, "
"refuse_non_local, allow, allow_setrd, "
"allow_snoop or allow_cookie as access control action");
}
}
diff --git a/contrib/unbound/util/data/msgencode.c b/contrib/unbound/util/data/msgencode.c
index a170eb7b8a67..80ae33a38661 100644
--- a/contrib/unbound/util/data/msgencode.c
+++ b/contrib/unbound/util/data/msgencode.c
@@ -1,1123 +1,1125 @@
/*
* util/data/msgencode.c - Encode DNS messages, queries and replies.
*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file contains a routines to encode DNS messages.
*/
#include "config.h"
#include "util/data/msgencode.h"
#include "util/data/msgreply.h"
#include "util/data/msgparse.h"
#include "util/data/dname.h"
#include "util/log.h"
#include "util/regional.h"
#include "util/net_help.h"
#include "sldns/sbuffer.h"
#include "services/localzone.h"
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#include <sys/time.h>
/** return code that means the function ran out of memory. negative so it does
* not conflict with DNS rcodes. */
#define RETVAL_OUTMEM -2
/** return code that means the data did not fit (completely) in the packet */
#define RETVAL_TRUNC -4
/** return code that means all is peachy keen. Equal to DNS rcode NOERROR */
#define RETVAL_OK 0
/**
* Data structure to help domain name compression in outgoing messages.
* A tree of dnames and their offsets in the packet is kept.
* It is kept sorted, not canonical, but by label at least, so that after
* a lookup of a name you know its closest match, and the parent from that
* closest match. These are possible compression targets.
*
* It is a binary tree, not a rbtree or balanced tree, as the effort
* of keeping it balanced probably outweighs usefulness (given typical
* DNS packet size).
*/
struct compress_tree_node {
/** left node in tree, all smaller to this */
struct compress_tree_node* left;
/** right node in tree, all larger than this */
struct compress_tree_node* right;
/** the parent node - not for tree, but zone parent. One less label */
struct compress_tree_node* parent;
/** the domain name for this node. Pointer to uncompressed memory. */
uint8_t* dname;
/** number of labels in domain name, kept to help compare func. */
int labs;
/** offset in packet that points to this dname */
size_t offset;
};
/**
* Find domain name in tree, returns exact and closest match.
* @param tree: root of tree.
* @param dname: pointer to uncompressed dname.
* @param labs: number of labels in domain name.
* @param match: closest or exact match.
* guaranteed to be smaller or equal to the sought dname.
* can be null if the tree is empty.
* @param matchlabels: number of labels that match with closest match.
* can be zero is there is no match.
* @param insertpt: insert location for dname, if not found.
* @return: 0 if no exact match.
*/
static int
compress_tree_search(struct compress_tree_node** tree, uint8_t* dname,
int labs, struct compress_tree_node** match, int* matchlabels,
struct compress_tree_node*** insertpt)
{
int c, n, closen=0;
struct compress_tree_node* p = *tree;
struct compress_tree_node* close = 0;
struct compress_tree_node** prev = tree;
while(p) {
if((c = dname_lab_cmp(dname, labs, p->dname, p->labs, &n))
== 0) {
*matchlabels = n;
*match = p;
return 1;
}
if(c<0) {
prev = &p->left;
p = p->left;
} else {
closen = n;
close = p; /* p->dname is smaller than dname */
prev = &p->right;
p = p->right;
}
}
*insertpt = prev;
*matchlabels = closen;
*match = close;
return 0;
}
/**
* Lookup a domain name in compression tree.
* @param tree: root of tree (not the node with '.').
* @param dname: pointer to uncompressed dname.
* @param labs: number of labels in domain name.
* @param insertpt: insert location for dname, if not found.
* @return: 0 if not found or compress treenode with best compression.
*/
static struct compress_tree_node*
compress_tree_lookup(struct compress_tree_node** tree, uint8_t* dname,
int labs, struct compress_tree_node*** insertpt)
{
struct compress_tree_node* p;
int m;
if(labs <= 1)
return 0; /* do not compress root node */
if(compress_tree_search(tree, dname, labs, &p, &m, insertpt)) {
/* exact match */
return p;
}
/* return some ancestor of p that compresses well. */
if(m>1) {
/* www.example.com. (labs=4) matched foo.example.com.(labs=4)
* then matchcount = 3. need to go up. */
while(p && p->labs > m)
p = p->parent;
return p;
}
return 0;
}
/**
* Create node for domain name compression tree.
* @param dname: pointer to uncompressed dname (stored in tree).
* @param labs: number of labels in dname.
* @param offset: offset into packet for dname.
* @param region: how to allocate memory for new node.
* @return new node or 0 on malloc failure.
*/
static struct compress_tree_node*
compress_tree_newnode(uint8_t* dname, int labs, size_t offset,
struct regional* region)
{
struct compress_tree_node* n = (struct compress_tree_node*)
regional_alloc(region, sizeof(struct compress_tree_node));
if(!n) return 0;
n->left = 0;
n->right = 0;
n->parent = 0;
n->dname = dname;
n->labs = labs;
n->offset = offset;
return n;
}
/**
* Store domain name and ancestors into compression tree.
* @param dname: pointer to uncompressed dname (stored in tree).
* @param labs: number of labels in dname.
* @param offset: offset into packet for dname.
* @param region: how to allocate memory for new node.
* @param closest: match from previous lookup, used to compress dname.
* may be NULL if no previous match.
* if the tree has an ancestor of dname already, this must be it.
* @param insertpt: where to insert the dname in tree.
* @return: 0 on memory error.
*/
static int
compress_tree_store(uint8_t* dname, int labs, size_t offset,
struct regional* region, struct compress_tree_node* closest,
struct compress_tree_node** insertpt)
{
uint8_t lablen;
struct compress_tree_node* newnode;
struct compress_tree_node* prevnode = NULL;
int uplabs = labs-1; /* does not store root in tree */
if(closest) uplabs = labs - closest->labs;
log_assert(uplabs >= 0);
/* algorithms builds up a vine of dname-labels to hang into tree */
while(uplabs--) {
if(offset > PTR_MAX_OFFSET) {
/* insertion failed, drop vine */
return 1; /* compression pointer no longer useful */
}
if(!(newnode = compress_tree_newnode(dname, labs, offset,
region))) {
/* insertion failed, drop vine */
return 0;
}
if(prevnode) {
/* chain nodes together, last one has one label more,
* so is larger than newnode, thus goes right. */
newnode->right = prevnode;
prevnode->parent = newnode;
}
/* next label */
lablen = *dname++;
dname += lablen;
offset += lablen+1;
prevnode = newnode;
labs--;
}
/* if we have a vine, hang the vine into the tree */
if(prevnode) {
*insertpt = prevnode;
prevnode->parent = closest;
}
return 1;
}
/** compress a domain name */
static int
write_compressed_dname(sldns_buffer* pkt, uint8_t* dname, int labs,
struct compress_tree_node* p)
{
/* compress it */
int labcopy = labs - p->labs;
uint8_t lablen;
uint16_t ptr;
if(labs == 1) {
/* write root label */
if(sldns_buffer_remaining(pkt) < 1)
return 0;
sldns_buffer_write_u8(pkt, 0);
return 1;
}
/* copy the first couple of labels */
while(labcopy--) {
lablen = *dname++;
if(sldns_buffer_remaining(pkt) < (size_t)lablen+1)
return 0;
sldns_buffer_write_u8(pkt, lablen);
sldns_buffer_write(pkt, dname, lablen);
dname += lablen;
}
/* insert compression ptr */
if(sldns_buffer_remaining(pkt) < 2)
return 0;
ptr = PTR_CREATE(p->offset);
sldns_buffer_write_u16(pkt, ptr);
return 1;
}
/** compress owner name of RR, return RETVAL_OUTMEM RETVAL_TRUNC */
static int
compress_owner(struct ub_packed_rrset_key* key, sldns_buffer* pkt,
struct regional* region, struct compress_tree_node** tree,
size_t owner_pos, uint16_t* owner_ptr, int owner_labs)
{
struct compress_tree_node* p;
struct compress_tree_node** insertpt = NULL;
if(!*owner_ptr) {
/* compress first time dname */
if((p = compress_tree_lookup(tree, key->rk.dname,
owner_labs, &insertpt))) {
if(p->labs == owner_labs)
/* avoid ptr chains, since some software is
* not capable of decoding ptr after a ptr. */
*owner_ptr = htons(PTR_CREATE(p->offset));
if(!write_compressed_dname(pkt, key->rk.dname,
owner_labs, p))
return RETVAL_TRUNC;
/* check if typeclass+4 ttl + rdatalen is available */
if(sldns_buffer_remaining(pkt) < 4+4+2)
return RETVAL_TRUNC;
} else {
/* no compress */
if(sldns_buffer_remaining(pkt) < key->rk.dname_len+4+4+2)
return RETVAL_TRUNC;
sldns_buffer_write(pkt, key->rk.dname,
key->rk.dname_len);
if(owner_pos <= PTR_MAX_OFFSET)
*owner_ptr = htons(PTR_CREATE(owner_pos));
}
if(!compress_tree_store(key->rk.dname, owner_labs,
owner_pos, region, p, insertpt))
return RETVAL_OUTMEM;
} else {
/* always compress 2nd-further RRs in RRset */
if(owner_labs == 1) {
if(sldns_buffer_remaining(pkt) < 1+4+4+2)
return RETVAL_TRUNC;
sldns_buffer_write_u8(pkt, 0);
} else {
if(sldns_buffer_remaining(pkt) < 2+4+4+2)
return RETVAL_TRUNC;
sldns_buffer_write(pkt, owner_ptr, 2);
}
}
return RETVAL_OK;
}
/** compress any domain name to the packet, return RETVAL_* */
static int
compress_any_dname(uint8_t* dname, sldns_buffer* pkt, int labs,
struct regional* region, struct compress_tree_node** tree)
{
struct compress_tree_node* p;
struct compress_tree_node** insertpt = NULL;
size_t pos = sldns_buffer_position(pkt);
if((p = compress_tree_lookup(tree, dname, labs, &insertpt))) {
if(!write_compressed_dname(pkt, dname, labs, p))
return RETVAL_TRUNC;
} else {
if(!dname_buffer_write(pkt, dname))
return RETVAL_TRUNC;
}
if(!compress_tree_store(dname, labs, pos, region, p, insertpt))
return RETVAL_OUTMEM;
return RETVAL_OK;
}
/** return true if type needs domain name compression in rdata */
static const sldns_rr_descriptor*
type_rdata_compressable(struct ub_packed_rrset_key* key)
{
uint16_t t = ntohs(key->rk.type);
if(sldns_rr_descript(t) &&
sldns_rr_descript(t)->_compress == LDNS_RR_COMPRESS)
return sldns_rr_descript(t);
return 0;
}
/** compress domain names in rdata, return RETVAL_* */
static int
compress_rdata(sldns_buffer* pkt, uint8_t* rdata, size_t todolen,
struct regional* region, struct compress_tree_node** tree,
const sldns_rr_descriptor* desc)
{
int labs, r, rdf = 0;
size_t dname_len, len, pos = sldns_buffer_position(pkt);
uint8_t count = desc->_dname_count;
sldns_buffer_skip(pkt, 2); /* rdata len fill in later */
/* space for rdatalen checked for already */
rdata += 2;
todolen -= 2;
while(todolen > 0 && count) {
switch(desc->_wireformat[rdf]) {
case LDNS_RDF_TYPE_DNAME:
labs = dname_count_size_labels(rdata, &dname_len);
if((r=compress_any_dname(rdata, pkt, labs, region,
tree)) != RETVAL_OK)
return r;
rdata += dname_len;
todolen -= dname_len;
count--;
len = 0;
break;
case LDNS_RDF_TYPE_STR:
len = *rdata + 1;
break;
default:
len = get_rdf_size(desc->_wireformat[rdf]);
}
if(len) {
/* copy over */
if(sldns_buffer_remaining(pkt) < len)
return RETVAL_TRUNC;
sldns_buffer_write(pkt, rdata, len);
todolen -= len;
rdata += len;
}
rdf++;
}
/* copy remainder */
if(todolen > 0) {
if(sldns_buffer_remaining(pkt) < todolen)
return RETVAL_TRUNC;
sldns_buffer_write(pkt, rdata, todolen);
}
/* set rdata len */
sldns_buffer_write_u16_at(pkt, pos, sldns_buffer_position(pkt)-pos-2);
return RETVAL_OK;
}
/** Returns true if RR type should be included */
static int
rrset_belongs_in_reply(sldns_pkt_section s, uint16_t rrtype, uint16_t qtype,
int dnssec)
{
if(dnssec)
return 1;
/* skip non DNSSEC types, except if directly queried for */
if(s == LDNS_SECTION_ANSWER) {
if(qtype == LDNS_RR_TYPE_ANY || qtype == rrtype)
return 1;
}
/* check DNSSEC-ness */
switch(rrtype) {
case LDNS_RR_TYPE_SIG:
case LDNS_RR_TYPE_KEY:
case LDNS_RR_TYPE_NXT:
case LDNS_RR_TYPE_DS:
case LDNS_RR_TYPE_RRSIG:
case LDNS_RR_TYPE_NSEC:
case LDNS_RR_TYPE_DNSKEY:
case LDNS_RR_TYPE_NSEC3:
case LDNS_RR_TYPE_NSEC3PARAMS:
return 0;
}
return 1;
}
/** store rrset in buffer in wireformat, return RETVAL_* */
static int
packed_rrset_encode(struct ub_packed_rrset_key* key, sldns_buffer* pkt,
uint16_t* num_rrs, time_t timenow, struct regional* region,
int do_data, int do_sig, struct compress_tree_node** tree,
sldns_pkt_section s, uint16_t qtype, int dnssec, size_t rr_offset)
{
size_t i, j, owner_pos;
int r, owner_labs;
uint16_t owner_ptr = 0;
time_t adjust = 0;
struct packed_rrset_data* data = (struct packed_rrset_data*)
key->entry.data;
/* does this RR type belong in the answer? */
if(!rrset_belongs_in_reply(s, ntohs(key->rk.type), qtype, dnssec))
return RETVAL_OK;
owner_labs = dname_count_labels(key->rk.dname);
owner_pos = sldns_buffer_position(pkt);
/** Determine relative time adjustment for TTL values.
* For an rrset with a fixed TTL, use the rrset's TTL as given. */
if((key->rk.flags & PACKED_RRSET_FIXEDTTL) != 0)
adjust = 0;
else
adjust = SERVE_ORIGINAL_TTL ? data->ttl_add : timenow;
if(do_data) {
const sldns_rr_descriptor* c = type_rdata_compressable(key);
for(i=0; i<data->count; i++) {
/* rrset roundrobin */
j = (i + rr_offset) % data->count;
if((r=compress_owner(key, pkt, region, tree,
owner_pos, &owner_ptr, owner_labs))
!= RETVAL_OK)
return r;
sldns_buffer_write(pkt, &key->rk.type, 2);
sldns_buffer_write(pkt, &key->rk.rrset_class, 2);
if(data->rr_ttl[j] < adjust)
sldns_buffer_write_u32(pkt,
SERVE_EXPIRED?SERVE_EXPIRED_REPLY_TTL:0);
else sldns_buffer_write_u32(pkt, data->rr_ttl[j]-adjust);
if(c) {
if((r=compress_rdata(pkt, data->rr_data[j],
data->rr_len[j], region, tree, c))
!= RETVAL_OK)
return r;
} else {
if(sldns_buffer_remaining(pkt) < data->rr_len[j])
return RETVAL_TRUNC;
sldns_buffer_write(pkt, data->rr_data[j],
data->rr_len[j]);
}
}
}
/* insert rrsigs */
if(do_sig && dnssec) {
size_t total = data->count+data->rrsig_count;
for(i=data->count; i<total; i++) {
if(owner_ptr && owner_labs != 1) {
if(sldns_buffer_remaining(pkt) <
2+4+4+data->rr_len[i])
return RETVAL_TRUNC;
sldns_buffer_write(pkt, &owner_ptr, 2);
} else {
if((r=compress_any_dname(key->rk.dname,
pkt, owner_labs, region, tree))
!= RETVAL_OK)
return r;
if(sldns_buffer_remaining(pkt) <
4+4+data->rr_len[i])
return RETVAL_TRUNC;
}
sldns_buffer_write_u16(pkt, LDNS_RR_TYPE_RRSIG);
sldns_buffer_write(pkt, &key->rk.rrset_class, 2);
if(data->rr_ttl[i] < adjust)
sldns_buffer_write_u32(pkt,
SERVE_EXPIRED?SERVE_EXPIRED_REPLY_TTL:0);
else sldns_buffer_write_u32(pkt, data->rr_ttl[i]-adjust);
/* rrsig rdata cannot be compressed, perform 100+ byte
* memcopy. */
sldns_buffer_write(pkt, data->rr_data[i],
data->rr_len[i]);
}
}
/* change rrnum only after we are sure it fits */
if(do_data)
*num_rrs += data->count;
if(do_sig && dnssec)
*num_rrs += data->rrsig_count;
return RETVAL_OK;
}
/** store msg section in wireformat buffer, return RETVAL_* */
static int
insert_section(struct reply_info* rep, size_t num_rrsets, uint16_t* num_rrs,
sldns_buffer* pkt, size_t rrsets_before, time_t timenow,
struct regional* region, struct compress_tree_node** tree,
sldns_pkt_section s, uint16_t qtype, int dnssec, size_t rr_offset)
{
int r;
size_t i, setstart;
/* we now allow this function to be called multiple times for the
* same section, incrementally updating num_rrs. The caller is
* responsible for initializing it (which is the case in the current
* implementation). */
if(s != LDNS_SECTION_ADDITIONAL) {
if(s == LDNS_SECTION_ANSWER && qtype == LDNS_RR_TYPE_ANY)
dnssec = 1; /* include all types in ANY answer */
for(i=0; i<num_rrsets; i++) {
setstart = sldns_buffer_position(pkt);
if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i],
pkt, num_rrs, timenow, region, 1, 1, tree,
s, qtype, dnssec, rr_offset))
!= RETVAL_OK) {
/* Bad, but if due to size must set TC bit */
/* trim off the rrset neatly. */
sldns_buffer_set_position(pkt, setstart);
return r;
}
}
} else {
for(i=0; i<num_rrsets; i++) {
setstart = sldns_buffer_position(pkt);
if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i],
pkt, num_rrs, timenow, region, 1, 0, tree,
s, qtype, dnssec, rr_offset))
!= RETVAL_OK) {
sldns_buffer_set_position(pkt, setstart);
return r;
}
}
if(dnssec)
for(i=0; i<num_rrsets; i++) {
setstart = sldns_buffer_position(pkt);
if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i],
pkt, num_rrs, timenow, region, 0, 1, tree,
s, qtype, dnssec, rr_offset))
!= RETVAL_OK) {
sldns_buffer_set_position(pkt, setstart);
return r;
}
}
}
return RETVAL_OK;
}
/** store query section in wireformat buffer, return RETVAL */
static int
insert_query(struct query_info* qinfo, struct compress_tree_node** tree,
sldns_buffer* buffer, struct regional* region)
{
uint8_t* qname = qinfo->local_alias ?
qinfo->local_alias->rrset->rk.dname : qinfo->qname;
size_t qname_len = qinfo->local_alias ?
qinfo->local_alias->rrset->rk.dname_len : qinfo->qname_len;
if(sldns_buffer_remaining(buffer) <
qinfo->qname_len+sizeof(uint16_t)*2)
return RETVAL_TRUNC; /* buffer too small */
/* the query is the first name inserted into the tree */
if(!compress_tree_store(qname, dname_count_labels(qname),
sldns_buffer_position(buffer), region, NULL, tree))
return RETVAL_OUTMEM;
if(sldns_buffer_current(buffer) == qname)
sldns_buffer_skip(buffer, (ssize_t)qname_len);
else sldns_buffer_write(buffer, qname, qname_len);
sldns_buffer_write_u16(buffer, qinfo->qtype);
sldns_buffer_write_u16(buffer, qinfo->qclass);
return RETVAL_OK;
}
static int
positive_answer(struct reply_info* rep, uint16_t qtype) {
size_t i;
if (FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_NOERROR)
return 0;
for(i=0;i<rep->an_numrrsets; i++) {
if(ntohs(rep->rrsets[i]->rk.type) == qtype) {
/* for priming queries, type NS, include addresses */
if(qtype == LDNS_RR_TYPE_NS)
return 0;
/* in case it is a wildcard with DNSSEC, there will
* be NSEC/NSEC3 records in the authority section
* that we cannot remove */
for(i=rep->an_numrrsets; i<rep->an_numrrsets+
rep->ns_numrrsets; i++) {
if(ntohs(rep->rrsets[i]->rk.type) ==
LDNS_RR_TYPE_NSEC ||
ntohs(rep->rrsets[i]->rk.type) ==
LDNS_RR_TYPE_NSEC3)
return 0;
}
return 1;
}
}
return 0;
}
static int
negative_answer(struct reply_info* rep) {
size_t i;
int ns_seen = 0;
if(FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NXDOMAIN)
return 1;
if(FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NOERROR &&
rep->an_numrrsets != 0)
return 0; /* positive */
if(FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_NOERROR &&
FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_NXDOMAIN)
return 0;
for(i=rep->an_numrrsets; i<rep->an_numrrsets+rep->ns_numrrsets; i++){
if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_SOA)
return 1;
if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NS)
ns_seen = 1;
}
if(ns_seen) return 0; /* could be referral, NS, but no SOA */
return 1;
}
int
reply_info_encode(struct query_info* qinfo, struct reply_info* rep,
uint16_t id, uint16_t flags, sldns_buffer* buffer, time_t timenow,
struct regional* region, uint16_t udpsize, int dnssec, int minimise)
{
uint16_t ancount=0, nscount=0, arcount=0;
struct compress_tree_node* tree = 0;
int r;
size_t rr_offset;
sldns_buffer_clear(buffer);
if(udpsize < sldns_buffer_limit(buffer))
sldns_buffer_set_limit(buffer, udpsize);
if(sldns_buffer_remaining(buffer) < LDNS_HEADER_SIZE)
return 0;
sldns_buffer_write(buffer, &id, sizeof(uint16_t));
sldns_buffer_write_u16(buffer, flags);
sldns_buffer_write_u16(buffer, rep->qdcount);
/* set an, ns, ar counts to zero in case of small packets */
sldns_buffer_write(buffer, "\000\000\000\000\000\000", 6);
/* insert query section */
if(rep->qdcount) {
if((r=insert_query(qinfo, &tree, buffer, region)) !=
RETVAL_OK) {
if(r == RETVAL_TRUNC) {
/* create truncated message */
sldns_buffer_write_u16_at(buffer, 4, 0);
LDNS_TC_SET(sldns_buffer_begin(buffer));
sldns_buffer_flip(buffer);
return 1;
}
return 0;
}
}
/* roundrobin offset. using query id for random number. With ntohs
* for different roundrobins for sequential id client senders. */
rr_offset = RRSET_ROUNDROBIN?ntohs(id)+(timenow?timenow:time(NULL)):0;
/* "prepend" any local alias records in the answer section if this
* response is supposed to be authoritative. Currently it should
* be a single CNAME record (sanity-checked in worker_handle_request())
* but it can be extended if and when we support more variations of
* aliases. */
if(qinfo->local_alias && (flags & BIT_AA)) {
struct reply_info arep;
time_t timezero = 0; /* to use the 'authoritative' TTL */
memset(&arep, 0, sizeof(arep));
arep.flags = rep->flags;
arep.an_numrrsets = 1;
arep.rrset_count = 1;
arep.rrsets = &qinfo->local_alias->rrset;
if((r=insert_section(&arep, 1, &ancount, buffer, 0,
timezero, region, &tree, LDNS_SECTION_ANSWER,
qinfo->qtype, dnssec, rr_offset)) != RETVAL_OK) {
if(r == RETVAL_TRUNC) {
/* create truncated message */
sldns_buffer_write_u16_at(buffer, 6, ancount);
LDNS_TC_SET(sldns_buffer_begin(buffer));
sldns_buffer_flip(buffer);
return 1;
}
return 0;
}
}
/* insert answer section */
if((r=insert_section(rep, rep->an_numrrsets, &ancount, buffer,
0, timenow, region, &tree, LDNS_SECTION_ANSWER, qinfo->qtype,
dnssec, rr_offset)) != RETVAL_OK) {
if(r == RETVAL_TRUNC) {
/* create truncated message */
sldns_buffer_write_u16_at(buffer, 6, ancount);
LDNS_TC_SET(sldns_buffer_begin(buffer));
sldns_buffer_flip(buffer);
return 1;
}
return 0;
}
sldns_buffer_write_u16_at(buffer, 6, ancount);
/* if response is positive answer, auth/add sections are not required */
if( ! (minimise && positive_answer(rep, qinfo->qtype)) ) {
/* insert auth section */
if((r=insert_section(rep, rep->ns_numrrsets, &nscount, buffer,
rep->an_numrrsets, timenow, region, &tree,
LDNS_SECTION_AUTHORITY, qinfo->qtype,
dnssec, rr_offset)) != RETVAL_OK) {
if(r == RETVAL_TRUNC) {
/* create truncated message */
sldns_buffer_write_u16_at(buffer, 8, nscount);
LDNS_TC_SET(sldns_buffer_begin(buffer));
sldns_buffer_flip(buffer);
return 1;
}
return 0;
}
sldns_buffer_write_u16_at(buffer, 8, nscount);
if(! (minimise && negative_answer(rep))) {
/* insert add section */
if((r=insert_section(rep, rep->ar_numrrsets, &arcount, buffer,
rep->an_numrrsets + rep->ns_numrrsets, timenow, region,
&tree, LDNS_SECTION_ADDITIONAL, qinfo->qtype,
dnssec, rr_offset)) != RETVAL_OK) {
if(r == RETVAL_TRUNC) {
/* no need to set TC bit, this is the additional */
sldns_buffer_write_u16_at(buffer, 10, arcount);
sldns_buffer_flip(buffer);
return 1;
}
return 0;
}
sldns_buffer_write_u16_at(buffer, 10, arcount);
}
}
sldns_buffer_flip(buffer);
return 1;
}
uint16_t
calc_edns_field_size(struct edns_data* edns)
{
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) {
rdatalen += 4 + opt->opt_len;
}
for(opt = edns->opt_list_out; opt; opt = opt->next) {
rdatalen += 4 + opt->opt_len;
}
/* domain root '.' + type + class + ttl + rdatalen */
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)
{
size_t len;
size_t rdatapos;
struct edns_option* opt;
struct edns_option* padding_option = NULL;
/* inc additional count */
sldns_buffer_write_u16_at(pkt, 10,
sldns_buffer_read_u16_at(pkt, 10) + 1);
len = sldns_buffer_limit(pkt);
sldns_buffer_clear(pkt);
sldns_buffer_set_position(pkt, len);
/* write EDNS record */
sldns_buffer_write_u8(pkt, 0); /* '.' label */
sldns_buffer_write_u16(pkt, LDNS_RR_TYPE_OPT); /* type */
sldns_buffer_write_u16(pkt, edns->udp_size); /* class */
sldns_buffer_write_u8(pkt, edns->ext_rcode); /* ttl */
sldns_buffer_write_u8(pkt, edns->edns_version);
sldns_buffer_write_u16(pkt, edns->bits);
rdatapos = sldns_buffer_position(pkt);
sldns_buffer_write_u16(pkt, 0); /* rdatalen */
/* write rdata */
for(opt=edns->opt_list_inplace_cb_out; opt; opt=opt->next) {
if (opt->opt_code == LDNS_EDNS_PADDING) {
padding_option = opt;
continue;
}
sldns_buffer_write_u16(pkt, opt->opt_code);
sldns_buffer_write_u16(pkt, opt->opt_len);
if(opt->opt_len != 0)
sldns_buffer_write(pkt, opt->opt_data, opt->opt_len);
}
for(opt=edns->opt_list_out; opt; opt=opt->next) {
if (opt->opt_code == LDNS_EDNS_PADDING) {
padding_option = opt;
continue;
}
sldns_buffer_write_u16(pkt, opt->opt_code);
sldns_buffer_write_u16(pkt, opt->opt_len);
if(opt->opt_len != 0)
sldns_buffer_write(pkt, opt->opt_data, opt->opt_len);
}
if (padding_option && edns->padding_block_size ) {
size_t pad_pos = sldns_buffer_position(pkt);
size_t msg_sz = ((pad_pos + 3) / edns->padding_block_size + 1)
* edns->padding_block_size;
size_t pad_sz;
if (msg_sz > max_msg_sz)
msg_sz = max_msg_sz;
/* By use of calc_edns_field_size, calling functions should
* have made sure that there is enough space for at least a
* zero sized padding option.
*/
log_assert(pad_pos + 4 <= msg_sz);
pad_sz = msg_sz - pad_pos - 4;
sldns_buffer_write_u16(pkt, LDNS_EDNS_PADDING);
sldns_buffer_write_u16(pkt, pad_sz);
if (pad_sz) {
memset(sldns_buffer_current(pkt), 0, pad_sz);
sldns_buffer_skip(pkt, pad_sz);
}
}
sldns_buffer_write_u16_at(pkt, rdatapos,
sldns_buffer_position(pkt)-rdatapos-2);
sldns_buffer_flip(pkt);
}
void
attach_edns_record(sldns_buffer* pkt, struct edns_data* edns)
{
if(!edns || !edns->edns_present)
return;
attach_edns_record_max_msg_sz(pkt, edns, edns->udp_size);
}
int
reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep,
uint16_t id, uint16_t qflags, sldns_buffer* pkt, time_t timenow,
int cached, struct regional* region, uint16_t udpsize,
struct edns_data* edns, int dnssec, int secure)
{
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. */
flags = rep->flags | (qflags & (BIT_RD|BIT_CD));
} else {
/* remove AA bit, copy RD and CD bits from query. */
flags = (rep->flags & ~BIT_AA) | (qflags & (BIT_RD|BIT_CD));
}
if(secure && (dnssec || (qflags&BIT_AD)))
flags |= BIT_AD;
/* restore AA bit if we have a local alias and the response can be
* authoritative. Also clear AD bit if set as the local data is the
* primary answer. */
if(qinf->local_alias &&
(FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NOERROR ||
FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NXDOMAIN)) {
flags |= BIT_AA;
flags &= ~BIT_AD;
}
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(!edns || !edns->edns_present) {
+ attach_edns = 0;
/* EDEs are optional, try to fit anything else before them */
- if(udpsize < LDNS_HEADER_SIZE + edns_field_size - ede_size) {
+ } else 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)edns_field_size - ede_size;
}
if(!reply_info_encode(qinf, rep, id, flags, pkt, timenow, region,
udpsize - attach_edns, dnssec, MINIMAL_RESPONSES)) {
log_err("reply encode: out of memory");
return 0;
}
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;
}
void
qinfo_query_encode(sldns_buffer* pkt, struct query_info* qinfo)
{
uint16_t flags = 0; /* QUERY, NOERROR */
const uint8_t* qname = qinfo->local_alias ?
qinfo->local_alias->rrset->rk.dname : qinfo->qname;
size_t qname_len = qinfo->local_alias ?
qinfo->local_alias->rrset->rk.dname_len : qinfo->qname_len;
sldns_buffer_clear(pkt);
log_assert(sldns_buffer_remaining(pkt) >= 12+255+4/*max query*/);
sldns_buffer_skip(pkt, 2); /* id done later */
sldns_buffer_write_u16(pkt, flags);
sldns_buffer_write_u16(pkt, 1); /* query count */
sldns_buffer_write(pkt, "\000\000\000\000\000\000", 6); /* counts */
sldns_buffer_write(pkt, qname, qname_len);
sldns_buffer_write_u16(pkt, qinfo->qtype);
sldns_buffer_write_u16(pkt, qinfo->qclass);
sldns_buffer_flip(pkt);
}
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 | (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;
else flags = 0;
sldns_buffer_write_u16(buf, flags);
flags = 0;
sldns_buffer_write(buf, &flags, sizeof(uint16_t));
sldns_buffer_write(buf, &flags, sizeof(uint16_t));
sldns_buffer_write(buf, &flags, sizeof(uint16_t));
if(qinfo) {
const uint8_t* qname = qinfo->local_alias ?
qinfo->local_alias->rrset->rk.dname : qinfo->qname;
size_t qname_len = qinfo->local_alias ?
qinfo->local_alias->rrset->rk.dname_len :
qinfo->qname_len;
if(sldns_buffer_current(buf) == qname)
sldns_buffer_skip(buf, (ssize_t)qname_len);
else sldns_buffer_write(buf, qname, qname_len);
sldns_buffer_write_u16(buf, qinfo->qtype);
sldns_buffer_write_u16(buf, qinfo->qclass);
}
sldns_buffer_flip(buf);
if(edns) {
struct edns_data es = *edns;
es.edns_version = EDNS_ADVERTISED_VERSION;
es.udp_size = EDNS_ADVERTISED_SIZE;
es.ext_rcode = (uint8_t)(rcode >> 4);
es.bits &= EDNS_DO;
if(sldns_buffer_limit(buf) + calc_edns_field_size(&es) >
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);
}
diff --git a/contrib/unbound/util/data/msgparse.c b/contrib/unbound/util/data/msgparse.c
index b5414c6d0a55..d06b7bb25e6e 100644
--- a/contrib/unbound/util/data/msgparse.c
+++ b/contrib/unbound/util/data/msgparse.c
@@ -1,1308 +1,1333 @@
/*
* util/data/msgparse.c - parse wireformat DNS messages.
*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
* Routines for message parsing a packet buffer to a descriptive structure.
*/
#include "config.h"
#include "util/config_file.h"
#include "util/data/msgparse.h"
#include "util/data/msgreply.h"
#include "util/data/dname.h"
#include "util/data/packed_rrset.h"
#include "util/netevent.h"
#include "util/storage/lookup3.h"
#include "util/regional.h"
#include "util/rfc_1982.h"
#include "util/edns.h"
+#include "util/net_help.h"
#include "sldns/rrdef.h"
#include "sldns/sbuffer.h"
#include "sldns/parseutil.h"
#include "sldns/wire2str.h"
/** smart comparison of (compressed, valid) dnames from packet */
static int
smart_compare(sldns_buffer* pkt, uint8_t* dnow,
uint8_t* dprfirst, uint8_t* dprlast)
{
if(LABEL_IS_PTR(*dnow)) {
/* ptr points to a previous dname */
uint8_t* p;
if((size_t)PTR_OFFSET(dnow[0], dnow[1])
>= sldns_buffer_limit(pkt))
return -1;
p = sldns_buffer_at(pkt, PTR_OFFSET(dnow[0], dnow[1]));
if( p == dprfirst || p == dprlast )
return 0;
/* prev dname is also a ptr, both ptrs are the same. */
if(LABEL_IS_PTR(*dprlast) &&
dprlast[0] == dnow[0] && dprlast[1] == dnow[1])
return 0;
}
return dname_pkt_compare(pkt, dnow, dprlast);
}
/**
* Allocate new rrset in region, fill with data.
*/
static struct rrset_parse*
new_rrset(struct msg_parse* msg, uint8_t* dname, size_t dnamelen,
uint16_t type, uint16_t dclass, hashvalue_type hash,
uint32_t rrset_flags, sldns_pkt_section section,
struct regional* region)
{
struct rrset_parse* p = regional_alloc(region, sizeof(*p));
if(!p) return NULL;
p->rrset_bucket_next = msg->hashtable[hash & (PARSE_TABLE_SIZE-1)];
msg->hashtable[hash & (PARSE_TABLE_SIZE-1)] = p;
p->rrset_all_next = 0;
if(msg->rrset_last)
msg->rrset_last->rrset_all_next = p;
else msg->rrset_first = p;
msg->rrset_last = p;
p->hash = hash;
p->section = section;
p->dname = dname;
p->dname_len = dnamelen;
p->type = type;
p->rrset_class = dclass;
p->flags = rrset_flags;
p->rr_count = 0;
p->size = 0;
p->rr_first = 0;
p->rr_last = 0;
p->rrsig_count = 0;
p->rrsig_first = 0;
p->rrsig_last = 0;
return p;
}
/** See if next rrset is nsec at zone apex */
static int
nsec_at_apex(sldns_buffer* pkt)
{
/* we are at ttl position in packet. */
size_t pos = sldns_buffer_position(pkt);
uint16_t rdatalen;
if(sldns_buffer_remaining(pkt) < 7) /* ttl+len+root */
return 0; /* eek! */
sldns_buffer_skip(pkt, 4); /* ttl */;
rdatalen = sldns_buffer_read_u16(pkt);
if(sldns_buffer_remaining(pkt) < rdatalen) {
sldns_buffer_set_position(pkt, pos);
return 0; /* parse error happens later */
}
/* must validate the nsec next domain name format */
if(pkt_dname_len(pkt) == 0) {
sldns_buffer_set_position(pkt, pos);
return 0; /* parse error */
}
/* see if SOA bit is set. */
if(sldns_buffer_position(pkt) < pos+4+rdatalen) {
/* nsec type bitmap contains items */
uint8_t win, blen, bits;
/* need: windownum, bitmap len, firstbyte */
if(sldns_buffer_position(pkt)+3 > pos+4+rdatalen) {
sldns_buffer_set_position(pkt, pos);
return 0; /* malformed nsec */
}
win = sldns_buffer_read_u8(pkt);
blen = sldns_buffer_read_u8(pkt);
bits = sldns_buffer_read_u8(pkt);
/* 0window always first window. bitlen >=1 or parse
error really. bit 0x2 is SOA. */
if(win == 0 && blen >= 1 && (bits & 0x02)) {
sldns_buffer_set_position(pkt, pos);
return 1;
}
}
sldns_buffer_set_position(pkt, pos);
return 0;
}
/** Calculate rrset flags */
static uint32_t
pkt_rrset_flags(sldns_buffer* pkt, uint16_t type, sldns_pkt_section sec)
{
uint32_t f = 0;
if(type == LDNS_RR_TYPE_NSEC && nsec_at_apex(pkt)) {
f |= PACKED_RRSET_NSEC_AT_APEX;
} else if(type == LDNS_RR_TYPE_SOA && sec == LDNS_SECTION_AUTHORITY) {
f |= PACKED_RRSET_SOA_NEG;
}
return f;
}
hashvalue_type
pkt_hash_rrset(sldns_buffer* pkt, uint8_t* dname, uint16_t type,
uint16_t dclass, uint32_t rrset_flags)
{
/* note this MUST be identical to rrset_key_hash in packed_rrset.c */
/* this routine handles compressed names */
hashvalue_type h = 0xab;
h = dname_pkt_hash(pkt, dname, h);
h = hashlittle(&type, sizeof(type), h); /* host order */
h = hashlittle(&dclass, sizeof(dclass), h); /* netw order */
h = hashlittle(&rrset_flags, sizeof(uint32_t), h);
return h;
}
/** create partial dname hash for rrset hash */
static hashvalue_type
pkt_hash_rrset_first(sldns_buffer* pkt, uint8_t* dname)
{
/* works together with pkt_hash_rrset_rest */
/* note this MUST be identical to rrset_key_hash in packed_rrset.c */
/* this routine handles compressed names */
hashvalue_type h = 0xab;
h = dname_pkt_hash(pkt, dname, h);
return h;
}
/** create a rrset hash from a partial dname hash */
static hashvalue_type
pkt_hash_rrset_rest(hashvalue_type dname_h, uint16_t type, uint16_t dclass,
uint32_t rrset_flags)
{
/* works together with pkt_hash_rrset_first */
/* note this MUST be identical to rrset_key_hash in packed_rrset.c */
hashvalue_type h;
h = hashlittle(&type, sizeof(type), dname_h); /* host order */
h = hashlittle(&dclass, sizeof(dclass), h); /* netw order */
h = hashlittle(&rrset_flags, sizeof(uint32_t), h);
return h;
}
/** compare rrset_parse with data */
static int
rrset_parse_equals(struct rrset_parse* p, sldns_buffer* pkt, hashvalue_type h,
uint32_t rrset_flags, uint8_t* dname, size_t dnamelen,
uint16_t type, uint16_t dclass)
{
if(p->hash == h && p->dname_len == dnamelen && p->type == type &&
p->rrset_class == dclass && p->flags == rrset_flags &&
dname_pkt_compare(pkt, dname, p->dname) == 0)
return 1;
return 0;
}
struct rrset_parse*
msgparse_hashtable_lookup(struct msg_parse* msg, sldns_buffer* pkt,
hashvalue_type h, uint32_t rrset_flags, uint8_t* dname,
size_t dnamelen, uint16_t type, uint16_t dclass)
{
struct rrset_parse* p = msg->hashtable[h & (PARSE_TABLE_SIZE-1)];
while(p) {
if(rrset_parse_equals(p, pkt, h, rrset_flags, dname, dnamelen,
type, dclass))
return p;
p = p->rrset_bucket_next;
}
return NULL;
}
/** return type networkformat that rrsig in packet covers */
static int
pkt_rrsig_covered(sldns_buffer* pkt, uint8_t* here, uint16_t* type)
{
size_t pos = sldns_buffer_position(pkt);
sldns_buffer_set_position(pkt, (size_t)(here-sldns_buffer_begin(pkt)));
/* ttl + len + size of small rrsig(rootlabel, no signature) */
if(sldns_buffer_remaining(pkt) < 4+2+19)
return 0;
sldns_buffer_skip(pkt, 4); /* ttl */
if(sldns_buffer_read_u16(pkt) < 19) /* too short */ {
sldns_buffer_set_position(pkt, pos);
return 0;
}
*type = sldns_buffer_read_u16(pkt);
sldns_buffer_set_position(pkt, pos);
return 1;
}
/** true if covered type equals prevtype */
static int
pkt_rrsig_covered_equals(sldns_buffer* pkt, uint8_t* here, uint16_t type)
{
uint16_t t;
if(pkt_rrsig_covered(pkt, here, &t) && t == type)
return 1;
return 0;
}
void
msgparse_bucket_remove(struct msg_parse* msg, struct rrset_parse* rrset)
{
struct rrset_parse** p;
p = &msg->hashtable[ rrset->hash & (PARSE_TABLE_SIZE-1) ];
while(*p) {
if(*p == rrset) {
*p = rrset->rrset_bucket_next;
return;
}
p = &( (*p)->rrset_bucket_next );
}
}
/** change section of rrset from previous to current section */
static void
change_section(struct msg_parse* msg, struct rrset_parse* rrset,
sldns_pkt_section section)
{
struct rrset_parse *p, *prev;
/* remove from list */
if(section == rrset->section)
return;
p = msg->rrset_first;
prev = 0;
while(p) {
if(p == rrset) {
if(prev) prev->rrset_all_next = p->rrset_all_next;
else msg->rrset_first = p->rrset_all_next;
if(msg->rrset_last == rrset)
msg->rrset_last = prev;
break;
}
prev = p;
p = p->rrset_all_next;
}
/* remove from count */
switch(rrset->section) {
case LDNS_SECTION_ANSWER: msg->an_rrsets--; break;
case LDNS_SECTION_AUTHORITY: msg->ns_rrsets--; break;
case LDNS_SECTION_ADDITIONAL: msg->ar_rrsets--; break;
default: log_assert(0);
}
/* insert at end of list */
rrset->rrset_all_next = 0;
if(msg->rrset_last)
msg->rrset_last->rrset_all_next = rrset;
else msg->rrset_first = rrset;
msg->rrset_last = rrset;
/* up count of new section */
switch(section) {
case LDNS_SECTION_AUTHORITY: msg->ns_rrsets++; break;
case LDNS_SECTION_ADDITIONAL: msg->ar_rrsets++; break;
default: log_assert(0);
}
rrset->section = section;
}
/** see if rrset of type RRSIG contains sig over given type */
static int
rrset_has_sigover(sldns_buffer* pkt, struct rrset_parse* rrset, uint16_t type,
int* hasother)
{
int res = 0;
struct rr_parse* rr = rrset->rr_first;
log_assert( rrset->type == LDNS_RR_TYPE_RRSIG );
while(rr) {
if(pkt_rrsig_covered_equals(pkt, rr->ttl_data, type))
res = 1;
else *hasother = 1;
rr = rr->next;
}
return res;
}
/** move rrsigs from sigset to dataset */
static int
moveover_rrsigs(sldns_buffer* pkt, struct regional* region,
struct rrset_parse* sigset, struct rrset_parse* dataset, int duplicate)
{
struct rr_parse* sig = sigset->rr_first;
struct rr_parse* prev = NULL;
struct rr_parse* insert;
struct rr_parse* nextsig;
while(sig) {
nextsig = sig->next;
if(pkt_rrsig_covered_equals(pkt, sig->ttl_data,
dataset->type)) {
if(duplicate) {
/* new */
insert = (struct rr_parse*)regional_alloc(
region, sizeof(struct rr_parse));
if(!insert) return 0;
insert->outside_packet = 0;
insert->ttl_data = sig->ttl_data;
insert->size = sig->size;
/* prev not used */
} else {
/* remove from sigset */
if(prev) prev->next = sig->next;
else sigset->rr_first = sig->next;
if(sigset->rr_last == sig)
sigset->rr_last = prev;
sigset->rr_count--;
sigset->size -= sig->size;
insert = sig;
/* prev not changed */
}
/* add to dataset */
dataset->rrsig_count++;
insert->next = 0;
if(dataset->rrsig_last)
dataset->rrsig_last->next = insert;
else dataset->rrsig_first = insert;
dataset->rrsig_last = insert;
dataset->size += insert->size;
} else {
prev = sig;
}
sig = nextsig;
}
return 1;
}
/** change an rrsig rrset for use as data rrset */
static struct rrset_parse*
change_rrsig_rrset(struct rrset_parse* sigset, struct msg_parse* msg,
sldns_buffer* pkt, uint16_t datatype, uint32_t rrset_flags,
int hasother, sldns_pkt_section section, struct regional* region)
{
struct rrset_parse* dataset = sigset;
hashvalue_type hash = pkt_hash_rrset(pkt, sigset->dname, datatype,
sigset->rrset_class, rrset_flags);
log_assert( sigset->type == LDNS_RR_TYPE_RRSIG );
log_assert( datatype != LDNS_RR_TYPE_RRSIG );
if(hasother) {
/* need to make new rrset to hold data type */
dataset = new_rrset(msg, sigset->dname, sigset->dname_len,
datatype, sigset->rrset_class, hash, rrset_flags,
section, region);
if(!dataset)
return NULL;
switch(section) {
case LDNS_SECTION_ANSWER: msg->an_rrsets++; break;
case LDNS_SECTION_AUTHORITY: msg->ns_rrsets++; break;
case LDNS_SECTION_ADDITIONAL: msg->ar_rrsets++; break;
default: log_assert(0);
}
if(!moveover_rrsigs(pkt, region, sigset, dataset,
msg->qtype == LDNS_RR_TYPE_RRSIG ||
(msg->qtype == LDNS_RR_TYPE_ANY &&
section != LDNS_SECTION_ANSWER) ))
return NULL;
return dataset;
}
/* changeover the type of the rrset to data set */
msgparse_bucket_remove(msg, dataset);
/* insert into new hash bucket */
dataset->rrset_bucket_next = msg->hashtable[hash&(PARSE_TABLE_SIZE-1)];
msg->hashtable[hash&(PARSE_TABLE_SIZE-1)] = dataset;
dataset->hash = hash;
/* use section of data item for result */
change_section(msg, dataset, section);
dataset->type = datatype;
dataset->flags = rrset_flags;
dataset->rrsig_count += dataset->rr_count;
dataset->rr_count = 0;
/* move sigs to end of siglist */
if(dataset->rrsig_last)
dataset->rrsig_last->next = dataset->rr_first;
else dataset->rrsig_first = dataset->rr_first;
dataset->rrsig_last = dataset->rr_last;
dataset->rr_first = 0;
dataset->rr_last = 0;
return dataset;
}
/** Find rrset. If equal to previous it is fast. hash if not so.
* @param msg: the message with hash table.
* @param pkt: the packet in wireformat (needed for compression ptrs).
* @param dname: pointer to start of dname (compressed) in packet.
* @param dnamelen: uncompressed wirefmt length of dname.
* @param type: type of current rr.
* @param dclass: class of current rr.
* @param hash: hash value is returned if the rrset could not be found.
* @param rrset_flags: is returned if the rrset could not be found.
* @param prev_dname_first: dname of last seen RR. First seen dname.
* @param prev_dname_last: dname of last seen RR. Last seen dname.
* @param prev_dnamelen: dname len of last seen RR.
* @param prev_type: type of last seen RR.
* @param prev_dclass: class of last seen RR.
* @param rrset_prev: last seen RRset.
* @param section: the current section in the packet.
* @param region: used to allocate temporary parsing data.
* @return 0 on out of memory.
*/
static int
find_rrset(struct msg_parse* msg, sldns_buffer* pkt, uint8_t* dname,
size_t dnamelen, uint16_t type, uint16_t dclass, hashvalue_type* hash,
uint32_t* rrset_flags,
uint8_t** prev_dname_first, uint8_t** prev_dname_last,
size_t* prev_dnamelen, uint16_t* prev_type,
uint16_t* prev_dclass, struct rrset_parse** rrset_prev,
sldns_pkt_section section, struct regional* region)
{
hashvalue_type dname_h = pkt_hash_rrset_first(pkt, dname);
uint16_t covtype;
if(*rrset_prev) {
/* check if equal to previous item */
if(type == *prev_type && dclass == *prev_dclass &&
dnamelen == *prev_dnamelen &&
smart_compare(pkt, dname, *prev_dname_first,
*prev_dname_last) == 0 &&
type != LDNS_RR_TYPE_RRSIG) {
/* same as previous */
*prev_dname_last = dname;
return 1;
}
/* check if rrsig over previous item */
if(type == LDNS_RR_TYPE_RRSIG && dclass == *prev_dclass &&
pkt_rrsig_covered_equals(pkt, sldns_buffer_current(pkt),
*prev_type) &&
smart_compare(pkt, dname, *prev_dname_first,
*prev_dname_last) == 0) {
/* covers previous */
*prev_dname_last = dname;
return 1;
}
}
/* find by hashing and lookup in hashtable */
*rrset_flags = pkt_rrset_flags(pkt, type, section);
/* if rrsig - try to lookup matching data set first */
if(type == LDNS_RR_TYPE_RRSIG && pkt_rrsig_covered(pkt,
sldns_buffer_current(pkt), &covtype)) {
*hash = pkt_hash_rrset_rest(dname_h, covtype, dclass,
*rrset_flags);
*rrset_prev = msgparse_hashtable_lookup(msg, pkt, *hash,
*rrset_flags, dname, dnamelen, covtype, dclass);
if(!*rrset_prev && covtype == LDNS_RR_TYPE_NSEC) {
/* if NSEC try with NSEC apex bit twiddled */
*rrset_flags ^= PACKED_RRSET_NSEC_AT_APEX;
*hash = pkt_hash_rrset_rest(dname_h, covtype, dclass,
*rrset_flags);
*rrset_prev = msgparse_hashtable_lookup(msg, pkt,
*hash, *rrset_flags, dname, dnamelen, covtype,
dclass);
if(!*rrset_prev) /* untwiddle if not found */
*rrset_flags ^= PACKED_RRSET_NSEC_AT_APEX;
}
if(!*rrset_prev && covtype == LDNS_RR_TYPE_SOA) {
/* if SOA try with SOA neg flag twiddled */
*rrset_flags ^= PACKED_RRSET_SOA_NEG;
*hash = pkt_hash_rrset_rest(dname_h, covtype, dclass,
*rrset_flags);
*rrset_prev = msgparse_hashtable_lookup(msg, pkt,
*hash, *rrset_flags, dname, dnamelen, covtype,
dclass);
if(!*rrset_prev) /* untwiddle if not found */
*rrset_flags ^= PACKED_RRSET_SOA_NEG;
}
if(*rrset_prev) {
*prev_dname_first = (*rrset_prev)->dname;
*prev_dname_last = dname;
*prev_dnamelen = dnamelen;
*prev_type = covtype;
*prev_dclass = dclass;
return 1;
}
}
if(type != LDNS_RR_TYPE_RRSIG) {
int hasother = 0;
/* find matching rrsig */
*hash = pkt_hash_rrset_rest(dname_h, LDNS_RR_TYPE_RRSIG,
dclass, 0);
*rrset_prev = msgparse_hashtable_lookup(msg, pkt, *hash,
0, dname, dnamelen, LDNS_RR_TYPE_RRSIG,
dclass);
if(*rrset_prev && rrset_has_sigover(pkt, *rrset_prev, type,
&hasother)) {
/* yes! */
*prev_dname_first = (*rrset_prev)->dname;
*prev_dname_last = dname;
*prev_dnamelen = dnamelen;
*prev_type = type;
*prev_dclass = dclass;
*rrset_prev = change_rrsig_rrset(*rrset_prev, msg,
pkt, type, *rrset_flags, hasother, section,
region);
if(!*rrset_prev) return 0;
return 1;
}
}
*hash = pkt_hash_rrset_rest(dname_h, type, dclass, *rrset_flags);
*rrset_prev = msgparse_hashtable_lookup(msg, pkt, *hash, *rrset_flags,
dname, dnamelen, type, dclass);
if(*rrset_prev)
*prev_dname_first = (*rrset_prev)->dname;
else *prev_dname_first = dname;
*prev_dname_last = dname;
*prev_dnamelen = dnamelen;
*prev_type = type;
*prev_dclass = dclass;
return 1;
}
/**
* Parse query section.
* @param pkt: packet, position at call must be at start of query section.
* at end position is after query section.
* @param msg: store results here.
* @return: 0 if OK, or rcode on error.
*/
static int
parse_query_section(sldns_buffer* pkt, struct msg_parse* msg)
{
if(msg->qdcount == 0)
return 0;
if(msg->qdcount > 1)
return LDNS_RCODE_FORMERR;
log_assert(msg->qdcount == 1);
if(sldns_buffer_remaining(pkt) <= 0)
return LDNS_RCODE_FORMERR;
msg->qname = sldns_buffer_current(pkt);
if((msg->qname_len = pkt_dname_len(pkt)) == 0)
return LDNS_RCODE_FORMERR;
if(sldns_buffer_remaining(pkt) < sizeof(uint16_t)*2)
return LDNS_RCODE_FORMERR;
msg->qtype = sldns_buffer_read_u16(pkt);
msg->qclass = sldns_buffer_read_u16(pkt);
return 0;
}
size_t
get_rdf_size(sldns_rdf_type rdf)
{
switch(rdf) {
case LDNS_RDF_TYPE_CLASS:
case LDNS_RDF_TYPE_ALG:
case LDNS_RDF_TYPE_INT8:
return 1;
break;
case LDNS_RDF_TYPE_INT16:
case LDNS_RDF_TYPE_TYPE:
case LDNS_RDF_TYPE_CERT_ALG:
return 2;
break;
case LDNS_RDF_TYPE_INT32:
case LDNS_RDF_TYPE_TIME:
case LDNS_RDF_TYPE_A:
case LDNS_RDF_TYPE_PERIOD:
return 4;
break;
case LDNS_RDF_TYPE_TSIGTIME:
return 6;
break;
case LDNS_RDF_TYPE_AAAA:
return 16;
break;
default:
log_assert(0); /* add type above */
/* only types that appear before a domain *
* name are needed. rest is simply copied. */
}
return 0;
}
/** calculate the size of one rr */
static int
calc_size(sldns_buffer* pkt, uint16_t type, struct rr_parse* rr)
{
const sldns_rr_descriptor* desc;
uint16_t pkt_len; /* length of rr inside the packet */
rr->size = sizeof(uint16_t); /* the rdatalen */
sldns_buffer_skip(pkt, 4); /* skip ttl */
pkt_len = sldns_buffer_read_u16(pkt);
if(sldns_buffer_remaining(pkt) < pkt_len)
return 0;
desc = sldns_rr_descript(type);
if(pkt_len > 0 && desc && desc->_dname_count > 0) {
int count = (int)desc->_dname_count;
int rdf = 0;
size_t len;
size_t oldpos;
/* skip first part. */
while(pkt_len > 0 && count) {
switch(desc->_wireformat[rdf]) {
case LDNS_RDF_TYPE_DNAME:
/* decompress every domain name */
oldpos = sldns_buffer_position(pkt);
if((len = pkt_dname_len(pkt)) == 0)
return 0; /* malformed dname */
if(sldns_buffer_position(pkt)-oldpos > pkt_len)
return 0; /* dname exceeds rdata */
pkt_len -= sldns_buffer_position(pkt)-oldpos;
rr->size += len;
count--;
len = 0;
break;
case LDNS_RDF_TYPE_STR:
if(pkt_len < 1) {
/* NOTREACHED, due to 'while(>0)' */
return 0; /* len byte exceeds rdata */
}
len = sldns_buffer_current(pkt)[0] + 1;
break;
default:
len = get_rdf_size(desc->_wireformat[rdf]);
}
if(len) {
if(pkt_len < len)
return 0; /* exceeds rdata */
pkt_len -= len;
sldns_buffer_skip(pkt, (ssize_t)len);
rr->size += len;
}
rdf++;
}
}
/* remaining rdata */
rr->size += pkt_len;
sldns_buffer_skip(pkt, (ssize_t)pkt_len);
return 1;
}
/** skip rr ttl and rdata */
static int
skip_ttl_rdata(sldns_buffer* pkt)
{
uint16_t rdatalen;
if(sldns_buffer_remaining(pkt) < 6) /* ttl + rdatalen */
return 0;
sldns_buffer_skip(pkt, 4); /* ttl */
rdatalen = sldns_buffer_read_u16(pkt);
if(sldns_buffer_remaining(pkt) < rdatalen)
return 0;
sldns_buffer_skip(pkt, (ssize_t)rdatalen);
return 1;
}
/** see if RRSIG is a duplicate of another */
static int
sig_is_double(sldns_buffer* pkt, struct rrset_parse* rrset, uint8_t* ttldata)
{
uint16_t rlen, siglen;
size_t pos = sldns_buffer_position(pkt);
struct rr_parse* sig;
if(sldns_buffer_remaining(pkt) < 6)
return 0;
sldns_buffer_skip(pkt, 4); /* ttl */
rlen = sldns_buffer_read_u16(pkt);
if(sldns_buffer_remaining(pkt) < rlen) {
sldns_buffer_set_position(pkt, pos);
return 0;
}
sldns_buffer_set_position(pkt, pos);
sig = rrset->rrsig_first;
while(sig) {
/* check if rdatalen is same */
memmove(&siglen, sig->ttl_data+4, sizeof(siglen));
siglen = ntohs(siglen);
/* checks if data in packet is exactly the same, this means
* also dname in rdata is the same, but rrsig is not allowed
* to have compressed dnames anyway. If it is compressed anyway
* it will lead to duplicate rrs for qtype=RRSIG. (or ANY).
*
* Cannot use sig->size because size of the other one is not
* calculated yet.
*/
if(siglen == rlen) {
if(siglen>0 && memcmp(sig->ttl_data+6, ttldata+6,
siglen) == 0) {
/* same! */
return 1;
}
}
sig = sig->next;
}
return 0;
}
/** Add rr (from packet here) to rrset, skips rr */
static int
add_rr_to_rrset(struct rrset_parse* rrset, sldns_buffer* pkt,
struct msg_parse* msg, struct regional* region,
sldns_pkt_section section, uint16_t type)
{
struct rr_parse* rr;
/* check section of rrset. */
if(rrset->section != section && type != LDNS_RR_TYPE_RRSIG &&
rrset->type != LDNS_RR_TYPE_RRSIG) {
/* silently drop it - we drop the last part, since
* trust in rr data depends on the section it is in.
* the less trustworthy part is discarded.
* also the last part is more likely to be incomplete.
* RFC 2181: must put RRset only once in response. */
/*
verbose(VERB_QUERY, "Packet contains rrset data in "
"multiple sections, dropped last part.");
log_buf(VERB_QUERY, "packet was", pkt);
*/
/* forwards */
if(!skip_ttl_rdata(pkt))
return LDNS_RCODE_FORMERR;
return 0;
}
if( (msg->qtype == LDNS_RR_TYPE_RRSIG ||
msg->qtype == LDNS_RR_TYPE_ANY)
&& sig_is_double(pkt, rrset, sldns_buffer_current(pkt))) {
if(!skip_ttl_rdata(pkt))
return LDNS_RCODE_FORMERR;
return 0;
}
/* create rr */
if(!(rr = (struct rr_parse*)regional_alloc(region, sizeof(*rr))))
return LDNS_RCODE_SERVFAIL;
rr->outside_packet = 0;
rr->ttl_data = sldns_buffer_current(pkt);
rr->next = 0;
if(type == LDNS_RR_TYPE_RRSIG && rrset->type != LDNS_RR_TYPE_RRSIG) {
if(rrset->rrsig_last)
rrset->rrsig_last->next = rr;
else rrset->rrsig_first = rr;
rrset->rrsig_last = rr;
rrset->rrsig_count++;
} else {
if(rrset->rr_last)
rrset->rr_last->next = rr;
else rrset->rr_first = rr;
rrset->rr_last = rr;
rrset->rr_count++;
}
/* calc decompressed size */
if(!calc_size(pkt, type, rr))
return LDNS_RCODE_FORMERR;
rrset->size += rr->size;
return 0;
}
/**
* Parse packet RR section, for answer, authority and additional sections.
* @param pkt: packet, position at call must be at start of section.
* at end position is after section.
* @param msg: store results here.
* @param region: how to alloc results.
* @param section: section enum.
* @param num_rrs: how many rrs are in the section.
* @param num_rrsets: returns number of rrsets in the section.
* @return: 0 if OK, or rcode on error.
*/
static int
parse_section(sldns_buffer* pkt, struct msg_parse* msg,
struct regional* region, sldns_pkt_section section,
uint16_t num_rrs, size_t* num_rrsets)
{
uint16_t i;
uint8_t* dname, *prev_dname_f = NULL, *prev_dname_l = NULL;
size_t dnamelen, prev_dnamelen = 0;
uint16_t type, prev_type = 0;
uint16_t dclass, prev_dclass = 0;
uint32_t rrset_flags = 0;
hashvalue_type hash = 0;
struct rrset_parse* rrset = NULL;
int r;
if(num_rrs == 0)
return 0;
if(sldns_buffer_remaining(pkt) <= 0)
return LDNS_RCODE_FORMERR;
for(i=0; i<num_rrs; i++) {
/* parse this RR. */
dname = sldns_buffer_current(pkt);
if((dnamelen = pkt_dname_len(pkt)) == 0)
return LDNS_RCODE_FORMERR;
if(sldns_buffer_remaining(pkt) < 10) /* type, class, ttl, len */
return LDNS_RCODE_FORMERR;
type = sldns_buffer_read_u16(pkt);
sldns_buffer_read(pkt, &dclass, sizeof(dclass));
if(0) { /* debug show what is being parsed. */
if(type == LDNS_RR_TYPE_RRSIG) {
uint16_t t;
if(pkt_rrsig_covered(pkt,
sldns_buffer_current(pkt), &t))
fprintf(stderr, "parse of %s(%d) [%s(%d)]",
sldns_rr_descript(type)?
sldns_rr_descript(type)->_name: "??",
(int)type,
sldns_rr_descript(t)?
sldns_rr_descript(t)->_name: "??",
(int)t);
} else
fprintf(stderr, "parse of %s(%d)",
sldns_rr_descript(type)?
sldns_rr_descript(type)->_name: "??",
(int)type);
fprintf(stderr, " %s(%d) ",
sldns_lookup_by_id(sldns_rr_classes,
(int)ntohs(dclass))?sldns_lookup_by_id(
sldns_rr_classes, (int)ntohs(dclass))->name:
"??", (int)ntohs(dclass));
dname_print(stderr, pkt, dname);
fprintf(stderr, "\n");
}
/* see if it is part of an existing RR set */
if(!find_rrset(msg, pkt, dname, dnamelen, type, dclass, &hash,
&rrset_flags, &prev_dname_f, &prev_dname_l,
&prev_dnamelen, &prev_type, &prev_dclass, &rrset,
section, region))
return LDNS_RCODE_SERVFAIL;
if(!rrset) {
/* it is a new RR set. hash&flags already calculated.*/
(*num_rrsets)++;
rrset = new_rrset(msg, dname, dnamelen, type, dclass,
hash, rrset_flags, section, region);
if(!rrset)
return LDNS_RCODE_SERVFAIL;
}
else if(0) {
fprintf(stderr, "is part of existing: ");
dname_print(stderr, pkt, rrset->dname);
fprintf(stderr, " type %s(%d)\n",
sldns_rr_descript(rrset->type)?
sldns_rr_descript(rrset->type)->_name: "??",
(int)rrset->type);
}
/* add to rrset. */
if((r=add_rr_to_rrset(rrset, pkt, msg, region, section,
type)) != 0)
return r;
}
return 0;
}
int
parse_packet(sldns_buffer* pkt, struct msg_parse* msg, struct regional* region)
{
int ret;
if(sldns_buffer_remaining(pkt) < LDNS_HEADER_SIZE)
return LDNS_RCODE_FORMERR;
/* read the header */
sldns_buffer_read(pkt, &msg->id, sizeof(uint16_t));
msg->flags = sldns_buffer_read_u16(pkt);
msg->qdcount = sldns_buffer_read_u16(pkt);
msg->ancount = sldns_buffer_read_u16(pkt);
msg->nscount = sldns_buffer_read_u16(pkt);
msg->arcount = sldns_buffer_read_u16(pkt);
if(msg->qdcount > 1)
return LDNS_RCODE_FORMERR;
if((ret = parse_query_section(pkt, msg)) != 0)
return ret;
if((ret = parse_section(pkt, msg, region, LDNS_SECTION_ANSWER,
msg->ancount, &msg->an_rrsets)) != 0)
return ret;
if((ret = parse_section(pkt, msg, region, LDNS_SECTION_AUTHORITY,
msg->nscount, &msg->ns_rrsets)) != 0)
return ret;
if(sldns_buffer_remaining(pkt) == 0 && msg->arcount == 1) {
/* BIND accepts leniently that an EDNS record is missing.
* so, we do too. */
} else if((ret = parse_section(pkt, msg, region,
LDNS_SECTION_ADDITIONAL, msg->arcount, &msg->ar_rrsets)) != 0)
return ret;
/* if(sldns_buffer_remaining(pkt) > 0) { */
/* there is spurious data at end of packet. ignore */
/* } */
msg->rrset_count = msg->an_rrsets + msg->ns_rrsets + msg->ar_rrsets;
return 0;
}
/** 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 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
* option must have 0 length data. Subsequent messages sent on that
* connection will have a TCP Keepalive option.
*
* In the if-statement below, the option is added unsolicited. This
* means that the client has sent an KEEPALIVE option earlier. We know
* here this is true, because c->tcp_keepalive is set.
*/
if (cfg && cfg->do_tcp_keepalive && c && c->type != comm_udp && c->tcp_keepalive) {
if(!edns_opt_list_append_keepalive(&edns->opt_list_out,
c->tcp_timeout_msec / 100, region)) {
log_err("out of memory");
return LDNS_RCODE_SERVFAIL;
}
}
/* while still more options, and have code+len to read */
/* ignores partial content (i.e. rdata len 3) */
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)
break; /* option code partial */
/* handle parse time edns options here */
switch(opt_code) {
case LDNS_EDNS_NSID:
if (!cfg || !cfg->nsid)
break;
if(!edns_opt_list_append(&edns->opt_list_out,
LDNS_EDNS_NSID, cfg->nsid_len,
cfg->nsid, region)) {
log_err("out of memory");
return LDNS_RCODE_SERVFAIL;
}
break;
case LDNS_EDNS_KEEPALIVE:
/* To respond with a Keepalive option, the client
* connection must have received one message with a TCP
* Keepalive EDNS option, and that option must have 0
* length data. Subsequent messages sent on that
* connection will have a TCP Keepalive option.
*
* This should be the first time the client sends this
* option, so c->tcp_keepalive is not set.
* Besides adding the reply KEEPALIVE option,
* c->tcp_keepalive will be set so that the
* option will be added unsolicited in subsequent
* responses (see the comment above the if-statement
* at the start of this function).
*/
if (!cfg || !cfg->do_tcp_keepalive || !c ||
c->type == comm_udp || c->tcp_keepalive)
break;
if(opt_len) {
verbose(VERB_ALGO, "query with bad edns keepalive.");
return LDNS_RCODE_FORMERR;
}
if(!edns_opt_list_append_keepalive(&edns->opt_list_out,
c->tcp_timeout_msec / 100,
region)) {
log_err("out of memory");
return LDNS_RCODE_SERVFAIL;
}
c->tcp_keepalive = 1;
break;
case LDNS_EDNS_PADDING:
if(!cfg || !cfg->pad_responses ||
!c || c->type != comm_tcp ||!c->ssl)
break;
if(!edns_opt_list_append(&edns->opt_list_out,
LDNS_EDNS_PADDING,
0, NULL, region)) {
log_err("out of memory");
return LDNS_RCODE_SERVFAIL;
}
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;
}
if(!edns_opt_list_append(&edns->opt_list_in,
opt_code, opt_len, rdata_ptr, region)) {
log_err("out of memory");
return LDNS_RCODE_SERVFAIL;
}
rdata_ptr += opt_len;
rdata_len -= opt_len;
}
return LDNS_RCODE_NOERROR;
}
int
parse_extract_edns_from_response_msg(struct msg_parse* msg,
struct edns_data* edns, struct regional* region)
{
struct rrset_parse* rrset = msg->rrset_first;
struct rrset_parse* prev = 0;
struct rrset_parse* found = 0;
struct rrset_parse* found_prev = 0;
size_t rdata_len;
uint8_t* rdata_ptr;
/* since the class encodes the UDP size, we cannot use hash table to
* find the EDNS OPT record. Scan the packet. */
while(rrset) {
if(rrset->type == LDNS_RR_TYPE_OPT) {
/* only one OPT RR allowed. */
if(found) return LDNS_RCODE_FORMERR;
/* found it! */
found_prev = prev;
found = rrset;
}
prev = rrset;
rrset = rrset->rrset_all_next;
}
if(!found) {
memset(edns, 0, sizeof(*edns));
edns->udp_size = 512;
return 0;
}
/* check the found RRset */
/* most lenient check possible. ignore dname, use last opt */
if(found->section != LDNS_SECTION_ADDITIONAL)
return LDNS_RCODE_FORMERR;
if(found->rr_count == 0)
return LDNS_RCODE_FORMERR;
if(0) { /* strict checking of dname and RRcount */
if(found->dname_len != 1 || !found->dname
|| found->dname[0] != 0) return LDNS_RCODE_FORMERR;
if(found->rr_count != 1) return LDNS_RCODE_FORMERR;
}
log_assert(found->rr_first && found->rr_last);
/* remove from packet */
if(found_prev) found_prev->rrset_all_next = found->rrset_all_next;
else msg->rrset_first = found->rrset_all_next;
if(found == msg->rrset_last)
msg->rrset_last = found_prev;
msg->arcount --;
msg->ar_rrsets --;
msg->rrset_count --;
/* take the data ! */
edns->edns_present = 1;
edns->ext_rcode = found->rr_last->ttl_data[0];
edns->edns_version = found->rr_last->ttl_data[1];
edns->bits = sldns_read_uint16(&found->rr_last->ttl_data[2]);
edns->udp_size = ntohs(found->rrset_class);
edns->opt_list_in = NULL;
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;
rdata_ptr = found->rr_first->ttl_data+6;
/* while still more options, and have code+len to read */
/* ignores partial content (i.e. rdata len 3) */
while(rdata_len >= 4) {
uint16_t opt_code = sldns_read_uint16(rdata_ptr);
uint16_t opt_len = sldns_read_uint16(rdata_ptr+2);
rdata_ptr += 4;
rdata_len -= 4;
if(opt_len > rdata_len)
break; /* option code partial */
if(!edns_opt_list_append(&edns->opt_list_in,
opt_code, opt_len, rdata_ptr, region)) {
log_err("out of memory");
break;
}
rdata_ptr += opt_len;
rdata_len -= opt_len;
}
/* ignore rrsigs */
return LDNS_RCODE_NOERROR;
}
/** skip RR in packet */
static int
skip_pkt_rr(sldns_buffer* pkt)
{
if(sldns_buffer_remaining(pkt) < 1) return 0;
if(!pkt_dname_len(pkt))
return 0;
if(sldns_buffer_remaining(pkt) < 4) return 0;
sldns_buffer_skip(pkt, 4); /* type and class */
if(!skip_ttl_rdata(pkt))
return 0;
return 1;
}
/** skip RRs from packet */
int
skip_pkt_rrs(sldns_buffer* pkt, int num)
{
int i;
for(i=0; i<num; i++) {
if(!skip_pkt_rr(pkt))
return 0;
}
return 1;
}
int
parse_edns_from_query_pkt(sldns_buffer* pkt, struct edns_data* edns,
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;
log_assert(LDNS_QDCOUNT(sldns_buffer_begin(pkt)) == 1);
memset(edns, 0, sizeof(*edns));
if(LDNS_ANCOUNT(sldns_buffer_begin(pkt)) != 0 ||
LDNS_NSCOUNT(sldns_buffer_begin(pkt)) != 0) {
if(!skip_pkt_rrs(pkt, ((int)LDNS_ANCOUNT(sldns_buffer_begin(pkt)))+
((int)LDNS_NSCOUNT(sldns_buffer_begin(pkt)))))
return LDNS_RCODE_FORMERR;
}
/* check edns section is present */
if(LDNS_ARCOUNT(sldns_buffer_begin(pkt)) > 1) {
return LDNS_RCODE_FORMERR;
}
if(LDNS_ARCOUNT(sldns_buffer_begin(pkt)) == 0) {
edns->udp_size = 512;
return 0;
}
/* domain name must be the root of length 1. */
if(pkt_dname_len(pkt) != 1)
return LDNS_RCODE_FORMERR;
if(sldns_buffer_remaining(pkt) < 10) /* type, class, ttl, rdatalen */
return LDNS_RCODE_FORMERR;
if(sldns_buffer_read_u16(pkt) != LDNS_RR_TYPE_OPT)
return LDNS_RCODE_FORMERR;
edns->edns_present = 1;
edns->udp_size = sldns_buffer_read_u16(pkt); /* class is udp size */
edns->ext_rcode = sldns_buffer_read_u8(pkt); /* ttl used for bits */
edns->edns_version = sldns_buffer_read_u8(pkt);
edns->bits = sldns_buffer_read_u16(pkt);
edns->opt_list_in = NULL;
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);
if(sldns_buffer_remaining(pkt) < rdata_len)
return LDNS_RCODE_FORMERR;
rdata_ptr = sldns_buffer_current(pkt);
/* ignore rrsigs */
return parse_edns_options_from_query(rdata_ptr, rdata_len, edns, cfg,
c, repinfo, now, region);
}
void
log_edns_opt_list(enum verbosity_value level, const char* info_str,
struct edns_option* list)
{
if(verbosity >= level && list) {
char str[128], *s;
size_t slen;
verbose(level, "%s", info_str);
while(list) {
s = str;
slen = sizeof(str);
(void)sldns_wire2str_edns_option_print(&s, &slen, list->opt_code,
list->opt_data, list->opt_len);
verbose(level, " %s", str);
list = list->next;
}
}
}
+/** remove RR from msgparse RRset, return true if rrset is entirely bad */
+int
+msgparse_rrset_remove_rr(const char* str, sldns_buffer* pkt, struct rrset_parse* rrset,
+ struct rr_parse* prev, struct rr_parse* rr, struct sockaddr_storage* addr, socklen_t addrlen)
+{
+ if(verbosity >= VERB_QUERY && rrset->dname_len <= LDNS_MAX_DOMAINLEN && str) {
+ uint8_t buf[LDNS_MAX_DOMAINLEN+1];
+ dname_pkt_copy(pkt, buf, rrset->dname);
+ if(addr)
+ log_name_addr(VERB_QUERY, str, buf, addr, addrlen);
+ else log_nametypeclass(VERB_QUERY, str, buf,
+ rrset->type, ntohs(rrset->rrset_class));
+ }
+ if(prev)
+ prev->next = rr->next;
+ else rrset->rr_first = rr->next;
+ if(rrset->rr_last == rr)
+ rrset->rr_last = prev;
+ rrset->rr_count --;
+ rrset->size -= rr->size;
+ /* rr struct still exists, but is unlinked, so that in the for loop
+ * the rr->next works fine to continue. */
+ return rrset->rr_count == 0;
+}
diff --git a/contrib/unbound/util/data/msgparse.h b/contrib/unbound/util/data/msgparse.h
index b7dc235d677c..8e5c94a28cba 100644
--- a/contrib/unbound/util/data/msgparse.h
+++ b/contrib/unbound/util/data/msgparse.h
@@ -1,374 +1,392 @@
/*
* util/data/msgparse.h - parse wireformat DNS messages.
*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
* Contains message parsing data structures.
* These point back into the packet buffer.
*
* During parsing RRSIGS are put together with the rrsets they (claim to) sign.
* This process works as follows:
* o if RRSIG follows the data rrset, it is added to the rrset rrsig list.
* o if no matching data rrset is found, the RRSIG becomes a new rrset.
* o If the data rrset later follows the RRSIG
* o See if the RRSIG rrset contains multiple types, and needs to
* have the rrsig(s) for that data type split off.
* o Put the data rr as data type in the rrset and rrsig in list.
* o RRSIGs are allowed to move to a different section. The section of
* the data item is used for the final rrset.
* o multiple signatures over an RRset are possible.
*
* For queries of qtype=RRSIG, some special handling is needed, to avoid
* splitting the RRSIG in the answer section.
* o duplicate, not split, RRSIGs from the answer section, if qtype=RRSIG.
* o check for doubles in the rrsig list when adding an RRSIG to data,
* so that a data rrset is signed by RRSIGs with different rdata.
* when qtype=RRSIG.
* This will move the RRSIG from the answer section to sign the data further
* in the packet (if possible). If then after that, more RRSIGs are found
* that sign the data as well, doubles are removed.
*/
#ifndef UTIL_DATA_MSGPARSE_H
#define UTIL_DATA_MSGPARSE_H
#include "util/storage/lruhash.h"
#include "sldns/pkthdr.h"
#include "sldns/rrdef.h"
struct sldns_buffer;
struct rrset_parse;
struct rr_parse;
struct regional;
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
/** Maximum TTL that is allowed. */
extern time_t MAX_TTL;
/** Minimum TTL that is allowed. */
extern time_t MIN_TTL;
/** Maximum Negative TTL that is allowed */
extern time_t MAX_NEG_TTL;
/** If we serve expired entries and prefetch them */
extern int SERVE_EXPIRED;
/** Time to serve records after expiration */
extern time_t SERVE_EXPIRED_TTL;
/** TTL to use for expired records */
extern time_t SERVE_EXPIRED_REPLY_TTL;
/** Negative cache time (for entries without any RRs.) */
#define NORR_TTL 5 /* seconds */
/** If we serve the original TTL or decrementing TTLs */
extern int SERVE_ORIGINAL_TTL;
/**
* Data stored in scratch pad memory during parsing.
* Stores the data that will enter into the msgreply and packet result.
*/
struct msg_parse {
/** id from message, network format. */
uint16_t id;
/** flags from message, host format. */
uint16_t flags;
/** count of RRs, host format */
uint16_t qdcount;
/** count of RRs, host format */
uint16_t ancount;
/** count of RRs, host format */
uint16_t nscount;
/** count of RRs, host format */
uint16_t arcount;
/** count of RRsets per section. */
size_t an_rrsets;
/** count of RRsets per section. */
size_t ns_rrsets;
/** count of RRsets per section. */
size_t ar_rrsets;
/** total number of rrsets found. */
size_t rrset_count;
/** query dname (pointer to start location in packet, NULL if none */
uint8_t* qname;
/** length of query dname in octets, 0 if none */
size_t qname_len;
/** query type, host order. 0 if qdcount=0 */
uint16_t qtype;
/** query class, host order. 0 if qdcount=0 */
uint16_t qclass;
/**
* Hash table array used during parsing to lookup rrset types.
* Based on name, type, class. Same hash value as in rrset cache.
*/
struct rrset_parse* hashtable[PARSE_TABLE_SIZE];
/** linked list of rrsets that have been found (in order). */
struct rrset_parse* rrset_first;
/** last element of rrset list. */
struct rrset_parse* rrset_last;
};
/**
* Data stored for an rrset during parsing.
*/
struct rrset_parse {
/** next in hash bucket */
struct rrset_parse* rrset_bucket_next;
/** next in list of all rrsets */
struct rrset_parse* rrset_all_next;
/** hash value of rrset */
hashvalue_type hash;
/** which section was it found in: one of
* LDNS_SECTION_ANSWER, LDNS_SECTION_AUTHORITY, LDNS_SECTION_ADDITIONAL
*/
sldns_pkt_section section;
/** start of (possibly compressed) dname in packet */
uint8_t* dname;
/** length of the dname uncompressed wireformat */
size_t dname_len;
/** type, host order. */
uint16_t type;
/** class, network order. var name so that it is not a c++ keyword. */
uint16_t rrset_class;
/** the flags for the rrset, like for packedrrset */
uint32_t flags;
/** number of RRs in the rr list */
size_t rr_count;
/** sum of RR rdata sizes */
size_t size;
/** linked list of RRs in this rrset. */
struct rr_parse* rr_first;
/** last in list of RRs in this rrset. */
struct rr_parse* rr_last;
/** number of RRSIGs over this rrset. */
size_t rrsig_count;
/** linked list of RRsig RRs over this rrset. */
struct rr_parse* rrsig_first;
/** last in list of RRSIG RRs over this rrset. */
struct rr_parse* rrsig_last;
};
/**
* Data stored for an RR during parsing.
*/
struct rr_parse {
/**
* Pointer to the RR. Points to start of TTL value in the packet.
* Rdata length and rdata follow it.
* its dname, type and class are the same and stored for the rrset.
*/
uint8_t* ttl_data;
/** true if ttl_data is not part of the packet, but elsewhere in mem.
* Set for generated CNAMEs for DNAMEs. */
int outside_packet;
/** the length of the rdata if allocated (with no dname compression)*/
size_t size;
/** next in list of RRs. */
struct rr_parse* next;
};
/** Check if label length is first octet of a compression pointer, pass u8. */
#define LABEL_IS_PTR(x) ( ((x)&0xc0) == 0xc0 )
/** Calculate destination offset of a compression pointer. pass first and
* second octets of the compression pointer. */
#define PTR_OFFSET(x, y) ( ((x)&0x3f)<<8 | (y) )
/** create a compression pointer to the given offset. */
#define PTR_CREATE(offset) ((uint16_t)(0xc000 | (offset)))
/** error codes, extended with EDNS, so > 15. */
#define EDNS_RCODE_BADVERS 16 /** bad EDNS version */
/** largest valid compression offset */
#define PTR_MAX_OFFSET 0x3fff
/**
* EDNS data storage
* rdata is parsed in a list (has accessor functions). allocated in a
* region.
*/
struct edns_data {
/** Extended RCODE */
uint8_t ext_rcode;
/** The EDNS version number */
uint8_t edns_version;
/** the EDNS bits field from ttl (host order): Z */
uint16_t bits;
/** UDP reassembly size. */
uint16_t udp_size;
/** rdata element list of options of an incoming packet created at
* parse time, or NULL if none */
struct edns_option* opt_list_in;
/** rdata element list of options to encode for outgoing packets,
* or NULL if none */
struct edns_option* opt_list_out;
/** rdata element list of outgoing edns options from modules
* or NULL if none */
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
*/
struct edns_option {
/** next item in list */
struct edns_option* next;
/** type of this edns option */
uint16_t opt_code;
/** length of this edns option (cannot exceed uint16 in encoding) */
size_t opt_len;
/** data of this edns option; allocated in region, or NULL if len=0 */
uint8_t* opt_data;
};
/**
* Obtain size in the packet of an rr type, that is before dname type.
* Do TYPE_DNAME, and type STR, yourself. Gives size for most regular types.
* @param rdf: the rdf type from the descriptor.
* @return: size in octets. 0 on failure.
*/
size_t get_rdf_size(sldns_rdf_type rdf);
/**
* Parse the packet.
* @param pkt: packet, position at call must be at start of packet.
* at end position is after packet.
* @param msg: where to store results.
* @param region: how to alloc results.
* @return: 0 if OK, or rcode on error.
*/
int parse_packet(struct sldns_buffer* pkt, struct msg_parse* msg,
struct regional* region);
/**
* After parsing the packet, extract EDNS data from packet.
* If not present this is noted in the data structure.
* If a parse error happens, an error code is returned.
*
* Quirks:
* o ignores OPT rdata.
* o ignores OPT owner name.
* o ignores extra OPT records, except the last one in the packet.
*
* @param msg: parsed message structure. Modified on exit, if EDNS was present
* it is removed from the additional section.
* @param edns: the edns data is stored here. Does not have to be initialised.
* @param region: region to alloc results in (edns option contents)
* @return: 0 on success. or an RCODE on an error.
* RCODE formerr if OPT in wrong section, and so on.
*/
int parse_extract_edns_from_response_msg(struct msg_parse* msg,
struct edns_data* edns, struct regional* region);
/**
* Skip RRs from packet
* @param pkt: the packet. position at start must be right after the query
* section. At end, right after EDNS data or no movement if failed.
* @param num: Limit of the number of records we want to parse.
* @return: 0 on success, 1 on failure.
*/
int skip_pkt_rrs(struct sldns_buffer* pkt, int num);
/**
* If EDNS data follows a query section, extract it and initialize edns struct.
* @param pkt: the packet. position at start must be right after the query
* section. At end, right after EDNS data or no movement if failed.
* @param edns: the edns data allocated by the caller. Does not have to be
* 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 comm_reply* repinfo, time_t now, struct regional* region);
/**
* Calculate hash value for rrset in packet.
* @param pkt: the packet.
* @param dname: pointer to uncompressed dname, or compressed dname in packet.
* @param type: rrset type in host order.
* @param dclass: rrset class in network order.
* @param rrset_flags: rrset flags (same as packed_rrset flags).
* @return hash value
*/
hashvalue_type pkt_hash_rrset(struct sldns_buffer* pkt, uint8_t* dname,
uint16_t type, uint16_t dclass, uint32_t rrset_flags);
/**
* Lookup in msg hashtable to find a rrset.
* @param msg: with the hashtable.
* @param pkt: packet for compressed names.
* @param h: hash value
* @param rrset_flags: flags of rrset sought for.
* @param dname: name of rrset sought for.
* @param dnamelen: len of dname.
* @param type: rrset type, host order.
* @param dclass: rrset class, network order.
* @return NULL or the rrset_parse if found.
*/
struct rrset_parse* msgparse_hashtable_lookup(struct msg_parse* msg,
struct sldns_buffer* pkt, hashvalue_type h, uint32_t rrset_flags,
uint8_t* dname, size_t dnamelen, uint16_t type, uint16_t dclass);
/**
* Remove rrset from hash table.
* @param msg: with hashtable.
* @param rrset: with hash value and id info.
*/
void msgparse_bucket_remove(struct msg_parse* msg, struct rrset_parse* rrset);
/**
* Log the edns options in the edns option list.
* @param level: the verbosity level.
* @param info_str: the informational string to be printed before the options.
* @param list: the edns option list.
*/
void log_edns_opt_list(enum verbosity_value level, const char* info_str,
struct edns_option* list);
+/**
+ * Remove RR from msgparse RRset.
+ * @param str: this string is used for logging if verbose. If NULL, there is
+ * no logging of the remove.
+ * @param pkt: packet in buffer that is removed from. Used to log the name
+ * of the item removed.
+ * @param rrset: RRset that the RR is removed from.
+ * @param prev: previous RR in list, or NULL.
+ * @param rr: RR that is removed.
+ * @param addr: address used for logging, if verbose, or NULL then it is not
+ * used.
+ * @param addrlen: length of addr, if that is not NULL.
+ * @return true if rrset is entirely bad, it would then need to be removed.
+ */
+int msgparse_rrset_remove_rr(const char* str, struct sldns_buffer* pkt,
+ struct rrset_parse* rrset, struct rr_parse* prev, struct rr_parse* rr,
+ struct sockaddr_storage* addr, socklen_t addrlen);
+
#endif /* UTIL_DATA_MSGPARSE_H */
diff --git a/contrib/unbound/util/fptr_wlist.c b/contrib/unbound/util/fptr_wlist.c
index 3b88da2358e5..43d38dc3797d 100644
--- a/contrib/unbound/util/fptr_wlist.c
+++ b/contrib/unbound/util/fptr_wlist.c
@@ -1,698 +1,700 @@
/*
* util/fptr_wlist.c - function pointer whitelists.
*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file contains functions that check function pointers.
* The functions contain a whitelist of known good callback values.
* Any other values lead to an error.
*
* Due to the listing nature, this file violates all the modularization
* boundaries in the program.
*/
#include "config.h"
#include "util/fptr_wlist.h"
#include "util/mini_event.h"
#include "services/outside_network.h"
#include "services/mesh.h"
#include "services/localzone.h"
#include "services/authzone.h"
#include "services/cache/infra.h"
#include "services/cache/rrset.h"
#include "services/view.h"
#include "dns64/dns64.h"
#include "iterator/iterator.h"
#include "iterator/iter_fwd.h"
#include "validator/validator.h"
#include "validator/val_anchor.h"
#include "validator/val_nsec3.h"
#include "validator/val_sigcrypt.h"
#include "validator/val_kentry.h"
#include "validator/val_neg.h"
#include "validator/autotrust.h"
#include "util/data/msgreply.h"
#include "util/data/packed_rrset.h"
#include "util/storage/slabhash.h"
#include "util/storage/dnstree.h"
#include "util/locks.h"
#include "libunbound/libworker.h"
#include "libunbound/context.h"
#include "libunbound/worker.h"
#include "util/tube.h"
#include "util/config_file.h"
#ifdef UB_ON_WINDOWS
#include "winrc/win_svc.h"
#endif
#include "respip/respip.h"
#ifdef WITH_PYTHONMODULE
#include "pythonmod/pythonmod.h"
#endif
#ifdef WITH_DYNLIBMODULE
#include "dynlibmod/dynlibmod.h"
#endif
#ifdef USE_CACHEDB
#include "cachedb/cachedb.h"
#endif
#ifdef USE_IPSECMOD
#include "ipsecmod/ipsecmod.h"
#endif
#ifdef CLIENT_SUBNET
#include "edns-subnet/subnetmod.h"
#endif
#ifdef USE_IPSET
#include "ipset/ipset.h"
#endif
#ifdef USE_DNSTAP
#include "dnstap/dtstream.h"
#endif
int
fptr_whitelist_comm_point(comm_point_callback_type *fptr)
{
if(fptr == &worker_handle_request) return 1;
else if(fptr == &outnet_udp_cb) return 1;
else if(fptr == &outnet_tcp_cb) return 1;
else if(fptr == &tube_handle_listen) return 1;
else if(fptr == &auth_xfer_probe_udp_callback) return 1;
else if(fptr == &auth_xfer_transfer_tcp_callback) return 1;
else if(fptr == &auth_xfer_transfer_http_callback) return 1;
return 0;
}
int
fptr_whitelist_comm_point_raw(comm_point_callback_type *fptr)
{
if(fptr == &tube_handle_listen) return 1;
else if(fptr == &tube_handle_write) return 1;
else if(fptr == &remote_accept_callback) return 1;
else if(fptr == &remote_control_callback) return 1;
return 0;
}
int
fptr_whitelist_comm_timer(void (*fptr)(void*))
{
if(fptr == &pending_udp_timer_cb) return 1;
else if(fptr == &outnet_tcptimer) return 1;
else if(fptr == &pending_udp_timer_delay_cb) return 1;
else if(fptr == &worker_stat_timer_cb) return 1;
else if(fptr == &worker_probe_timer_cb) return 1;
#ifdef UB_ON_WINDOWS
else if(fptr == &wsvc_cron_cb) return 1;
#endif
else if(fptr == &auth_xfer_timer) return 1;
else if(fptr == &auth_xfer_probe_timer_callback) return 1;
else if(fptr == &auth_xfer_transfer_timer_callback) return 1;
else if(fptr == &mesh_serve_expired_callback) return 1;
else if(fptr == &serviced_timer_cb) return 1;
#ifdef USE_DNSTAP
else if(fptr == &mq_wakeup_cb) return 1;
#endif
return 0;
}
int
fptr_whitelist_comm_signal(void (*fptr)(int, void*))
{
if(fptr == &worker_sighandler) return 1;
return 0;
}
int fptr_whitelist_start_accept(void (*fptr)(void*))
{
if(fptr == &worker_start_accept) return 1;
return 0;
}
int fptr_whitelist_stop_accept(void (*fptr)(void*))
{
if(fptr == &worker_stop_accept) return 1;
return 0;
}
int
fptr_whitelist_event(void (*fptr)(int, short, void *))
{
if(fptr == &comm_point_udp_callback) return 1;
+#if defined(AF_INET6) && defined(IPV6_PKTINFO) && defined(HAVE_RECVMSG)
else if(fptr == &comm_point_udp_ancil_callback) return 1;
+#endif
else if(fptr == &comm_point_tcp_accept_callback) return 1;
else if(fptr == &comm_point_tcp_handle_callback) return 1;
else if(fptr == &comm_timer_callback) return 1;
else if(fptr == &comm_signal_callback) return 1;
else if(fptr == &comm_point_local_handle_callback) return 1;
else if(fptr == &comm_point_raw_handle_callback) return 1;
else if(fptr == &tube_handle_signal) return 1;
else if(fptr == &comm_base_handle_slow_accept) return 1;
else if(fptr == &comm_point_http_handle_callback) return 1;
#ifdef USE_DNSTAP
else if(fptr == &dtio_output_cb) return 1;
else if(fptr == &dtio_cmd_cb) return 1;
else if(fptr == &dtio_reconnect_timeout_cb) return 1;
else if(fptr == &dtio_stop_timer_cb) return 1;
else if(fptr == &dtio_stop_ev_cb) return 1;
else if(fptr == &dtio_tap_callback) return 1;
else if(fptr == &dtio_mainfdcallback) return 1;
#endif
#ifdef UB_ON_WINDOWS
else if(fptr == &worker_win_stop_cb) return 1;
#endif
return 0;
}
int
fptr_whitelist_pending_udp(comm_point_callback_type *fptr)
{
if(fptr == &serviced_udp_callback) return 1;
return 0;
}
int
fptr_whitelist_pending_tcp(comm_point_callback_type *fptr)
{
if(fptr == &serviced_tcp_callback) return 1;
return 0;
}
int
fptr_whitelist_serviced_query(comm_point_callback_type *fptr)
{
if(fptr == &worker_handle_service_reply) return 1;
else if(fptr == &libworker_handle_service_reply) return 1;
return 0;
}
int
fptr_whitelist_rbtree_cmp(int (*fptr) (const void *, const void *))
{
if(fptr == &mesh_state_compare) return 1;
else if(fptr == &mesh_state_ref_compare) return 1;
else if(fptr == &addr_tree_compare) return 1;
else if(fptr == &addr_tree_addrport_compare) return 1;
else if(fptr == &local_zone_cmp) return 1;
else if(fptr == &local_data_cmp) return 1;
else if(fptr == &fwd_cmp) return 1;
else if(fptr == &pending_cmp) return 1;
else if(fptr == &serviced_cmp) return 1;
else if(fptr == &reuse_cmp) return 1;
else if(fptr == &reuse_id_cmp) return 1;
else if(fptr == &name_tree_compare) return 1;
else if(fptr == &order_lock_cmp) return 1;
else if(fptr == &codeline_cmp) return 1;
else if(fptr == &nsec3_hash_cmp) return 1;
else if(fptr == &mini_ev_cmp) return 1;
else if(fptr == &anchor_cmp) return 1;
else if(fptr == &canonical_tree_compare) return 1;
else if(fptr == &context_query_cmp) return 1;
else if(fptr == &val_neg_data_compare) return 1;
else if(fptr == &val_neg_zone_compare) return 1;
else if(fptr == &probetree_cmp) return 1;
else if(fptr == &replay_var_compare) return 1;
else if(fptr == &view_cmp) return 1;
else if(fptr == &auth_zone_cmp) return 1;
else if(fptr == &auth_data_cmp) return 1;
else if(fptr == &auth_xfer_cmp) return 1;
return 0;
}
int
fptr_whitelist_hash_sizefunc(lruhash_sizefunc_type fptr)
{
if(fptr == &msgreply_sizefunc) return 1;
else if(fptr == &ub_rrset_sizefunc) return 1;
else if(fptr == &infra_sizefunc) return 1;
else if(fptr == &key_entry_sizefunc) return 1;
else if(fptr == &rate_sizefunc) return 1;
else if(fptr == &ip_rate_sizefunc) return 1;
else if(fptr == &test_slabhash_sizefunc) return 1;
#ifdef CLIENT_SUBNET
else if(fptr == &msg_cache_sizefunc) return 1;
#endif
#ifdef USE_DNSCRYPT
else if(fptr == &dnsc_shared_secrets_sizefunc) return 1;
else if(fptr == &dnsc_nonces_sizefunc) return 1;
#endif
return 0;
}
int
fptr_whitelist_hash_compfunc(lruhash_compfunc_type fptr)
{
if(fptr == &query_info_compare) return 1;
else if(fptr == &ub_rrset_compare) return 1;
else if(fptr == &infra_compfunc) return 1;
else if(fptr == &key_entry_compfunc) return 1;
else if(fptr == &rate_compfunc) return 1;
else if(fptr == &ip_rate_compfunc) return 1;
else if(fptr == &test_slabhash_compfunc) return 1;
#ifdef USE_DNSCRYPT
else if(fptr == &dnsc_shared_secrets_compfunc) return 1;
else if(fptr == &dnsc_nonces_compfunc) return 1;
#endif
return 0;
}
int
fptr_whitelist_hash_delkeyfunc(lruhash_delkeyfunc_type fptr)
{
if(fptr == &query_entry_delete) return 1;
else if(fptr == &ub_rrset_key_delete) return 1;
else if(fptr == &infra_delkeyfunc) return 1;
else if(fptr == &key_entry_delkeyfunc) return 1;
else if(fptr == &rate_delkeyfunc) return 1;
else if(fptr == &ip_rate_delkeyfunc) return 1;
else if(fptr == &test_slabhash_delkey) return 1;
#ifdef USE_DNSCRYPT
else if(fptr == &dnsc_shared_secrets_delkeyfunc) return 1;
else if(fptr == &dnsc_nonces_delkeyfunc) return 1;
#endif
return 0;
}
int
fptr_whitelist_hash_deldatafunc(lruhash_deldatafunc_type fptr)
{
if(fptr == &reply_info_delete) return 1;
else if(fptr == &rrset_data_delete) return 1;
else if(fptr == &infra_deldatafunc) return 1;
else if(fptr == &key_entry_deldatafunc) return 1;
else if(fptr == &rate_deldatafunc) return 1;
else if(fptr == &test_slabhash_deldata) return 1;
#ifdef CLIENT_SUBNET
else if(fptr == &subnet_data_delete) return 1;
#endif
#ifdef USE_DNSCRYPT
else if(fptr == &dnsc_shared_secrets_deldatafunc) return 1;
else if(fptr == &dnsc_nonces_deldatafunc) return 1;
#endif
return 0;
}
int
fptr_whitelist_hash_markdelfunc(lruhash_markdelfunc_type fptr)
{
if(fptr == NULL) return 1;
else if(fptr == &rrset_markdel) return 1;
#ifdef CLIENT_SUBNET
else if(fptr == &subnet_markdel) return 1;
#endif
return 0;
}
/** whitelist env->send_query callbacks */
int
fptr_whitelist_modenv_send_query(struct outbound_entry* (*fptr)(
struct query_info* qinfo, uint16_t flags, int dnssec, int want_dnssec,
int nocaps, int check_ratelimit, struct sockaddr_storage* addr,
socklen_t addrlen, uint8_t* zone, size_t zonelen, int tcp_upstream,
int ssl_upstream, char* tls_auth_name, struct module_qstate* q,
int* was_ratelimited))
{
if(fptr == &worker_send_query) return 1;
else if(fptr == &libworker_send_query) return 1;
return 0;
}
int
fptr_whitelist_modenv_detach_subs(void (*fptr)(
struct module_qstate* qstate))
{
if(fptr == &mesh_detach_subs) return 1;
return 0;
}
int
fptr_whitelist_modenv_attach_sub(int (*fptr)(
struct module_qstate* qstate, struct query_info* qinfo,
uint16_t qflags, int prime, int valrec, struct module_qstate** newq))
{
if(fptr == &mesh_attach_sub) return 1;
return 0;
}
int
fptr_whitelist_modenv_add_sub(int (*fptr)(
struct module_qstate* qstate, struct query_info* qinfo,
uint16_t qflags, int prime, int valrec, struct module_qstate** newq,
struct mesh_state** sub))
{
if(fptr == &mesh_add_sub) return 1;
return 0;
}
int
fptr_whitelist_modenv_kill_sub(void (*fptr)(struct module_qstate* newq))
{
if(fptr == &mesh_state_delete) return 1;
return 0;
}
int
fptr_whitelist_modenv_detect_cycle(int (*fptr)(
struct module_qstate* qstate, struct query_info* qinfo,
uint16_t flags, int prime, int valrec))
{
if(fptr == &mesh_detect_cycle) return 1;
return 0;
}
int
fptr_whitelist_mod_init(int (*fptr)(struct module_env* env, int id))
{
if(fptr == &iter_init) return 1;
else if(fptr == &val_init) return 1;
else if(fptr == &dns64_init) return 1;
else if(fptr == &respip_init) return 1;
#ifdef WITH_PYTHONMODULE
else if(fptr == &pythonmod_init) return 1;
#endif
#ifdef WITH_DYNLIBMODULE
else if(fptr == &dynlibmod_init) return 1;
#endif
#ifdef USE_CACHEDB
else if(fptr == &cachedb_init) return 1;
#endif
#ifdef USE_IPSECMOD
else if(fptr == &ipsecmod_init) return 1;
#endif
#ifdef CLIENT_SUBNET
else if(fptr == &subnetmod_init) return 1;
#endif
#ifdef USE_IPSET
else if(fptr == &ipset_init) return 1;
#endif
return 0;
}
int
fptr_whitelist_mod_deinit(void (*fptr)(struct module_env* env, int id))
{
if(fptr == &iter_deinit) return 1;
else if(fptr == &val_deinit) return 1;
else if(fptr == &dns64_deinit) return 1;
else if(fptr == &respip_deinit) return 1;
#ifdef WITH_PYTHONMODULE
else if(fptr == &pythonmod_deinit) return 1;
#endif
#ifdef WITH_DYNLIBMODULE
else if(fptr == &dynlibmod_deinit) return 1;
#endif
#ifdef USE_CACHEDB
else if(fptr == &cachedb_deinit) return 1;
#endif
#ifdef USE_IPSECMOD
else if(fptr == &ipsecmod_deinit) return 1;
#endif
#ifdef CLIENT_SUBNET
else if(fptr == &subnetmod_deinit) return 1;
#endif
#ifdef USE_IPSET
else if(fptr == &ipset_deinit) return 1;
#endif
return 0;
}
int
fptr_whitelist_mod_operate(void (*fptr)(struct module_qstate* qstate,
enum module_ev event, int id, struct outbound_entry* outbound))
{
if(fptr == &iter_operate) return 1;
else if(fptr == &val_operate) return 1;
else if(fptr == &dns64_operate) return 1;
else if(fptr == &respip_operate) return 1;
#ifdef WITH_PYTHONMODULE
else if(fptr == &pythonmod_operate) return 1;
#endif
#ifdef WITH_DYNLIBMODULE
else if(fptr == &dynlibmod_operate) return 1;
#endif
#ifdef USE_CACHEDB
else if(fptr == &cachedb_operate) return 1;
#endif
#ifdef USE_IPSECMOD
else if(fptr == &ipsecmod_operate) return 1;
#endif
#ifdef CLIENT_SUBNET
else if(fptr == &subnetmod_operate) return 1;
#endif
#ifdef USE_IPSET
else if(fptr == &ipset_operate) return 1;
#endif
return 0;
}
int
fptr_whitelist_mod_inform_super(void (*fptr)(
struct module_qstate* qstate, int id, struct module_qstate* super))
{
if(fptr == &iter_inform_super) return 1;
else if(fptr == &val_inform_super) return 1;
else if(fptr == &dns64_inform_super) return 1;
else if(fptr == &respip_inform_super) return 1;
#ifdef WITH_PYTHONMODULE
else if(fptr == &pythonmod_inform_super) return 1;
#endif
#ifdef WITH_DYNLIBMODULE
else if(fptr == &dynlibmod_inform_super) return 1;
#endif
#ifdef USE_CACHEDB
else if(fptr == &cachedb_inform_super) return 1;
#endif
#ifdef USE_IPSECMOD
else if(fptr == &ipsecmod_inform_super) return 1;
#endif
#ifdef CLIENT_SUBNET
else if(fptr == &subnetmod_inform_super) return 1;
#endif
#ifdef USE_IPSET
else if(fptr == &ipset_inform_super) return 1;
#endif
return 0;
}
int
fptr_whitelist_mod_clear(void (*fptr)(struct module_qstate* qstate,
int id))
{
if(fptr == &iter_clear) return 1;
else if(fptr == &val_clear) return 1;
else if(fptr == &dns64_clear) return 1;
else if(fptr == &respip_clear) return 1;
#ifdef WITH_PYTHONMODULE
else if(fptr == &pythonmod_clear) return 1;
#endif
#ifdef WITH_DYNLIBMODULE
else if(fptr == &dynlibmod_clear) return 1;
#endif
#ifdef USE_CACHEDB
else if(fptr == &cachedb_clear) return 1;
#endif
#ifdef USE_IPSECMOD
else if(fptr == &ipsecmod_clear) return 1;
#endif
#ifdef CLIENT_SUBNET
else if(fptr == &subnetmod_clear) return 1;
#endif
#ifdef USE_IPSET
else if(fptr == &ipset_clear) return 1;
#endif
return 0;
}
int
fptr_whitelist_mod_get_mem(size_t (*fptr)(struct module_env* env, int id))
{
if(fptr == &iter_get_mem) return 1;
else if(fptr == &val_get_mem) return 1;
else if(fptr == &dns64_get_mem) return 1;
else if(fptr == &respip_get_mem) return 1;
#ifdef WITH_PYTHONMODULE
else if(fptr == &pythonmod_get_mem) return 1;
#endif
#ifdef WITH_DYNLIBMODULE
else if(fptr == &dynlibmod_get_mem) return 1;
#endif
#ifdef USE_CACHEDB
else if(fptr == &cachedb_get_mem) return 1;
#endif
#ifdef USE_IPSECMOD
else if(fptr == &ipsecmod_get_mem) return 1;
#endif
#ifdef CLIENT_SUBNET
else if(fptr == &subnetmod_get_mem) return 1;
#endif
#ifdef USE_IPSET
else if(fptr == &ipset_get_mem) return 1;
#endif
return 0;
}
int
fptr_whitelist_alloc_cleanup(void (*fptr)(void*))
{
if(fptr == &worker_alloc_cleanup) return 1;
return 0;
}
int fptr_whitelist_tube_listen(tube_callback_type* fptr)
{
if(fptr == &worker_handle_control_cmd) return 1;
else if(fptr == &libworker_handle_control_cmd) return 1;
return 0;
}
int fptr_whitelist_mesh_cb(mesh_cb_func_type fptr)
{
if(fptr == &libworker_fg_done_cb) return 1;
else if(fptr == &libworker_bg_done_cb) return 1;
else if(fptr == &libworker_event_done_cb) return 1;
else if(fptr == &probe_answer_cb) return 1;
else if(fptr == &auth_xfer_probe_lookup_callback) return 1;
else if(fptr == &auth_xfer_transfer_lookup_callback) return 1;
else if(fptr == &auth_zonemd_dnskey_lookup_callback) return 1;
return 0;
}
int fptr_whitelist_print_func(void (*fptr)(char*,void*))
{
if(fptr == &config_print_func) return 1;
else if(fptr == &config_collate_func) return 1;
else if(fptr == &remote_get_opt_ssl) return 1;
return 0;
}
int fptr_whitelist_inplace_cb_reply_generic(inplace_cb_reply_func_type* fptr,
enum inplace_cb_list_type type)
{
#ifndef WITH_PYTHONMODULE
(void)fptr;
#endif
if(type == inplace_cb_reply) {
#ifdef WITH_PYTHONMODULE
if(fptr == &python_inplace_cb_reply_generic) return 1;
#endif
#ifdef WITH_DYNLIBMODULE
if(fptr == &dynlib_inplace_cb_reply_generic) return 1;
#endif
} else if(type == inplace_cb_reply_cache) {
#ifdef WITH_PYTHONMODULE
if(fptr == &python_inplace_cb_reply_generic) return 1;
#endif
#ifdef WITH_DYNLIBMODULE
if(fptr == &dynlib_inplace_cb_reply_generic) return 1;
#endif
} else if(type == inplace_cb_reply_local) {
#ifdef WITH_PYTHONMODULE
if(fptr == &python_inplace_cb_reply_generic) return 1;
#endif
#ifdef WITH_DYNLIBMODULE
if(fptr == &dynlib_inplace_cb_reply_generic) return 1;
#endif
} else if(type == inplace_cb_reply_servfail) {
#ifdef WITH_PYTHONMODULE
if(fptr == &python_inplace_cb_reply_generic) return 1;
#endif
#ifdef WITH_DYNLIBMODULE
if(fptr == &dynlib_inplace_cb_reply_generic) return 1;
#endif
}
return 0;
}
int fptr_whitelist_inplace_cb_query(inplace_cb_query_func_type* fptr)
{
#ifdef CLIENT_SUBNET
if(fptr == &ecs_whitelist_check)
return 1;
#endif
#ifdef WITH_PYTHONMODULE
if(fptr == &python_inplace_cb_query_generic)
return 1;
#endif
#ifdef WITH_DYNLIBMODULE
if(fptr == &dynlib_inplace_cb_query_generic)
return 1;
#endif
(void)fptr;
return 0;
}
int fptr_whitelist_inplace_cb_edns_back_parsed(
inplace_cb_edns_back_parsed_func_type* fptr)
{
#ifdef CLIENT_SUBNET
if(fptr == &ecs_edns_back_parsed)
return 1;
#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;
#endif
return 0;
}
int fptr_whitelist_inplace_cb_query_response(
inplace_cb_query_response_func_type* fptr)
{
#ifdef CLIENT_SUBNET
if(fptr == &ecs_query_response)
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;
#endif
return 0;
}
int fptr_whitelist_serve_expired_lookup(serve_expired_lookup_func_type* fptr)
{
if(fptr == &mesh_serve_expired_lookup)
return 1;
return 0;
}
diff --git a/contrib/unbound/util/log.c b/contrib/unbound/util/log.c
index dfbb23349944..a15ee920c0fb 100644
--- a/contrib/unbound/util/log.c
+++ b/contrib/unbound/util/log.c
@@ -1,538 +1,538 @@
/*
* util/log.c - implementation of the log code
*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
* Implementation of log.h.
*/
#include "config.h"
#include "util/log.h"
#include "util/locks.h"
#include "sldns/sbuffer.h"
#include <stdarg.h>
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#ifdef HAVE_SYSLOG_H
# include <syslog.h>
#else
/**define LOG_ constants */
# define LOG_CRIT 2
# define LOG_ERR 3
# define LOG_WARNING 4
# define LOG_NOTICE 5
# define LOG_INFO 6
# define LOG_DEBUG 7
#endif
#ifdef UB_ON_WINDOWS
# include "winrc/win_svc.h"
#endif
/* default verbosity */
enum verbosity_value verbosity = NO_VERBOSE;
/** the file logged to. */
static FILE* logfile = 0;
/** if key has been created */
static int key_created = 0;
/** pthread key for thread ids in logfile */
static ub_thread_key_type logkey;
#ifndef THREADS_DISABLED
/** pthread mutex to protect FILE* */
static lock_basic_type log_lock;
#endif
/** the identity of this executable/process */
static const char* ident="unbound";
static const char* default_ident="unbound";
#if defined(HAVE_SYSLOG_H) || defined(UB_ON_WINDOWS)
/** are we using syslog(3) to log to */
static int logging_to_syslog = 0;
#endif /* HAVE_SYSLOG_H */
/** print time in UTC or in secondsfrom1970 */
static int log_time_asc = 0;
void
log_init(const char* filename, int use_syslog, const char* chrootdir)
{
FILE *f;
if(!key_created) {
key_created = 1;
ub_thread_key_create(&logkey, NULL);
lock_basic_init(&log_lock);
}
lock_basic_lock(&log_lock);
if(logfile
#if defined(HAVE_SYSLOG_H) || defined(UB_ON_WINDOWS)
|| logging_to_syslog
#endif
) {
lock_basic_unlock(&log_lock); /* verbose() needs the lock */
verbose(VERB_QUERY, "switching log to %s",
use_syslog?"syslog":(filename&&filename[0]?filename:"stderr"));
lock_basic_lock(&log_lock);
}
if(logfile && logfile != stderr) {
FILE* cl = logfile;
logfile = NULL; /* set to NULL before it is closed, so that
other threads have a valid logfile or NULL */
fclose(cl);
}
#ifdef HAVE_SYSLOG_H
if(logging_to_syslog) {
closelog();
logging_to_syslog = 0;
}
if(use_syslog) {
/* do not delay opening until first write, because we may
* chroot and no longer be able to access dev/log and so on */
/* the facility is LOG_DAEMON by default, but
* --with-syslog-facility=LOCAL[0-7] can override it */
openlog(ident, LOG_NDELAY, UB_SYSLOG_FACILITY);
logging_to_syslog = 1;
lock_basic_unlock(&log_lock);
return;
}
#elif defined(UB_ON_WINDOWS)
if(logging_to_syslog) {
logging_to_syslog = 0;
}
if(use_syslog) {
logging_to_syslog = 1;
lock_basic_unlock(&log_lock);
return;
}
#endif /* HAVE_SYSLOG_H */
if(!filename || !filename[0]) {
logfile = stderr;
lock_basic_unlock(&log_lock);
return;
}
/* open the file for logging */
if(chrootdir && chrootdir[0] && strncmp(filename, chrootdir,
strlen(chrootdir)) == 0)
filename += strlen(chrootdir);
f = fopen(filename, "a");
if(!f) {
lock_basic_unlock(&log_lock);
log_err("Could not open logfile %s: %s", filename,
strerror(errno));
return;
}
#ifndef UB_ON_WINDOWS
/* line buffering does not work on windows */
setvbuf(f, NULL, (int)_IOLBF, 0);
#endif
logfile = f;
lock_basic_unlock(&log_lock);
}
void log_file(FILE *f)
{
lock_basic_lock(&log_lock);
logfile = f;
lock_basic_unlock(&log_lock);
}
void log_thread_set(int* num)
{
ub_thread_key_set(logkey, num);
}
int log_thread_get(void)
{
unsigned int* tid;
if(!key_created) return 0;
tid = (unsigned int*)ub_thread_key_get(logkey);
return (int)(tid?*tid:0);
}
void log_ident_set(const char* id)
{
ident = id;
}
void log_ident_set_default(const char* id)
{
default_ident = id;
}
-void log_ident_revert_to_default()
+void log_ident_revert_to_default(void)
{
ident = default_ident;
}
void log_ident_set_or_default(const char* identity)
{
if(identity == NULL || identity[0] == 0)
log_ident_set(default_ident);
else
log_ident_set(identity);
}
void log_set_time_asc(int use_asc)
{
log_time_asc = use_asc;
}
void* log_get_lock(void)
{
if(!key_created)
return NULL;
#ifndef THREADS_DISABLED
return (void*)&log_lock;
#else
return NULL;
#endif
}
void
log_vmsg(int pri, const char* type,
const char *format, va_list args)
{
char message[MAXSYSLOGMSGLEN];
unsigned int* tid = (unsigned int*)ub_thread_key_get(logkey);
time_t now;
#if defined(HAVE_STRFTIME) && defined(HAVE_LOCALTIME_R)
char tmbuf[32];
struct tm tm;
#elif defined(UB_ON_WINDOWS)
char tmbuf[128], dtbuf[128];
#endif
(void)pri;
vsnprintf(message, sizeof(message), format, args);
#ifdef HAVE_SYSLOG_H
if(logging_to_syslog) {
syslog(pri, "[%d:%x] %s: %s",
(int)getpid(), tid?*tid:0, type, message);
return;
}
#elif defined(UB_ON_WINDOWS)
if(logging_to_syslog) {
char m[32768];
HANDLE* s;
LPCTSTR str = m;
DWORD tp = MSG_GENERIC_ERR;
WORD wt = EVENTLOG_ERROR_TYPE;
if(strcmp(type, "info") == 0) {
tp=MSG_GENERIC_INFO;
wt=EVENTLOG_INFORMATION_TYPE;
} else if(strcmp(type, "warning") == 0) {
tp=MSG_GENERIC_WARN;
wt=EVENTLOG_WARNING_TYPE;
} else if(strcmp(type, "notice") == 0
|| strcmp(type, "debug") == 0) {
tp=MSG_GENERIC_SUCCESS;
wt=EVENTLOG_SUCCESS;
}
snprintf(m, sizeof(m), "[%s:%x] %s: %s",
ident, tid?*tid:0, type, message);
s = RegisterEventSource(NULL, SERVICE_NAME);
if(!s) return;
ReportEvent(s, wt, 0, tp, NULL, 1, 0, &str, NULL);
DeregisterEventSource(s);
return;
}
#endif /* HAVE_SYSLOG_H */
lock_basic_lock(&log_lock);
if(!logfile) {
lock_basic_unlock(&log_lock);
return;
}
now = (time_t)time(NULL);
#if defined(HAVE_STRFTIME) && defined(HAVE_LOCALTIME_R)
if(log_time_asc && strftime(tmbuf, sizeof(tmbuf), "%b %d %H:%M:%S",
localtime_r(&now, &tm))%(sizeof(tmbuf)) != 0) {
/* %sizeof buf!=0 because old strftime returned max on error */
fprintf(logfile, "%s %s[%d:%x] %s: %s\n", tmbuf,
ident, (int)getpid(), tid?*tid:0, type, message);
} else
#elif defined(UB_ON_WINDOWS)
if(log_time_asc && GetTimeFormat(LOCALE_USER_DEFAULT, 0, NULL, NULL,
tmbuf, sizeof(tmbuf)) && GetDateFormat(LOCALE_USER_DEFAULT, 0,
NULL, NULL, dtbuf, sizeof(dtbuf))) {
fprintf(logfile, "%s %s %s[%d:%x] %s: %s\n", dtbuf, tmbuf,
ident, (int)getpid(), tid?*tid:0, type, message);
} else
#endif
fprintf(logfile, "[" ARG_LL "d] %s[%d:%x] %s: %s\n", (long long)now,
ident, (int)getpid(), tid?*tid:0, type, message);
#ifdef UB_ON_WINDOWS
/* line buffering does not work on windows */
fflush(logfile);
#endif
lock_basic_unlock(&log_lock);
}
/**
* implementation of log_info
* @param format: format string printf-style.
*/
void
log_info(const char *format, ...)
{
va_list args;
va_start(args, format);
log_vmsg(LOG_INFO, "info", format, args);
va_end(args);
}
/**
* implementation of log_err
* @param format: format string printf-style.
*/
void
log_err(const char *format, ...)
{
va_list args;
va_start(args, format);
log_vmsg(LOG_ERR, "error", format, args);
va_end(args);
}
/**
* implementation of log_warn
* @param format: format string printf-style.
*/
void
log_warn(const char *format, ...)
{
va_list args;
va_start(args, format);
log_vmsg(LOG_WARNING, "warning", format, args);
va_end(args);
}
/**
* implementation of fatal_exit
* @param format: format string printf-style.
*/
void
fatal_exit(const char *format, ...)
{
va_list args;
va_start(args, format);
log_vmsg(LOG_CRIT, "fatal error", format, args);
va_end(args);
exit(1);
}
/**
* implementation of verbose
* @param level: verbose level for the message.
* @param format: format string printf-style.
*/
void
verbose(enum verbosity_value level, const char* format, ...)
{
va_list args;
va_start(args, format);
if(verbosity >= level) {
if(level == VERB_OPS)
log_vmsg(LOG_NOTICE, "notice", format, args);
else if(level == VERB_DETAIL)
log_vmsg(LOG_INFO, "info", format, args);
else log_vmsg(LOG_DEBUG, "debug", format, args);
}
va_end(args);
}
/** log hex data */
static void
log_hex_f(enum verbosity_value v, const char* msg, void* data, size_t length)
{
size_t i, j;
uint8_t* data8 = (uint8_t*)data;
const char* hexchar = "0123456789ABCDEF";
char buf[1024+1]; /* alloc blocksize hex chars + \0 */
const size_t blocksize = 512;
size_t len;
if(length == 0) {
verbose(v, "%s[%u]", msg, (unsigned)length);
return;
}
for(i=0; i<length; i+=blocksize/2) {
len = blocksize/2;
if(length - i < blocksize/2)
len = length - i;
for(j=0; j<len; j++) {
buf[j*2] = hexchar[ data8[i+j] >> 4 ];
buf[j*2 + 1] = hexchar[ data8[i+j] & 0xF ];
}
buf[len*2] = 0;
verbose(v, "%s[%u:%u] %.*s", msg, (unsigned)length,
(unsigned)i, (int)len*2, buf);
}
}
void
log_hex(const char* msg, void* data, size_t length)
{
log_hex_f(verbosity, msg, data, length);
}
void
log_query(const char *format, ...)
{
va_list args;
va_start(args, format);
log_vmsg(LOG_INFO, "query", format, args);
va_end(args);
}
void
log_reply(const char *format, ...)
{
va_list args;
va_start(args, format);
log_vmsg(LOG_INFO, "reply", format, args);
va_end(args);
}
void log_buf(enum verbosity_value level, const char* msg, sldns_buffer* buf)
{
if(verbosity < level)
return;
log_hex_f(level, msg, sldns_buffer_begin(buf), sldns_buffer_limit(buf));
}
#ifdef USE_WINSOCK
char* wsa_strerror(DWORD err)
{
static char unknown[32];
switch(err) {
case WSA_INVALID_HANDLE: return "Specified event object handle is invalid.";
case WSA_NOT_ENOUGH_MEMORY: return "Insufficient memory available.";
case WSA_INVALID_PARAMETER: return "One or more parameters are invalid.";
case WSA_OPERATION_ABORTED: return "Overlapped operation aborted.";
case WSA_IO_INCOMPLETE: return "Overlapped I/O event object not in signaled state.";
case WSA_IO_PENDING: return "Overlapped operations will complete later.";
case WSAEINTR: return "Interrupted function call.";
case WSAEBADF: return "File handle is not valid.";
case WSAEACCES: return "Permission denied.";
case WSAEFAULT: return "Bad address.";
case WSAEINVAL: return "Invalid argument.";
case WSAEMFILE: return "Too many open files.";
case WSAEWOULDBLOCK: return "Resource temporarily unavailable.";
case WSAEINPROGRESS: return "Operation now in progress.";
case WSAEALREADY: return "Operation already in progress.";
case WSAENOTSOCK: return "Socket operation on nonsocket.";
case WSAEDESTADDRREQ: return "Destination address required.";
case WSAEMSGSIZE: return "Message too long.";
case WSAEPROTOTYPE: return "Protocol wrong type for socket.";
case WSAENOPROTOOPT: return "Bad protocol option.";
case WSAEPROTONOSUPPORT: return "Protocol not supported.";
case WSAESOCKTNOSUPPORT: return "Socket type not supported.";
case WSAEOPNOTSUPP: return "Operation not supported.";
case WSAEPFNOSUPPORT: return "Protocol family not supported.";
case WSAEAFNOSUPPORT: return "Address family not supported by protocol family.";
case WSAEADDRINUSE: return "Address already in use.";
case WSAEADDRNOTAVAIL: return "Cannot assign requested address.";
case WSAENETDOWN: return "Network is down.";
case WSAENETUNREACH: return "Network is unreachable.";
case WSAENETRESET: return "Network dropped connection on reset.";
case WSAECONNABORTED: return "Software caused connection abort.";
case WSAECONNRESET: return "Connection reset by peer.";
case WSAENOBUFS: return "No buffer space available.";
case WSAEISCONN: return "Socket is already connected.";
case WSAENOTCONN: return "Socket is not connected.";
case WSAESHUTDOWN: return "Cannot send after socket shutdown.";
case WSAETOOMANYREFS: return "Too many references.";
case WSAETIMEDOUT: return "Connection timed out.";
case WSAECONNREFUSED: return "Connection refused.";
case WSAELOOP: return "Cannot translate name.";
case WSAENAMETOOLONG: return "Name too long.";
case WSAEHOSTDOWN: return "Host is down.";
case WSAEHOSTUNREACH: return "No route to host.";
case WSAENOTEMPTY: return "Directory not empty.";
case WSAEPROCLIM: return "Too many processes.";
case WSAEUSERS: return "User quota exceeded.";
case WSAEDQUOT: return "Disk quota exceeded.";
case WSAESTALE: return "Stale file handle reference.";
case WSAEREMOTE: return "Item is remote.";
case WSASYSNOTREADY: return "Network subsystem is unavailable.";
case WSAVERNOTSUPPORTED: return "Winsock.dll version out of range.";
case WSANOTINITIALISED: return "Successful WSAStartup not yet performed.";
case WSAEDISCON: return "Graceful shutdown in progress.";
case WSAENOMORE: return "No more results.";
case WSAECANCELLED: return "Call has been canceled.";
case WSAEINVALIDPROCTABLE: return "Procedure call table is invalid.";
case WSAEINVALIDPROVIDER: return "Service provider is invalid.";
case WSAEPROVIDERFAILEDINIT: return "Service provider failed to initialize.";
case WSASYSCALLFAILURE: return "System call failure.";
case WSASERVICE_NOT_FOUND: return "Service not found.";
case WSATYPE_NOT_FOUND: return "Class type not found.";
case WSA_E_NO_MORE: return "No more results.";
case WSA_E_CANCELLED: return "Call was canceled.";
case WSAEREFUSED: return "Database query was refused.";
case WSAHOST_NOT_FOUND: return "Host not found.";
case WSATRY_AGAIN: return "Nonauthoritative host not found.";
case WSANO_RECOVERY: return "This is a nonrecoverable error.";
case WSANO_DATA: return "Valid name, no data record of requested type.";
case WSA_QOS_RECEIVERS: return "QOS receivers.";
case WSA_QOS_SENDERS: return "QOS senders.";
case WSA_QOS_NO_SENDERS: return "No QOS senders.";
case WSA_QOS_NO_RECEIVERS: return "QOS no receivers.";
case WSA_QOS_REQUEST_CONFIRMED: return "QOS request confirmed.";
case WSA_QOS_ADMISSION_FAILURE: return "QOS admission error.";
case WSA_QOS_POLICY_FAILURE: return "QOS policy failure.";
case WSA_QOS_BAD_STYLE: return "QOS bad style.";
case WSA_QOS_BAD_OBJECT: return "QOS bad object.";
case WSA_QOS_TRAFFIC_CTRL_ERROR: return "QOS traffic control error.";
case WSA_QOS_GENERIC_ERROR: return "QOS generic error.";
case WSA_QOS_ESERVICETYPE: return "QOS service type error.";
case WSA_QOS_EFLOWSPEC: return "QOS flowspec error.";
case WSA_QOS_EPROVSPECBUF: return "Invalid QOS provider buffer.";
case WSA_QOS_EFILTERSTYLE: return "Invalid QOS filter style.";
case WSA_QOS_EFILTERTYPE: return "Invalid QOS filter type.";
case WSA_QOS_EFILTERCOUNT: return "Incorrect QOS filter count.";
case WSA_QOS_EOBJLENGTH: return "Invalid QOS object length.";
case WSA_QOS_EFLOWCOUNT: return "Incorrect QOS flow count.";
/*case WSA_QOS_EUNKOWNPSOBJ: return "Unrecognized QOS object.";*/
case WSA_QOS_EPOLICYOBJ: return "Invalid QOS policy object.";
case WSA_QOS_EFLOWDESC: return "Invalid QOS flow descriptor.";
case WSA_QOS_EPSFLOWSPEC: return "Invalid QOS provider-specific flowspec.";
case WSA_QOS_EPSFILTERSPEC: return "Invalid QOS provider-specific filterspec.";
case WSA_QOS_ESDMODEOBJ: return "Invalid QOS shape discard mode object.";
case WSA_QOS_ESHAPERATEOBJ: return "Invalid QOS shaping rate object.";
case WSA_QOS_RESERVED_PETYPE: return "Reserved policy QOS element type.";
default:
snprintf(unknown, sizeof(unknown),
"unknown WSA error code %d", (int)err);
return unknown;
}
}
#endif /* USE_WINSOCK */
diff --git a/contrib/unbound/util/module.c b/contrib/unbound/util/module.c
index 773dab853d2f..62e5de4a05bb 100644
--- a/contrib/unbound/util/module.c
+++ b/contrib/unbound/util/module.c
@@ -1,396 +1,414 @@
/*
* util/module.c - module interface
*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
* Implementation of module.h.
*/
#include "config.h"
#include "util/module.h"
#include "sldns/wire2str.h"
#include "util/config_file.h"
#include "util/regional.h"
#include "util/data/dname.h"
#include "util/net_help.h"
const char*
strextstate(enum module_ext_state s)
{
switch(s) {
case module_state_initial: return "module_state_initial";
case module_wait_reply: return "module_wait_reply";
case module_wait_module: return "module_wait_module";
case module_restart_next: return "module_restart_next";
case module_wait_subquery: return "module_wait_subquery";
case module_error: return "module_error";
case module_finished: return "module_finished";
}
return "bad_extstate_value";
}
const char*
strmodulevent(enum module_ev e)
{
switch(e) {
case module_event_new: return "module_event_new";
case module_event_pass: return "module_event_pass";
case module_event_reply: return "module_event_reply";
case module_event_noreply: return "module_event_noreply";
case module_event_capsfail: return "module_event_capsfail";
case module_event_moddone: return "module_event_moddone";
case module_event_error: return "module_event_error";
}
return "bad_event_value";
}
void errinf(struct module_qstate* qstate, const char* str)
{
errinf_ede(qstate, str, LDNS_EDE_NONE);
}
void errinf_ede(struct module_qstate* qstate,
const char* str, sldns_ede_code reason_bogus)
{
struct errinf_strlist* p;
if(!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->next = NULL;
p->str = regional_strdup(qstate->region, str);
p->reason_bogus = reason_bogus;
if(!p->str) {
log_err("malloc failure in validator-error-info string");
return;
}
/* add at end */
if(qstate->errinf) {
struct errinf_strlist* q = qstate->errinf;
while(q->next)
q = q->next;
q->next = p;
} else qstate->errinf = p;
}
void errinf_origin(struct module_qstate* qstate, struct sock_list *origin)
{
struct sock_list* p;
if(qstate->env->cfg->val_log_level < 2 && !qstate->env->cfg->log_servfail)
return;
for(p=origin; p; p=p->next) {
char buf[256];
if(p == origin)
snprintf(buf, sizeof(buf), "from ");
else snprintf(buf, sizeof(buf), "and ");
if(p->len == 0)
snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf),
"cache");
else
addr_to_str(&p->addr, p->len, buf+strlen(buf),
sizeof(buf)-strlen(buf));
errinf(qstate, buf);
}
}
char* errinf_to_str_bogus(struct module_qstate* qstate)
{
char buf[20480];
char* p = buf;
size_t left = sizeof(buf);
struct errinf_strlist* s;
char dname[LDNS_MAX_DOMAINLEN+1];
char t[16], c[16];
sldns_wire2str_type_buf(qstate->qinfo.qtype, t, sizeof(t));
sldns_wire2str_class_buf(qstate->qinfo.qclass, c, sizeof(c));
dname_str(qstate->qinfo.qname, dname);
snprintf(p, left, "validation failure <%s %s %s>:", dname, t, c);
left -= strlen(p); p += strlen(p);
if(!qstate->errinf)
snprintf(p, left, " misc failure");
else for(s=qstate->errinf; s; s=s->next) {
snprintf(p, left, " %s", s->str);
left -= strlen(p); p += strlen(p);
}
p = strdup(buf);
if(!p)
log_err("malloc failure in errinf_to_str");
return p;
}
/* 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) continue;
if(ede != LDNS_EDE_NONE
&& ede != LDNS_EDE_DNSSEC_BOGUS
&& s->reason_bogus == LDNS_EDE_DNSSEC_BOGUS) continue;
ede = s->reason_bogus;
}
return ede;
}
char* errinf_to_str_servfail(struct module_qstate* qstate)
{
char buf[20480];
char* p = buf;
size_t left = sizeof(buf);
struct errinf_strlist* s;
char dname[LDNS_MAX_DOMAINLEN+1];
char t[16], c[16];
sldns_wire2str_type_buf(qstate->qinfo.qtype, t, sizeof(t));
sldns_wire2str_class_buf(qstate->qinfo.qclass, c, sizeof(c));
dname_str(qstate->qinfo.qname, dname);
snprintf(p, left, "SERVFAIL <%s %s %s>:", dname, t, c);
left -= strlen(p); p += strlen(p);
if(!qstate->errinf)
snprintf(p, left, " misc failure");
else for(s=qstate->errinf; s; s=s->next) {
snprintf(p, left, " %s", s->str);
left -= strlen(p); p += strlen(p);
}
p = strdup(buf);
if(!p)
log_err("malloc failure in errinf_to_str");
return p;
}
+char* errinf_to_str_misc(struct module_qstate* qstate)
+{
+ char buf[20480];
+ char* p = buf;
+ size_t left = sizeof(buf);
+ struct errinf_strlist* s;
+ if(!qstate->errinf)
+ snprintf(p, left, "misc failure");
+ else for(s=qstate->errinf; s; s=s->next) {
+ snprintf(p, left, "%s%s", (s==qstate->errinf?"":" "), s->str);
+ left -= strlen(p); p += strlen(p);
+ }
+ p = strdup(buf);
+ if(!p)
+ log_err("malloc failure in errinf_to_str");
+ return p;
+}
+
void errinf_rrset(struct module_qstate* qstate, struct ub_packed_rrset_key *rr)
{
char buf[1024];
char dname[LDNS_MAX_DOMAINLEN+1];
char t[16], c[16];
if((qstate->env->cfg->val_log_level < 2 && !qstate->env->cfg->log_servfail) || !rr)
return;
sldns_wire2str_type_buf(ntohs(rr->rk.type), t, sizeof(t));
sldns_wire2str_class_buf(ntohs(rr->rk.rrset_class), c, sizeof(c));
dname_str(rr->rk.dname, dname);
snprintf(buf, sizeof(buf), "for <%s %s %s>", dname, t, c);
errinf(qstate, buf);
}
void errinf_dname(struct module_qstate* qstate, const char* str, uint8_t* dname)
{
char b[1024];
char buf[LDNS_MAX_DOMAINLEN+1];
if((qstate->env->cfg->val_log_level < 2 && !qstate->env->cfg->log_servfail) || !str || !dname)
return;
dname_str(dname, buf);
snprintf(b, sizeof(b), "%s %s", str, buf);
errinf(qstate, b);
}
int
edns_known_options_init(struct module_env* env)
{
env->edns_known_options_num = 0;
env->edns_known_options = (struct edns_known_option*)calloc(
MAX_KNOWN_EDNS_OPTS, sizeof(struct edns_known_option));
if(!env->edns_known_options) return 0;
return 1;
}
void
edns_known_options_delete(struct module_env* env)
{
free(env->edns_known_options);
env->edns_known_options = NULL;
env->edns_known_options_num = 0;
}
int
edns_register_option(uint16_t opt_code, int bypass_cache_stage,
int no_aggregation, struct module_env* env)
{
size_t i;
if(env->worker) {
log_err("invalid edns registration: "
"trying to register option after module init phase");
return 0;
}
/**
* Checking if we are full first is faster but it does not provide
* the option to change the flags when the array is full.
* It only impacts unbound initialization, leave it for now.
*/
/* Check if the option is already registered. */
for(i=0; i<env->edns_known_options_num; i++)
if(env->edns_known_options[i].opt_code == opt_code)
break;
/* If it is not yet registered check if we have space to add a new one. */
if(i == env->edns_known_options_num) {
if(env->edns_known_options_num >= MAX_KNOWN_EDNS_OPTS) {
log_err("invalid edns registration: maximum options reached");
return 0;
}
env->edns_known_options_num++;
}
env->edns_known_options[i].opt_code = opt_code;
env->edns_known_options[i].bypass_cache_stage = bypass_cache_stage;
env->edns_known_options[i].no_aggregation = no_aggregation;
return 1;
}
int
inplace_cb_register(void* cb, enum inplace_cb_list_type type, void* cbarg,
struct module_env* env, int id)
{
struct inplace_cb* callback;
struct inplace_cb** prevp;
if(env->worker) {
log_err("invalid edns callback registration: "
"trying to register callback after module init phase");
return 0;
}
callback = (struct inplace_cb*)calloc(1, sizeof(*callback));
if(callback == NULL) {
log_err("out of memory during edns callback registration.");
return 0;
}
callback->id = id;
callback->next = NULL;
callback->cb = cb;
callback->cb_arg = cbarg;
prevp = (struct inplace_cb**) &env->inplace_cb_lists[type];
/* append at end of list */
while(*prevp != NULL)
prevp = &((*prevp)->next);
*prevp = callback;
return 1;
}
void
inplace_cb_delete(struct module_env* env, enum inplace_cb_list_type type,
int id)
{
struct inplace_cb* temp = env->inplace_cb_lists[type];
struct inplace_cb* prev = NULL;
while(temp) {
if(temp->id == id) {
if(!prev) {
env->inplace_cb_lists[type] = temp->next;
free(temp);
temp = env->inplace_cb_lists[type];
}
else {
prev->next = temp->next;
free(temp);
temp = prev->next;
}
}
else {
prev = temp;
temp = temp->next;
}
}
}
struct edns_known_option*
edns_option_is_known(uint16_t opt_code, struct module_env* env)
{
size_t i;
for(i=0; i<env->edns_known_options_num; i++)
if(env->edns_known_options[i].opt_code == opt_code)
return env->edns_known_options + i;
return NULL;
}
int
edns_bypass_cache_stage(struct edns_option* list, struct module_env* env)
{
size_t i;
for(; list; list=list->next)
for(i=0; i<env->edns_known_options_num; i++)
if(env->edns_known_options[i].opt_code == list->opt_code &&
env->edns_known_options[i].bypass_cache_stage == 1)
return 1;
return 0;
}
int
unique_mesh_state(struct edns_option* list, struct module_env* env)
{
size_t i;
if(env->unique_mesh)
return 1;
for(; list; list=list->next)
for(i=0; i<env->edns_known_options_num; i++)
if(env->edns_known_options[i].opt_code == list->opt_code &&
env->edns_known_options[i].no_aggregation == 1)
return 1;
return 0;
}
void
log_edns_known_options(enum verbosity_value level, struct module_env* env)
{
size_t i;
char str[32], *s;
size_t slen;
if(env->edns_known_options_num > 0 && verbosity >= level) {
verbose(level, "EDNS known options:");
verbose(level, " Code: Bypass_cache_stage: Aggregate_mesh:");
for(i=0; i<env->edns_known_options_num; i++) {
s = str;
slen = sizeof(str);
(void)sldns_wire2str_edns_option_code_print(&s, &slen,
env->edns_known_options[i].opt_code);
verbose(level, " %-8.8s %-19s %-15s", str,
env->edns_known_options[i].bypass_cache_stage?"YES":"NO",
env->edns_known_options[i].no_aggregation?"NO":"YES");
}
}
}
void
copy_state_to_super(struct module_qstate* qstate, int ATTR_UNUSED(id),
struct module_qstate* super)
{
/* Overwrite super's was_ratelimited only when it was not set */
if(!super->was_ratelimited) {
super->was_ratelimited = qstate->was_ratelimited;
}
}
diff --git a/contrib/unbound/util/module.h b/contrib/unbound/util/module.h
index 5b6fcc93cf1e..8a9da3f931a1 100644
--- a/contrib/unbound/util/module.h
+++ b/contrib/unbound/util/module.h
@@ -1,951 +1,961 @@
/*
* util/module.h - DNS handling module interface
*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file contains the interface for DNS handling modules.
*
* The module interface uses the DNS modules as state machines. The
* state machines are activated in sequence to operate on queries. Once
* they are done, the reply is passed back. In the usual setup the mesh
* is the caller of the state machines and once things are done sends replies
* and invokes result callbacks.
*
* The module provides a number of functions, listed in the module_func_block.
* The module is inited and destroyed and memory usage queries, for the
* module as a whole, for entire-module state (such as a cache). And per-query
* functions are called, operate to move the state machine and cleanup of
* the per-query state.
*
* Most per-query state should simply be allocated in the query region.
* This is destroyed at the end of the query.
*
* The module environment contains services and information and caches
* shared by the modules and the rest of the system. It also contains
* function pointers for module-specific tasks (like sending queries).
*
* *** Example module calls for a normal query
*
* In this example, the query does not need recursion, all the other data
* can be found in the cache. This makes the example shorter.
*
* At the start of the program the iterator module is initialised.
* The iterator module sets up its global state, such as donotquery lists
* and private address trees.
*
* A query comes in, and a mesh entry is created for it. The mesh
* starts the resolution process. The validator module is the first
* in the list of modules, and it is started on this new query. The
* operate() function is called. The validator decides it needs not do
* anything yet until there is a result and returns wait_module, that
* causes the next module in the list to be started.
*
* The next module is the iterator. It is started on the passed query and
* decides to perform a lookup. For this simple example, the delegation
* point information is available, and all the iterator wants to do is
* send a UDP query. The iterator uses env.send_query() to send the
* query. Then the iterator suspends (returns from the operate call).
*
* When the UDP reply comes back (and on errors and timeouts), the
* operate function is called for the query, on the iterator module,
* with the event that there is a reply. The iterator decides that this
* is enough, the work is done. It returns the value finished from the
* operate call, which causes the previous module to be started.
*
* The previous module, the validator module, is started with the event
* that the iterator module is done. The validator decides to validate
* the query. Once it is done (which could take recursive lookups, but
* in this example no recursive lookups are needed), it returns from the
* operate function with finished.
*
* There is no previous module from the validator module, and the mesh
* takes this to mean that the query is finally done. The mesh invokes
* callbacks and sends packets to queriers.
*
* If other modules had been waiting (recursively) on the answer to this
* query, then the mesh will tell them about it. It calls the inform_super
* routine on all the waiting modules, and once that is done it calls all of
* them with the operate() call. During inform_super the query that is done
* still exists and information can be copied from it (but the module should
* not really re-entry codepoints and services). During the operate call
* the modules can use stored state to continue operation with the results.
* (network buffers are used to contain the answer packet during the
* inform_super phase, but after that the network buffers will be cleared
* of their contents so that other tasks can be performed).
*
* *** Example module calls for recursion
*
* A module is called in operate, and it decides that it wants to perform
* recursion. That is, it wants the full state-machine-list to operate on
* a different query. It calls env.attach_sub() to create a new query state.
* The routine returns the newly created state, and potentially the module
* can edit the module-states for the newly created query (i.e. pass along
* some information, like delegation points). The module then suspends,
* returns from the operate routine.
*
* The mesh meanwhile will have the newly created query (or queries) on
* a waiting list, and will call operate() on this query (or queries).
* It starts again at the start of the module list for them. The query
* (or queries) continue to operate their state machines, until they are
* done. When they are done the mesh calls inform_super on the module that
* wanted the recursion. After that the mesh calls operate() on the module
* that wanted to do the recursion, and during this phase the module could,
* for example, decide to create more recursions.
*
* If the module decides it no longer wants the recursive information
* it can call detach_subs. Those queries will still run to completion,
* potentially filling the cache with information. Inform_super is not
* called any more.
*
* The iterator module will fetch items from the cache, so a recursion
* attempt may complete very quickly if the item is in cache. The calling
* module has to wait for completion or eventual timeout. A recursive query
* that times out returns a servfail rcode (servfail is also returned for
* other errors during the lookup).
*
* Results are passed in the qstate, the rcode member is used to pass
* errors without requiring memory allocation, so that the code can continue
* in out-of-memory conditions. If the rcode member is 0 (NOERROR) then
* the dns_msg entry contains a filled out message. This message may
* also contain an rcode that is nonzero, but in this case additional
* information (query, additional) can be passed along.
*
* The rcode and dns_msg are used to pass the result from the the rightmost
* module towards the leftmost modules and then towards the user.
*
* If you want to avoid recursion-cycles where queries need other queries
* that need the first one, use detect_cycle() to see if that will happen.
*
*/
#ifndef UTIL_MODULE_H
#define UTIL_MODULE_H
#include "util/storage/lruhash.h"
#include "util/data/msgreply.h"
#include "util/data/msgparse.h"
struct sldns_buffer;
struct alloc_cache;
struct rrset_cache;
struct key_cache;
struct config_file;
struct slabhash;
struct query_info;
struct edns_data;
struct regional;
struct worker;
struct comm_base;
struct auth_zones;
struct outside_network;
struct module_qstate;
struct ub_randstate;
struct mesh_area;
struct mesh_state;
struct val_anchors;
struct val_neg_cache;
struct iter_forwards;
struct iter_hints;
struct respip_set;
struct respip_client_info;
struct respip_addr_info;
/** Maximum number of modules in operation */
#define MAX_MODULE 16
/** Maximum number of known edns options */
#define MAX_KNOWN_EDNS_OPTS 256
struct errinf_strlist {
/** next item in list */
struct errinf_strlist* next;
/** config option string */
char* str;
/** EDE code companion to the error str */
int reason_bogus;
};
enum inplace_cb_list_type {
/* Inplace callbacks for when a resolved reply is ready to be sent to the
* front.*/
inplace_cb_reply = 0,
/* Inplace callbacks for when a reply is given from the cache. */
inplace_cb_reply_cache,
/* Inplace callbacks for when a reply is given with local data
* (or Chaos reply). */
inplace_cb_reply_local,
/* Inplace callbacks for when the reply is servfail. */
inplace_cb_reply_servfail,
/* Inplace callbacks for when a query is ready to be sent to the back.*/
inplace_cb_query,
/* Inplace callback for when a reply is received from the back. */
inplace_cb_query_response,
/* Inplace callback for when EDNS is parsed on a reply received from the
* back. */
inplace_cb_edns_back_parsed,
/* Total number of types. Used for array initialization.
* Should always be last. */
inplace_cb_types_total
};
/** Known edns option. Can be populated during modules' init. */
struct edns_known_option {
/** type of this edns option */
uint16_t opt_code;
/** whether the option needs to bypass the cache stage */
int bypass_cache_stage;
/** whether the option needs mesh aggregation */
int no_aggregation;
};
/**
* Inplace callback list of registered routines to be called.
*/
struct inplace_cb {
/** next in list */
struct inplace_cb* next;
/** Inplace callback routine */
void* cb;
void* cb_arg;
/** module id */
int id;
};
/**
* Inplace callback function called before replying.
* Called as func(qinfo, qstate, rep, rcode, edns, opt_list_out, repinfo,
* region, id, python_callback)
* Where:
* qinfo: the query info.
* qstate: the module state. NULL when calling before the query reaches the
* mesh states.
* rep: reply_info. Could be NULL.
* rcode: the return code.
* edns: the edns_data of the reply. When qstate is NULL, it is also used as
* the edns input.
* opt_list_out: the edns options list for the reply.
* repinfo: reply information for a communication point. NULL when calling
* during the mesh states; the same could be found from
* qstate->mesh_info->reply_list.
* region: region to store data.
* id: module id.
* python_callback: only used for registering a python callback function.
*/
typedef int inplace_cb_reply_func_type(struct query_info* qinfo,
struct module_qstate* qstate, struct reply_info* rep, int rcode,
struct edns_data* edns, struct edns_option** opt_list_out,
struct comm_reply* repinfo, struct regional* region,
struct timeval* start_time, int id, void* callback);
/**
* Inplace callback function called before sending the query to a nameserver.
* Called as func(qinfo, flags, qstate, addr, addrlen, zone, zonelen, region,
* id, python_callback)
* Where:
* qinfo: query info.
* flags: flags of the query.
* qstate: query state.
* addr: to which server to send the query.
* addrlen: length of addr.
* zone: name of the zone of the delegation point. wireformat dname.
* This is the delegation point name for which the server is deemed
* authoritative.
* zonelen: length of zone.
* region: region to store data.
* id: module id.
* python_callback: only used for registering a python callback function.
*/
typedef int inplace_cb_query_func_type(struct query_info* qinfo, uint16_t flags,
struct module_qstate* qstate, struct sockaddr_storage* addr,
socklen_t addrlen, uint8_t* zone, size_t zonelen, struct regional* region,
int id, void* callback);
/**
* Inplace callback function called after parsing edns on query reply.
* Called as func(qstate, id, cb_args)
* Where:
* qstate: the query state.
* id: module id.
* cb_args: argument passed when registering callback.
*/
typedef int inplace_cb_edns_back_parsed_func_type(struct module_qstate* qstate,
int id, void* cb_args);
/**
* Inplace callback function called after parsing query response.
* Called as func(qstate, response, id, cb_args)
* Where:
* qstate: the query state.
* response: query response.
* id: module id.
* cb_args: argument passed when registering callback.
*/
typedef int inplace_cb_query_response_func_type(struct module_qstate* qstate,
struct dns_msg* response, int id, void* cb_args);
/**
* Function called when looking for (expired) cached answers during the serve
* expired logic.
* Called as func(qstate, lookup_qinfo)
* Where:
* qstate: the query state.
* lookup_qinfo: the qinfo to lookup for.
*/
typedef struct dns_msg* serve_expired_lookup_func_type(
struct module_qstate* qstate, struct query_info* lookup_qinfo);
/**
* Module environment.
* Services and data provided to the module.
*/
struct module_env {
/* --- data --- */
/** config file with config options */
struct config_file* cfg;
/** shared message cache */
struct slabhash* msg_cache;
/** shared rrset cache */
struct rrset_cache* rrset_cache;
/** shared infrastructure cache (edns, lameness) */
struct infra_cache* infra_cache;
/** shared key cache */
struct key_cache* key_cache;
/* --- services --- */
/**
* Send serviced DNS query to server. UDP/TCP and EDNS is handled.
* operate() should return with wait_reply. Later on a callback
* will cause operate() to be called with event timeout or reply.
* The time until a timeout is calculated from roundtrip timing,
* several UDP retries are attempted.
* @param qinfo: query info.
* @param flags: host order flags word, with opcode and CD bit.
* @param dnssec: if set, EDNS record will have bits set.
* If EDNS_DO bit is set, DO bit is set in EDNS records.
* If BIT_CD is set, CD bit is set in queries with EDNS records.
* @param want_dnssec: if set, the validator wants DNSSEC. Without
* EDNS, the answer is likely to be useless for this domain.
* @param nocaps: do not use caps_for_id, use the qname as given.
* (ignored if caps_for_id is disabled).
* @param check_ratelimit: if set, will check ratelimit before sending out.
* @param addr: where to.
* @param addrlen: length of addr.
* @param zone: delegation point name.
* @param zonelen: length of zone name.
* @param tcp_upstream: use TCP for upstream queries.
* @param ssl_upstream: use SSL for upstream queries.
* @param tls_auth_name: if ssl_upstream, use this name with TLS
* authentication.
* @param q: which query state to reactivate upon return.
* @param was_ratelimited: it will signal back if the query failed to pass the
* ratelimit check.
* @return: false on failure (memory or socket related). no query was
* sent. Or returns an outbound entry with qsent and qstate set.
* This outbound_entry will be used on later module invocations
* that involve this query (timeout, error or reply).
*/
struct outbound_entry* (*send_query)(struct query_info* qinfo,
uint16_t flags, int dnssec, int want_dnssec, int nocaps,
int check_ratelimit,
struct sockaddr_storage* addr, socklen_t addrlen,
uint8_t* zone, size_t zonelen, int tcp_upstream, int ssl_upstream,
char* tls_auth_name, struct module_qstate* q, int* was_ratelimited);
/**
* Detach-subqueries.
* Remove all sub-query references from this query state.
* Keeps super-references of those sub-queries correct.
* Updates stat items in mesh_area structure.
* @param qstate: used to find mesh state.
*/
void (*detach_subs)(struct module_qstate* qstate);
/**
* Attach subquery.
* Creates it if it does not exist already.
* Keeps sub and super references correct.
* Updates stat items in mesh_area structure.
* Pass if it is priming query or not.
* return:
* o if error (malloc) happened.
* o need to initialise the new state (module init; it is a new state).
* so that the next run of the query with this module is successful.
* o no init needed, attachment successful.
*
* @param qstate: the state to find mesh state, and that wants to
* receive the results from the new subquery.
* @param qinfo: what to query for (copied).
* @param qflags: what flags to use (RD, CD flag or not).
* @param prime: if it is a (stub) priming query.
* @param valrec: validation lookup recursion, does not need validation
* @param newq: If the new subquery needs initialisation, it is
* returned, otherwise NULL is returned.
* @return: false on error, true if success (and init may be needed).
*/
int (*attach_sub)(struct module_qstate* qstate,
struct query_info* qinfo, uint16_t qflags, int prime,
int valrec, struct module_qstate** newq);
/**
* Add detached query.
* Creates it if it does not exist already.
* Does not make super/sub references.
* Performs a cycle detection - for double check - and fails if there is
* one.
* Updates stat items in mesh_area structure.
* Pass if it is priming query or not.
* return:
* o if error (malloc) happened.
* o need to initialise the new state (module init; it is a new state).
* so that the next run of the query with this module is successful.
* o no init needed, attachment successful.
* o added subquery, created if it did not exist already.
*
* @param qstate: the state to find mesh state, and that wants to receive
* the results from the new subquery.
* @param qinfo: what to query for (copied).
* @param qflags: what flags to use (RD / CD flag or not).
* @param prime: if it is a (stub) priming query.
* @param valrec: if it is a validation recursion query (lookup of key, DS).
* @param newq: If the new subquery needs initialisation, it is returned,
* otherwise NULL is returned.
* @param sub: The added mesh state, created if it did not exist already.
* @return: false on error, true if success (and init may be needed).
*/
int (*add_sub)(struct module_qstate* qstate,
struct query_info* qinfo, uint16_t qflags, int prime,
int valrec, struct module_qstate** newq,
struct mesh_state** sub);
/**
* Kill newly attached sub. If attach_sub returns newq for
* initialisation, but that fails, then this routine will cleanup and
* delete the freshly created sub.
* @param newq: the new subquery that is no longer needed.
* It is removed.
*/
void (*kill_sub)(struct module_qstate* newq);
/**
* Detect if adding a dependency for qstate on name,type,class will
* create a dependency cycle.
* @param qstate: given mesh querystate.
* @param qinfo: query info for dependency.
* @param flags: query flags of dependency, RD/CD flags.
* @param prime: if dependency is a priming query or not.
* @param valrec: validation lookup recursion, does not need validation
* @return true if the name,type,class exists and the given
* qstate mesh exists as a dependency of that name. Thus
* if qstate becomes dependent on name,type,class then a
* cycle is created.
*/
int (*detect_cycle)(struct module_qstate* qstate,
struct query_info* qinfo, uint16_t flags, int prime,
int valrec);
/** region for temporary usage. May be cleared after operate() call. */
struct regional* scratch;
/** buffer for temporary usage. May be cleared after operate() call. */
struct sldns_buffer* scratch_buffer;
/** internal data for daemon - worker thread. */
struct worker* worker;
/** the worker event base */
struct comm_base* worker_base;
/** the outside network */
struct outside_network* outnet;
/** mesh area with query state dependencies */
struct mesh_area* mesh;
/** allocation service */
struct alloc_cache* alloc;
/** random table to generate random numbers */
struct ub_randstate* rnd;
/** time in seconds, converted to integer */
time_t* now;
/** time in microseconds. Relatively recent. */
struct timeval* now_tv;
/** is validation required for messages, controls client-facing
* validation status (AD bits) and servfails */
int need_to_validate;
/** trusted key storage; these are the configured keys, if not NULL,
* otherwise configured by validator. These are the trust anchors,
* and are not primed and ready for validation, but on the bright
* side, they are read only memory, thus no locks and fast. */
struct val_anchors* anchors;
/** negative cache, configured by the validator. if not NULL,
* contains NSEC record lookup trees. */
struct val_neg_cache* neg_cache;
/** the 5011-probe timer (if any) */
struct comm_timer* probe_timer;
/** auth zones */
struct auth_zones* auth_zones;
/** Mapping of forwarding zones to targets.
* iterator forwarder information. per-thread, created by worker */
struct iter_forwards* fwds;
/**
* iterator forwarder information. per-thread, created by worker.
* The hints -- these aren't stored in the cache because they don't
* expire. The hints are always used to "prime" the cache. Note
* that both root hints and stub zone "hints" are stored in this
* data structure.
*/
struct iter_hints* hints;
/** module specific data. indexed by module id. */
void* modinfo[MAX_MODULE];
/* Shared linked list of inplace callback functions */
struct inplace_cb* inplace_cb_lists[inplace_cb_types_total];
/**
* Shared array of known edns options (size MAX_KNOWN_EDNS_OPTS).
* Filled by edns literate modules during init.
*/
struct edns_known_option* edns_known_options;
/* Number of known edns options */
size_t edns_known_options_num;
/** EDNS client string information */
struct edns_strings* edns_strings;
/* Make every mesh state unique, do not aggregate mesh states. */
int unique_mesh;
};
/**
* External visible states of the module state machine
* Modules may also have an internal state.
* Modules are supposed to run to completion or until blocked.
*/
enum module_ext_state {
/** initial state - new query */
module_state_initial = 0,
/** waiting for reply to outgoing network query */
module_wait_reply,
/** module is waiting for another module */
module_wait_module,
/** module is waiting for another module; that other is restarted */
module_restart_next,
/** module is waiting for sub-query */
module_wait_subquery,
/** module could not finish the query */
module_error,
/** module is finished with query */
module_finished
};
/**
* Events that happen to modules, that start or wakeup modules.
*/
enum module_ev {
/** new query */
module_event_new = 0,
/** query passed by other module */
module_event_pass,
/** reply inbound from server */
module_event_reply,
/** no reply, timeout or other error */
module_event_noreply,
/** reply is there, but capitalisation check failed */
module_event_capsfail,
/** next module is done, and its reply is awaiting you */
module_event_moddone,
/** error */
module_event_error
};
/**
* Linked list of sockaddrs
* May be allocated such that only 'len' bytes of addr exist for the structure.
*/
struct sock_list {
/** next in list */
struct sock_list* next;
/** length of addr */
socklen_t len;
/** sockaddr */
struct sockaddr_storage addr;
};
struct respip_action_info;
/**
* Struct to hold relevant data for serve expired
*/
struct serve_expired_data {
struct comm_timer* timer;
serve_expired_lookup_func_type* get_cached_answer;
};
/**
* Module state, per query.
*/
struct module_qstate {
/** which query is being answered: name, type, class */
struct query_info qinfo;
/** flags uint16 from query */
uint16_t query_flags;
/** if this is a (stub or root) priming query (with hints) */
int is_priming;
/** 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;
/** the reply message, with message for client and calling module */
struct dns_msg* return_msg;
/** the rcode, in case of error, instead of a reply message */
int return_rcode;
/** origin of the reply (can be NULL from cache, list for cnames) */
struct sock_list* reply_origin;
/** IP blacklist for queries */
struct sock_list* blacklist;
/** region for this query. Cleared when query process finishes. */
struct regional* region;
/** failure reason information if val-log-level is high */
struct errinf_strlist* errinf;
/** which module is executing */
int curmod;
/** module states */
enum module_ext_state ext_state[MAX_MODULE];
/** module specific data for query. indexed by module id. */
void* minfo[MAX_MODULE];
/** environment for this query */
struct module_env* env;
/** mesh related information for this query */
struct mesh_state* mesh_info;
/** how many seconds before expiry is this prefetched (0 if not) */
time_t prefetch_leeway;
/** serve expired data */
struct serve_expired_data* serve_expired_data;
/** incoming edns options from the front end */
struct edns_option* edns_opts_front_in;
/** outgoing edns options to the back end */
struct edns_option* edns_opts_back_out;
/** incoming edns options from the back end */
struct edns_option* edns_opts_back_in;
/** outgoing edns options to the front end */
struct edns_option* edns_opts_front_out;
/** whether modules should answer from the cache */
int no_cache_lookup;
/** whether modules should store answer in the cache */
int no_cache_store;
/** whether to refetch a fresh answer on finishing this state*/
int need_refetch;
/** whether the query (or a subquery) was ratelimited */
int was_ratelimited;
/** time when query was started. This is when the qstate is created.
* This is used so that type NS data cannot be overwritten by them
* expiring while the lookup is in progress, using data fetched from
* those servers. By comparing expiry time with qstarttime for type NS.
*/
time_t qstarttime;
/** 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
* actions.
*/
struct respip_client_info* client_info;
/** Extended result of response-ip action processing, mainly
* for logging purposes. */
struct respip_action_info* respip_action_info;
/** if the query is rpz passthru, no further rpz processing for it */
int rpz_passthru;
+ /* Flag tcp required. */
+ int tcp_required;
/** whether the reply should be dropped */
int is_drop;
};
/**
* Module functionality block
*/
struct module_func_block {
/** text string name of module */
const char* name;
/**
* init the module. Called once for the global state.
* This is the place to apply settings from the config file.
* @param env: module environment.
* @param id: module id number.
* return: 0 on error
*/
int (*init)(struct module_env* env, int id);
/**
* de-init, delete, the module. Called once for the global state.
* @param env: module environment.
* @param id: module id number.
*/
void (*deinit)(struct module_env* env, int id);
/**
* accept a new query, or work further on existing query.
* Changes the qstate->ext_state to be correct on exit.
* @param ev: event that causes the module state machine to
* (re-)activate.
* @param qstate: the query state.
* Note that this method is not allowed to change the
* query state 'identity', that is query info, qflags,
* and priming status.
* Attach a subquery to get results to a different query.
* @param id: module id number that operate() is called on.
* @param outbound: if not NULL this event is due to the reply/timeout
* or error on this outbound query.
* @return: if at exit the ext_state is:
* o wait_module: next module is started. (with pass event).
* o error or finished: previous module is resumed.
* o otherwise it waits until that event happens (assumes
* the service routine to make subrequest or send message
* have been called.
*/
void (*operate)(struct module_qstate* qstate, enum module_ev event,
int id, struct outbound_entry* outbound);
/**
* inform super querystate about the results from this subquerystate.
* Is called when the querystate is finished. The method invoked is
* the one from the current module active in the super querystate.
* @param qstate: the query state that is finished.
* Examine return_rcode and return_reply in the qstate.
* @param id: module id for this module.
* This coincides with the current module for the super qstate.
* @param super: the super querystate that needs to be informed.
*/
void (*inform_super)(struct module_qstate* qstate, int id,
struct module_qstate* super);
/**
* clear module specific data
*/
void (*clear)(struct module_qstate* qstate, int id);
/**
* How much memory is the module specific data using.
* @param env: module environment.
* @param id: the module id.
* @return the number of bytes that are alloced.
*/
size_t (*get_mem)(struct module_env* env, int id);
};
/**
* Debug utility: module external qstate to string
* @param s: the state value.
* @return descriptive string.
*/
const char* strextstate(enum module_ext_state s);
/**
* Debug utility: module event to string
* @param e: the module event value.
* @return descriptive string.
*/
const char* strmodulevent(enum module_ev e);
/**
* Append text to the error info for validation.
* @param qstate: query state.
* @param str: copied into query region and appended.
* Failures to allocate are logged.
*/
void errinf(struct module_qstate* qstate, const char* str);
void errinf_ede(struct module_qstate* qstate, const char* str,
sldns_ede_code reason_bogus);
/**
* Append text to error info: from 1.2.3.4
* @param qstate: query state.
* @param origin: sock list with origin of trouble.
* Every element added.
* If NULL: nothing is added.
* if 0len element: 'from cache' is added.
*/
void errinf_origin(struct module_qstate* qstate, struct sock_list *origin);
/**
* Append text to error info: for RRset name type class
* @param qstate: query state.
* @param rr: rrset_key.
*/
void errinf_rrset(struct module_qstate* qstate, struct ub_packed_rrset_key *rr);
/**
* Append text to error info: str dname
* @param qstate: query state.
* @param str: explanation string
* @param dname: the dname.
*/
void errinf_dname(struct module_qstate* qstate, const char* str,
uint8_t* dname);
/**
* Create error info in string. For validation failures.
* @param qstate: query state.
* @return string or NULL on malloc failure (already logged).
* This string is malloced and has to be freed by caller.
*/
char* errinf_to_str_bogus(struct module_qstate* qstate);
/**
* Check the sldns_ede_code of the qstate->errinf.
* @param qstate: query state.
* @return the latest explicitly set sldns_ede_code or LDNS_EDE_NONE.
*/
sldns_ede_code errinf_to_reason_bogus(struct module_qstate* qstate);
/**
* Create error info in string. For other servfails.
* @param qstate: query state.
* @return string or NULL on malloc failure (already logged).
* This string is malloced and has to be freed by caller.
*/
char* errinf_to_str_servfail(struct module_qstate* qstate);
+/**
+ * Create error info in string. For misc failures that are not servfail.
+ * @param qstate: query state.
+ * @return string or NULL on malloc failure (already logged).
+ * This string is malloced and has to be freed by caller.
+ */
+char* errinf_to_str_misc(struct module_qstate* qstate);
+
/**
* Initialize the edns known options by allocating the required space.
* @param env: the module environment.
* @return false on failure (no memory).
*/
int edns_known_options_init(struct module_env* env);
/**
* Free the allocated space for the known edns options.
* @param env: the module environment.
*/
void edns_known_options_delete(struct module_env* env);
/**
* Register a known edns option. Overwrite the flags if it is already
* registered. Used before creating workers to register known edns options.
* @param opt_code: the edns option code.
* @param bypass_cache_stage: whether the option interacts with the cache.
* @param no_aggregation: whether the option implies more specific
* aggregation.
* @param env: the module environment.
* @return true on success, false on failure (registering more options than
* allowed or trying to register after the environment is copied to the
* threads.)
*/
int edns_register_option(uint16_t opt_code, int bypass_cache_stage,
int no_aggregation, struct module_env* env);
/**
* Register an inplace callback function.
* @param cb: pointer to the callback function.
* @param type: inplace callback type.
* @param cbarg: argument for the callback function, or NULL.
* @param env: the module environment.
* @param id: module id.
* @return true on success, false on failure (out of memory or trying to
* register after the environment is copied to the threads.)
*/
int
inplace_cb_register(void* cb, enum inplace_cb_list_type type, void* cbarg,
struct module_env* env, int id);
/**
* Delete callback for specified type and module id.
* @param env: the module environment.
* @param type: inplace callback type.
* @param id: module id.
*/
void
inplace_cb_delete(struct module_env* env, enum inplace_cb_list_type type,
int id);
/**
* Delete all the inplace callback linked lists.
* @param env: the module environment.
*/
void inplace_cb_lists_delete(struct module_env* env);
/**
* Check if an edns option is known.
* @param opt_code: the edns option code.
* @param env: the module environment.
* @return pointer to registered option if the edns option is known,
* NULL otherwise.
*/
struct edns_known_option* edns_option_is_known(uint16_t opt_code,
struct module_env* env);
/**
* Check if an edns option needs to bypass the reply from cache stage.
* @param list: the edns options.
* @param env: the module environment.
* @return true if an edns option needs to bypass the cache stage,
* false otherwise.
*/
int edns_bypass_cache_stage(struct edns_option* list,
struct module_env* env);
/**
* Check if an unique mesh state is required. Might be triggered by EDNS option
* or set for the complete env.
* @param list: the edns options.
* @param env: the module environment.
* @return true if an edns option needs a unique mesh state,
* false otherwise.
*/
int unique_mesh_state(struct edns_option* list, struct module_env* env);
/**
* Log the known edns options.
* @param level: the desired verbosity level.
* @param env: the module environment.
*/
void log_edns_known_options(enum verbosity_value level,
struct module_env* env);
/**
* Copy state that may have happened in the subquery and is always relevant to
* the super.
* @param qstate: query state that finished.
* @param id: module id.
* @param super: the qstate to inform.
*/
void copy_state_to_super(struct module_qstate* qstate, int id,
struct module_qstate* super);
#endif /* UTIL_MODULE_H */
diff --git a/contrib/unbound/util/net_help.c b/contrib/unbound/util/net_help.c
index e559c9b2fa6a..8970247926d7 100644
--- a/contrib/unbound/util/net_help.c
+++ b/contrib/unbound/util/net_help.c
@@ -1,1741 +1,1847 @@
/*
* util/net_help.c - implementation of the network helper code
*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
* Implementation of net_help.h.
*/
#include "config.h"
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef HAVE_NET_IF_H
#include <net/if.h>
#endif
#ifdef HAVE_NETIOAPI_H
#include <netioapi.h>
#endif
#include "util/net_help.h"
#include "util/log.h"
#include "util/data/dname.h"
#include "util/module.h"
#include "util/regional.h"
#include "util/config_file.h"
#include "sldns/parseutil.h"
#include "sldns/wire2str.h"
#include "sldns/str2wire.h"
#include <fcntl.h>
#ifdef HAVE_OPENSSL_SSL_H
#include <openssl/ssl.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#endif
#ifdef HAVE_OPENSSL_ERR_H
#include <openssl/err.h>
#endif
#ifdef HAVE_OPENSSL_CORE_NAMES_H
#include <openssl/core_names.h>
#endif
#ifdef USE_WINSOCK
#include <wincrypt.h>
#endif
#ifdef HAVE_NGHTTP2_NGHTTP2_H
#include <nghttp2/nghttp2.h>
#endif
/** max length of an IP address (the address portion) that we allow */
#define MAX_ADDR_STRLEN 128 /* characters */
/** default value for EDNS ADVERTISED size */
uint16_t EDNS_ADVERTISED_SIZE = 4096;
/** minimal responses when positive answer: default is no */
int MINIMAL_RESPONSES = 0;
/** rrset order roundrobin: default is yes */
int RRSET_ROUNDROBIN = 1;
/** log tag queries with name instead of 'info' for filtering */
int LOG_TAG_QUERYREPLY = 0;
static struct tls_session_ticket_key {
unsigned char *key_name;
unsigned char *aes_key;
unsigned char *hmac_key;
} *ticket_keys;
#ifdef HAVE_SSL
/**
* callback TLS session ticket encrypt and decrypt
* For use with SSL_CTX_set_tlsext_ticket_key_cb or
* SSL_CTX_set_tlsext_ticket_key_evp_cb
* @param s: the SSL_CTX to use (from connect_sslctx_create())
* @param key_name: secret name, 16 bytes
* @param iv: up to EVP_MAX_IV_LENGTH.
* @param evp_ctx: the evp cipher context, function sets this.
* @param hmac_ctx: the hmac context, function sets this.
* with ..key_cb it is of type HMAC_CTX*
* with ..key_evp_cb it is of type EVP_MAC_CTX*
* @param enc: 1 is encrypt, 0 is decrypt
* @return 0 on no ticket, 1 for okay, and 2 for okay but renew the ticket
* (the ticket is decrypt only). and <0 for failures.
*/
int tls_session_ticket_key_cb(SSL *s, unsigned char* key_name,
unsigned char* iv, EVP_CIPHER_CTX *evp_ctx,
#ifdef HAVE_SSL_CTX_SET_TLSEXT_TICKET_KEY_EVP_CB
EVP_MAC_CTX *hmac_ctx,
#else
HMAC_CTX* hmac_ctx,
#endif
int enc);
#endif /* HAVE_SSL */
/* returns true is string addr is an ip6 specced address */
int
str_is_ip6(const char* str)
{
if(strchr(str, ':'))
return 1;
else return 0;
}
int
fd_set_nonblock(int s)
{
#ifdef HAVE_FCNTL
int flag;
if((flag = fcntl(s, F_GETFL)) == -1) {
log_err("can't fcntl F_GETFL: %s", strerror(errno));
flag = 0;
}
flag |= O_NONBLOCK;
if(fcntl(s, F_SETFL, flag) == -1) {
log_err("can't fcntl F_SETFL: %s", strerror(errno));
return 0;
}
#elif defined(HAVE_IOCTLSOCKET)
unsigned long on = 1;
if(ioctlsocket(s, FIONBIO, &on) != 0) {
log_err("can't ioctlsocket FIONBIO on: %s",
wsa_strerror(WSAGetLastError()));
}
#endif
return 1;
}
int
fd_set_block(int s)
{
#ifdef HAVE_FCNTL
int flag;
if((flag = fcntl(s, F_GETFL)) == -1) {
log_err("cannot fcntl F_GETFL: %s", strerror(errno));
flag = 0;
}
flag &= ~O_NONBLOCK;
if(fcntl(s, F_SETFL, flag) == -1) {
log_err("cannot fcntl F_SETFL: %s", strerror(errno));
return 0;
}
#elif defined(HAVE_IOCTLSOCKET)
unsigned long off = 0;
if(ioctlsocket(s, FIONBIO, &off) != 0) {
if(WSAGetLastError() != WSAEINVAL || verbosity >= 4)
log_err("can't ioctlsocket FIONBIO off: %s",
wsa_strerror(WSAGetLastError()));
}
#endif
return 1;
}
int
is_pow2(size_t num)
{
if(num == 0) return 1;
return (num & (num-1)) == 0;
}
void*
memdup(void* data, size_t len)
{
void* d;
if(!data) return NULL;
if(len == 0) return NULL;
d = malloc(len);
if(!d) return NULL;
memcpy(d, data, len);
return d;
}
void
log_addr(enum verbosity_value v, const char* str,
struct sockaddr_storage* addr, socklen_t addrlen)
{
uint16_t port;
const char* family = "unknown";
char dest[100];
int af = (int)((struct sockaddr_in*)addr)->sin_family;
void* sinaddr = &((struct sockaddr_in*)addr)->sin_addr;
if(verbosity < v)
return;
switch(af) {
case AF_INET: family="ip4"; break;
case AF_INET6: family="ip6";
sinaddr = &((struct sockaddr_in6*)addr)->sin6_addr;
break;
case AF_LOCAL:
dest[0]=0;
(void)inet_ntop(af, sinaddr, dest,
(socklen_t)sizeof(dest));
verbose(v, "%s local %s", str, dest);
return; /* do not continue and try to get port */
default: break;
}
if(inet_ntop(af, sinaddr, dest, (socklen_t)sizeof(dest)) == 0) {
(void)strlcpy(dest, "(inet_ntop error)", sizeof(dest));
}
dest[sizeof(dest)-1] = 0;
port = ntohs(((struct sockaddr_in*)addr)->sin_port);
if(verbosity >= 4)
verbose(v, "%s %s %s port %d (len %d)", str, family, dest,
(int)port, (int)addrlen);
else verbose(v, "%s %s port %d", str, dest, (int)port);
}
int
extstrtoaddr(const char* str, struct sockaddr_storage* addr,
socklen_t* addrlen, int port)
{
char* s;
if((s=strchr(str, '@'))) {
char buf[MAX_ADDR_STRLEN];
if(s-str >= MAX_ADDR_STRLEN) {
return 0;
}
(void)strlcpy(buf, str, sizeof(buf));
buf[s-str] = 0;
port = atoi(s+1);
if(port == 0 && strcmp(s+1,"0")!=0) {
return 0;
}
return ipstrtoaddr(buf, port, addr, addrlen);
}
return ipstrtoaddr(str, port, addr, addrlen);
}
int
ipstrtoaddr(const char* ip, int port, struct sockaddr_storage* addr,
socklen_t* addrlen)
{
uint16_t p;
if(!ip) return 0;
p = (uint16_t) port;
if(str_is_ip6(ip)) {
char buf[MAX_ADDR_STRLEN];
char* s;
struct sockaddr_in6* sa = (struct sockaddr_in6*)addr;
*addrlen = (socklen_t)sizeof(struct sockaddr_in6);
memset(sa, 0, *addrlen);
sa->sin6_family = AF_INET6;
sa->sin6_port = (in_port_t)htons(p);
if((s=strchr(ip, '%'))) { /* ip6%interface, rfc 4007 */
if(s-ip >= MAX_ADDR_STRLEN)
return 0;
(void)strlcpy(buf, ip, sizeof(buf));
buf[s-ip]=0;
#ifdef HAVE_IF_NAMETOINDEX
if (!(sa->sin6_scope_id = if_nametoindex(s+1)))
#endif /* HAVE_IF_NAMETOINDEX */
sa->sin6_scope_id = (uint32_t)atoi(s+1);
ip = buf;
}
if(inet_pton((int)sa->sin6_family, ip, &sa->sin6_addr) <= 0) {
return 0;
}
} else { /* ip4 */
struct sockaddr_in* sa = (struct sockaddr_in*)addr;
*addrlen = (socklen_t)sizeof(struct sockaddr_in);
memset(sa, 0, *addrlen);
sa->sin_family = AF_INET;
sa->sin_port = (in_port_t)htons(p);
if(inet_pton((int)sa->sin_family, ip, &sa->sin_addr) <= 0) {
return 0;
}
}
return 1;
}
int netblockstrtoaddr(const char* str, int port, struct sockaddr_storage* addr,
socklen_t* addrlen, int* net)
{
char buf[64];
char* s;
*net = (str_is_ip6(str)?128:32);
if((s=strchr(str, '/'))) {
if(atoi(s+1) > *net) {
log_err("netblock too large: %s", str);
return 0;
}
*net = atoi(s+1);
if(*net == 0 && strcmp(s+1, "0") != 0) {
log_err("cannot parse netblock: '%s'", str);
return 0;
}
strlcpy(buf, str, sizeof(buf));
s = strchr(buf, '/');
if(s) *s = 0;
s = buf;
}
if(!ipstrtoaddr(s?s:str, port, addr, addrlen)) {
log_err("cannot parse ip address: '%s'", str);
return 0;
}
if(s) {
addr_mask(addr, *addrlen, *net);
}
return 1;
}
/* RPZ format address dname to network byte order address */
static int ipdnametoaddr(uint8_t* dname, size_t dnamelen,
struct sockaddr_storage* addr, socklen_t* addrlen, int* af)
{
uint8_t* ia;
int dnamelabs = dname_count_labels(dname);
uint8_t lablen;
char* e = NULL;
int z = 0;
size_t len = 0;
int i;
*af = AF_INET;
/* need 1 byte for label length */
if(dnamelen < 1)
return 0;
if(dnamelabs > 6 ||
dname_has_label(dname, dnamelen, (uint8_t*)"\002zz")) {
*af = AF_INET6;
}
len = *dname;
lablen = *dname++;
i = (*af == AF_INET) ? 3 : 15;
if(*af == AF_INET6) {
struct sockaddr_in6* sa = (struct sockaddr_in6*)addr;
*addrlen = (socklen_t)sizeof(struct sockaddr_in6);
memset(sa, 0, *addrlen);
sa->sin6_family = AF_INET6;
ia = (uint8_t*)&sa->sin6_addr;
} else { /* ip4 */
struct sockaddr_in* sa = (struct sockaddr_in*)addr;
*addrlen = (socklen_t)sizeof(struct sockaddr_in);
memset(sa, 0, *addrlen);
sa->sin_family = AF_INET;
ia = (uint8_t*)&sa->sin_addr;
}
while(lablen && i >= 0 && len <= dnamelen) {
char buff[LDNS_MAX_LABELLEN+1];
uint16_t chunk; /* big enough to not overflow on IPv6 hextet */
if((*af == AF_INET && (lablen > 3 || dnamelabs > 6)) ||
(*af == AF_INET6 && (lablen > 4 || dnamelabs > 10))) {
return 0;
}
if(memcmp(dname, "zz", 2) == 0 && *af == AF_INET6) {
/* Add one or more 0 labels. Address is initialised at
* 0, so just skip the zero part. */
int zl = 11 - dnamelabs;
if(z || zl < 0)
return 0;
z = 1;
i -= (zl*2);
} else {
memcpy(buff, dname, lablen);
buff[lablen] = '\0';
chunk = strtol(buff, &e, (*af == AF_INET) ? 10 : 16);
if(!e || *e != '\0' || (*af == AF_INET && chunk > 255))
return 0;
if(*af == AF_INET) {
log_assert(i < 4 && i >= 0);
ia[i] = (uint8_t)chunk;
i--;
} else {
log_assert(i < 16 && i >= 1);
/* ia in network byte order */
ia[i-1] = (uint8_t)(chunk >> 8);
ia[i] = (uint8_t)(chunk & 0x00FF);
i -= 2;
}
}
dname += lablen;
lablen = *dname++;
len += lablen;
}
if(i != -1)
/* input too short */
return 0;
return 1;
}
int netblockdnametoaddr(uint8_t* dname, size_t dnamelen,
struct sockaddr_storage* addr, socklen_t* addrlen, int* net, int* af)
{
char buff[3 /* 3 digit netblock */ + 1];
size_t nlablen;
if(dnamelen < 1 || *dname > 3)
/* netblock invalid */
return 0;
nlablen = *dname;
if(dnamelen < 1 + nlablen)
return 0;
memcpy(buff, dname+1, nlablen);
buff[nlablen] = '\0';
*net = atoi(buff);
if(*net == 0 && strcmp(buff, "0") != 0)
return 0;
dname += nlablen;
dname++;
if(!ipdnametoaddr(dname, dnamelen-1-nlablen, addr, addrlen, af))
return 0;
if((*af == AF_INET6 && *net > 128) || (*af == AF_INET && *net > 32))
return 0;
return 1;
}
int authextstrtoaddr(char* str, struct sockaddr_storage* addr,
socklen_t* addrlen, char** auth_name)
{
char* s;
int port = UNBOUND_DNS_PORT;
if((s=strchr(str, '@'))) {
char buf[MAX_ADDR_STRLEN];
size_t len = (size_t)(s-str);
char* hash = strchr(s+1, '#');
if(hash) {
*auth_name = hash+1;
} else {
*auth_name = NULL;
}
if(len >= MAX_ADDR_STRLEN) {
return 0;
}
(void)strlcpy(buf, str, sizeof(buf));
buf[len] = 0;
port = atoi(s+1);
if(port == 0) {
if(!hash && strcmp(s+1,"0")!=0)
return 0;
if(hash && strncmp(s+1,"0#",2)!=0)
return 0;
}
return ipstrtoaddr(buf, port, addr, addrlen);
}
if((s=strchr(str, '#'))) {
char buf[MAX_ADDR_STRLEN];
size_t len = (size_t)(s-str);
if(len >= MAX_ADDR_STRLEN) {
return 0;
}
(void)strlcpy(buf, str, sizeof(buf));
buf[len] = 0;
port = UNBOUND_DNS_OVER_TLS_PORT;
*auth_name = s+1;
return ipstrtoaddr(buf, port, addr, addrlen);
}
*auth_name = NULL;
return ipstrtoaddr(str, port, addr, addrlen);
}
uint8_t* authextstrtodname(char* str, int* port, char** auth_name)
{
char* s;
uint8_t* dname;
size_t dname_len;
*port = UNBOUND_DNS_PORT;
*auth_name = NULL;
if((s=strchr(str, '@'))) {
char* hash = strchr(s+1, '#');
if(hash) {
*auth_name = hash+1;
} else {
*auth_name = NULL;
}
*port = atoi(s+1);
if(*port == 0) {
if(!hash && strcmp(s+1,"0")!=0)
return 0;
if(hash && strncmp(s+1,"0#",2)!=0)
return 0;
}
*s = 0;
dname = sldns_str2wire_dname(str, &dname_len);
*s = '@';
} else if((s=strchr(str, '#'))) {
*port = UNBOUND_DNS_OVER_TLS_PORT;
*auth_name = s+1;
*s = 0;
dname = sldns_str2wire_dname(str, &dname_len);
*s = '#';
} else {
dname = sldns_str2wire_dname(str, &dname_len);
}
return dname;
}
/** store port number into sockaddr structure */
void
sockaddr_store_port(struct sockaddr_storage* addr, socklen_t addrlen, int port)
{
if(addr_is_ip6(addr, addrlen)) {
struct sockaddr_in6* sa = (struct sockaddr_in6*)addr;
sa->sin6_port = (in_port_t)htons((uint16_t)port);
} else {
struct sockaddr_in* sa = (struct sockaddr_in*)addr;
sa->sin_port = (in_port_t)htons((uint16_t)port);
}
}
void
log_nametypeclass(enum verbosity_value v, const char* str, uint8_t* name,
uint16_t type, uint16_t dclass)
{
char buf[LDNS_MAX_DOMAINLEN+1];
char t[12], c[12];
const char *ts, *cs;
if(verbosity < v)
return;
dname_str(name, buf);
if(type == LDNS_RR_TYPE_TSIG) ts = "TSIG";
else if(type == LDNS_RR_TYPE_IXFR) ts = "IXFR";
else if(type == LDNS_RR_TYPE_AXFR) ts = "AXFR";
else if(type == LDNS_RR_TYPE_MAILB) ts = "MAILB";
else if(type == LDNS_RR_TYPE_MAILA) ts = "MAILA";
else if(type == LDNS_RR_TYPE_ANY) ts = "ANY";
else if(sldns_rr_descript(type) && sldns_rr_descript(type)->_name)
ts = sldns_rr_descript(type)->_name;
else {
snprintf(t, sizeof(t), "TYPE%d", (int)type);
ts = t;
}
if(sldns_lookup_by_id(sldns_rr_classes, (int)dclass) &&
sldns_lookup_by_id(sldns_rr_classes, (int)dclass)->name)
cs = sldns_lookup_by_id(sldns_rr_classes, (int)dclass)->name;
else {
snprintf(c, sizeof(c), "CLASS%d", (int)dclass);
cs = c;
}
log_info("%s %s %s %s", str, buf, ts, cs);
}
void
log_query_in(const char* str, uint8_t* name, uint16_t type, uint16_t dclass)
{
char buf[LDNS_MAX_DOMAINLEN+1];
char t[12], c[12];
const char *ts, *cs;
dname_str(name, buf);
if(type == LDNS_RR_TYPE_TSIG) ts = "TSIG";
else if(type == LDNS_RR_TYPE_IXFR) ts = "IXFR";
else if(type == LDNS_RR_TYPE_AXFR) ts = "AXFR";
else if(type == LDNS_RR_TYPE_MAILB) ts = "MAILB";
else if(type == LDNS_RR_TYPE_MAILA) ts = "MAILA";
else if(type == LDNS_RR_TYPE_ANY) ts = "ANY";
else if(sldns_rr_descript(type) && sldns_rr_descript(type)->_name)
ts = sldns_rr_descript(type)->_name;
else {
snprintf(t, sizeof(t), "TYPE%d", (int)type);
ts = t;
}
if(sldns_lookup_by_id(sldns_rr_classes, (int)dclass) &&
sldns_lookup_by_id(sldns_rr_classes, (int)dclass)->name)
cs = sldns_lookup_by_id(sldns_rr_classes, (int)dclass)->name;
else {
snprintf(c, sizeof(c), "CLASS%d", (int)dclass);
cs = c;
}
if(LOG_TAG_QUERYREPLY)
log_query("%s %s %s %s", str, buf, ts, cs);
else log_info("%s %s %s %s", str, buf, ts, cs);
}
void log_name_addr(enum verbosity_value v, const char* str, uint8_t* zone,
struct sockaddr_storage* addr, socklen_t addrlen)
{
uint16_t port;
const char* family = "unknown_family ";
char namebuf[LDNS_MAX_DOMAINLEN+1];
char dest[100];
int af = (int)((struct sockaddr_in*)addr)->sin_family;
void* sinaddr = &((struct sockaddr_in*)addr)->sin_addr;
if(verbosity < v)
return;
switch(af) {
case AF_INET: family=""; break;
case AF_INET6: family="";
sinaddr = &((struct sockaddr_in6*)addr)->sin6_addr;
break;
case AF_LOCAL: family="local "; break;
default: break;
}
if(inet_ntop(af, sinaddr, dest, (socklen_t)sizeof(dest)) == 0) {
(void)strlcpy(dest, "(inet_ntop error)", sizeof(dest));
}
dest[sizeof(dest)-1] = 0;
port = ntohs(((struct sockaddr_in*)addr)->sin_port);
dname_str(zone, namebuf);
if(af != AF_INET && af != AF_INET6)
verbose(v, "%s <%s> %s%s#%d (addrlen %d)",
str, namebuf, family, dest, (int)port, (int)addrlen);
else verbose(v, "%s <%s> %s%s#%d",
str, namebuf, family, dest, (int)port);
}
void log_err_addr(const char* str, const char* err,
struct sockaddr_storage* addr, socklen_t addrlen)
{
uint16_t port;
char dest[100];
int af = (int)((struct sockaddr_in*)addr)->sin_family;
void* sinaddr = &((struct sockaddr_in*)addr)->sin_addr;
if(af == AF_INET6)
sinaddr = &((struct sockaddr_in6*)addr)->sin6_addr;
if(inet_ntop(af, sinaddr, dest, (socklen_t)sizeof(dest)) == 0) {
(void)strlcpy(dest, "(inet_ntop error)", sizeof(dest));
}
dest[sizeof(dest)-1] = 0;
port = ntohs(((struct sockaddr_in*)addr)->sin_port);
if(verbosity >= 4)
log_err("%s: %s for %s port %d (len %d)", str, err, dest,
(int)port, (int)addrlen);
else log_err("%s: %s for %s port %d", str, err, dest, (int)port);
}
int
sockaddr_cmp(struct sockaddr_storage* addr1, socklen_t len1,
struct sockaddr_storage* addr2, socklen_t len2)
{
struct sockaddr_in* p1_in = (struct sockaddr_in*)addr1;
struct sockaddr_in* p2_in = (struct sockaddr_in*)addr2;
struct sockaddr_in6* p1_in6 = (struct sockaddr_in6*)addr1;
struct sockaddr_in6* p2_in6 = (struct sockaddr_in6*)addr2;
if(len1 < len2)
return -1;
if(len1 > len2)
return 1;
log_assert(len1 == len2);
if( p1_in->sin_family < p2_in->sin_family)
return -1;
if( p1_in->sin_family > p2_in->sin_family)
return 1;
log_assert( p1_in->sin_family == p2_in->sin_family );
/* compare ip4 */
if( p1_in->sin_family == AF_INET ) {
/* just order it, ntohs not required */
if(p1_in->sin_port < p2_in->sin_port)
return -1;
if(p1_in->sin_port > p2_in->sin_port)
return 1;
log_assert(p1_in->sin_port == p2_in->sin_port);
return memcmp(&p1_in->sin_addr, &p2_in->sin_addr, INET_SIZE);
} else if (p1_in6->sin6_family == AF_INET6) {
/* just order it, ntohs not required */
if(p1_in6->sin6_port < p2_in6->sin6_port)
return -1;
if(p1_in6->sin6_port > p2_in6->sin6_port)
return 1;
log_assert(p1_in6->sin6_port == p2_in6->sin6_port);
return memcmp(&p1_in6->sin6_addr, &p2_in6->sin6_addr,
INET6_SIZE);
} else {
/* eek unknown type, perform this comparison for sanity. */
return memcmp(addr1, addr2, len1);
}
}
int
sockaddr_cmp_addr(struct sockaddr_storage* addr1, socklen_t len1,
struct sockaddr_storage* addr2, socklen_t len2)
{
struct sockaddr_in* p1_in = (struct sockaddr_in*)addr1;
struct sockaddr_in* p2_in = (struct sockaddr_in*)addr2;
struct sockaddr_in6* p1_in6 = (struct sockaddr_in6*)addr1;
struct sockaddr_in6* p2_in6 = (struct sockaddr_in6*)addr2;
if(len1 < len2)
return -1;
if(len1 > len2)
return 1;
log_assert(len1 == len2);
if( p1_in->sin_family < p2_in->sin_family)
return -1;
if( p1_in->sin_family > p2_in->sin_family)
return 1;
log_assert( p1_in->sin_family == p2_in->sin_family );
/* compare ip4 */
if( p1_in->sin_family == AF_INET ) {
return memcmp(&p1_in->sin_addr, &p2_in->sin_addr, INET_SIZE);
} else if (p1_in6->sin6_family == AF_INET6) {
return memcmp(&p1_in6->sin6_addr, &p2_in6->sin6_addr,
INET6_SIZE);
} else {
/* eek unknown type, perform this comparison for sanity. */
return memcmp(addr1, addr2, len1);
}
}
int
addr_is_ip6(struct sockaddr_storage* addr, socklen_t len)
{
if(len == (socklen_t)sizeof(struct sockaddr_in6) &&
((struct sockaddr_in6*)addr)->sin6_family == AF_INET6)
return 1;
else return 0;
}
void
addr_mask(struct sockaddr_storage* addr, socklen_t len, int net)
{
uint8_t mask[8] = {0x0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe};
int i, max;
uint8_t* s;
if(addr_is_ip6(addr, len)) {
s = (uint8_t*)&((struct sockaddr_in6*)addr)->sin6_addr;
max = 128;
} else {
s = (uint8_t*)&((struct sockaddr_in*)addr)->sin_addr;
max = 32;
}
if(net >= max)
return;
for(i=net/8+1; i<max/8; i++) {
s[i] = 0;
}
s[net/8] &= mask[net&0x7];
}
int
addr_in_common(struct sockaddr_storage* addr1, int net1,
struct sockaddr_storage* addr2, int net2, socklen_t addrlen)
{
int min = (net1<net2)?net1:net2;
int i, to;
int match = 0;
uint8_t* s1, *s2;
if(addr_is_ip6(addr1, addrlen)) {
s1 = (uint8_t*)&((struct sockaddr_in6*)addr1)->sin6_addr;
s2 = (uint8_t*)&((struct sockaddr_in6*)addr2)->sin6_addr;
to = 16;
} else {
s1 = (uint8_t*)&((struct sockaddr_in*)addr1)->sin_addr;
s2 = (uint8_t*)&((struct sockaddr_in*)addr2)->sin_addr;
to = 4;
}
/* match = bits_in_common(s1, s2, to); */
for(i=0; i<to; i++) {
if(s1[i] == s2[i]) {
match += 8;
} else {
uint8_t z = s1[i]^s2[i];
log_assert(z);
while(!(z&0x80)) {
match++;
z<<=1;
}
break;
}
}
if(match > min) match = min;
return match;
}
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;
void* sinaddr = &((struct sockaddr_in*)addr)->sin_addr;
if(addr_is_ip6(addr, addrlen))
sinaddr = &((struct sockaddr_in6*)addr)->sin6_addr;
if(inet_ntop(af, sinaddr, buf, (socklen_t)len) == 0) {
snprintf(buf, len, "(inet_ntop_error)");
}
}
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;
+ int i;
/* 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++) {
+ for(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 */
const uint8_t map_prefix[16] =
{0,0,0,0, 0,0,0,0, 0,0,0xff,0xff, 0,0,0,0};
uint8_t* s;
if(!addr_is_ip6(addr, addrlen))
return 0;
/* s is 16 octet ipv6 address string */
s = (uint8_t*)&((struct sockaddr_in6*)addr)->sin6_addr;
return (memcmp(s, map_prefix, 12) == 0);
}
int addr_is_broadcast(struct sockaddr_storage* addr, socklen_t addrlen)
{
int af = (int)((struct sockaddr_in*)addr)->sin_family;
void* sinaddr = &((struct sockaddr_in*)addr)->sin_addr;
return af == AF_INET && addrlen>=(socklen_t)sizeof(struct sockaddr_in)
&& memcmp(sinaddr, "\377\377\377\377", 4) == 0;
}
int addr_is_any(struct sockaddr_storage* addr, socklen_t addrlen)
{
int af = (int)((struct sockaddr_in*)addr)->sin_family;
void* sinaddr = &((struct sockaddr_in*)addr)->sin_addr;
void* sin6addr = &((struct sockaddr_in6*)addr)->sin6_addr;
if(af == AF_INET && addrlen>=(socklen_t)sizeof(struct sockaddr_in)
&& memcmp(sinaddr, "\000\000\000\000", 4) == 0)
return 1;
else if(af==AF_INET6 && addrlen>=(socklen_t)sizeof(struct sockaddr_in6)
&& memcmp(sin6addr, "\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000", 16) == 0)
return 1;
return 0;
}
void sock_list_insert(struct sock_list** list, struct sockaddr_storage* addr,
socklen_t len, struct regional* region)
{
struct sock_list* add = (struct sock_list*)regional_alloc(region,
sizeof(*add) - sizeof(add->addr) + (size_t)len);
if(!add) {
log_err("out of memory in socketlist insert");
return;
}
log_assert(list);
add->next = *list;
add->len = len;
*list = add;
if(len) memmove(&add->addr, addr, len);
}
void sock_list_prepend(struct sock_list** list, struct sock_list* add)
{
struct sock_list* last = add;
if(!last)
return;
while(last->next)
last = last->next;
last->next = *list;
*list = add;
}
int sock_list_find(struct sock_list* list, struct sockaddr_storage* addr,
socklen_t len)
{
while(list) {
if(len == list->len) {
if(len == 0 || sockaddr_cmp_addr(addr, len,
&list->addr, list->len) == 0)
return 1;
}
list = list->next;
}
return 0;
}
void sock_list_merge(struct sock_list** list, struct regional* region,
struct sock_list* add)
{
struct sock_list* p;
for(p=add; p; p=p->next) {
if(!sock_list_find(*list, &p->addr, p->len))
sock_list_insert(list, &p->addr, p->len, region);
}
}
void
log_crypto_err(const char* str)
{
#ifdef HAVE_SSL
log_crypto_err_code(str, ERR_get_error());
#else
(void)str;
#endif /* HAVE_SSL */
}
void log_crypto_err_code(const char* str, unsigned long err)
{
#ifdef HAVE_SSL
/* error:[error code]:[library name]:[function name]:[reason string] */
char buf[128];
unsigned long e;
ERR_error_string_n(err, buf, sizeof(buf));
log_err("%s crypto %s", str, buf);
while( (e=ERR_get_error()) ) {
ERR_error_string_n(e, buf, sizeof(buf));
log_err("and additionally crypto %s", buf);
}
#else
(void)str;
(void)err;
#endif /* HAVE_SSL */
}
+#ifdef HAVE_SSL
+/** Print crypt erro with SSL_get_error want code and err_get_error code */
+static void log_crypto_err_io_code_arg(const char* str, int r,
+ unsigned long err, int err_present)
+{
+ int print_errno = 0, print_crypto_err = 0;
+ const char* inf = NULL;
+
+ switch(r) {
+ case SSL_ERROR_NONE:
+ inf = "no error";
+ break;
+ case SSL_ERROR_ZERO_RETURN:
+ inf = "channel closed";
+ break;
+ case SSL_ERROR_WANT_READ:
+ inf = "want read";
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ inf = "want write";
+ break;
+ case SSL_ERROR_WANT_CONNECT:
+ inf = "want connect";
+ break;
+ case SSL_ERROR_WANT_ACCEPT:
+ inf = "want accept";
+ break;
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ inf = "want X509 lookup";
+ break;
+#ifdef SSL_ERROR_WANT_ASYNC
+ case SSL_ERROR_WANT_ASYNC:
+ inf = "want async";
+ break;
+#endif
+#ifdef SSL_ERROR_WANT_ASYNC_JOB
+ case SSL_ERROR_WANT_ASYNC_JOB:
+ inf = "want async job";
+ break;
+#endif
+#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB
+ case SSL_ERROR_WANT_CLIENT_HELLO_CB:
+ inf = "want client hello cb";
+ break;
+#endif
+ case SSL_ERROR_SYSCALL:
+ print_errno = 1;
+ inf = "syscall";
+ break;
+ case SSL_ERROR_SSL:
+ print_crypto_err = 1;
+ inf = "SSL, usually protocol, error";
+ break;
+ default:
+ inf = "unknown SSL_get_error result code";
+ print_errno = 1;
+ print_crypto_err = 1;
+ }
+ if(print_crypto_err) {
+ if(print_errno) {
+ char buf[1024];
+ snprintf(buf, sizeof(buf), "%s with errno %s",
+ str, strerror(errno));
+ if(err_present)
+ log_crypto_err_code(buf, err);
+ else log_crypto_err(buf);
+ } else {
+ if(err_present)
+ log_crypto_err_code(str, err);
+ else log_crypto_err(str);
+ }
+ } else {
+ if(print_errno) {
+ if(errno == 0)
+ log_err("str: syscall error with errno %s",
+ strerror(errno));
+ else log_err("str: %s", strerror(errno));
+ } else {
+ log_err("str: %s", inf);
+ }
+ }
+}
+#endif /* HAVE_SSL */
+
+void log_crypto_err_io(const char* str, int r)
+{
+#ifdef HAVE_SSL
+ log_crypto_err_io_code_arg(str, r, 0, 0);
+#else
+ (void)str;
+ (void)r;
+#endif /* HAVE_SSL */
+}
+
+void log_crypto_err_io_code(const char* str, int r, unsigned long err)
+{
+#ifdef HAVE_SSL
+ log_crypto_err_io_code_arg(str, r, err, 1);
+#else
+ (void)str;
+ (void)r;
+ (void)err;
+#endif /* HAVE_SSL */
+}
+
#ifdef HAVE_SSL
/** log certificate details */
void
log_cert(unsigned level, const char* str, void* cert)
{
BIO* bio;
char nul = 0;
char* pp = NULL;
long len;
if(verbosity < level) return;
bio = BIO_new(BIO_s_mem());
if(!bio) return;
X509_print_ex(bio, (X509*)cert, 0, (unsigned long)-1
^(X509_FLAG_NO_SUBJECT
|X509_FLAG_NO_ISSUER|X509_FLAG_NO_VALIDITY
|X509_FLAG_NO_EXTENSIONS|X509_FLAG_NO_AUX
|X509_FLAG_NO_ATTRIBUTES));
BIO_write(bio, &nul, (int)sizeof(nul));
len = BIO_get_mem_data(bio, &pp);
if(len != 0 && pp) {
/* reduce size of cert printout */
char* s;
while((s=strstr(pp, " "))!=NULL)
memmove(s, s+1, strlen(s+1)+1);
while((s=strstr(pp, "\t\t"))!=NULL)
memmove(s, s+1, strlen(s+1)+1);
verbose(level, "%s: \n%s", str, pp);
}
BIO_free(bio);
}
#endif /* HAVE_SSL */
#if defined(HAVE_SSL) && defined(HAVE_NGHTTP2) && defined(HAVE_SSL_CTX_SET_ALPN_SELECT_CB)
static int alpn_select_cb(SSL* ATTR_UNUSED(ssl), const unsigned char** out,
unsigned char* outlen, const unsigned char* in, unsigned int inlen,
void* ATTR_UNUSED(arg))
{
int rv = nghttp2_select_next_protocol((unsigned char **)out, outlen, in,
inlen);
if(rv == -1) {
return SSL_TLSEXT_ERR_NOACK;
}
/* either http/1.1 or h2 selected */
return SSL_TLSEXT_ERR_OK;
}
#endif
int
listen_sslctx_setup(void* ctxt)
{
#ifdef HAVE_SSL
SSL_CTX* ctx = (SSL_CTX*)ctxt;
/* no SSLv2, SSLv3 because has defects */
#if SSL_OP_NO_SSLv2 != 0
if((SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2) & SSL_OP_NO_SSLv2)
!= SSL_OP_NO_SSLv2){
log_crypto_err("could not set SSL_OP_NO_SSLv2");
return 0;
}
#endif
if((SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3) & SSL_OP_NO_SSLv3)
!= SSL_OP_NO_SSLv3){
log_crypto_err("could not set SSL_OP_NO_SSLv3");
return 0;
}
#if defined(SSL_OP_NO_TLSv1) && defined(SSL_OP_NO_TLSv1_1)
/* if we have tls 1.1 disable 1.0 */
if((SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1) & SSL_OP_NO_TLSv1)
!= SSL_OP_NO_TLSv1){
log_crypto_err("could not set SSL_OP_NO_TLSv1");
return 0;
}
#endif
#if defined(SSL_OP_NO_TLSv1_1) && defined(SSL_OP_NO_TLSv1_2)
/* if we have tls 1.2 disable 1.1 */
if((SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_1) & SSL_OP_NO_TLSv1_1)
!= SSL_OP_NO_TLSv1_1){
log_crypto_err("could not set SSL_OP_NO_TLSv1_1");
return 0;
}
#endif
#if defined(SSL_OP_NO_RENEGOTIATION)
/* disable client renegotiation */
if((SSL_CTX_set_options(ctx, SSL_OP_NO_RENEGOTIATION) &
SSL_OP_NO_RENEGOTIATION) != SSL_OP_NO_RENEGOTIATION) {
log_crypto_err("could not set SSL_OP_NO_RENEGOTIATION");
return 0;
}
#endif
#if defined(SHA256_DIGEST_LENGTH) && defined(USE_ECDSA)
/* if we detect system-wide crypto policies, use those */
if (access( "/etc/crypto-policies/config", F_OK ) != 0 ) {
/* if we have sha256, set the cipher list to have no known vulns */
if(!SSL_CTX_set_cipher_list(ctx, "TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-256-GCM-SHA384:TLS13-AES-128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256"))
log_crypto_err("could not set cipher list with SSL_CTX_set_cipher_list");
}
#endif
#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_OP_CIPHER_SERVER_PREFERENCE) {
log_crypto_err("could not set SSL_OP_CIPHER_SERVER_PREFERENCE");
return 0;
}
#ifdef HAVE_SSL_CTX_SET_SECURITY_LEVEL
SSL_CTX_set_security_level(ctx, 0);
#endif
#if defined(HAVE_SSL_CTX_SET_ALPN_SELECT_CB) && defined(HAVE_NGHTTP2)
SSL_CTX_set_alpn_select_cb(ctx, alpn_select_cb, NULL);
#endif
#else
(void)ctxt;
#endif /* HAVE_SSL */
return 1;
}
void
listen_sslctx_setup_2(void* ctxt)
{
#ifdef HAVE_SSL
SSL_CTX* ctx = (SSL_CTX*)ctxt;
(void)ctx;
#if HAVE_DECL_SSL_CTX_SET_ECDH_AUTO
if(!SSL_CTX_set_ecdh_auto(ctx,1)) {
log_crypto_err("Error in SSL_CTX_ecdh_auto, not enabling ECDHE");
}
#elif defined(USE_ECDSA)
if(1) {
EC_KEY *ecdh = EC_KEY_new_by_curve_name (NID_X9_62_prime256v1);
if (!ecdh) {
log_crypto_err("could not find p256, not enabling ECDHE");
} else {
if (1 != SSL_CTX_set_tmp_ecdh (ctx, ecdh)) {
log_crypto_err("Error in SSL_CTX_set_tmp_ecdh, not enabling ECDHE");
}
EC_KEY_free (ecdh);
}
}
#endif
#else
(void)ctxt;
#endif /* HAVE_SSL */
}
void* listen_sslctx_create(char* key, char* pem, char* verifypem)
{
#ifdef HAVE_SSL
SSL_CTX* ctx = SSL_CTX_new(SSLv23_server_method());
if(!ctx) {
log_crypto_err("could not SSL_CTX_new");
return NULL;
}
if(!key || key[0] == 0) {
log_err("error: no tls-service-key file specified");
SSL_CTX_free(ctx);
return NULL;
}
if(!pem || pem[0] == 0) {
log_err("error: no tls-service-pem file specified");
SSL_CTX_free(ctx);
return NULL;
}
if(!listen_sslctx_setup(ctx)) {
SSL_CTX_free(ctx);
return NULL;
}
if(!SSL_CTX_use_certificate_chain_file(ctx, pem)) {
log_err("error for cert file: %s", pem);
log_crypto_err("error in SSL_CTX use_certificate_chain_file");
SSL_CTX_free(ctx);
return NULL;
}
if(!SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM)) {
log_err("error for private key file: %s", key);
log_crypto_err("Error in SSL_CTX use_PrivateKey_file");
SSL_CTX_free(ctx);
return NULL;
}
if(!SSL_CTX_check_private_key(ctx)) {
log_err("error for key file: %s", key);
log_crypto_err("Error in SSL_CTX check_private_key");
SSL_CTX_free(ctx);
return NULL;
}
listen_sslctx_setup_2(ctx);
if(verifypem && verifypem[0]) {
if(!SSL_CTX_load_verify_locations(ctx, verifypem, NULL)) {
log_crypto_err("Error in SSL_CTX verify locations");
SSL_CTX_free(ctx);
return NULL;
}
SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(
verifypem));
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
}
return ctx;
#else
(void)key; (void)pem; (void)verifypem;
return NULL;
#endif
}
#ifdef USE_WINSOCK
/* For windows, the CA trust store is not read by openssl.
Add code to open the trust store using wincrypt API and add
the root certs into openssl trust store */
static int
add_WIN_cacerts_to_openssl_store(SSL_CTX* tls_ctx)
{
HCERTSTORE hSystemStore;
PCCERT_CONTEXT pTargetCert = NULL;
X509_STORE* store;
verbose(VERB_ALGO, "Adding Windows certificates from system root store to CA store");
/* load just once per context lifetime for this version
TODO: dynamically update CA trust changes as they are available */
if (!tls_ctx)
return 0;
/* Call wincrypt's CertOpenStore to open the CA root store. */
if ((hSystemStore = CertOpenStore(
CERT_STORE_PROV_SYSTEM,
0,
0,
/* NOTE: mingw does not have this const: replace with 1 << 16 from code
CERT_SYSTEM_STORE_CURRENT_USER, */
1 << 16,
L"root")) == 0)
{
return 0;
}
store = SSL_CTX_get_cert_store(tls_ctx);
if (!store)
return 0;
/* failure if the CA store is empty or the call fails */
if ((pTargetCert = CertEnumCertificatesInStore(
hSystemStore, pTargetCert)) == 0) {
verbose(VERB_ALGO, "CA certificate store for Windows is empty.");
return 0;
}
/* iterate over the windows cert store and add to openssl store */
do
{
X509 *cert1 = d2i_X509(NULL,
(const unsigned char **)&pTargetCert->pbCertEncoded,
pTargetCert->cbCertEncoded);
if (!cert1) {
unsigned long error = ERR_get_error();
/* return error if a cert fails */
verbose(VERB_ALGO, "%s %d:%s",
"Unable to parse certificate in memory",
(int)error, ERR_error_string(error, NULL));
return 0;
}
else {
/* return error if a cert add to store fails */
if (X509_STORE_add_cert(store, cert1) == 0) {
unsigned long error = ERR_peek_last_error();
/* Ignore error X509_R_CERT_ALREADY_IN_HASH_TABLE which means the
* certificate is already in the store. */
if(ERR_GET_LIB(error) != ERR_LIB_X509 ||
ERR_GET_REASON(error) != X509_R_CERT_ALREADY_IN_HASH_TABLE) {
error = ERR_get_error();
verbose(VERB_ALGO, "%s %d:%s\n",
"Error adding certificate", (int)error,
ERR_error_string(error, NULL));
X509_free(cert1);
return 0;
}
}
X509_free(cert1);
}
} while ((pTargetCert = CertEnumCertificatesInStore(
hSystemStore, pTargetCert)) != 0);
/* Clean up memory and quit. */
if (pTargetCert)
CertFreeCertificateContext(pTargetCert);
if (hSystemStore)
{
if (!CertCloseStore(
hSystemStore, 0))
return 0;
}
verbose(VERB_ALGO, "Completed adding Windows certificates to CA store successfully");
return 1;
}
#endif /* USE_WINSOCK */
void* connect_sslctx_create(char* key, char* pem, char* verifypem, int wincert)
{
#ifdef HAVE_SSL
SSL_CTX* ctx = SSL_CTX_new(SSLv23_client_method());
if(!ctx) {
log_crypto_err("could not allocate SSL_CTX pointer");
return NULL;
}
#if SSL_OP_NO_SSLv2 != 0
if((SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2) & SSL_OP_NO_SSLv2)
!= SSL_OP_NO_SSLv2) {
log_crypto_err("could not set SSL_OP_NO_SSLv2");
SSL_CTX_free(ctx);
return NULL;
}
#endif
if((SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3) & SSL_OP_NO_SSLv3)
!= SSL_OP_NO_SSLv3) {
log_crypto_err("could not set SSL_OP_NO_SSLv3");
SSL_CTX_free(ctx);
return NULL;
}
#if defined(SSL_OP_NO_RENEGOTIATION)
/* disable client renegotiation */
if((SSL_CTX_set_options(ctx, SSL_OP_NO_RENEGOTIATION) &
SSL_OP_NO_RENEGOTIATION) != SSL_OP_NO_RENEGOTIATION) {
log_crypto_err("could not set SSL_OP_NO_RENEGOTIATION");
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)) {
log_err("error in client certificate %s", pem);
log_crypto_err("error in certificate file");
SSL_CTX_free(ctx);
return NULL;
}
if(!SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM)) {
log_err("error in client private key %s", key);
log_crypto_err("error in key file");
SSL_CTX_free(ctx);
return NULL;
}
if(!SSL_CTX_check_private_key(ctx)) {
log_err("error in client key %s", key);
log_crypto_err("error in SSL_CTX_check_private_key");
SSL_CTX_free(ctx);
return NULL;
}
}
if((verifypem && verifypem[0]) || wincert) {
if(verifypem && verifypem[0]) {
if(!SSL_CTX_load_verify_locations(ctx, verifypem, NULL)) {
log_crypto_err("error in SSL_CTX verify");
SSL_CTX_free(ctx);
return NULL;
}
}
#ifdef USE_WINSOCK
if(wincert) {
if(!add_WIN_cacerts_to_openssl_store(ctx)) {
log_crypto_err("error in add_WIN_cacerts_to_openssl_store");
SSL_CTX_free(ctx);
return NULL;
}
}
#else
if(wincert) {
if(!SSL_CTX_set_default_verify_paths(ctx)) {
log_crypto_err("error in default_verify_paths");
SSL_CTX_free(ctx);
return NULL;
}
}
#endif
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
}
return ctx;
#else
(void)key; (void)pem; (void)verifypem; (void)wincert;
return NULL;
#endif
}
void* incoming_ssl_fd(void* sslctx, int fd)
{
#ifdef HAVE_SSL
SSL* ssl = SSL_new((SSL_CTX*)sslctx);
if(!ssl) {
log_crypto_err("could not SSL_new");
return NULL;
}
SSL_set_accept_state(ssl);
(void)SSL_set_mode(ssl, (long)SSL_MODE_AUTO_RETRY);
if(!SSL_set_fd(ssl, fd)) {
log_crypto_err("could not SSL_set_fd");
SSL_free(ssl);
return NULL;
}
return ssl;
#else
(void)sslctx; (void)fd;
return NULL;
#endif
}
void* outgoing_ssl_fd(void* sslctx, int fd)
{
#ifdef HAVE_SSL
SSL* ssl = SSL_new((SSL_CTX*)sslctx);
if(!ssl) {
log_crypto_err("could not SSL_new");
return NULL;
}
SSL_set_connect_state(ssl);
(void)SSL_set_mode(ssl, (long)SSL_MODE_AUTO_RETRY);
if(!SSL_set_fd(ssl, fd)) {
log_crypto_err("could not SSL_set_fd");
SSL_free(ssl);
return NULL;
}
return ssl;
#else
(void)sslctx; (void)fd;
return NULL;
#endif
}
int check_auth_name_for_ssl(char* auth_name)
{
if(!auth_name) return 1;
#if defined(HAVE_SSL) && !defined(HAVE_SSL_SET1_HOST) && !defined(HAVE_X509_VERIFY_PARAM_SET1_HOST)
log_err("the query has an auth_name %s, but libssl has no call to "
"perform TLS authentication. Remove that name from config "
"or upgrade the ssl crypto library.", auth_name);
return 0;
#else
return 1;
#endif
}
/** set the authname on an SSL structure, SSL* ssl */
int set_auth_name_on_ssl(void* ssl, char* auth_name, int use_sni)
{
if(!auth_name) return 1;
#ifdef HAVE_SSL
if(use_sni) {
(void)SSL_set_tlsext_host_name(ssl, auth_name);
}
#else
(void)ssl;
(void)use_sni;
#endif
#ifdef HAVE_SSL_SET1_HOST
SSL_set_verify(ssl, SSL_VERIFY_PEER, NULL);
/* setting the hostname makes openssl verify the
* host name in the x509 certificate in the
* SSL connection*/
if(!SSL_set1_host(ssl, auth_name)) {
log_err("SSL_set1_host failed");
return 0;
}
#elif defined(HAVE_X509_VERIFY_PARAM_SET1_HOST)
/* openssl 1.0.2 has this function that can be used for
* set1_host like verification */
if(auth_name) {
X509_VERIFY_PARAM* param = SSL_get0_param(ssl);
# ifdef X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS
X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
# endif
if(!X509_VERIFY_PARAM_set1_host(param, auth_name, strlen(auth_name))) {
log_err("X509_VERIFY_PARAM_set1_host failed");
return 0;
}
SSL_set_verify(ssl, SSL_VERIFY_PEER, NULL);
}
#else
verbose(VERB_ALGO, "the query has an auth_name, but libssl has no call to perform TLS authentication");
#endif /* HAVE_SSL_SET1_HOST */
return 1;
}
#if defined(HAVE_SSL) && defined(OPENSSL_THREADS) && !defined(THREADS_DISABLED) && defined(CRYPTO_LOCK) && OPENSSL_VERSION_NUMBER < 0x10100000L
/** global lock list for openssl locks */
static lock_basic_type *ub_openssl_locks = NULL;
/** callback that gets thread id for openssl */
#ifdef HAVE_CRYPTO_THREADID_SET_CALLBACK
static void
ub_crypto_id_cb(CRYPTO_THREADID *id)
{
CRYPTO_THREADID_set_numeric(id, (unsigned long)log_thread_get());
}
#else
static unsigned long
ub_crypto_id_cb(void)
{
return (unsigned long)log_thread_get();
}
#endif
static void
ub_crypto_lock_cb(int mode, int type, const char *ATTR_UNUSED(file),
int ATTR_UNUSED(line))
{
if((mode&CRYPTO_LOCK)) {
lock_basic_lock(&ub_openssl_locks[type]);
} else {
lock_basic_unlock(&ub_openssl_locks[type]);
}
}
#endif /* OPENSSL_THREADS */
int ub_openssl_lock_init(void)
{
#if defined(HAVE_SSL) && defined(OPENSSL_THREADS) && !defined(THREADS_DISABLED) && defined(CRYPTO_LOCK) && OPENSSL_VERSION_NUMBER < 0x10100000L
int i;
ub_openssl_locks = (lock_basic_type*)reallocarray(
NULL, (size_t)CRYPTO_num_locks(), sizeof(lock_basic_type));
if(!ub_openssl_locks)
return 0;
for(i=0; i<CRYPTO_num_locks(); i++) {
lock_basic_init(&ub_openssl_locks[i]);
}
# ifdef HAVE_CRYPTO_THREADID_SET_CALLBACK
CRYPTO_THREADID_set_callback(&ub_crypto_id_cb);
# else
CRYPTO_set_id_callback(&ub_crypto_id_cb);
# endif
CRYPTO_set_locking_callback(&ub_crypto_lock_cb);
#endif /* OPENSSL_THREADS */
return 1;
}
void ub_openssl_lock_delete(void)
{
#if defined(HAVE_SSL) && defined(OPENSSL_THREADS) && !defined(THREADS_DISABLED) && defined(CRYPTO_LOCK) && OPENSSL_VERSION_NUMBER < 0x10100000L
int i;
if(!ub_openssl_locks)
return;
# ifdef HAVE_CRYPTO_THREADID_SET_CALLBACK
CRYPTO_THREADID_set_callback(NULL);
# else
CRYPTO_set_id_callback(NULL);
# endif
CRYPTO_set_locking_callback(NULL);
for(i=0; i<CRYPTO_num_locks(); i++) {
lock_basic_destroy(&ub_openssl_locks[i]);
}
free(ub_openssl_locks);
#endif /* OPENSSL_THREADS */
}
int listen_sslctx_setup_ticket_keys(void* sslctx, struct config_strlist* tls_session_ticket_keys) {
#ifdef HAVE_SSL
size_t s = 1;
struct config_strlist* p;
struct tls_session_ticket_key *keys;
for(p = tls_session_ticket_keys; p; p = p->next) {
s++;
}
keys = calloc(s, sizeof(struct tls_session_ticket_key));
if(!keys)
return 0;
memset(keys, 0, s*sizeof(*keys));
ticket_keys = keys;
for(p = tls_session_ticket_keys; p; p = p->next) {
size_t n;
unsigned char *data;
FILE *f;
data = (unsigned char *)malloc(80);
if(!data)
return 0;
f = fopen(p->str, "rb");
if(!f) {
log_err("could not read tls-session-ticket-key %s: %s", p->str, strerror(errno));
free(data);
return 0;
}
n = fread(data, 1, 80, f);
fclose(f);
if(n != 80) {
log_err("tls-session-ticket-key %s is %d bytes, must be 80 bytes", p->str, (int)n);
free(data);
return 0;
}
verbose(VERB_OPS, "read tls-session-ticket-key: %s", p->str);
keys->key_name = data;
keys->aes_key = data + 16;
keys->hmac_key = data + 48;
keys++;
}
/* terminate array with NULL key name entry */
keys->key_name = NULL;
# ifdef HAVE_SSL_CTX_SET_TLSEXT_TICKET_KEY_EVP_CB
if(SSL_CTX_set_tlsext_ticket_key_evp_cb(sslctx, tls_session_ticket_key_cb) == 0) {
log_err("no support for TLS session ticket");
return 0;
}
# else
if(SSL_CTX_set_tlsext_ticket_key_cb(sslctx, tls_session_ticket_key_cb) == 0) {
log_err("no support for TLS session ticket");
return 0;
}
# endif
return 1;
#else
(void)sslctx;
(void)tls_session_ticket_keys;
return 0;
#endif
}
#ifdef HAVE_SSL
int tls_session_ticket_key_cb(SSL *ATTR_UNUSED(sslctx), unsigned char* key_name,
unsigned char* iv, EVP_CIPHER_CTX *evp_sctx,
#ifdef HAVE_SSL_CTX_SET_TLSEXT_TICKET_KEY_EVP_CB
EVP_MAC_CTX *hmac_ctx,
#else
HMAC_CTX* hmac_ctx,
#endif
int enc)
{
#ifdef HAVE_SSL
# ifdef HAVE_SSL_CTX_SET_TLSEXT_TICKET_KEY_EVP_CB
OSSL_PARAM params[3];
# else
const EVP_MD *digest;
# endif
const EVP_CIPHER *cipher;
int evp_cipher_length;
# ifndef HAVE_SSL_CTX_SET_TLSEXT_TICKET_KEY_EVP_CB
digest = EVP_sha256();
# endif
cipher = EVP_aes_256_cbc();
evp_cipher_length = EVP_CIPHER_iv_length(cipher);
if( enc == 1 ) {
/* encrypt */
verbose(VERB_CLIENT, "start session encrypt");
memcpy(key_name, ticket_keys->key_name, 16);
if (RAND_bytes(iv, evp_cipher_length) != 1) {
verbose(VERB_CLIENT, "RAND_bytes failed");
return -1;
}
if (EVP_EncryptInit_ex(evp_sctx, cipher, NULL, ticket_keys->aes_key, iv) != 1) {
verbose(VERB_CLIENT, "EVP_EncryptInit_ex failed");
return -1;
}
#ifdef HAVE_SSL_CTX_SET_TLSEXT_TICKET_KEY_EVP_CB
params[0] = OSSL_PARAM_construct_octet_string(OSSL_MAC_PARAM_KEY,
ticket_keys->hmac_key, 32);
params[1] = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST,
"sha256", 0);
params[2] = OSSL_PARAM_construct_end();
#ifdef HAVE_EVP_MAC_CTX_SET_PARAMS
EVP_MAC_CTX_set_params(hmac_ctx, params);
#else
EVP_MAC_set_ctx_params(hmac_ctx, params);
#endif
#elif !defined(HMAC_INIT_EX_RETURNS_VOID)
if (HMAC_Init_ex(hmac_ctx, ticket_keys->hmac_key, 32, digest, NULL) != 1) {
verbose(VERB_CLIENT, "HMAC_Init_ex failed");
return -1;
}
#else
HMAC_Init_ex(hmac_ctx, ticket_keys->hmac_key, 32, digest, NULL);
#endif
return 1;
} else if (enc == 0) {
/* decrypt */
struct tls_session_ticket_key *key;
verbose(VERB_CLIENT, "start session decrypt");
for(key = ticket_keys; key->key_name != NULL; key++) {
if (!memcmp(key_name, key->key_name, 16)) {
verbose(VERB_CLIENT, "Found session_key");
break;
}
}
if(key->key_name == NULL) {
verbose(VERB_CLIENT, "Not found session_key");
return 0;
}
#ifdef HAVE_SSL_CTX_SET_TLSEXT_TICKET_KEY_EVP_CB
params[0] = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY,
key->hmac_key, 32);
params[1] = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST,
"sha256", 0);
params[2] = OSSL_PARAM_construct_end();
#ifdef HAVE_EVP_MAC_CTX_SET_PARAMS
EVP_MAC_CTX_set_params(hmac_ctx, params);
#else
EVP_MAC_set_ctx_params(hmac_ctx, params);
#endif
#elif !defined(HMAC_INIT_EX_RETURNS_VOID)
if (HMAC_Init_ex(hmac_ctx, key->hmac_key, 32, digest, NULL) != 1) {
verbose(VERB_CLIENT, "HMAC_Init_ex failed");
return -1;
}
#else
HMAC_Init_ex(hmac_ctx, key->hmac_key, 32, digest, NULL);
#endif
if (EVP_DecryptInit_ex(evp_sctx, cipher, NULL, key->aes_key, iv) != 1) {
log_err("EVP_DecryptInit_ex failed");
return -1;
}
return (key == ticket_keys) ? 1 : 2;
}
return -1;
#else
(void)key_name;
(void)iv;
(void)evp_sctx;
(void)hmac_ctx;
(void)enc;
return 0;
#endif
}
#endif /* HAVE_SSL */
void
listen_sslctx_delete_ticket_keys(void)
{
struct tls_session_ticket_key *key;
if(!ticket_keys) return;
for(key = ticket_keys; key->key_name != NULL; key++) {
/* wipe key data from memory*/
#ifdef HAVE_EXPLICIT_BZERO
explicit_bzero(key->key_name, 80);
#else
memset(key->key_name, 0xdd, 80);
#endif
free(key->key_name);
}
free(ticket_keys);
ticket_keys = NULL;
}
# ifndef USE_WINSOCK
char*
sock_strerror(int errn)
{
return strerror(errn);
}
void
sock_close(int socket)
{
close(socket);
}
# else
char*
sock_strerror(int ATTR_UNUSED(errn))
{
return wsa_strerror(WSAGetLastError());
}
void
sock_close(int socket)
{
closesocket(socket);
}
# endif /* USE_WINSOCK */
diff --git a/contrib/unbound/util/net_help.h b/contrib/unbound/util/net_help.h
index a9de910d5461..edaea42353d4 100644
--- a/contrib/unbound/util/net_help.h
+++ b/contrib/unbound/util/net_help.h
@@ -1,549 +1,567 @@
/*
* util/net_help.h - network help functions
*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file contains functions to perform network related tasks.
*/
#ifndef NET_HELP_H
#define NET_HELP_H
#include "util/log.h"
#include "util/random.h"
struct sock_list;
struct regional;
struct config_strlist;
/** DNS constants for uint16_t style flag manipulation. host byteorder.
* 1 1 1 1 1 1
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* |QR| Opcode |AA|TC|RD|RA| Z|AD|CD| RCODE |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
*/
/** CD flag */
#define BIT_CD 0x0010
/** AD flag */
#define BIT_AD 0x0020
/** Z flag */
#define BIT_Z 0x0040
/** RA flag */
#define BIT_RA 0x0080
/** RD flag */
#define BIT_RD 0x0100
/** TC flag */
#define BIT_TC 0x0200
/** AA flag */
#define BIT_AA 0x0400
/** QR flag */
#define BIT_QR 0x8000
/** get RCODE bits from uint16 flags */
#define FLAGS_GET_RCODE(f) ((f) & 0xf)
/** set RCODE bits in uint16 flags */
#define FLAGS_SET_RCODE(f, r) (f = (((f) & 0xfff0) | (r)))
/** timeout in milliseconds for UDP queries to auth servers. */
#define UDP_AUTH_QUERY_TIMEOUT 3000
/** Advertised version of EDNS capabilities */
#define EDNS_ADVERTISED_VERSION 0
/** Advertised size of EDNS capabilities */
extern uint16_t EDNS_ADVERTISED_SIZE;
/** bits for EDNS bitfield */
#define EDNS_DO 0x8000 /* Dnssec Ok */
/** byte size of ip4 address */
#define INET_SIZE 4
/** byte size of ip6 address */
#define INET6_SIZE 16
/** DNSKEY zone sign key flag */
#define DNSKEY_BIT_ZSK 0x0100
/** DNSKEY secure entry point, KSK flag */
#define DNSKEY_BIT_SEP 0x0001
/** return a random 16-bit number given a random source */
#define GET_RANDOM_ID(rnd) (((unsigned)ub_random(rnd)>>8) & 0xffff)
/** define MSG_DONTWAIT for unsupported platforms */
#ifndef MSG_DONTWAIT
#define MSG_DONTWAIT 0
#endif
/** minimal responses when positive answer */
extern int MINIMAL_RESPONSES;
/** rrset order roundrobin */
extern int RRSET_ROUNDROBIN;
/** log tag queries with name instead of 'info' for filtering */
extern int LOG_TAG_QUERYREPLY;
/**
* See if string is ip4 or ip6.
* @param str: IP specification.
* @return: true if string addr is an ip6 specced address.
*/
int str_is_ip6(const char* str);
/**
* Set fd nonblocking.
* @param s: file descriptor.
* @return: 0 on error (error is printed to log).
*/
int fd_set_nonblock(int s);
/**
* Set fd (back to) blocking.
* @param s: file descriptor.
* @return: 0 on error (error is printed to log).
*/
int fd_set_block(int s);
/**
* See if number is a power of 2.
* @param num: the value.
* @return: true if the number is a power of 2.
*/
int is_pow2(size_t num);
/**
* Allocate memory and copy over contents.
* @param data: what to copy over.
* @param len: length of data.
* @return: NULL on malloc failure, or newly malloced data.
*/
void* memdup(void* data, size_t len);
/**
* Prints the sockaddr in readable format with log_info. Debug helper.
* @param v: at what verbosity level to print this.
* @param str: descriptive string printed with it.
* @param addr: the sockaddr to print. Can be ip4 or ip6.
* @param addrlen: length of addr.
*/
void log_addr(enum verbosity_value v, const char* str,
struct sockaddr_storage* addr, socklen_t addrlen);
/**
* Prints zone name and sockaddr in readable format with log_info. Debug.
* @param v: at what verbosity level to print this.
* @param str: descriptive string printed with it.
* @param zone: DNS domain name, uncompressed wireformat.
* @param addr: the sockaddr to print. Can be ip4 or ip6.
* @param addrlen: length of addr.
*/
void log_name_addr(enum verbosity_value v, const char* str, uint8_t* zone,
struct sockaddr_storage* addr, socklen_t addrlen);
/**
* Log errno and addr.
* @param str: descriptive string printed with it.
* @param err: errno string to print, i.e. strerror(errno).
* @param addr: the sockaddr to print. Can be ip4 or ip6.
* @param addrlen: length of addr.
*/
void log_err_addr(const char* str, const char* err,
struct sockaddr_storage* addr, socklen_t addrlen);
/**
* Convert address string, with "@port" appendix, to sockaddr.
* Uses DNS port by default.
* @param str: the string
* @param addr: where to store sockaddr.
* @param addrlen: length of stored sockaddr is returned.
* @param port: default port.
* @return 0 on error.
*/
int extstrtoaddr(const char* str, struct sockaddr_storage* addr,
socklen_t* addrlen, int port);
/**
* Convert ip address string and port to sockaddr.
* @param ip: ip4 or ip6 address string.
* @param port: port number, host format.
* @param addr: where to store sockaddr.
* @param addrlen: length of stored sockaddr is returned.
* @return 0 on error.
*/
int ipstrtoaddr(const char* ip, int port, struct sockaddr_storage* addr,
socklen_t* addrlen);
/**
* Convert ip netblock (ip/netsize) string and port to sockaddr.
* performs a copy internally to avoid writing over 'ip' string.
* @param ip: ip4 or ip6 address string.
* @param port: port number, host format.
* @param addr: where to store sockaddr.
* @param addrlen: length of stored sockaddr is returned.
* @param net: netblock size is returned.
* @return 0 on error.
*/
int netblockstrtoaddr(const char* ip, int port, struct sockaddr_storage* addr,
socklen_t* addrlen, int* net);
/**
* Convert address string, with "@port" appendix, to sockaddr.
* It can also have an "#tls-auth-name" appendix (after the port).
* The returned auth_name string is a pointer into the input string.
* Uses DNS port by default; TLS port when a "#tls-auth-name" is configured.
* @param str: the string
* @param addr: where to store sockaddr.
* @param addrlen: length of stored sockaddr is returned.
* @param auth_name: returned pointer to tls_auth_name, or NULL if none.
* @return 0 on error.
*/
int authextstrtoaddr(char* str, struct sockaddr_storage* addr,
socklen_t* addrlen, char** auth_name);
/**
* Convert domain string, with "@port" appendix, to dname.
* It can also have an "#tls-auth-name" appendix (after the port).
* The return port is the parsed port.
* Uses DNS port by default; TLS port when a "#tls-auth-name" is configured.
* The returned auth_name string is a pointer into the input string.
* @param str: the string
* @param port: pointer to be assigned the parsed port value.
* @param auth_name: returned pointer to tls_auth_name, or NULL if none.
* @return pointer to the dname.
*/
uint8_t* authextstrtodname(char* str, int* port, char** auth_name);
/**
* Store port number into sockaddr structure
* @param addr: sockaddr structure, ip4 or ip6.
* @param addrlen: length of addr.
* @param port: port number to put into the addr.
*/
void sockaddr_store_port(struct sockaddr_storage* addr, socklen_t addrlen,
int port);
/**
* Print string with neat domain name, type and class.
* @param v: at what verbosity level to print this.
* @param str: string of message.
* @param name: domain name uncompressed wireformat.
* @param type: host format RR type.
* @param dclass: host format RR class.
*/
void log_nametypeclass(enum verbosity_value v, const char* str,
uint8_t* name, uint16_t type, uint16_t dclass);
/**
* Like log_nametypeclass, but logs with log_query for query logging
*/
void log_query_in(const char* str, uint8_t* name, uint16_t type,
uint16_t dclass);
/**
* Compare two sockaddrs. Imposes an ordering on the addresses.
* Compares address and port.
* @param addr1: address 1.
* @param len1: lengths of addr1.
* @param addr2: address 2.
* @param len2: lengths of addr2.
* @return: 0 if addr1 == addr2. -1 if addr1 is smaller, +1 if larger.
*/
int sockaddr_cmp(struct sockaddr_storage* addr1, socklen_t len1,
struct sockaddr_storage* addr2, socklen_t len2);
/**
* Compare two sockaddrs. Compares address, not the port.
* @param addr1: address 1.
* @param len1: lengths of addr1.
* @param addr2: address 2.
* @param len2: lengths of addr2.
* @return: 0 if addr1 == addr2. -1 if addr1 is smaller, +1 if larger.
*/
int sockaddr_cmp_addr(struct sockaddr_storage* addr1, socklen_t len1,
struct sockaddr_storage* addr2, socklen_t len2);
/**
* Checkout address family.
* @param addr: the sockaddr to examine.
* @param len: the length of addr.
* @return: true if sockaddr is ip6.
*/
int addr_is_ip6(struct sockaddr_storage* addr, socklen_t len);
/**
* Make sure the sockaddr ends in zeroes. For tree insertion and subsequent
* comparison.
* @param addr: the ip4 or ip6 addr.
* @param len: length of addr.
* @param net: number of bits to leave untouched, the rest of the netblock
* address is zeroed.
*/
void addr_mask(struct sockaddr_storage* addr, socklen_t len, int net);
/**
* See how many bits are shared, equal, between two addrs.
* @param addr1: first addr.
* @param net1: netblock size of first addr.
* @param addr2: second addr.
* @param net2: netblock size of second addr.
* @param addrlen: length of first addr and of second addr.
* They must be of the same length (i.e. same type IP4, IP6).
* @return: number of bits the same.
*/
int addr_in_common(struct sockaddr_storage* addr1, int net1,
struct sockaddr_storage* addr2, int net2, socklen_t addrlen);
/**
* Put address into string, works for IPv4 and IPv6.
* @param addr: address
* @param addrlen: length of address
* @param buf: result string stored here
* @param len: length of buf.
* On failure a string with "error" is stored inside.
*/
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
* @param addrlen: length of address
* @return true if so
*/
int addr_is_ip4mapped(struct sockaddr_storage* addr, socklen_t addrlen);
/**
* See if sockaddr is 255.255.255.255.
* @param addr: address
* @param addrlen: length of address
* @return true if so
*/
int addr_is_broadcast(struct sockaddr_storage* addr, socklen_t addrlen);
/**
* See if sockaddr is 0.0.0.0 or ::0.
* @param addr: address
* @param addrlen: length of address
* @return true if so
*/
int addr_is_any(struct sockaddr_storage* addr, socklen_t addrlen);
/**
* Insert new socket list item. If fails logs error.
* @param list: pointer to pointer to first item.
* @param addr: address or NULL if 'cache'.
* @param len: length of addr, or 0 if 'cache'.
* @param region: where to allocate
*/
void sock_list_insert(struct sock_list** list, struct sockaddr_storage* addr,
socklen_t len, struct regional* region);
/**
* Append one list to another. Must both be from same qstate(regional).
* @param list: pointer to result list that is modified.
* @param add: item(s) to add. They are prepended to list.
*/
void sock_list_prepend(struct sock_list** list, struct sock_list* add);
/**
* Find addr in list.
* @param list: to search in
* @param addr: address to look for.
* @param len: length. Can be 0, look for 'cache entry'.
* @return true if found.
*/
int sock_list_find(struct sock_list* list, struct sockaddr_storage* addr,
socklen_t len);
/**
* Merge socklist into another socket list. Allocates the new entries
* freshly and copies them over, so also performs a region switchover.
* Allocation failures are logged.
* @param list: the destination list (checked for duplicates)
* @param region: where to allocate
* @param add: the list of entries to add.
*/
void sock_list_merge(struct sock_list** list, struct regional* region,
struct sock_list* add);
/**
* Log libcrypto error with descriptive string. Calls log_err().
* @param str: what failed.
*/
void log_crypto_err(const char* str);
/**
* Log libcrypto error from errcode with descriptive string, calls log_err.
* @param str: what failed.
* @param err: error code from ERR_get_error.
*/
void log_crypto_err_code(const char* str, unsigned long err);
+/**
+ * Log an error from libcrypto that came from SSL_write and so on, with
+ * a value from SSL_get_error, calls log_err. If that fails it logs with
+ * log_crypto_err.
+ * @param str: what failed
+ * @param r: output of SSL_get_error on the I/O operation result.
+ */
+void log_crypto_err_io(const char* str, int r);
+
+/**
+ * Log an error from libcrypt that came from an I/O routine with the
+ * errcode from ERR_get_error. Calls log_err() and log_crypto_err_code.
+ * @param str: what failed
+ * @param r: output of SSL_get_error on the I/O operation result.
+ * @param err: error code from ERR_get_error
+ */
+void log_crypto_err_io_code(const char* str, int r, unsigned long err);
+
/**
* Log certificate details verbosity, string, of X509 cert
* @param level: verbosity level
* @param str: string to prefix on output
* @param cert: X509* structure.
*/
void log_cert(unsigned level, const char* str, void* cert);
/**
* Set SSL_OP_NOxxx options on SSL context to disable bad crypto
* @param ctxt: SSL_CTX*
* @return false on failure.
*/
int listen_sslctx_setup(void* ctxt);
/**
* Further setup of listening SSL context, after keys loaded.
* @param ctxt: SSL_CTX*
*/
void listen_sslctx_setup_2(void* ctxt);
/**
* create SSL listen context
* @param key: private key file.
* @param pem: public key cert.
* @param verifypem: if nonNULL, verifylocation file.
* return SSL_CTX* or NULL on failure (logged).
*/
void* listen_sslctx_create(char* key, char* pem, char* verifypem);
/**
* create SSL connect context
* @param key: if nonNULL (also pem nonNULL), the client private key.
* @param pem: client public key (or NULL if key is NULL).
* @param verifypem: if nonNULL used for verifylocation file.
* @param wincert: add system certificate store to ctx (add to verifypem ca
* certs).
* @return SSL_CTX* or NULL on failure (logged).
*/
void* connect_sslctx_create(char* key, char* pem, char* verifypem, int wincert);
/**
* accept a new fd and wrap it in a BIO in SSL
* @param sslctx: the SSL_CTX to use (from listen_sslctx_create()).
* @param fd: from accept, nonblocking.
* @return SSL or NULL on alloc failure.
*/
void* incoming_ssl_fd(void* sslctx, int fd);
/**
* connect a new fd and wrap it in a BIO in SSL
* @param sslctx: the SSL_CTX to use (from connect_sslctx_create())
* @param fd: from connect.
* @return SSL or NULL on alloc failure
*/
void* outgoing_ssl_fd(void* sslctx, int fd);
/**
* check if authname SSL functionality is available, false if not
* @param auth_name: the name for the remote server, used for error print.
* @return false if SSL functionality to check the SSL name is not available.
*/
int check_auth_name_for_ssl(char* auth_name);
/**
* set auth name on SSL for verification
* @param ssl: SSL* to set
* @param auth_name: if NULL nothing happens, otherwise the name to check.
* @param use_sni: if SNI will be used.
* @return 1 on success or NULL auth_name, 0 on failure.
*/
int set_auth_name_on_ssl(void* ssl, char* auth_name, int use_sni);
/**
* Initialize openssl locking for thread safety
* @return false on failure (alloc failure).
*/
int ub_openssl_lock_init(void);
/**
* De-init the allocated openssl locks
*/
void ub_openssl_lock_delete(void);
/**
* setup TLS session ticket
* @param sslctx: the SSL_CTX to use (from connect_sslctx_create())
* @param tls_session_ticket_keys: TLS ticket secret filenames
* @return false on failure (alloc failure).
*/
int listen_sslctx_setup_ticket_keys(void* sslctx,
struct config_strlist* tls_session_ticket_keys);
/** Free memory used for TLS session ticket keys */
void listen_sslctx_delete_ticket_keys(void);
/**
* RPZ format netblock to network byte order address and netblock
* example RPZ netblock format dnames:
* - 24.10.100.51.198.rpz-ip -> 198.51.100.10/24
* - 32.10.zz.db8.2001.rpz-ip -> 2001:db8:0:0:0:0:0:10/32
* @param dname: the dname containing RPZ format netblock
* @param dnamelen: length of dname
* @param addr: where to store sockaddr.
* @param addrlen: length of stored sockaddr is returned.
* @param net: where to store netmask
* @param af: where to store address family.
* @return 0 on error.
*/
int netblockdnametoaddr(uint8_t* dname, size_t dnamelen,
struct sockaddr_storage* addr, socklen_t* addrlen, int* net, int* af);
/** Return strerror or wsastrerror for socket error printout */
char* sock_strerror(int errn);
/** close the socket with close, or wsa closesocket */
void sock_close(int socket);
#endif /* NET_HELP_H */
diff --git a/contrib/unbound/util/netevent.c b/contrib/unbound/util/netevent.c
index b9395a8998b9..141e48d48b3f 100644
--- a/contrib/unbound/util/netevent.c
+++ b/contrib/unbound/util/netevent.c
@@ -1,5079 +1,5103 @@
/*
* util/netevent.c - event notification
*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file contains event notification functions.
*/
#include "config.h"
#include "util/netevent.h"
#include "util/ub_event.h"
#include "util/log.h"
#include "util/net_help.h"
#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"
#include "dnstap/dnstap.h"
#include "dnscrypt/dnscrypt.h"
#include "services/listen_dnsport.h"
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#ifdef HAVE_POLL_H
#include <poll.h>
#endif
#ifdef HAVE_OPENSSL_SSL_H
#include <openssl/ssl.h>
#endif
#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
# ifdef __CMSG_ALIGN
# define CMSG_ALIGN(n) __CMSG_ALIGN(n)
# elif defined(CMSG_DATA_ALIGN)
# define CMSG_ALIGN _CMSG_DATA_ALIGN
# else
# define CMSG_ALIGN(len) (((len)+sizeof(long)-1) & ~(sizeof(long)-1))
# endif
#endif
/** if CMSG_LEN is not defined on this platform, a workaround */
#ifndef CMSG_LEN
# define CMSG_LEN(len) (CMSG_ALIGN(sizeof(struct cmsghdr))+(len))
#endif
/** if CMSG_SPACE is not defined on this platform, a workaround */
#ifndef CMSG_SPACE
# ifdef _CMSG_HDR_ALIGN
# define CMSG_SPACE(l) (CMSG_ALIGN(l)+_CMSG_HDR_ALIGN(sizeof(struct cmsghdr)))
# else
# define CMSG_SPACE(l) (CMSG_ALIGN(l)+CMSG_ALIGN(sizeof(struct cmsghdr)))
# endif
#endif
/** The TCP writing query timeout in milliseconds */
#define TCP_QUERY_TIMEOUT 120000
/** The minimum actual TCP timeout to use, regardless of what we advertise,
* in msec */
#define TCP_QUERY_TIMEOUT_MINIMUM 200
#ifndef NONBLOCKING_IS_BROKEN
/** number of UDP reads to perform per read indication from select */
#define NUM_UDP_PER_SELECT 100
#else
#define NUM_UDP_PER_SELECT 1
#endif
/** timeout in millisec to wait for write to unblock, packets dropped after.*/
#define SEND_BLOCKED_WAIT_TIMEOUT 200
/** max number of times to wait for write to unblock, packets dropped after.*/
#define SEND_BLOCKED_MAX_RETRY 5
/** 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.
*/
struct internal_event {
/** the comm base */
struct comm_base* base;
/** ub_event event type */
struct ub_event* ev;
};
/**
* Internal base structure, so that every thread has its own events.
*/
struct internal_base {
/** ub_event event_base type. */
struct ub_event_base* base;
/** seconds time pointer points here */
time_t secs;
/** timeval with current time */
struct timeval now;
/** the event used for slow_accept timeouts */
struct ub_event* slow_accept;
/** true if slow_accept is enabled */
int slow_accept_enabled;
/** last log time for slow logging of file descriptor errors */
time_t last_slow_log;
/** last log time for slow logging of write wait failures */
time_t last_writewait_log;
};
/**
* Internal timer structure, to store timer event in.
*/
struct internal_timer {
/** the super struct from which derived */
struct comm_timer super;
/** the comm base */
struct comm_base* base;
/** ub_event event type */
struct ub_event* ev;
/** is timer enabled */
uint8_t enabled;
};
/**
* Internal signal structure, to store signal event in.
*/
struct internal_signal {
/** ub_event event type */
struct ub_event* ev;
/** next in signal list */
struct internal_signal* next;
};
/** create a tcp handler with a parent */
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);
/* -------- End of local definitions -------- */
struct comm_base*
comm_base_create(int sigs)
{
struct comm_base* b = (struct comm_base*)calloc(1,
sizeof(struct comm_base));
const char *evnm="event", *evsys="", *evmethod="";
if(!b)
return NULL;
b->eb = (struct internal_base*)calloc(1, sizeof(struct internal_base));
if(!b->eb) {
free(b);
return NULL;
}
b->eb->base = ub_default_event_base(sigs, &b->eb->secs, &b->eb->now);
if(!b->eb->base) {
free(b->eb);
free(b);
return NULL;
}
ub_comm_base_now(b);
ub_get_event_sys(b->eb->base, &evnm, &evsys, &evmethod);
verbose(VERB_ALGO, "%s %s uses %s method.", evnm, evsys, evmethod);
return b;
}
struct comm_base*
comm_base_create_event(struct ub_event_base* base)
{
struct comm_base* b = (struct comm_base*)calloc(1,
sizeof(struct comm_base));
if(!b)
return NULL;
b->eb = (struct internal_base*)calloc(1, sizeof(struct internal_base));
if(!b->eb) {
free(b);
return NULL;
}
b->eb->base = base;
ub_comm_base_now(b);
return b;
}
void
comm_base_delete(struct comm_base* b)
{
if(!b)
return;
if(b->eb->slow_accept_enabled) {
if(ub_event_del(b->eb->slow_accept) != 0) {
log_err("could not event_del slow_accept");
}
ub_event_free(b->eb->slow_accept);
}
ub_event_base_free(b->eb->base);
b->eb->base = NULL;
free(b->eb);
free(b);
}
void
comm_base_delete_no_base(struct comm_base* b)
{
if(!b)
return;
if(b->eb->slow_accept_enabled) {
if(ub_event_del(b->eb->slow_accept) != 0) {
log_err("could not event_del slow_accept");
}
ub_event_free(b->eb->slow_accept);
}
b->eb->base = NULL;
free(b->eb);
free(b);
}
void
comm_base_timept(struct comm_base* b, time_t** tt, struct timeval** tv)
{
*tt = &b->eb->secs;
*tv = &b->eb->now;
}
void
comm_base_dispatch(struct comm_base* b)
{
int retval;
retval = ub_event_base_dispatch(b->eb->base);
if(retval < 0) {
fatal_exit("event_dispatch returned error %d, "
"errno is %s", retval, strerror(errno));
}
}
void comm_base_exit(struct comm_base* b)
{
if(ub_event_base_loopexit(b->eb->base) != 0) {
log_err("Could not loopexit");
}
}
void comm_base_set_slow_accept_handlers(struct comm_base* b,
void (*stop_acc)(void*), void (*start_acc)(void*), void* arg)
{
b->stop_accept = stop_acc;
b->start_accept = start_acc;
b->cb_arg = arg;
}
struct ub_event_base* comm_base_internal(struct comm_base* b)
{
return b->eb->base;
}
/** see if errno for udp has to be logged or not uses globals */
static int
udp_send_errno_needs_log(struct sockaddr* addr, socklen_t addrlen)
{
/* do not log transient errors (unless high verbosity) */
#if defined(ENETUNREACH) || defined(EHOSTDOWN) || defined(EHOSTUNREACH) || defined(ENETDOWN)
switch(errno) {
# ifdef ENETUNREACH
case ENETUNREACH:
# endif
# ifdef EHOSTDOWN
case EHOSTDOWN:
# endif
# ifdef EHOSTUNREACH
case EHOSTUNREACH:
# endif
# ifdef ENETDOWN
case ENETDOWN:
# endif
case EPERM:
case EACCES:
if(verbosity < VERB_ALGO)
return 0;
default:
break;
}
#endif
/* permission denied is gotten for every send if the
* network is disconnected (on some OS), squelch it */
if( ((errno == EPERM)
# ifdef EADDRNOTAVAIL
/* 'Cannot assign requested address' also when disconnected */
|| (errno == EADDRNOTAVAIL)
# endif
) && verbosity < VERB_ALGO)
return 0;
# ifdef EADDRINUSE
/* If SO_REUSEADDR is set, we could try to connect to the same server
* from the same source port twice. */
if(errno == EADDRINUSE && verbosity < VERB_DETAIL)
return 0;
# endif
/* squelch errors where people deploy AAAA ::ffff:bla for
* authority servers, which we try for intranets. */
if(errno == EINVAL && addr_is_ip4mapped(
(struct sockaddr_storage*)addr, addrlen) &&
verbosity < VERB_DETAIL)
return 0;
/* SO_BROADCAST sockopt can give access to 255.255.255.255,
* but a dns cache does not need it. */
if(errno == EACCES && addr_is_broadcast(
(struct sockaddr_storage*)addr, addrlen) &&
verbosity < VERB_DETAIL)
return 0;
return 1;
}
int tcp_connect_errno_needs_log(struct sockaddr* addr, socklen_t addrlen)
{
return udp_send_errno_needs_log(addr, addrlen);
}
/* send a UDP reply */
int
comm_point_send_udp_msg(struct comm_point *c, sldns_buffer* packet,
struct sockaddr* addr, socklen_t addrlen, int is_connected)
{
ssize_t sent;
log_assert(c->fd != -1);
#ifdef UNBOUND_DEBUG
if(sldns_buffer_remaining(packet) == 0)
log_err("error: send empty UDP packet");
#endif
log_assert(addr && addrlen > 0);
if(!is_connected) {
sent = sendto(c->fd, (void*)sldns_buffer_begin(packet),
sldns_buffer_remaining(packet), 0,
addr, addrlen);
} else {
sent = send(c->fd, (void*)sldns_buffer_begin(packet),
sldns_buffer_remaining(packet), 0);
}
if(sent == -1) {
/* try again and block, waiting for IO to complete,
* we want to send the answer, and we will wait for
* the ethernet interface buffer to have space. */
#ifndef USE_WINSOCK
if(errno == EAGAIN || errno == EINTR ||
# ifdef EWOULDBLOCK
errno == EWOULDBLOCK ||
# endif
errno == ENOBUFS) {
#else
if(WSAGetLastError() == WSAEINPROGRESS ||
WSAGetLastError() == WSAEINTR ||
WSAGetLastError() == WSAENOBUFS ||
WSAGetLastError() == WSAEWOULDBLOCK) {
#endif
int retries = 0;
/* if we set the fd blocking, other threads suddenly
* have a blocking fd that they operate on */
while(sent == -1 && retries < SEND_BLOCKED_MAX_RETRY && (
#ifndef USE_WINSOCK
errno == EAGAIN || errno == EINTR ||
# ifdef EWOULDBLOCK
errno == EWOULDBLOCK ||
# endif
errno == ENOBUFS
#else
WSAGetLastError() == WSAEINPROGRESS ||
WSAGetLastError() == WSAEINTR ||
WSAGetLastError() == WSAENOBUFS ||
WSAGetLastError() == WSAEWOULDBLOCK
#endif
)) {
#if defined(HAVE_POLL) || defined(USE_WINSOCK)
int send_nobufs = (
#ifndef USE_WINSOCK
errno == ENOBUFS
#else
WSAGetLastError() == WSAENOBUFS
#endif
);
struct pollfd p;
int pret;
memset(&p, 0, sizeof(p));
p.fd = c->fd;
p.events = POLLOUT | POLLERR | POLLHUP;
# ifndef USE_WINSOCK
pret = poll(&p, 1, SEND_BLOCKED_WAIT_TIMEOUT);
# else
pret = WSAPoll(&p, 1,
SEND_BLOCKED_WAIT_TIMEOUT);
# endif
if(pret == 0) {
/* timer expired */
struct comm_base* b = c->ev->base;
if(b->eb->last_writewait_log+SLOW_LOG_TIME <=
b->eb->secs) {
b->eb->last_writewait_log = b->eb->secs;
verbose(VERB_OPS, "send udp blocked "
"for long, dropping packet.");
}
return 0;
} else if(pret < 0 &&
#ifndef USE_WINSOCK
errno != EAGAIN && errno != EINTR &&
# ifdef EWOULDBLOCK
errno != EWOULDBLOCK &&
# endif
errno != ENOBUFS
#else
WSAGetLastError() != WSAEINPROGRESS &&
WSAGetLastError() != WSAEINTR &&
WSAGetLastError() != WSAENOBUFS &&
WSAGetLastError() != WSAEWOULDBLOCK
#endif
) {
log_err("poll udp out failed: %s",
sock_strerror(errno));
return 0;
} else if((pret < 0 &&
#ifndef USE_WINSOCK
errno == ENOBUFS
#else
WSAGetLastError() == WSAENOBUFS
#endif
) || (send_nobufs && retries > 0)) {
/* ENOBUFS, and poll returned without
* a timeout. Or the retried send call
* returned ENOBUFS. It is good to
* wait a bit for the error to clear. */
/* The timeout is 20*(2^(retries+1)),
* it increases exponentially, starting
* at 40 msec. After 5 tries, 1240 msec
* have passed in total, when poll
* returned the error, and 1200 msec
* when send returned the errors. */
#ifndef USE_WINSOCK
pret = poll(NULL, 0, (SEND_BLOCKED_WAIT_TIMEOUT/10)<<(retries+1));
#else
pret = WSAPoll(NULL, 0, (SEND_BLOCKED_WAIT_TIMEOUT/10)<<(retries+1));
#endif
if(pret < 0 &&
#ifndef USE_WINSOCK
errno != EAGAIN && errno != EINTR &&
# ifdef EWOULDBLOCK
errno != EWOULDBLOCK &&
# endif
errno != ENOBUFS
#else
WSAGetLastError() != WSAEINPROGRESS &&
WSAGetLastError() != WSAEINTR &&
WSAGetLastError() != WSAENOBUFS &&
WSAGetLastError() != WSAEWOULDBLOCK
#endif
) {
log_err("poll udp out timer failed: %s",
sock_strerror(errno));
}
}
#endif /* defined(HAVE_POLL) || defined(USE_WINSOCK) */
retries++;
if (!is_connected) {
sent = sendto(c->fd, (void*)sldns_buffer_begin(packet),
sldns_buffer_remaining(packet), 0,
addr, addrlen);
} else {
sent = send(c->fd, (void*)sldns_buffer_begin(packet),
sldns_buffer_remaining(packet), 0);
}
}
}
}
if(sent == -1) {
if(!udp_send_errno_needs_log(addr, addrlen))
return 0;
if (!is_connected) {
verbose(VERB_OPS, "sendto failed: %s", sock_strerror(errno));
} else {
verbose(VERB_OPS, "send failed: %s", sock_strerror(errno));
}
if(addr)
log_addr(VERB_OPS, "remote address is",
(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",
(int)sent, (int)sldns_buffer_remaining(packet));
return 0;
}
return 1;
}
#if defined(AF_INET6) && defined(IPV6_PKTINFO) && (defined(HAVE_RECVMSG) || defined(HAVE_SENDMSG))
/** print debug ancillary info */
static void p_ancil(const char* str, struct comm_reply* r)
{
if(r->srctype != 4 && r->srctype != 6) {
log_info("%s: unknown srctype %d", str, r->srctype);
return;
}
if(r->srctype == 6) {
#ifdef IPV6_PKTINFO
char buf[1024];
if(inet_ntop(AF_INET6, &r->pktinfo.v6info.ipi6_addr,
buf, (socklen_t)sizeof(buf)) == 0) {
(void)strlcpy(buf, "(inet_ntop error)", sizeof(buf));
}
buf[sizeof(buf)-1]=0;
log_info("%s: %s %d", str, buf, r->pktinfo.v6info.ipi6_ifindex);
#endif
} else if(r->srctype == 4) {
#ifdef IP_PKTINFO
char buf1[1024], buf2[1024];
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,
buf2, (socklen_t)sizeof(buf2)) == 0) {
(void)strlcpy(buf2, "(inet_ntop error)", sizeof(buf2));
}
buf2[sizeof(buf2)-1]=0;
#else
buf2[0]=0;
#endif
log_info("%s: %d %s %s", str, r->pktinfo.v4info.ipi_ifindex,
buf1, buf2);
#elif defined(IP_RECVDSTADDR)
char buf1[1024];
if(inet_ntop(AF_INET, &r->pktinfo.v4addr,
buf1, (socklen_t)sizeof(buf1)) == 0) {
(void)strlcpy(buf1, "(inet_ntop error)", sizeof(buf1));
}
buf1[sizeof(buf1)-1]=0;
log_info("%s: %s", str, buf1);
#endif /* IP_PKTINFO or PI_RECVDSTDADDR */
}
}
#endif /* AF_INET6 && IPV6_PKTINFO && HAVE_RECVMSG||HAVE_SENDMSG */
/** 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)
{
#if defined(AF_INET6) && defined(IPV6_PKTINFO) && defined(HAVE_SENDMSG)
ssize_t sent;
struct msghdr msg;
struct iovec iov[1];
union {
struct cmsghdr hdr;
char buf[256];
} control;
#ifndef S_SPLINT_S
struct cmsghdr *cmsg;
#endif /* S_SPLINT_S */
log_assert(c->fd != -1);
#ifdef UNBOUND_DEBUG
if(sldns_buffer_remaining(packet) == 0)
log_err("error: send empty UDP packet");
#endif
log_assert(addr && addrlen > 0);
msg.msg_name = addr;
msg.msg_namelen = addrlen;
iov[0].iov_base = sldns_buffer_begin(packet);
iov[0].iov_len = sldns_buffer_remaining(packet);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = control.buf;
#ifndef S_SPLINT_S
msg.msg_controllen = sizeof(control.buf);
#endif /* S_SPLINT_S */
msg.msg_flags = 0;
#ifndef S_SPLINT_S
cmsg = CMSG_FIRSTHDR(&msg);
if(r->srctype == 4) {
#ifdef IP_PKTINFO
void* cmsg_data;
msg.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo));
log_assert(msg.msg_controllen <= sizeof(control.buf));
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_PKTINFO;
memmove(CMSG_DATA(cmsg), &r->pktinfo.v4info,
sizeof(struct in_pktinfo));
/* unset the ifindex to not bypass the routing tables */
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));
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_SENDSRCADDR;
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;
#endif /* IP_PKTINFO or IP_SENDSRCADDR */
} else if(r->srctype == 6) {
void* cmsg_data;
msg.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
log_assert(msg.msg_controllen <= sizeof(control.buf));
cmsg->cmsg_level = IPPROTO_IPV6;
cmsg->cmsg_type = IPV6_PKTINFO;
memmove(CMSG_DATA(cmsg), &r->pktinfo.v6info,
sizeof(struct in6_pktinfo));
/* unset the ifindex to not bypass the routing tables */
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));
log_assert(msg.msg_controllen <= sizeof(control.buf));
cmsg->cmsg_level = IPPROTO_IPV6;
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 && r->srctype != 0)
p_ancil("send_udp over interface", r);
sent = sendmsg(c->fd, &msg, 0);
if(sent == -1) {
/* try again and block, waiting for IO to complete,
* we want to send the answer, and we will wait for
* the ethernet interface buffer to have space. */
#ifndef USE_WINSOCK
if(errno == EAGAIN || errno == EINTR ||
# ifdef EWOULDBLOCK
errno == EWOULDBLOCK ||
# endif
errno == ENOBUFS) {
#else
if(WSAGetLastError() == WSAEINPROGRESS ||
WSAGetLastError() == WSAEINTR ||
WSAGetLastError() == WSAENOBUFS ||
WSAGetLastError() == WSAEWOULDBLOCK) {
#endif
int retries = 0;
while(sent == -1 && retries < SEND_BLOCKED_MAX_RETRY && (
#ifndef USE_WINSOCK
errno == EAGAIN || errno == EINTR ||
# ifdef EWOULDBLOCK
errno == EWOULDBLOCK ||
# endif
errno == ENOBUFS
#else
WSAGetLastError() == WSAEINPROGRESS ||
WSAGetLastError() == WSAEINTR ||
WSAGetLastError() == WSAENOBUFS ||
WSAGetLastError() == WSAEWOULDBLOCK
#endif
)) {
#if defined(HAVE_POLL) || defined(USE_WINSOCK)
int send_nobufs = (
#ifndef USE_WINSOCK
errno == ENOBUFS
#else
WSAGetLastError() == WSAENOBUFS
#endif
);
struct pollfd p;
int pret;
memset(&p, 0, sizeof(p));
p.fd = c->fd;
p.events = POLLOUT | POLLERR | POLLHUP;
# ifndef USE_WINSOCK
pret = poll(&p, 1, SEND_BLOCKED_WAIT_TIMEOUT);
# else
pret = WSAPoll(&p, 1,
SEND_BLOCKED_WAIT_TIMEOUT);
# endif
if(pret == 0) {
/* timer expired */
struct comm_base* b = c->ev->base;
if(b->eb->last_writewait_log+SLOW_LOG_TIME <=
b->eb->secs) {
b->eb->last_writewait_log = b->eb->secs;
verbose(VERB_OPS, "send udp blocked "
"for long, dropping packet.");
}
return 0;
} else if(pret < 0 &&
#ifndef USE_WINSOCK
errno != EAGAIN && errno != EINTR &&
# ifdef EWOULDBLOCK
errno != EWOULDBLOCK &&
# endif
errno != ENOBUFS
#else
WSAGetLastError() != WSAEINPROGRESS &&
WSAGetLastError() != WSAEINTR &&
WSAGetLastError() != WSAENOBUFS &&
WSAGetLastError() != WSAEWOULDBLOCK
#endif
) {
log_err("poll udp out failed: %s",
sock_strerror(errno));
return 0;
} else if((pret < 0 &&
#ifndef USE_WINSOCK
errno == ENOBUFS
#else
WSAGetLastError() == WSAENOBUFS
#endif
) || (send_nobufs && retries > 0)) {
/* ENOBUFS, and poll returned without
* a timeout. Or the retried send call
* returned ENOBUFS. It is good to
* wait a bit for the error to clear. */
/* The timeout is 20*(2^(retries+1)),
* it increases exponentially, starting
* at 40 msec. After 5 tries, 1240 msec
* have passed in total, when poll
* returned the error, and 1200 msec
* when send returned the errors. */
#ifndef USE_WINSOCK
pret = poll(NULL, 0, (SEND_BLOCKED_WAIT_TIMEOUT/10)<<(retries+1));
#else
pret = WSAPoll(NULL, 0, (SEND_BLOCKED_WAIT_TIMEOUT/10)<<(retries+1));
#endif
if(pret < 0 &&
#ifndef USE_WINSOCK
errno != EAGAIN && errno != EINTR &&
# ifdef EWOULDBLOCK
errno != EWOULDBLOCK &&
# endif
errno != ENOBUFS
#else
WSAGetLastError() != WSAEINPROGRESS &&
WSAGetLastError() != WSAEINTR &&
WSAGetLastError() != WSAENOBUFS &&
WSAGetLastError() != WSAEWOULDBLOCK
#endif
) {
log_err("poll udp out timer failed: %s",
sock_strerror(errno));
}
}
#endif /* defined(HAVE_POLL) || defined(USE_WINSOCK) */
retries++;
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",
(struct sockaddr_storage*)addr, addrlen);
#ifdef __NetBSD__
/* netbsd 7 has IP_PKTINFO for recv but not send */
if(errno == EINVAL && r->srctype == 4)
log_err("sendmsg: No support for sendmsg(IP_PKTINFO). "
"Please disable interface-automatic");
#endif
return 0;
} else if((size_t)sent != sldns_buffer_remaining(packet)) {
log_err("sent %d in place of %d bytes",
(int)sent, (int)sldns_buffer_remaining(packet));
return 0;
}
return 1;
#else
(void)c;
(void)packet;
(void)addr;
(void)addrlen;
(void)r;
log_err("sendmsg: IPV6_PKTINFO not supported");
return 0;
#endif /* AF_INET6 && IPV6_PKTINFO && HAVE_SENDMSG */
}
/** return true is UDP receive error needs to be logged */
static int udp_recv_needs_log(int err)
{
switch(err) {
case EACCES: /* some hosts send ICMP 'Permission Denied' */
#ifndef USE_WINSOCK
case ECONNREFUSED:
# ifdef ENETUNREACH
case ENETUNREACH:
# endif
# ifdef EHOSTDOWN
case EHOSTDOWN:
# endif
# ifdef EHOSTUNREACH
case EHOSTUNREACH:
# endif
# ifdef ENETDOWN
case ENETDOWN:
# endif
#else /* USE_WINSOCK */
case WSAECONNREFUSED:
case WSAENETUNREACH:
case WSAEHOSTDOWN:
case WSAEHOSTUNREACH:
case WSAENETDOWN:
#endif
if(verbosity >= VERB_ALGO)
return 1;
return 0;
default:
break;
}
return 1;
}
/** Parses the PROXYv2 header from buf and updates the comm_reply struct.
* Returns 1 on success, 0 on failure. */
static int consume_pp2_header(struct sldns_buffer* buf, struct comm_reply* rep,
int stream) {
size_t size;
- struct pp2_header *header = pp2_read_header(buf);
- if(header == NULL) return 0;
+ struct pp2_header *header;
+ int err = pp2_read_header(sldns_buffer_begin(buf),
+ sldns_buffer_remaining(buf));
+ if(err) return 0;
+ header = (struct pp2_header*)sldns_buffer_begin(buf);
size = PP2_HEADER_SIZE + ntohs(header->len);
if((header->ver_cmd & 0xF) == PP2_CMD_LOCAL) {
/* A connection from the proxy itself.
* No need to do anything with addresses. */
goto done;
}
- if(header->fam_prot == 0x00) {
+ if(header->fam_prot == PP2_UNSPEC_UNSPEC) {
/* Unspecified family and protocol. This could be used for
* health checks by proxies.
* No need to do anything with addresses. */
goto done;
}
/* Read the proxied address */
switch(header->fam_prot) {
- case 0x11: /* AF_INET|STREAM */
- case 0x12: /* AF_INET|DGRAM */
+ case PP2_INET_STREAM:
+ case PP2_INET_DGRAM:
{
struct sockaddr_in* addr =
(struct sockaddr_in*)&rep->client_addr;
addr->sin_family = AF_INET;
addr->sin_addr.s_addr = header->addr.addr4.src_addr;
addr->sin_port = header->addr.addr4.src_port;
rep->client_addrlen = (socklen_t)sizeof(struct sockaddr_in);
}
/* Ignore the destination address; it should be us. */
break;
- case 0x21: /* AF_INET6|STREAM */
- case 0x22: /* AF_INET6|DGRAM */
+ case PP2_INET6_STREAM:
+ case PP2_INET6_DGRAM:
{
struct sockaddr_in6* addr =
(struct sockaddr_in6*)&rep->client_addr;
memset(addr, 0, sizeof(*addr));
addr->sin6_family = AF_INET6;
memcpy(&addr->sin6_addr,
header->addr.addr6.src_addr, 16);
addr->sin6_port = header->addr.addr6.src_port;
rep->client_addrlen = (socklen_t)sizeof(struct sockaddr_in6);
}
/* Ignore the destination address; it should be us. */
break;
+ default:
+ log_err("proxy_protocol: unsupported family and "
+ "protocol 0x%x", (int)header->fam_prot);
+ return 0;
}
rep->is_proxied = 1;
done:
if(!stream) {
/* 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, ((char*)header)+size,
sldns_buffer_limit(buf)-size);
sldns_buffer_set_limit(buf, sldns_buffer_limit(buf)-size);
}
return 1;
}
+#if defined(AF_INET6) && defined(IPV6_PKTINFO) && defined(HAVE_RECVMSG)
void
comm_point_udp_ancil_callback(int fd, short event, void* arg)
{
-#if defined(AF_INET6) && defined(IPV6_PKTINFO) && defined(HAVE_RECVMSG)
struct comm_reply rep;
struct msghdr msg;
struct iovec iov[1];
ssize_t rcv;
union {
struct cmsghdr hdr;
char buf[256];
} ancil;
int i;
#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);
if(!(event&UB_EV_READ))
return;
log_assert(rep.c && rep.c->buffer && rep.c->fd == fd);
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);
msg.msg_name = &rep.remote_addr;
msg.msg_namelen = (socklen_t)sizeof(rep.remote_addr);
iov[0].iov_base = sldns_buffer_begin(rep.c->buffer);
iov[0].iov_len = sldns_buffer_remaining(rep.c->buffer);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = ancil.buf;
#ifndef S_SPLINT_S
msg.msg_controllen = sizeof(ancil.buf);
#endif /* S_SPLINT_S */
msg.msg_flags = 0;
rcv = recvmsg(fd, &msg, MSG_DONTWAIT);
if(rcv == -1) {
if(errno != EAGAIN && errno != EINTR
&& udp_recv_needs_log(errno)) {
log_err("recvmsg failed: %s", strerror(errno));
}
return;
}
rep.remote_addrlen = msg.msg_namelen;
sldns_buffer_skip(rep.c->buffer, rcv);
sldns_buffer_flip(rep.c->buffer);
rep.srctype = 0;
rep.is_proxied = 0;
#ifndef S_SPLINT_S
for(cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if( cmsg->cmsg_level == IPPROTO_IPV6 &&
cmsg->cmsg_type == IPV6_PKTINFO) {
rep.srctype = 6;
memmove(&rep.pktinfo.v6info, CMSG_DATA(cmsg),
sizeof(struct in6_pktinfo));
break;
#ifdef IP_PKTINFO
} else if( cmsg->cmsg_level == IPPROTO_IP &&
cmsg->cmsg_type == IP_PKTINFO) {
rep.srctype = 4;
memmove(&rep.pktinfo.v4info, CMSG_DATA(cmsg),
sizeof(struct in_pktinfo));
break;
#elif defined(IP_RECVDSTADDR)
} else if( cmsg->cmsg_level == IPPROTO_IP &&
cmsg->cmsg_type == IP_RECVDSTADDR) {
rep.srctype = 4;
memmove(&rep.pktinfo.v4addr, CMSG_DATA(cmsg),
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 && rep.srctype != 0)
p_ancil("receive_udp on interface", &rep);
#endif /* S_SPLINT_S */
if(rep.c->pp2_enabled && !consume_pp2_header(rep.c->buffer,
&rep, 0)) {
log_err("proxy_protocol: could not consume PROXYv2 header");
return;
}
if(!rep.is_proxied) {
rep.client_addrlen = rep.remote_addrlen;
memmove(&rep.client_addr, &rep.remote_addr,
rep.remote_addrlen);
}
fptr_ok(fptr_whitelist_comm_point(rep.c->callback));
if((*rep.c->callback)(rep.c, rep.c->cb_arg, NETEVENT_NOERROR, &rep)) {
/* send back immediate reply */
- (void)comm_point_send_udp_msg_if(rep.c, rep.c->buffer,
+ struct sldns_buffer *buffer;
+#ifdef USE_DNSCRYPT
+ buffer = rep.c->dnscrypt_buffer;
+#else
+ buffer = rep.c->buffer;
+#endif
+ (void)comm_point_send_udp_msg_if(rep.c, buffer,
(struct sockaddr*)&rep.remote_addr,
rep.remote_addrlen, &rep);
}
if(!rep.c || rep.c->fd == -1) /* commpoint closed */
break;
}
-#else
- (void)fd;
- (void)event;
- (void)arg;
- fatal_exit("recvmsg: No support for IPV6_PKTINFO; IP_PKTINFO or IP_RECVDSTADDR. "
- "Please disable interface-automatic");
-#endif /* AF_INET6 && IPV6_PKTINFO && HAVE_RECVMSG */
}
+#endif /* AF_INET6 && IPV6_PKTINFO && HAVE_RECVMSG */
void
comm_point_udp_callback(int fd, short event, void* arg)
{
struct comm_reply rep;
ssize_t rcv;
int i;
struct sldns_buffer *buffer;
rep.c = (struct comm_point*)arg;
log_assert(rep.c->type == comm_udp);
if(!(event&UB_EV_READ))
return;
log_assert(rep.c && rep.c->buffer && rep.c->fd == fd);
ub_comm_base_now(rep.c->ev->base);
for(i=0; i<NUM_UDP_PER_SELECT; i++) {
sldns_buffer_clear(rep.c->buffer);
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),
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",
fd, strerror(errno));
#else
if(WSAGetLastError() != WSAEINPROGRESS &&
WSAGetLastError() != WSAECONNRESET &&
WSAGetLastError()!= WSAEWOULDBLOCK &&
udp_recv_needs_log(WSAGetLastError()))
log_err("recvfrom failed: %s",
wsa_strerror(WSAGetLastError()));
#endif
return;
}
sldns_buffer_skip(rep.c->buffer, rcv);
sldns_buffer_flip(rep.c->buffer);
rep.srctype = 0;
rep.is_proxied = 0;
if(rep.c->pp2_enabled && !consume_pp2_header(rep.c->buffer,
&rep, 0)) {
log_err("proxy_protocol: could not consume PROXYv2 header");
return;
}
if(!rep.is_proxied) {
rep.client_addrlen = rep.remote_addrlen;
memmove(&rep.client_addr, &rep.remote_addr,
rep.remote_addrlen);
}
fptr_ok(fptr_whitelist_comm_point(rep.c->callback));
if((*rep.c->callback)(rep.c, rep.c->cb_arg, NETEVENT_NOERROR, &rep)) {
/* send back immediate reply */
#ifdef USE_DNSCRYPT
buffer = rep.c->dnscrypt_buffer;
#else
buffer = rep.c->buffer;
#endif
(void)comm_point_send_udp_msg(rep.c, buffer,
(struct sockaddr*)&rep.remote_addr,
rep.remote_addrlen, 0);
}
if(!rep.c || rep.c->fd != fd) /* commpoint closed to -1 or reused for
another UDP port. Note rep.c cannot be reused with TCP fd. */
break;
}
}
int adjusted_tcp_timeout(struct comm_point* c)
{
if(c->tcp_timeout_msec < TCP_QUERY_TIMEOUT_MINIMUM)
return TCP_QUERY_TIMEOUT_MINIMUM;
return c->tcp_timeout_msec;
}
/** 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)
{
int handler_usage;
log_assert(c->type == comm_tcp || c->type == comm_http);
log_assert(c->fd == -1);
sldns_buffer_clear(c->buffer);
#ifdef USE_DNSCRYPT
if (c->dnscrypt)
sldns_buffer_clear(c->dnscrypt_buffer);
#endif
c->tcp_is_reading = 1;
c->tcp_byte_count = 0;
c->tcp_keepalive = 0;
/* if more than half the tcp handlers are in use, use a shorter
* timeout for this TCP connection, we need to make space for
* other connections to be able to get attention */
/* If > 50% TCP handler structures in use, set timeout to 1/100th
* configured value.
* If > 65%TCP handler structures in use, set to 1/500th configured
* value.
* If > 80% TCP handler structures in use, set to 0.
*
* If the timeout to use falls below 200 milliseconds, an actual
* timeout of 200ms is used.
*/
handler_usage = (cur * 100) / max;
if(handler_usage > 50 && handler_usage <= 65)
c->tcp_timeout_msec /= 100;
else if (handler_usage > 65 && handler_usage <= 80)
c->tcp_timeout_msec /= 500;
else if (handler_usage > 80)
c->tcp_timeout_msec = 0;
comm_point_start_listening(c, fd, adjusted_tcp_timeout(c));
}
void comm_base_handle_slow_accept(int ATTR_UNUSED(fd),
short ATTR_UNUSED(event), void* arg)
{
struct comm_base* b = (struct comm_base*)arg;
/* timeout for the slow accept, re-enable accepts again */
if(b->start_accept) {
verbose(VERB_ALGO, "wait is over, slow accept disabled");
fptr_ok(fptr_whitelist_start_accept(b->start_accept));
(*b->start_accept)(b->cb_arg);
b->eb->slow_accept_enabled = 0;
}
}
int comm_point_perform_accept(struct comm_point* c,
struct sockaddr_storage* addr, socklen_t* addrlen)
{
int new_fd;
*addrlen = (socklen_t)sizeof(*addr);
#ifndef HAVE_ACCEPT4
new_fd = accept(c->fd, (struct sockaddr*)addr, addrlen);
#else
/* SOCK_NONBLOCK saves extra calls to fcntl for the same result */
new_fd = accept4(c->fd, (struct sockaddr*)addr, addrlen, SOCK_NONBLOCK);
#endif
if(new_fd == -1) {
#ifndef USE_WINSOCK
/* EINTR is signal interrupt. others are closed connection. */
if( errno == EINTR || errno == EAGAIN
#ifdef EWOULDBLOCK
|| errno == EWOULDBLOCK
#endif
#ifdef ECONNABORTED
|| errno == ECONNABORTED
#endif
#ifdef EPROTO
|| errno == EPROTO
#endif /* EPROTO */
)
return -1;
#if defined(ENFILE) && defined(EMFILE)
if(errno == ENFILE || errno == EMFILE) {
/* out of file descriptors, likely outside of our
* control. stop accept() calls for some time */
if(c->ev->base->stop_accept) {
struct comm_base* b = c->ev->base;
struct timeval tv;
verbose(VERB_ALGO, "out of file descriptors: "
"slow accept");
ub_comm_base_now(b);
if(b->eb->last_slow_log+SLOW_LOG_TIME <=
b->eb->secs) {
b->eb->last_slow_log = b->eb->secs;
verbose(VERB_OPS, "accept failed, "
"slow down accept for %d "
"msec: %s",
NETEVENT_SLOW_ACCEPT_TIME,
sock_strerror(errno));
}
b->eb->slow_accept_enabled = 1;
fptr_ok(fptr_whitelist_stop_accept(
b->stop_accept));
(*b->stop_accept)(b->cb_arg);
/* set timeout, no mallocs */
tv.tv_sec = NETEVENT_SLOW_ACCEPT_TIME/1000;
tv.tv_usec = (NETEVENT_SLOW_ACCEPT_TIME%1000)*1000;
b->eb->slow_accept = ub_event_new(b->eb->base,
-1, UB_EV_TIMEOUT,
comm_base_handle_slow_accept, b);
if(b->eb->slow_accept == NULL) {
/* we do not want to log here, because
* that would spam the logfiles.
* error: "event_base_set failed." */
}
else if(ub_event_add(b->eb->slow_accept, &tv)
!= 0) {
/* we do not want to log here,
* error: "event_add failed." */
}
} else {
log_err("accept, with no slow down, "
"failed: %s", sock_strerror(errno));
}
return -1;
}
#endif
#else /* USE_WINSOCK */
if(WSAGetLastError() == WSAEINPROGRESS ||
WSAGetLastError() == WSAECONNRESET)
return -1;
if(WSAGetLastError() == WSAEWOULDBLOCK) {
ub_winsock_tcp_wouldblock(c->ev->ev, UB_EV_READ);
return -1;
}
#endif
log_err_addr("accept failed", sock_strerror(errno), addr,
*addrlen);
return -1;
}
if(c->tcp_conn_limit && c->type == comm_tcp_accept) {
c->tcl_addr = tcl_addr_lookup(c->tcp_conn_limit, addr, *addrlen);
if(!tcl_new_connection(c->tcl_addr)) {
if(verbosity >= 3)
log_err_addr("accept rejected",
"connection limit exceeded", addr, *addrlen);
close(new_fd);
return -1;
}
}
#ifndef HAVE_ACCEPT4
fd_set_nonblock(new_fd);
#endif
return new_fd;
}
#ifdef USE_WINSOCK
static long win_bio_cb(BIO *b, int oper, const char* ATTR_UNUSED(argp),
#ifdef HAVE_BIO_SET_CALLBACK_EX
size_t ATTR_UNUSED(len),
#endif
int ATTR_UNUSED(argi), long argl,
#ifndef HAVE_BIO_SET_CALLBACK_EX
long retvalue
#else
int retvalue, size_t* ATTR_UNUSED(processed)
#endif
)
{
int wsa_err = WSAGetLastError(); /* store errcode before it is gone */
verbose(VERB_ALGO, "bio_cb %d, %s %s %s", oper,
(oper&BIO_CB_RETURN)?"return":"before",
(oper&BIO_CB_READ)?"read":((oper&BIO_CB_WRITE)?"write":"other"),
wsa_err==WSAEWOULDBLOCK?"wsawb":"");
/* on windows, check if previous operation caused EWOULDBLOCK */
if( (oper == (BIO_CB_READ|BIO_CB_RETURN) && argl == 0) ||
(oper == (BIO_CB_GETS|BIO_CB_RETURN) && argl == 0)) {
if(wsa_err == WSAEWOULDBLOCK)
ub_winsock_tcp_wouldblock((struct ub_event*)
BIO_get_callback_arg(b), UB_EV_READ);
}
if( (oper == (BIO_CB_WRITE|BIO_CB_RETURN) && argl == 0) ||
(oper == (BIO_CB_PUTS|BIO_CB_RETURN) && argl == 0)) {
if(wsa_err == WSAEWOULDBLOCK)
ub_winsock_tcp_wouldblock((struct ub_event*)
BIO_get_callback_arg(b), UB_EV_WRITE);
}
/* return original return value */
return retvalue;
}
/** set win bio callbacks for nonblocking operations */
void
comm_point_tcp_win_bio_cb(struct comm_point* c, void* thessl)
{
SSL* ssl = (SSL*)thessl;
/* set them both just in case, but usually they are the same BIO */
#ifdef HAVE_BIO_SET_CALLBACK_EX
BIO_set_callback_ex(SSL_get_rbio(ssl), &win_bio_cb);
#else
BIO_set_callback(SSL_get_rbio(ssl), &win_bio_cb);
#endif
BIO_set_callback_arg(SSL_get_rbio(ssl), (char*)c->ev->ev);
#ifdef HAVE_BIO_SET_CALLBACK_EX
BIO_set_callback_ex(SSL_get_wbio(ssl), &win_bio_cb);
#else
BIO_set_callback(SSL_get_wbio(ssl), &win_bio_cb);
#endif
BIO_set_callback_arg(SSL_get_wbio(ssl), (char*)c->ev->ev);
}
#endif
#ifdef HAVE_NGHTTP2
/** Create http2 session server. Per connection, after TCP accepted.*/
static int http2_session_server_create(struct http2_session* h2_session)
{
log_assert(h2_session->callbacks);
h2_session->is_drop = 0;
if(nghttp2_session_server_new(&h2_session->session,
h2_session->callbacks,
h2_session) == NGHTTP2_ERR_NOMEM) {
log_err("failed to create nghttp2 session server");
return 0;
}
return 1;
}
/** Submit http2 setting to session. Once per session. */
static int http2_submit_settings(struct http2_session* h2_session)
{
int ret;
nghttp2_settings_entry settings[1] = {
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS,
h2_session->c->http2_max_streams}};
ret = nghttp2_submit_settings(h2_session->session, NGHTTP2_FLAG_NONE,
settings, 1);
if(ret) {
verbose(VERB_QUERY, "http2: submit_settings failed, "
"error: %s", nghttp2_strerror(ret));
return 0;
}
return 1;
}
#endif /* HAVE_NGHTTP2 */
void
comm_point_tcp_accept_callback(int fd, short event, void* arg)
{
struct comm_point* c = (struct comm_point*)arg, *c_hdl;
int new_fd;
log_assert(c->type == comm_tcp_accept);
if(!(event & UB_EV_READ)) {
log_info("ignoring tcp accept event %d", (int)event);
return;
}
ub_comm_base_now(c->ev->base);
/* find free tcp handler. */
if(!c->tcp_free) {
log_warn("accepted too many tcp, connections full");
return;
}
/* accept incoming connection. */
c_hdl = c->tcp_free;
/* clear leftover flags from previous use, and then set the
* correct event base for the event structure for libevent */
ub_event_free(c_hdl->ev->ev);
c_hdl->ev->ev = NULL;
if((c_hdl->type == comm_tcp && c_hdl->tcp_req_info) ||
c_hdl->type == comm_local || c_hdl->type == comm_raw)
c_hdl->tcp_do_toggle_rw = 0;
else c_hdl->tcp_do_toggle_rw = 1;
if(c_hdl->type == comm_http) {
#ifdef HAVE_NGHTTP2
if(!c_hdl->h2_session ||
!http2_session_server_create(c_hdl->h2_session)) {
log_warn("failed to create nghttp2");
return;
}
if(!c_hdl->h2_session ||
!http2_submit_settings(c_hdl->h2_session)) {
log_warn("failed to submit http2 settings");
return;
}
if(!c->ssl) {
c_hdl->tcp_do_toggle_rw = 0;
c_hdl->use_h2 = 1;
}
#endif
c_hdl->ev->ev = ub_event_new(c_hdl->ev->base->eb->base, -1,
UB_EV_PERSIST | UB_EV_READ | UB_EV_TIMEOUT,
comm_point_http_handle_callback, c_hdl);
} else {
c_hdl->ev->ev = ub_event_new(c_hdl->ev->base->eb->base, -1,
UB_EV_PERSIST | UB_EV_READ | UB_EV_TIMEOUT,
comm_point_tcp_handle_callback, c_hdl);
}
if(!c_hdl->ev->ev) {
log_warn("could not ub_event_new, dropped tcp");
return;
}
log_assert(fd != -1);
(void)fd;
new_fd = comm_point_perform_accept(c, &c_hdl->repinfo.remote_addr,
&c_hdl->repinfo.remote_addrlen);
if(new_fd == -1)
return;
/* Copy remote_address to client_address.
* Simplest way/time for streams to do that. */
c_hdl->repinfo.client_addrlen = c_hdl->repinfo.remote_addrlen;
memmove(&c_hdl->repinfo.client_addr,
&c_hdl->repinfo.remote_addr,
c_hdl->repinfo.remote_addrlen);
if(c->ssl) {
c_hdl->ssl = incoming_ssl_fd(c->ssl, new_fd);
if(!c_hdl->ssl) {
c_hdl->fd = new_fd;
comm_point_close(c_hdl);
return;
}
c_hdl->ssl_shake_state = comm_ssl_shake_read;
#ifdef USE_WINSOCK
comm_point_tcp_win_bio_cb(c_hdl, c_hdl->ssl);
#endif
}
/* grab the tcp handler buffers */
c->cur_tcp_count++;
c->tcp_free = c_hdl->tcp_free;
c_hdl->tcp_free = NULL;
if(!c->tcp_free) {
/* stop accepting incoming queries for now. */
comm_point_stop_listening(c);
}
setup_tcp_handler(c_hdl, new_fd, c->cur_tcp_count, c->max_tcp_count);
}
/** Make tcp handler free for next assignment */
static void
reclaim_tcp_handler(struct comm_point* c)
{
log_assert(c->type == comm_tcp);
if(c->ssl) {
#ifdef HAVE_SSL
SSL_shutdown(c->ssl);
SSL_free(c->ssl);
c->ssl = NULL;
#endif
}
comm_point_close(c);
if(c->tcp_parent) {
if(c != c->tcp_parent->tcp_free) {
c->tcp_parent->cur_tcp_count--;
c->tcp_free = c->tcp_parent->tcp_free;
c->tcp_parent->tcp_free = c;
}
if(!c->tcp_free) {
/* re-enable listening on accept socket */
comm_point_start_listening(c->tcp_parent, -1, -1);
}
}
c->tcp_more_read_again = NULL;
c->tcp_more_write_again = NULL;
c->tcp_byte_count = 0;
c->pp2_header_state = pp2_header_none;
sldns_buffer_clear(c->buffer);
}
/** do the callback when writing is done */
static void
tcp_callback_writer(struct comm_point* c)
{
log_assert(c->type == comm_tcp);
if(!c->tcp_write_and_read) {
sldns_buffer_clear(c->buffer);
c->tcp_byte_count = 0;
}
if(c->tcp_do_toggle_rw)
c->tcp_is_reading = 1;
/* switch from listening(write) to listening(read) */
if(c->tcp_req_info) {
tcp_req_info_handle_writedone(c->tcp_req_info);
} else {
comm_point_stop_listening(c);
if(c->tcp_write_and_read) {
fptr_ok(fptr_whitelist_comm_point(c->callback));
if( (*c->callback)(c, c->cb_arg, NETEVENT_PKT_WRITTEN,
&c->repinfo) ) {
comm_point_start_listening(c, -1,
adjusted_tcp_timeout(c));
}
} else {
comm_point_start_listening(c, -1,
adjusted_tcp_timeout(c));
}
}
}
/** do the callback when reading is done */
static void
tcp_callback_reader(struct comm_point* c)
{
log_assert(c->type == comm_tcp || c->type == comm_local);
sldns_buffer_flip(c->buffer);
if(c->tcp_do_toggle_rw)
c->tcp_is_reading = 0;
c->tcp_byte_count = 0;
if(c->tcp_req_info) {
tcp_req_info_handle_readdone(c->tcp_req_info);
} else {
if(c->type == comm_tcp)
comm_point_stop_listening(c);
fptr_ok(fptr_whitelist_comm_point(c->callback));
if( (*c->callback)(c, c->cb_arg, NETEVENT_NOERROR, &c->repinfo) ) {
comm_point_start_listening(c, -1,
adjusted_tcp_timeout(c));
}
}
}
#ifdef HAVE_SSL
/** true if the ssl handshake error has to be squelched from the logs */
int
squelch_err_ssl_handshake(unsigned long err)
{
if(verbosity >= VERB_QUERY)
return 0; /* only squelch on low verbosity */
if(ERR_GET_LIB(err) == ERR_LIB_SSL &&
(ERR_GET_REASON(err) == SSL_R_HTTPS_PROXY_REQUEST ||
ERR_GET_REASON(err) == SSL_R_HTTP_REQUEST ||
ERR_GET_REASON(err) == SSL_R_WRONG_VERSION_NUMBER ||
ERR_GET_REASON(err) == SSL_R_SSLV3_ALERT_BAD_CERTIFICATE
#ifdef SSL_F_TLS_POST_PROCESS_CLIENT_HELLO
|| ERR_GET_REASON(err) == SSL_R_NO_SHARED_CIPHER
#endif
#ifdef SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO
|| ERR_GET_REASON(err) == SSL_R_UNKNOWN_PROTOCOL
|| ERR_GET_REASON(err) == SSL_R_UNSUPPORTED_PROTOCOL
# ifdef SSL_R_VERSION_TOO_LOW
|| ERR_GET_REASON(err) == SSL_R_VERSION_TOO_LOW
# endif
#endif
))
return 1;
return 0;
}
#endif /* HAVE_SSL */
/** continue ssl handshake */
#ifdef HAVE_SSL
static int
ssl_handshake(struct comm_point* c)
{
int r;
if(c->ssl_shake_state == comm_ssl_shake_hs_read) {
/* read condition satisfied back to writing */
comm_point_listen_for_rw(c, 0, 1);
c->ssl_shake_state = comm_ssl_shake_none;
return 1;
}
if(c->ssl_shake_state == comm_ssl_shake_hs_write) {
/* write condition satisfied, back to reading */
comm_point_listen_for_rw(c, 1, 0);
c->ssl_shake_state = comm_ssl_shake_none;
return 1;
}
ERR_clear_error();
r = SSL_do_handshake(c->ssl);
if(r != 1) {
int want = SSL_get_error(c->ssl, r);
if(want == SSL_ERROR_WANT_READ) {
if(c->ssl_shake_state == comm_ssl_shake_read)
return 1;
c->ssl_shake_state = comm_ssl_shake_read;
comm_point_listen_for_rw(c, 1, 0);
return 1;
} else if(want == SSL_ERROR_WANT_WRITE) {
if(c->ssl_shake_state == comm_ssl_shake_write)
return 1;
c->ssl_shake_state = comm_ssl_shake_write;
comm_point_listen_for_rw(c, 0, 1);
return 1;
} else if(r == 0) {
return 0; /* closed */
} else if(want == SSL_ERROR_SYSCALL) {
/* SYSCALL and errno==0 means closed uncleanly */
#ifdef EPIPE
if(errno == EPIPE && verbosity < 2)
return 0; /* silence 'broken pipe' */
#endif
#ifdef ECONNRESET
if(errno == ECONNRESET && verbosity < 2)
return 0; /* silence reset by peer */
#endif
if(!tcp_connect_errno_needs_log(
(struct sockaddr*)&c->repinfo.remote_addr,
c->repinfo.remote_addrlen))
return 0; /* silence connect failures that
show up because after connect this is the
first system call that accesses the socket */
if(errno != 0)
log_err("SSL_handshake syscall: %s",
strerror(errno));
return 0;
} else {
unsigned long err = ERR_get_error();
if(!squelch_err_ssl_handshake(err)) {
- log_crypto_err_code("ssl handshake failed", err);
+ log_crypto_err_io_code("ssl handshake failed",
+ want, err);
log_addr(VERB_OPS, "ssl handshake failed",
&c->repinfo.remote_addr,
c->repinfo.remote_addrlen);
}
return 0;
}
}
/* this is where peer verification could take place */
if((SSL_get_verify_mode(c->ssl)&SSL_VERIFY_PEER)) {
/* verification */
if(SSL_get_verify_result(c->ssl) == X509_V_OK) {
#ifdef HAVE_SSL_GET1_PEER_CERTIFICATE
X509* x = SSL_get1_peer_certificate(c->ssl);
#else
X509* x = SSL_get_peer_certificate(c->ssl);
#endif
if(!x) {
log_addr(VERB_ALGO, "SSL connection failed: "
"no certificate",
&c->repinfo.remote_addr,
c->repinfo.remote_addrlen);
return 0;
}
log_cert(VERB_ALGO, "peer certificate", x);
#ifdef HAVE_SSL_GET0_PEERNAME
if(SSL_get0_peername(c->ssl)) {
char buf[255];
snprintf(buf, sizeof(buf), "SSL connection "
"to %s authenticated",
SSL_get0_peername(c->ssl));
log_addr(VERB_ALGO, buf, &c->repinfo.remote_addr,
c->repinfo.remote_addrlen);
} else {
#endif
log_addr(VERB_ALGO, "SSL connection "
"authenticated", &c->repinfo.remote_addr,
c->repinfo.remote_addrlen);
#ifdef HAVE_SSL_GET0_PEERNAME
}
#endif
X509_free(x);
} else {
#ifdef HAVE_SSL_GET1_PEER_CERTIFICATE
X509* x = SSL_get1_peer_certificate(c->ssl);
#else
X509* x = SSL_get_peer_certificate(c->ssl);
#endif
if(x) {
log_cert(VERB_ALGO, "peer certificate", x);
X509_free(x);
}
log_addr(VERB_ALGO, "SSL connection failed: "
"failed to authenticate",
&c->repinfo.remote_addr,
c->repinfo.remote_addrlen);
return 0;
}
} else {
/* unauthenticated, the verify peer flag was not set
* in c->ssl when the ssl object was created from ssl_ctx */
log_addr(VERB_ALGO, "SSL connection", &c->repinfo.remote_addr,
c->repinfo.remote_addrlen);
}
#ifdef HAVE_SSL_GET0_ALPN_SELECTED
/* check if http2 use is negotiated */
if(c->type == comm_http && c->h2_session) {
const unsigned char *alpn;
unsigned int alpnlen = 0;
SSL_get0_alpn_selected(c->ssl, &alpn, &alpnlen);
if(alpnlen == 2 && memcmp("h2", alpn, 2) == 0) {
/* connection upgraded to HTTP2 */
c->tcp_do_toggle_rw = 0;
c->use_h2 = 1;
}
}
#endif
/* setup listen rw correctly */
if(c->tcp_is_reading) {
if(c->ssl_shake_state != comm_ssl_shake_read)
comm_point_listen_for_rw(c, 1, 0);
} else {
comm_point_listen_for_rw(c, 0, 1);
}
c->ssl_shake_state = comm_ssl_shake_none;
return 1;
}
#endif /* HAVE_SSL */
/** ssl read callback on TCP */
static int
ssl_handle_read(struct comm_point* c)
{
#ifdef HAVE_SSL
int r;
if(c->ssl_shake_state != comm_ssl_shake_none) {
if(!ssl_handshake(c))
return 0;
if(c->ssl_shake_state != comm_ssl_shake_none)
return 1;
}
if(c->pp2_enabled && c->pp2_header_state != pp2_header_done) {
struct pp2_header* header = NULL;
size_t want_read_size = 0;
size_t current_read_size = 0;
if(c->pp2_header_state == pp2_header_none) {
want_read_size = PP2_HEADER_SIZE;
if(sldns_buffer_remaining(c->buffer)<want_read_size) {
log_err_addr("proxy_protocol: not enough "
"buffer size to read PROXYv2 header", "",
&c->repinfo.remote_addr,
c->repinfo.remote_addrlen);
return 0;
}
verbose(VERB_ALGO, "proxy_protocol: reading fixed "
"part of PROXYv2 header (len %lu)",
(unsigned long)want_read_size);
current_read_size = want_read_size;
if(c->tcp_byte_count < current_read_size) {
ERR_clear_error();
if((r=SSL_read(c->ssl, (void*)sldns_buffer_at(
c->buffer, c->tcp_byte_count),
current_read_size -
c->tcp_byte_count)) <= 0) {
int want = SSL_get_error(c->ssl, r);
if(want == SSL_ERROR_ZERO_RETURN) {
if(c->tcp_req_info)
return tcp_req_info_handle_read_close(c->tcp_req_info);
return 0; /* shutdown, closed */
} else if(want == SSL_ERROR_WANT_READ) {
#ifdef USE_WINSOCK
ub_winsock_tcp_wouldblock(c->ev->ev, UB_EV_READ);
#endif
return 1; /* read more later */
} else if(want == SSL_ERROR_WANT_WRITE) {
c->ssl_shake_state = comm_ssl_shake_hs_write;
comm_point_listen_for_rw(c, 0, 1);
return 1;
} else if(want == SSL_ERROR_SYSCALL) {
#ifdef ECONNRESET
if(errno == ECONNRESET && verbosity < 2)
return 0; /* silence reset by peer */
#endif
if(errno != 0)
log_err("SSL_read syscall: %s",
strerror(errno));
return 0;
}
- log_crypto_err("could not SSL_read");
+ log_crypto_err_io("could not SSL_read",
+ want);
return 0;
}
c->tcp_byte_count += r;
+ sldns_buffer_skip(c->buffer, r);
if(c->tcp_byte_count != current_read_size) return 1;
c->pp2_header_state = pp2_header_init;
}
}
if(c->pp2_header_state == pp2_header_init) {
- header = pp2_read_header(c->buffer);
- if(!header) {
+ int err;
+ err = pp2_read_header(
+ sldns_buffer_begin(c->buffer),
+ sldns_buffer_limit(c->buffer));
+ if(err) {
log_err("proxy_protocol: could not parse "
- "PROXYv2 header");
+ "PROXYv2 header (%s)",
+ pp_lookup_error(err));
return 0;
}
+ header = (struct pp2_header*)sldns_buffer_begin(c->buffer);
want_read_size = ntohs(header->len);
- if(sldns_buffer_remaining(c->buffer) <
+ if(sldns_buffer_limit(c->buffer) <
PP2_HEADER_SIZE + want_read_size) {
log_err_addr("proxy_protocol: not enough "
"buffer size to read PROXYv2 header", "",
&c->repinfo.remote_addr,
c->repinfo.remote_addrlen);
return 0;
}
verbose(VERB_ALGO, "proxy_protocol: reading variable "
"part of PROXYv2 header (len %lu)",
(unsigned long)want_read_size);
current_read_size = PP2_HEADER_SIZE + want_read_size;
if(want_read_size == 0) {
/* nothing more to read; header is complete */
c->pp2_header_state = pp2_header_done;
} else if(c->tcp_byte_count < current_read_size) {
ERR_clear_error();
if((r=SSL_read(c->ssl, (void*)sldns_buffer_at(
c->buffer, c->tcp_byte_count),
current_read_size -
c->tcp_byte_count)) <= 0) {
int want = SSL_get_error(c->ssl, r);
if(want == SSL_ERROR_ZERO_RETURN) {
if(c->tcp_req_info)
return tcp_req_info_handle_read_close(c->tcp_req_info);
return 0; /* shutdown, closed */
} else if(want == SSL_ERROR_WANT_READ) {
#ifdef USE_WINSOCK
ub_winsock_tcp_wouldblock(c->ev->ev, UB_EV_READ);
#endif
return 1; /* read more later */
} else if(want == SSL_ERROR_WANT_WRITE) {
c->ssl_shake_state = comm_ssl_shake_hs_write;
comm_point_listen_for_rw(c, 0, 1);
return 1;
} else if(want == SSL_ERROR_SYSCALL) {
#ifdef ECONNRESET
if(errno == ECONNRESET && verbosity < 2)
return 0; /* silence reset by peer */
#endif
if(errno != 0)
log_err("SSL_read syscall: %s",
strerror(errno));
return 0;
}
- log_crypto_err("could not SSL_read");
+ log_crypto_err_io("could not SSL_read",
+ want);
return 0;
}
c->tcp_byte_count += r;
+ sldns_buffer_skip(c->buffer, r);
if(c->tcp_byte_count != current_read_size) return 1;
c->pp2_header_state = pp2_header_done;
}
}
if(c->pp2_header_state != pp2_header_done || !header) {
log_err_addr("proxy_protocol: wrong state for the "
"PROXYv2 header", "", &c->repinfo.remote_addr,
c->repinfo.remote_addrlen);
return 0;
}
+ sldns_buffer_flip(c->buffer);
if(!consume_pp2_header(c->buffer, &c->repinfo, 1)) {
log_err_addr("proxy_protocol: could not consume "
"PROXYv2 header", "", &c->repinfo.remote_addr,
c->repinfo.remote_addrlen);
return 0;
}
verbose(VERB_ALGO, "proxy_protocol: successful read of "
"PROXYv2 header");
/* Clear and reset the buffer to read the following
* DNS packet(s). */
sldns_buffer_clear(c->buffer);
c->tcp_byte_count = 0;
return 1;
}
if(c->tcp_byte_count < sizeof(uint16_t)) {
/* read length bytes */
ERR_clear_error();
if((r=SSL_read(c->ssl, (void*)sldns_buffer_at(c->buffer,
c->tcp_byte_count), (int)(sizeof(uint16_t) -
c->tcp_byte_count))) <= 0) {
int want = SSL_get_error(c->ssl, r);
if(want == SSL_ERROR_ZERO_RETURN) {
if(c->tcp_req_info)
return tcp_req_info_handle_read_close(c->tcp_req_info);
return 0; /* shutdown, closed */
} else if(want == SSL_ERROR_WANT_READ) {
#ifdef USE_WINSOCK
ub_winsock_tcp_wouldblock(c->ev->ev, UB_EV_READ);
#endif
return 1; /* read more later */
} else if(want == SSL_ERROR_WANT_WRITE) {
c->ssl_shake_state = comm_ssl_shake_hs_write;
comm_point_listen_for_rw(c, 0, 1);
return 1;
} else if(want == SSL_ERROR_SYSCALL) {
#ifdef ECONNRESET
if(errno == ECONNRESET && verbosity < 2)
return 0; /* silence reset by peer */
#endif
if(errno != 0)
log_err("SSL_read syscall: %s",
strerror(errno));
return 0;
}
- log_crypto_err("could not SSL_read");
+ log_crypto_err_io("could not SSL_read", want);
return 0;
}
c->tcp_byte_count += r;
if(c->tcp_byte_count < sizeof(uint16_t))
return 1;
if(sldns_buffer_read_u16_at(c->buffer, 0) >
sldns_buffer_capacity(c->buffer)) {
verbose(VERB_QUERY, "ssl: dropped larger than buffer");
return 0;
}
sldns_buffer_set_limit(c->buffer,
sldns_buffer_read_u16_at(c->buffer, 0));
if(sldns_buffer_limit(c->buffer) < LDNS_HEADER_SIZE) {
verbose(VERB_QUERY, "ssl: dropped bogus too short.");
return 0;
}
sldns_buffer_skip(c->buffer, (ssize_t)(c->tcp_byte_count-sizeof(uint16_t)));
verbose(VERB_ALGO, "Reading ssl tcp query of length %d",
(int)sldns_buffer_limit(c->buffer));
}
if(sldns_buffer_remaining(c->buffer) > 0) {
ERR_clear_error();
r = SSL_read(c->ssl, (void*)sldns_buffer_current(c->buffer),
(int)sldns_buffer_remaining(c->buffer));
if(r <= 0) {
int want = SSL_get_error(c->ssl, r);
if(want == SSL_ERROR_ZERO_RETURN) {
if(c->tcp_req_info)
return tcp_req_info_handle_read_close(c->tcp_req_info);
return 0; /* shutdown, closed */
} else if(want == SSL_ERROR_WANT_READ) {
#ifdef USE_WINSOCK
ub_winsock_tcp_wouldblock(c->ev->ev, UB_EV_READ);
#endif
return 1; /* read more later */
} else if(want == SSL_ERROR_WANT_WRITE) {
c->ssl_shake_state = comm_ssl_shake_hs_write;
comm_point_listen_for_rw(c, 0, 1);
return 1;
} else if(want == SSL_ERROR_SYSCALL) {
#ifdef ECONNRESET
if(errno == ECONNRESET && verbosity < 2)
return 0; /* silence reset by peer */
#endif
if(errno != 0)
log_err("SSL_read syscall: %s",
strerror(errno));
return 0;
}
- log_crypto_err("could not SSL_read");
+ log_crypto_err_io("could not SSL_read", want);
return 0;
}
sldns_buffer_skip(c->buffer, (ssize_t)r);
}
if(sldns_buffer_remaining(c->buffer) <= 0) {
tcp_callback_reader(c);
}
return 1;
#else
(void)c;
return 0;
#endif /* HAVE_SSL */
}
/** ssl write callback on TCP */
static int
ssl_handle_write(struct comm_point* c)
{
#ifdef HAVE_SSL
int r;
if(c->ssl_shake_state != comm_ssl_shake_none) {
if(!ssl_handshake(c))
return 0;
if(c->ssl_shake_state != comm_ssl_shake_none)
return 1;
}
/* ignore return, if fails we may simply block */
(void)SSL_set_mode(c->ssl, (long)SSL_MODE_ENABLE_PARTIAL_WRITE);
if((c->tcp_write_and_read?c->tcp_write_byte_count:c->tcp_byte_count) < sizeof(uint16_t)) {
uint16_t len = htons(c->tcp_write_and_read?c->tcp_write_pkt_len:sldns_buffer_limit(c->buffer));
ERR_clear_error();
if(c->tcp_write_and_read) {
if(c->tcp_write_pkt_len + 2 < LDNS_RR_BUF_SIZE) {
/* combine the tcp length and the query for
* write, this emulates writev */
uint8_t buf[LDNS_RR_BUF_SIZE];
memmove(buf, &len, sizeof(uint16_t));
memmove(buf+sizeof(uint16_t),
c->tcp_write_pkt,
c->tcp_write_pkt_len);
r = SSL_write(c->ssl,
(void*)(buf+c->tcp_write_byte_count),
c->tcp_write_pkt_len + 2 -
c->tcp_write_byte_count);
} else {
r = SSL_write(c->ssl,
(void*)(((uint8_t*)&len)+c->tcp_write_byte_count),
(int)(sizeof(uint16_t)-c->tcp_write_byte_count));
}
} else if(sizeof(uint16_t)+sldns_buffer_remaining(c->buffer) <
LDNS_RR_BUF_SIZE) {
/* combine the tcp length and the query for write,
* this emulates writev */
uint8_t buf[LDNS_RR_BUF_SIZE];
memmove(buf, &len, sizeof(uint16_t));
memmove(buf+sizeof(uint16_t),
sldns_buffer_current(c->buffer),
sldns_buffer_remaining(c->buffer));
r = SSL_write(c->ssl, (void*)(buf+c->tcp_byte_count),
(int)(sizeof(uint16_t)+
sldns_buffer_remaining(c->buffer)
- c->tcp_byte_count));
} else {
r = SSL_write(c->ssl,
(void*)(((uint8_t*)&len)+c->tcp_byte_count),
(int)(sizeof(uint16_t)-c->tcp_byte_count));
}
if(r <= 0) {
int want = SSL_get_error(c->ssl, r);
if(want == SSL_ERROR_ZERO_RETURN) {
return 0; /* closed */
} else if(want == SSL_ERROR_WANT_READ) {
c->ssl_shake_state = comm_ssl_shake_hs_read;
comm_point_listen_for_rw(c, 1, 0);
return 1; /* wait for read condition */
} else if(want == SSL_ERROR_WANT_WRITE) {
#ifdef USE_WINSOCK
ub_winsock_tcp_wouldblock(c->ev->ev, UB_EV_WRITE);
#endif
return 1; /* write more later */
} else if(want == SSL_ERROR_SYSCALL) {
#ifdef EPIPE
if(errno == EPIPE && verbosity < 2)
return 0; /* silence 'broken pipe' */
#endif
if(errno != 0)
log_err("SSL_write syscall: %s",
strerror(errno));
return 0;
}
- log_crypto_err("could not SSL_write");
+ log_crypto_err_io("could not SSL_write", want);
return 0;
}
if(c->tcp_write_and_read) {
c->tcp_write_byte_count += r;
if(c->tcp_write_byte_count < sizeof(uint16_t))
return 1;
} else {
c->tcp_byte_count += r;
if(c->tcp_byte_count < sizeof(uint16_t))
return 1;
sldns_buffer_set_position(c->buffer, c->tcp_byte_count -
sizeof(uint16_t));
}
if((!c->tcp_write_and_read && sldns_buffer_remaining(c->buffer) == 0) || (c->tcp_write_and_read && c->tcp_write_byte_count == c->tcp_write_pkt_len + 2)) {
tcp_callback_writer(c);
return 1;
}
}
log_assert(c->tcp_write_and_read || sldns_buffer_remaining(c->buffer) > 0);
log_assert(!c->tcp_write_and_read || c->tcp_write_byte_count < c->tcp_write_pkt_len + 2);
ERR_clear_error();
if(c->tcp_write_and_read) {
r = SSL_write(c->ssl, (void*)(c->tcp_write_pkt + c->tcp_write_byte_count - 2),
(int)(c->tcp_write_pkt_len + 2 - c->tcp_write_byte_count));
} else {
r = SSL_write(c->ssl, (void*)sldns_buffer_current(c->buffer),
(int)sldns_buffer_remaining(c->buffer));
}
if(r <= 0) {
int want = SSL_get_error(c->ssl, r);
if(want == SSL_ERROR_ZERO_RETURN) {
return 0; /* closed */
} else if(want == SSL_ERROR_WANT_READ) {
c->ssl_shake_state = comm_ssl_shake_hs_read;
comm_point_listen_for_rw(c, 1, 0);
return 1; /* wait for read condition */
} else if(want == SSL_ERROR_WANT_WRITE) {
#ifdef USE_WINSOCK
ub_winsock_tcp_wouldblock(c->ev->ev, UB_EV_WRITE);
#endif
return 1; /* write more later */
} else if(want == SSL_ERROR_SYSCALL) {
#ifdef EPIPE
if(errno == EPIPE && verbosity < 2)
return 0; /* silence 'broken pipe' */
#endif
if(errno != 0)
log_err("SSL_write syscall: %s",
strerror(errno));
return 0;
}
- log_crypto_err("could not SSL_write");
+ log_crypto_err_io("could not SSL_write", want);
return 0;
}
if(c->tcp_write_and_read) {
c->tcp_write_byte_count += r;
} else {
sldns_buffer_skip(c->buffer, (ssize_t)r);
}
if((!c->tcp_write_and_read && sldns_buffer_remaining(c->buffer) == 0) || (c->tcp_write_and_read && c->tcp_write_byte_count == c->tcp_write_pkt_len + 2)) {
tcp_callback_writer(c);
}
return 1;
#else
(void)c;
return 0;
#endif /* HAVE_SSL */
}
/** handle ssl tcp connection with dns contents */
static int
ssl_handle_it(struct comm_point* c, int is_write)
{
/* handle case where renegotiation wants read during write call
* or write during read calls */
if(is_write && c->ssl_shake_state == comm_ssl_shake_hs_write)
return ssl_handle_read(c);
else if(!is_write && c->ssl_shake_state == comm_ssl_shake_hs_read)
return ssl_handle_write(c);
/* handle read events for read operation and write events for a
* write operation */
else if(!is_write)
return ssl_handle_read(c);
return ssl_handle_write(c);
}
/**
* Handle tcp reading callback.
* @param fd: file descriptor of socket.
* @param c: comm point to read from into buffer.
* @param short_ok: if true, very short packets are OK (for comm_local).
* @return: 0 on error
*/
static int
comm_point_tcp_handle_read(int fd, struct comm_point* c, int short_ok)
{
ssize_t r;
int recv_initial = 0;
log_assert(c->type == comm_tcp || c->type == comm_local);
if(c->ssl)
return ssl_handle_it(c, 0);
if(!c->tcp_is_reading && !c->tcp_write_and_read)
return 0;
log_assert(fd != -1);
if(c->pp2_enabled && c->pp2_header_state != pp2_header_done) {
struct pp2_header* header = NULL;
size_t want_read_size = 0;
size_t current_read_size = 0;
if(c->pp2_header_state == pp2_header_none) {
want_read_size = PP2_HEADER_SIZE;
if(sldns_buffer_remaining(c->buffer)<want_read_size) {
log_err_addr("proxy_protocol: not enough "
"buffer size to read PROXYv2 header", "",
&c->repinfo.remote_addr,
c->repinfo.remote_addrlen);
return 0;
}
verbose(VERB_ALGO, "proxy_protocol: reading fixed "
"part of PROXYv2 header (len %lu)",
(unsigned long)want_read_size);
current_read_size = want_read_size;
if(c->tcp_byte_count < current_read_size) {
r = recv(fd, (void*)sldns_buffer_at(c->buffer,
c->tcp_byte_count),
current_read_size-c->tcp_byte_count, MSG_DONTWAIT);
if(r == 0) {
if(c->tcp_req_info)
return tcp_req_info_handle_read_close(c->tcp_req_info);
return 0;
} else if(r == -1) {
goto recv_error_initial;
}
c->tcp_byte_count += r;
+ sldns_buffer_skip(c->buffer, r);
if(c->tcp_byte_count != current_read_size) return 1;
c->pp2_header_state = pp2_header_init;
}
}
if(c->pp2_header_state == pp2_header_init) {
- header = pp2_read_header(c->buffer);
- if(!header) {
+ int err;
+ err = pp2_read_header(
+ sldns_buffer_begin(c->buffer),
+ sldns_buffer_limit(c->buffer));
+ if(err) {
log_err("proxy_protocol: could not parse "
- "PROXYv2 header");
+ "PROXYv2 header (%s)",
+ pp_lookup_error(err));
return 0;
}
+ header = (struct pp2_header*)sldns_buffer_begin(c->buffer);
want_read_size = ntohs(header->len);
- if(sldns_buffer_remaining(c->buffer) <
+ if(sldns_buffer_limit(c->buffer) <
PP2_HEADER_SIZE + want_read_size) {
log_err_addr("proxy_protocol: not enough "
"buffer size to read PROXYv2 header", "",
&c->repinfo.remote_addr,
c->repinfo.remote_addrlen);
return 0;
}
verbose(VERB_ALGO, "proxy_protocol: reading variable "
"part of PROXYv2 header (len %lu)",
(unsigned long)want_read_size);
current_read_size = PP2_HEADER_SIZE + want_read_size;
if(want_read_size == 0) {
/* nothing more to read; header is complete */
c->pp2_header_state = pp2_header_done;
} else if(c->tcp_byte_count < current_read_size) {
r = recv(fd, (void*)sldns_buffer_at(c->buffer,
c->tcp_byte_count),
current_read_size-c->tcp_byte_count, MSG_DONTWAIT);
if(r == 0) {
if(c->tcp_req_info)
return tcp_req_info_handle_read_close(c->tcp_req_info);
return 0;
} else if(r == -1) {
goto recv_error;
}
c->tcp_byte_count += r;
+ sldns_buffer_skip(c->buffer, r);
if(c->tcp_byte_count != current_read_size) return 1;
c->pp2_header_state = pp2_header_done;
}
}
if(c->pp2_header_state != pp2_header_done || !header) {
log_err_addr("proxy_protocol: wrong state for the "
"PROXYv2 header", "", &c->repinfo.remote_addr,
c->repinfo.remote_addrlen);
return 0;
}
+ sldns_buffer_flip(c->buffer);
if(!consume_pp2_header(c->buffer, &c->repinfo, 1)) {
log_err_addr("proxy_protocol: could not consume "
"PROXYv2 header", "", &c->repinfo.remote_addr,
c->repinfo.remote_addrlen);
return 0;
}
verbose(VERB_ALGO, "proxy_protocol: successful read of "
"PROXYv2 header");
/* Clear and reset the buffer to read the following
* DNS packet(s). */
sldns_buffer_clear(c->buffer);
c->tcp_byte_count = 0;
return 1;
}
if(c->tcp_byte_count < sizeof(uint16_t)) {
/* read length bytes */
r = recv(fd,(void*)sldns_buffer_at(c->buffer,c->tcp_byte_count),
sizeof(uint16_t)-c->tcp_byte_count, MSG_DONTWAIT);
if(r == 0) {
if(c->tcp_req_info)
return tcp_req_info_handle_read_close(c->tcp_req_info);
return 0;
} else if(r == -1) {
if(c->pp2_enabled) goto recv_error;
goto recv_error_initial;
}
c->tcp_byte_count += r;
if(c->tcp_byte_count != sizeof(uint16_t))
return 1;
if(sldns_buffer_read_u16_at(c->buffer, 0) >
sldns_buffer_capacity(c->buffer)) {
verbose(VERB_QUERY, "tcp: dropped larger than buffer");
return 0;
}
sldns_buffer_set_limit(c->buffer,
sldns_buffer_read_u16_at(c->buffer, 0));
if(!short_ok &&
sldns_buffer_limit(c->buffer) < LDNS_HEADER_SIZE) {
verbose(VERB_QUERY, "tcp: dropped bogus too short.");
return 0;
}
verbose(VERB_ALGO, "Reading tcp query of length %d",
(int)sldns_buffer_limit(c->buffer));
}
if(sldns_buffer_remaining(c->buffer) == 0)
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),
sldns_buffer_remaining(c->buffer), MSG_DONTWAIT);
if(r == 0) {
if(c->tcp_req_info)
return tcp_req_info_handle_read_close(c->tcp_req_info);
return 0;
} else if(r == -1) {
goto recv_error;
}
sldns_buffer_skip(c->buffer, r);
if(sldns_buffer_remaining(c->buffer) <= 0) {
tcp_callback_reader(c);
}
return 1;
recv_error_initial:
recv_initial = 1;
recv_error:
#ifndef USE_WINSOCK
if(errno == EINTR || errno == EAGAIN)
return 1;
if(recv_initial) {
#ifdef ECONNRESET
if(errno == ECONNRESET && verbosity < 2)
return 0; /* silence reset by peer */
#endif
#ifdef ECONNREFUSED
if(errno == ECONNREFUSED && verbosity < 2)
return 0; /* silence reset by peer */
#endif
#ifdef ENETUNREACH
if(errno == ENETUNREACH && verbosity < 2)
return 0; /* silence it */
#endif
#ifdef EHOSTDOWN
if(errno == EHOSTDOWN && verbosity < 2)
return 0; /* silence it */
#endif
#ifdef EHOSTUNREACH
if(errno == EHOSTUNREACH && verbosity < 2)
return 0; /* silence it */
#endif
#ifdef ENETDOWN
if(errno == ENETDOWN && verbosity < 2)
return 0; /* silence it */
#endif
#ifdef EACCES
if(errno == EACCES && verbosity < 2)
return 0; /* silence it */
#endif
#ifdef ENOTCONN
if(errno == ENOTCONN) {
log_err_addr("read (in tcp s) failed and this "
"could be because TCP Fast Open is "
"enabled [--disable-tfo-client "
"--disable-tfo-server] but does not "
"work", sock_strerror(errno),
&c->repinfo.remote_addr,
c->repinfo.remote_addrlen);
return 0;
}
#endif
}
#else /* USE_WINSOCK */
if(recv_initial) {
if(WSAGetLastError() == WSAECONNREFUSED && verbosity < 2)
return 0;
if(WSAGetLastError() == WSAEHOSTDOWN && verbosity < 2)
return 0;
if(WSAGetLastError() == WSAEHOSTUNREACH && verbosity < 2)
return 0;
if(WSAGetLastError() == WSAENETDOWN && verbosity < 2)
return 0;
if(WSAGetLastError() == WSAENETUNREACH && verbosity < 2)
return 0;
}
if(WSAGetLastError() == WSAECONNRESET)
return 0;
if(WSAGetLastError() == WSAEINPROGRESS)
return 1;
if(WSAGetLastError() == WSAEWOULDBLOCK) {
ub_winsock_tcp_wouldblock(c->ev->ev,
UB_EV_READ);
return 1;
}
#endif
log_err_addr("read (in tcp s)", sock_strerror(errno),
&c->repinfo.remote_addr, c->repinfo.remote_addrlen);
return 0;
}
/**
* Handle tcp writing callback.
* @param fd: file descriptor of socket.
* @param c: comm point to write buffer out of.
* @return: 0 on error
*/
static int
comm_point_tcp_handle_write(int fd, struct comm_point* c)
{
ssize_t r;
struct sldns_buffer *buffer;
log_assert(c->type == comm_tcp);
#ifdef USE_DNSCRYPT
buffer = c->dnscrypt_buffer;
#else
buffer = c->buffer;
#endif
if(c->tcp_is_reading && !c->ssl && !c->tcp_write_and_read)
return 0;
log_assert(fd != -1);
if(((!c->tcp_write_and_read && c->tcp_byte_count == 0) || (c->tcp_write_and_read && c->tcp_write_byte_count == 0)) && c->tcp_check_nb_connect) {
/* check for pending error from nonblocking connect */
/* 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,
&len) < 0){
#ifndef USE_WINSOCK
error = errno; /* on solaris errno is error */
#else /* USE_WINSOCK */
error = WSAGetLastError();
#endif
}
#ifndef USE_WINSOCK
#if defined(EINPROGRESS) && defined(EWOULDBLOCK)
if(error == EINPROGRESS || error == EWOULDBLOCK)
return 1; /* try again later */
else
#endif
if(error != 0 && verbosity < 2)
return 0; /* silence lots of chatter in the logs */
else if(error != 0) {
log_err_addr("tcp connect", strerror(error),
&c->repinfo.remote_addr,
c->repinfo.remote_addrlen);
#else /* USE_WINSOCK */
/* examine error */
if(error == WSAEINPROGRESS)
return 1;
else if(error == WSAEWOULDBLOCK) {
ub_winsock_tcp_wouldblock(c->ev->ev, UB_EV_WRITE);
return 1;
} else if(error != 0 && verbosity < 2)
return 0;
else if(error != 0) {
log_err_addr("tcp connect", wsa_strerror(error),
&c->repinfo.remote_addr,
c->repinfo.remote_addrlen);
#endif /* USE_WINSOCK */
return 0;
}
}
if(c->ssl)
return ssl_handle_it(c, 1);
#ifdef USE_MSG_FASTOPEN
/* 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) {
/* this form of sendmsg() does both a connect() and send() so need to
look for various flavours of error*/
uint16_t len = htons(c->tcp_write_and_read?c->tcp_write_pkt_len:sldns_buffer_limit(buffer));
struct msghdr msg;
struct iovec iov[2];
c->tcp_do_fastopen = 0;
memset(&msg, 0, sizeof(msg));
if(c->tcp_write_and_read) {
iov[0].iov_base = (uint8_t*)&len + c->tcp_write_byte_count;
iov[0].iov_len = sizeof(uint16_t) - c->tcp_write_byte_count;
iov[1].iov_base = c->tcp_write_pkt;
iov[1].iov_len = c->tcp_write_pkt_len;
} else {
iov[0].iov_base = (uint8_t*)&len + c->tcp_byte_count;
iov[0].iov_len = sizeof(uint16_t) - c->tcp_byte_count;
iov[1].iov_base = sldns_buffer_begin(buffer);
iov[1].iov_len = sldns_buffer_limit(buffer);
}
log_assert(iov[0].iov_len > 0);
msg.msg_name = &c->repinfo.remote_addr;
msg.msg_namelen = c->repinfo.remote_addrlen;
msg.msg_iov = iov;
msg.msg_iovlen = 2;
r = sendmsg(fd, &msg, MSG_FASTOPEN);
if (r == -1) {
#if defined(EINPROGRESS) && defined(EWOULDBLOCK)
/* Handshake is underway, maybe because no TFO cookie available.
Come back to write the message*/
if(errno == EINPROGRESS || errno == EWOULDBLOCK)
return 1;
#endif
if(errno == EINTR || errno == EAGAIN)
return 1;
/* Not handling EISCONN here as shouldn't ever hit that case.*/
if(errno != EPIPE
#ifdef EOPNOTSUPP
/* if /proc/sys/net/ipv4/tcp_fastopen is
* disabled on Linux, sendmsg may return
* 'Operation not supported', if so
* fallthrough to ordinary connect. */
&& errno != EOPNOTSUPP
#endif
&& errno != 0) {
if(verbosity < 2)
return 0; /* silence lots of chatter in the logs */
log_err_addr("tcp sendmsg", strerror(errno),
&c->repinfo.remote_addr,
c->repinfo.remote_addrlen);
return 0;
}
verbose(VERB_ALGO, "tcp sendmsg for fastopen failed (with %s), try normal connect", strerror(errno));
/* fallthrough to nonFASTOPEN
* (MSG_FASTOPEN on Linux 3 produces EPIPE)
* we need to perform connect() */
if(connect(fd, (struct sockaddr *)&c->repinfo.remote_addr,
c->repinfo.remote_addrlen) == -1) {
#ifdef EINPROGRESS
if(errno == EINPROGRESS)
return 1; /* wait until connect done*/
#endif
#ifdef USE_WINSOCK
if(WSAGetLastError() == WSAEINPROGRESS ||
WSAGetLastError() == WSAEWOULDBLOCK)
return 1; /* wait until connect done*/
#endif
if(tcp_connect_errno_needs_log(
(struct sockaddr *)&c->repinfo.remote_addr,
c->repinfo.remote_addrlen)) {
log_err_addr("outgoing tcp: connect after EPIPE for fastopen",
strerror(errno),
&c->repinfo.remote_addr,
c->repinfo.remote_addrlen);
}
return 0;
}
} else {
if(c->tcp_write_and_read) {
c->tcp_write_byte_count += r;
if(c->tcp_write_byte_count < sizeof(uint16_t))
return 1;
} else {
c->tcp_byte_count += r;
if(c->tcp_byte_count < sizeof(uint16_t))
return 1;
sldns_buffer_set_position(buffer, c->tcp_byte_count -
sizeof(uint16_t));
}
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;
}
}
}
#endif /* USE_MSG_FASTOPEN */
if((c->tcp_write_and_read?c->tcp_write_byte_count:c->tcp_byte_count) < sizeof(uint16_t)) {
uint16_t len = htons(c->tcp_write_and_read?c->tcp_write_pkt_len:sldns_buffer_limit(buffer));
#ifdef HAVE_WRITEV
struct iovec iov[2];
if(c->tcp_write_and_read) {
iov[0].iov_base = (uint8_t*)&len + c->tcp_write_byte_count;
iov[0].iov_len = sizeof(uint16_t) - c->tcp_write_byte_count;
iov[1].iov_base = c->tcp_write_pkt;
iov[1].iov_len = c->tcp_write_pkt_len;
} else {
iov[0].iov_base = (uint8_t*)&len + c->tcp_byte_count;
iov[0].iov_len = sizeof(uint16_t) - c->tcp_byte_count;
iov[1].iov_base = sldns_buffer_begin(buffer);
iov[1].iov_len = sldns_buffer_limit(buffer);
}
log_assert(iov[0].iov_len > 0);
r = writev(fd, iov, 2);
#else /* HAVE_WRITEV */
if(c->tcp_write_and_read) {
r = send(fd, (void*)(((uint8_t*)&len)+c->tcp_write_byte_count),
sizeof(uint16_t)-c->tcp_write_byte_count, 0);
} else {
r = send(fd, (void*)(((uint8_t*)&len)+c->tcp_byte_count),
sizeof(uint16_t)-c->tcp_byte_count, 0);
}
#endif /* HAVE_WRITEV */
if(r == -1) {
#ifndef USE_WINSOCK
# ifdef EPIPE
if(errno == EPIPE && verbosity < 2)
return 0; /* silence 'broken pipe' */
#endif
if(errno == EINTR || errno == EAGAIN)
return 1;
#ifdef ECONNRESET
if(errno == ECONNRESET && verbosity < 2)
return 0; /* silence reset by peer */
#endif
# ifdef HAVE_WRITEV
log_err_addr("tcp writev", strerror(errno),
&c->repinfo.remote_addr,
c->repinfo.remote_addrlen);
# else /* HAVE_WRITEV */
log_err_addr("tcp send s", strerror(errno),
&c->repinfo.remote_addr,
c->repinfo.remote_addrlen);
# endif /* HAVE_WRITEV */
#else
if(WSAGetLastError() == WSAENOTCONN)
return 1;
if(WSAGetLastError() == WSAEINPROGRESS)
return 1;
if(WSAGetLastError() == WSAEWOULDBLOCK) {
ub_winsock_tcp_wouldblock(c->ev->ev,
UB_EV_WRITE);
return 1;
}
if(WSAGetLastError() == WSAECONNRESET && verbosity < 2)
return 0; /* silence reset by peer */
log_err_addr("tcp send s",
wsa_strerror(WSAGetLastError()),
&c->repinfo.remote_addr,
c->repinfo.remote_addrlen);
#endif
return 0;
}
if(c->tcp_write_and_read) {
c->tcp_write_byte_count += r;
if(c->tcp_write_byte_count < sizeof(uint16_t))
return 1;
} else {
c->tcp_byte_count += r;
if(c->tcp_byte_count < sizeof(uint16_t))
return 1;
sldns_buffer_set_position(buffer, c->tcp_byte_count -
sizeof(uint16_t));
}
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;
}
}
log_assert(c->tcp_write_and_read || sldns_buffer_remaining(buffer) > 0);
log_assert(!c->tcp_write_and_read || c->tcp_write_byte_count < c->tcp_write_pkt_len + 2);
if(c->tcp_write_and_read) {
r = send(fd, (void*)(c->tcp_write_pkt + c->tcp_write_byte_count - 2),
c->tcp_write_pkt_len + 2 - c->tcp_write_byte_count, 0);
} else {
r = send(fd, (void*)sldns_buffer_current(buffer),
sldns_buffer_remaining(buffer), 0);
}
if(r == -1) {
#ifndef USE_WINSOCK
if(errno == EINTR || errno == EAGAIN)
return 1;
#ifdef ECONNRESET
if(errno == ECONNRESET && verbosity < 2)
return 0; /* silence reset by peer */
#endif
#else
if(WSAGetLastError() == WSAEINPROGRESS)
return 1;
if(WSAGetLastError() == WSAEWOULDBLOCK) {
ub_winsock_tcp_wouldblock(c->ev->ev, UB_EV_WRITE);
return 1;
}
if(WSAGetLastError() == WSAECONNRESET && verbosity < 2)
return 0; /* silence reset by peer */
#endif
log_err_addr("tcp send r", sock_strerror(errno),
&c->repinfo.remote_addr,
c->repinfo.remote_addrlen);
return 0;
}
if(c->tcp_write_and_read) {
c->tcp_write_byte_count += r;
} else {
sldns_buffer_skip(buffer, r);
}
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, 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) {
int r;
c->tcp_req_info->read_again = 0;
if(c->tcp_is_reading)
r = comm_point_tcp_handle_read(fd, c, 0);
else r = comm_point_tcp_handle_write(fd, c);
if(!r) {
reclaim_tcp_handler(c);
if(!c->tcp_do_close) {
fptr_ok(fptr_whitelist_comm_point(
c->callback));
(void)(*c->callback)(c, c->cb_arg,
NETEVENT_CLOSED, NULL);
}
return 0;
}
}
return 1;
}
/** read again to drain buffers when there could be more to read */
static void
tcp_more_read_again(int fd, struct comm_point* c)
{
/* if the packet is done, but another one could be waiting on
* the connection, the callback signals this, and we try again */
/* this continues until the read routines get EAGAIN or so,
* and thus does not call the callback, and the bool is 0 */
int* moreread = c->tcp_more_read_again;
while(moreread && *moreread) {
*moreread = 0;
if(!comm_point_tcp_handle_read(fd, c, 0)) {
reclaim_tcp_handler(c);
if(!c->tcp_do_close) {
fptr_ok(fptr_whitelist_comm_point(
c->callback));
(void)(*c->callback)(c, c->cb_arg,
NETEVENT_CLOSED, NULL);
}
return;
}
}
}
/** write again to fill up when there could be more to write */
static void
tcp_more_write_again(int fd, struct comm_point* c)
{
/* if the packet is done, but another is waiting to be written,
* the callback signals it and we try again. */
/* this continues until the write routines get EAGAIN or so,
* and thus does not call the callback, and the bool is 0 */
int* morewrite = c->tcp_more_write_again;
while(morewrite && *morewrite) {
*morewrite = 0;
if(!comm_point_tcp_handle_write(fd, c)) {
reclaim_tcp_handler(c);
if(!c->tcp_do_close) {
fptr_ok(fptr_whitelist_comm_point(
c->callback));
(void)(*c->callback)(c, c->cb_arg,
NETEVENT_CLOSED, NULL);
}
return;
}
}
}
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) {
c->dnscrypt = c->tcp_parent->dnscrypt;
}
if(c->dnscrypt && c->dnscrypt_buffer == c->buffer) {
c->dnscrypt_buffer = sldns_buffer_new(sldns_buffer_capacity(c->buffer));
if(!c->dnscrypt_buffer) {
log_err("Could not allocate dnscrypt buffer");
reclaim_tcp_handler(c);
if(!c->tcp_do_close) {
fptr_ok(fptr_whitelist_comm_point(
c->callback));
(void)(*c->callback)(c, c->cb_arg,
NETEVENT_CLOSED, NULL);
}
return;
}
}
#endif
if(event&UB_EV_TIMEOUT) {
verbose(VERB_QUERY, "tcp took too long, dropped");
reclaim_tcp_handler(c);
if(!c->tcp_do_close) {
fptr_ok(fptr_whitelist_comm_point(c->callback));
(void)(*c->callback)(c, c->cb_arg,
NETEVENT_TIMEOUT, NULL);
}
return;
}
if(event&UB_EV_READ
#ifdef USE_MSG_FASTOPEN
&& !(c->tcp_do_fastopen && (event&UB_EV_WRITE))
#endif
) {
int has_tcpq = (c->tcp_req_info != NULL);
int* moreread = c->tcp_more_read_again;
if(!comm_point_tcp_handle_read(fd, c, 0)) {
reclaim_tcp_handler(c);
if(!c->tcp_do_close) {
fptr_ok(fptr_whitelist_comm_point(
c->callback));
(void)(*c->callback)(c, c->cb_arg,
NETEVENT_CLOSED, NULL);
}
return;
}
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;
}
if(event&UB_EV_WRITE) {
int has_tcpq = (c->tcp_req_info != NULL);
int* morewrite = c->tcp_more_write_again;
if(!comm_point_tcp_handle_write(fd, c)) {
reclaim_tcp_handler(c);
if(!c->tcp_do_close) {
fptr_ok(fptr_whitelist_comm_point(
c->callback));
(void)(*c->callback)(c, c->cb_arg,
NETEVENT_CLOSED, NULL);
}
return;
}
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;
}
log_err("Ignored event %d for tcphdl.", event);
}
/** Make http handler free for next assignment */
static void
reclaim_http_handler(struct comm_point* c)
{
log_assert(c->type == comm_http);
if(c->ssl) {
#ifdef HAVE_SSL
SSL_shutdown(c->ssl);
SSL_free(c->ssl);
c->ssl = NULL;
#endif
}
comm_point_close(c);
if(c->tcp_parent) {
if(c != c->tcp_parent->tcp_free) {
c->tcp_parent->cur_tcp_count--;
c->tcp_free = c->tcp_parent->tcp_free;
c->tcp_parent->tcp_free = c;
}
if(!c->tcp_free) {
/* re-enable listening on accept socket */
comm_point_start_listening(c->tcp_parent, -1, -1);
}
}
}
/** read more data for http (with ssl) */
static int
ssl_http_read_more(struct comm_point* c)
{
#ifdef HAVE_SSL
int r;
log_assert(sldns_buffer_remaining(c->buffer) > 0);
ERR_clear_error();
r = SSL_read(c->ssl, (void*)sldns_buffer_current(c->buffer),
(int)sldns_buffer_remaining(c->buffer));
if(r <= 0) {
int want = SSL_get_error(c->ssl, r);
if(want == SSL_ERROR_ZERO_RETURN) {
return 0; /* shutdown, closed */
} else if(want == SSL_ERROR_WANT_READ) {
return 1; /* read more later */
} else if(want == SSL_ERROR_WANT_WRITE) {
c->ssl_shake_state = comm_ssl_shake_hs_write;
comm_point_listen_for_rw(c, 0, 1);
return 1;
} else if(want == SSL_ERROR_SYSCALL) {
#ifdef ECONNRESET
if(errno == ECONNRESET && verbosity < 2)
return 0; /* silence reset by peer */
#endif
if(errno != 0)
log_err("SSL_read syscall: %s",
strerror(errno));
return 0;
}
- log_crypto_err("could not SSL_read");
+ log_crypto_err_io("could not SSL_read", want);
return 0;
}
verbose(VERB_ALGO, "ssl http read more skip to %d + %d",
(int)sldns_buffer_position(c->buffer), (int)r);
sldns_buffer_skip(c->buffer, (ssize_t)r);
return 1;
#else
(void)c;
return 0;
#endif /* HAVE_SSL */
}
/** read more data for http */
static int
http_read_more(int fd, struct comm_point* c)
{
ssize_t r;
log_assert(sldns_buffer_remaining(c->buffer) > 0);
r = recv(fd, (void*)sldns_buffer_current(c->buffer),
sldns_buffer_remaining(c->buffer), MSG_DONTWAIT);
if(r == 0) {
return 0;
} else if(r == -1) {
#ifndef USE_WINSOCK
if(errno == EINTR || errno == EAGAIN)
return 1;
#else /* USE_WINSOCK */
if(WSAGetLastError() == WSAECONNRESET)
return 0;
if(WSAGetLastError() == WSAEINPROGRESS)
return 1;
if(WSAGetLastError() == WSAEWOULDBLOCK) {
ub_winsock_tcp_wouldblock(c->ev->ev, UB_EV_READ);
return 1;
}
#endif
log_err_addr("read (in http r)", sock_strerror(errno),
&c->repinfo.remote_addr, c->repinfo.remote_addrlen);
return 0;
}
verbose(VERB_ALGO, "http read more skip to %d + %d",
(int)sldns_buffer_position(c->buffer), (int)r);
sldns_buffer_skip(c->buffer, r);
return 1;
}
/** return true if http header has been read (one line complete) */
static int
http_header_done(sldns_buffer* buf)
{
size_t i;
for(i=sldns_buffer_position(buf); i<sldns_buffer_limit(buf); i++) {
/* there was a \r before the \n, but we ignore that */
if((char)sldns_buffer_read_u8_at(buf, i) == '\n')
return 1;
}
return 0;
}
/** return character string into buffer for header line, moves buffer
* past that line and puts zero terminator into linefeed-newline */
static char*
http_header_line(sldns_buffer* buf)
{
char* result = (char*)sldns_buffer_current(buf);
size_t i;
for(i=sldns_buffer_position(buf); i<sldns_buffer_limit(buf); i++) {
/* terminate the string on the \r */
if((char)sldns_buffer_read_u8_at(buf, i) == '\r')
sldns_buffer_write_u8_at(buf, i, 0);
/* terminate on the \n and skip past the it and done */
if((char)sldns_buffer_read_u8_at(buf, i) == '\n') {
sldns_buffer_write_u8_at(buf, i, 0);
sldns_buffer_set_position(buf, i+1);
return result;
}
}
return NULL;
}
/** move unread buffer to start and clear rest for putting the rest into it */
static void
http_moveover_buffer(sldns_buffer* buf)
{
size_t pos = sldns_buffer_position(buf);
size_t len = sldns_buffer_remaining(buf);
sldns_buffer_clear(buf);
memmove(sldns_buffer_begin(buf), sldns_buffer_at(buf, pos), len);
sldns_buffer_set_position(buf, len);
}
/** a http header is complete, process it */
static int
http_process_initial_header(struct comm_point* c)
{
char* line = http_header_line(c->buffer);
if(!line) return 1;
verbose(VERB_ALGO, "http header: %s", line);
if(strncasecmp(line, "HTTP/1.1 ", 9) == 0) {
/* check returncode */
if(line[9] != '2') {
verbose(VERB_ALGO, "http bad status %s", line+9);
return 0;
}
} else if(strncasecmp(line, "Content-Length: ", 16) == 0) {
if(!c->http_is_chunked)
c->tcp_byte_count = (size_t)atoi(line+16);
} else if(strncasecmp(line, "Transfer-Encoding: chunked", 19+7) == 0) {
c->tcp_byte_count = 0;
c->http_is_chunked = 1;
} else if(line[0] == 0) {
/* end of initial headers */
c->http_in_headers = 0;
if(c->http_is_chunked)
c->http_in_chunk_headers = 1;
/* remove header text from front of buffer
* the buffer is going to be used to return the data segment
* itself and we don't want the header to get returned
* prepended with it */
http_moveover_buffer(c->buffer);
sldns_buffer_flip(c->buffer);
return 1;
}
/* ignore other headers */
return 1;
}
/** a chunk header is complete, process it, return 0=fail, 1=continue next
* header line, 2=done with chunked transfer*/
static int
http_process_chunk_header(struct comm_point* c)
{
char* line = http_header_line(c->buffer);
if(!line) return 1;
if(c->http_in_chunk_headers == 3) {
verbose(VERB_ALGO, "http chunk trailer: %s", line);
/* are we done ? */
if(line[0] == 0 && c->tcp_byte_count == 0) {
/* callback of http reader when NETEVENT_DONE,
* end of data, with no data in buffer */
sldns_buffer_set_position(c->buffer, 0);
sldns_buffer_set_limit(c->buffer, 0);
fptr_ok(fptr_whitelist_comm_point(c->callback));
(void)(*c->callback)(c, c->cb_arg, NETEVENT_DONE, NULL);
/* return that we are done */
return 2;
}
if(line[0] == 0) {
/* continue with header of the next chunk */
c->http_in_chunk_headers = 1;
/* remove header text from front of buffer */
http_moveover_buffer(c->buffer);
sldns_buffer_flip(c->buffer);
return 1;
}
/* ignore further trail headers */
return 1;
}
verbose(VERB_ALGO, "http chunk header: %s", line);
if(c->http_in_chunk_headers == 1) {
/* read chunked start line */
char* end = NULL;
c->tcp_byte_count = (size_t)strtol(line, &end, 16);
if(end == line)
return 0;
c->http_in_chunk_headers = 0;
/* remove header text from front of buffer */
http_moveover_buffer(c->buffer);
sldns_buffer_flip(c->buffer);
if(c->tcp_byte_count == 0) {
/* done with chunks, process chunk_trailer lines */
c->http_in_chunk_headers = 3;
}
return 1;
}
/* ignore other headers */
return 1;
}
/** handle nonchunked data segment, 0=fail, 1=wait */
static int
http_nonchunk_segment(struct comm_point* c)
{
/* c->buffer at position..limit has new data we read in.
* the buffer itself is full of nonchunked data.
* we are looking to read tcp_byte_count more data
* and then the transfer is done. */
size_t remainbufferlen;
size_t got_now = sldns_buffer_limit(c->buffer);
if(c->tcp_byte_count <= got_now) {
/* done, this is the last data fragment */
c->http_stored = 0;
sldns_buffer_set_position(c->buffer, 0);
fptr_ok(fptr_whitelist_comm_point(c->callback));
(void)(*c->callback)(c, c->cb_arg, NETEVENT_DONE, NULL);
return 1;
}
/* if we have the buffer space,
* read more data collected into the buffer */
remainbufferlen = sldns_buffer_capacity(c->buffer) -
sldns_buffer_limit(c->buffer);
if(remainbufferlen+got_now >= c->tcp_byte_count ||
remainbufferlen >= (size_t)(c->ssl?16384:2048)) {
size_t total = sldns_buffer_limit(c->buffer);
sldns_buffer_clear(c->buffer);
sldns_buffer_set_position(c->buffer, total);
c->http_stored = total;
/* return and wait to read more */
return 1;
}
/* call callback with this data amount, then
* wait for more */
c->tcp_byte_count -= got_now;
c->http_stored = 0;
sldns_buffer_set_position(c->buffer, 0);
fptr_ok(fptr_whitelist_comm_point(c->callback));
(void)(*c->callback)(c, c->cb_arg, NETEVENT_NOERROR, NULL);
/* c->callback has to buffer_clear(c->buffer). */
/* return and wait to read more */
return 1;
}
/** handle chunked data segment, return 0=fail, 1=wait, 2=process more */
static int
http_chunked_segment(struct comm_point* c)
{
/* the c->buffer has from position..limit new data we read. */
/* the current chunk has length tcp_byte_count.
* once we read that read more chunk headers.
*/
size_t remainbufferlen;
size_t got_now = sldns_buffer_limit(c->buffer) - c->http_stored;
verbose(VERB_ALGO, "http_chunked_segment: got now %d, tcpbytcount %d, http_stored %d, buffer pos %d, buffer limit %d", (int)got_now, (int)c->tcp_byte_count, (int)c->http_stored, (int)sldns_buffer_position(c->buffer), (int)sldns_buffer_limit(c->buffer));
if(c->tcp_byte_count <= got_now) {
/* the chunk has completed (with perhaps some extra data
* from next chunk header and next chunk) */
/* save too much info into temp buffer */
size_t fraglen;
struct comm_reply repinfo;
c->http_stored = 0;
sldns_buffer_skip(c->buffer, (ssize_t)c->tcp_byte_count);
sldns_buffer_clear(c->http_temp);
sldns_buffer_write(c->http_temp,
sldns_buffer_current(c->buffer),
sldns_buffer_remaining(c->buffer));
sldns_buffer_flip(c->http_temp);
/* callback with this fragment */
fraglen = sldns_buffer_position(c->buffer);
sldns_buffer_set_position(c->buffer, 0);
sldns_buffer_set_limit(c->buffer, fraglen);
repinfo = c->repinfo;
fptr_ok(fptr_whitelist_comm_point(c->callback));
(void)(*c->callback)(c, c->cb_arg, NETEVENT_NOERROR, &repinfo);
/* c->callback has to buffer_clear(). */
/* is commpoint deleted? */
if(!repinfo.c) {
return 1;
}
/* copy waiting info */
sldns_buffer_clear(c->buffer);
sldns_buffer_write(c->buffer,
sldns_buffer_begin(c->http_temp),
sldns_buffer_remaining(c->http_temp));
sldns_buffer_flip(c->buffer);
/* process end of chunk trailer header lines, until
* an empty line */
c->http_in_chunk_headers = 3;
/* process more data in buffer (if any) */
return 2;
}
c->tcp_byte_count -= got_now;
/* if we have the buffer space,
* read more data collected into the buffer */
remainbufferlen = sldns_buffer_capacity(c->buffer) -
sldns_buffer_limit(c->buffer);
if(remainbufferlen >= c->tcp_byte_count ||
remainbufferlen >= 2048) {
size_t total = sldns_buffer_limit(c->buffer);
sldns_buffer_clear(c->buffer);
sldns_buffer_set_position(c->buffer, total);
c->http_stored = total;
/* 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);
fptr_ok(fptr_whitelist_comm_point(c->callback));
(void)(*c->callback)(c, c->cb_arg, NETEVENT_NOERROR, NULL);
/* c->callback has to buffer_clear(c->buffer). */
/* return and wait to read more */
return 1;
}
#ifdef HAVE_NGHTTP2
/** Create new http2 session. Called when creating handling comm point. */
static struct http2_session* http2_session_create(struct comm_point* c)
{
struct http2_session* session = calloc(1, sizeof(*session));
if(!session) {
log_err("malloc failure while creating http2 session");
return NULL;
}
session->c = c;
return session;
}
#endif
/** Delete http2 session. After closing connection or on error */
static void http2_session_delete(struct http2_session* h2_session)
{
#ifdef HAVE_NGHTTP2
if(h2_session->callbacks)
nghttp2_session_callbacks_del(h2_session->callbacks);
free(h2_session);
#else
(void)h2_session;
#endif
}
#ifdef HAVE_NGHTTP2
struct http2_stream* http2_stream_create(int32_t stream_id)
{
struct http2_stream* h2_stream = calloc(1, sizeof(*h2_stream));
if(!h2_stream) {
log_err("malloc failure while creating http2 stream");
return NULL;
}
h2_stream->stream_id = stream_id;
return h2_stream;
}
/** Delete http2 stream. After session delete or stream close callback */
static void http2_stream_delete(struct http2_session* h2_session,
struct http2_stream* h2_stream)
{
if(h2_stream->mesh_state) {
mesh_state_remove_reply(h2_stream->mesh, h2_stream->mesh_state,
h2_session->c);
h2_stream->mesh_state = NULL;
}
http2_req_stream_clear(h2_stream);
free(h2_stream);
}
#endif
void http2_stream_add_meshstate(struct http2_stream* h2_stream,
struct mesh_area* mesh, struct mesh_state* m)
{
h2_stream->mesh = mesh;
h2_stream->mesh_state = m;
}
/** delete http2 session server. After closing connection. */
static void http2_session_server_delete(struct http2_session* h2_session)
{
#ifdef HAVE_NGHTTP2
struct http2_stream* h2_stream, *next;
nghttp2_session_del(h2_session->session); /* NULL input is fine */
h2_session->session = NULL;
for(h2_stream = h2_session->first_stream; h2_stream;) {
next = h2_stream->next;
http2_stream_delete(h2_session, h2_stream);
h2_stream = next;
}
h2_session->first_stream = NULL;
h2_session->is_drop = 0;
h2_session->postpone_drop = 0;
h2_session->c->h2_stream = NULL;
#endif
(void)h2_session;
}
#ifdef HAVE_NGHTTP2
void http2_session_add_stream(struct http2_session* h2_session,
struct http2_stream* h2_stream)
{
if(h2_session->first_stream)
h2_session->first_stream->prev = h2_stream;
h2_stream->next = h2_session->first_stream;
h2_session->first_stream = h2_stream;
}
/** remove stream from session linked list. After stream close callback or
* closing connection */
static void http2_session_remove_stream(struct http2_session* h2_session,
struct http2_stream* h2_stream)
{
if(h2_stream->prev)
h2_stream->prev->next = h2_stream->next;
else
h2_session->first_stream = h2_stream->next;
if(h2_stream->next)
h2_stream->next->prev = h2_stream->prev;
}
int http2_stream_close_cb(nghttp2_session* ATTR_UNUSED(session),
int32_t stream_id, uint32_t ATTR_UNUSED(error_code), void* cb_arg)
{
struct http2_stream* h2_stream;
struct http2_session* h2_session = (struct http2_session*)cb_arg;
if(!(h2_stream = nghttp2_session_get_stream_user_data(
h2_session->session, stream_id))) {
return 0;
}
http2_session_remove_stream(h2_session, h2_stream);
http2_stream_delete(h2_session, h2_stream);
return 0;
}
ssize_t http2_recv_cb(nghttp2_session* ATTR_UNUSED(session), uint8_t* buf,
size_t len, int ATTR_UNUSED(flags), void* cb_arg)
{
struct http2_session* h2_session = (struct http2_session*)cb_arg;
ssize_t ret;
log_assert(h2_session->c->type == comm_http);
log_assert(h2_session->c->h2_session);
#ifdef HAVE_SSL
if(h2_session->c->ssl) {
int r;
ERR_clear_error();
r = SSL_read(h2_session->c->ssl, buf, len);
if(r <= 0) {
int want = SSL_get_error(h2_session->c->ssl, r);
if(want == SSL_ERROR_ZERO_RETURN) {
return NGHTTP2_ERR_EOF;
} else if(want == SSL_ERROR_WANT_READ) {
return NGHTTP2_ERR_WOULDBLOCK;
} else if(want == SSL_ERROR_WANT_WRITE) {
h2_session->c->ssl_shake_state = comm_ssl_shake_hs_write;
comm_point_listen_for_rw(h2_session->c, 0, 1);
return NGHTTP2_ERR_WOULDBLOCK;
} else if(want == SSL_ERROR_SYSCALL) {
#ifdef ECONNRESET
if(errno == ECONNRESET && verbosity < 2)
return NGHTTP2_ERR_CALLBACK_FAILURE;
#endif
if(errno != 0)
log_err("SSL_read syscall: %s",
strerror(errno));
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
- log_crypto_err("could not SSL_read");
+ log_crypto_err_io("could not SSL_read", want);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
return r;
}
#endif /* HAVE_SSL */
ret = recv(h2_session->c->fd, buf, len, MSG_DONTWAIT);
if(ret == 0) {
return NGHTTP2_ERR_EOF;
} else if(ret < 0) {
#ifndef USE_WINSOCK
if(errno == EINTR || errno == EAGAIN)
return NGHTTP2_ERR_WOULDBLOCK;
#ifdef ECONNRESET
if(errno == ECONNRESET && verbosity < 2)
return NGHTTP2_ERR_CALLBACK_FAILURE;
#endif
log_err_addr("could not http2 recv: %s", strerror(errno),
&h2_session->c->repinfo.remote_addr,
h2_session->c->repinfo.remote_addrlen);
#else /* USE_WINSOCK */
if(WSAGetLastError() == WSAECONNRESET)
return NGHTTP2_ERR_CALLBACK_FAILURE;
if(WSAGetLastError() == WSAEINPROGRESS)
return NGHTTP2_ERR_WOULDBLOCK;
if(WSAGetLastError() == WSAEWOULDBLOCK) {
ub_winsock_tcp_wouldblock(h2_session->c->ev->ev,
UB_EV_READ);
return NGHTTP2_ERR_WOULDBLOCK;
}
log_err_addr("could not http2 recv: %s",
wsa_strerror(WSAGetLastError()),
&h2_session->c->repinfo.remote_addr,
h2_session->c->repinfo.remote_addrlen);
#endif
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
return ret;
}
#endif /* HAVE_NGHTTP2 */
/** Handle http2 read */
static int
comm_point_http2_handle_read(int ATTR_UNUSED(fd), struct comm_point* c)
{
#ifdef HAVE_NGHTTP2
int ret;
log_assert(c->h2_session);
/* reading until recv cb returns NGHTTP2_ERR_WOULDBLOCK */
ret = nghttp2_session_recv(c->h2_session->session);
if(ret) {
if(ret != NGHTTP2_ERR_EOF &&
ret != NGHTTP2_ERR_CALLBACK_FAILURE) {
char a[256];
addr_to_str(&c->repinfo.remote_addr,
c->repinfo.remote_addrlen, a, sizeof(a));
verbose(VERB_QUERY, "http2: session_recv from %s failed, "
"error: %s", a, nghttp2_strerror(ret));
}
return 0;
}
if(nghttp2_session_want_write(c->h2_session->session)) {
c->tcp_is_reading = 0;
comm_point_stop_listening(c);
comm_point_start_listening(c, -1, adjusted_tcp_timeout(c));
} else if(!nghttp2_session_want_read(c->h2_session->session))
return 0; /* connection can be closed */
return 1;
#else
(void)c;
return 0;
#endif
}
/**
* Handle http reading callback.
* @param fd: file descriptor of socket.
* @param c: comm point to read from into buffer.
* @return: 0 on error
*/
static int
comm_point_http_handle_read(int fd, struct comm_point* c)
{
log_assert(c->type == comm_http);
log_assert(fd != -1);
/* if we are in ssl handshake, handle SSL handshake */
#ifdef HAVE_SSL
if(c->ssl && c->ssl_shake_state != comm_ssl_shake_none) {
if(!ssl_handshake(c))
return 0;
if(c->ssl_shake_state != comm_ssl_shake_none)
return 1;
}
#endif /* HAVE_SSL */
if(!c->tcp_is_reading)
return 1;
if(c->use_h2) {
return comm_point_http2_handle_read(fd, c);
}
/* http version is <= http/1.1 */
if(c->http_min_version >= http_version_2) {
/* HTTP/2 failed, not allowed to use lower version. */
return 0;
}
/* read more data */
if(c->ssl) {
if(!ssl_http_read_more(c))
return 0;
} else {
if(!http_read_more(fd, c))
return 0;
}
if(c->http_stored >= sldns_buffer_position(c->buffer)) {
/* read did not work but we wanted more data, there is
* no bytes to process now. */
return 1;
}
sldns_buffer_flip(c->buffer);
/* if we are partway in a segment of data, position us at the point
* where we left off previously */
if(c->http_stored < sldns_buffer_limit(c->buffer))
sldns_buffer_set_position(c->buffer, c->http_stored);
else sldns_buffer_set_position(c->buffer, sldns_buffer_limit(c->buffer));
while(sldns_buffer_remaining(c->buffer) > 0) {
/* Handle HTTP/1.x data */
/* if we are reading headers, read more headers */
if(c->http_in_headers || c->http_in_chunk_headers) {
/* if header is done, process the header */
if(!http_header_done(c->buffer)) {
/* copy remaining data to front of buffer
* and set rest for writing into it */
http_moveover_buffer(c->buffer);
/* return and wait to read more */
return 1;
}
if(!c->http_in_chunk_headers) {
/* process initial headers */
if(!http_process_initial_header(c))
return 0;
} else {
/* process chunk headers */
int r = http_process_chunk_header(c);
if(r == 0) return 0;
if(r == 2) return 1; /* done */
/* r == 1, continue */
}
/* see if we have more to process */
continue;
}
if(!c->http_is_chunked) {
/* if we are reading nonchunks, process that*/
return http_nonchunk_segment(c);
} else {
/* if we are reading chunks, read the chunk */
int r = http_chunked_segment(c);
if(r == 0) return 0;
if(r == 1) return 1;
continue;
}
}
/* broke out of the loop; could not process header instead need
* to read more */
/* moveover any remaining data and read more data */
http_moveover_buffer(c->buffer);
/* return and wait to read more */
return 1;
}
/** check pending connect for http */
static int
http_check_connect(int fd, struct comm_point* c)
{
/* check for pending error from nonblocking connect */
/* 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,
&len) < 0){
#ifndef USE_WINSOCK
error = errno; /* on solaris errno is error */
#else /* USE_WINSOCK */
error = WSAGetLastError();
#endif
}
#ifndef USE_WINSOCK
#if defined(EINPROGRESS) && defined(EWOULDBLOCK)
if(error == EINPROGRESS || error == EWOULDBLOCK)
return 1; /* try again later */
else
#endif
if(error != 0 && verbosity < 2)
return 0; /* silence lots of chatter in the logs */
else if(error != 0) {
log_err_addr("http connect", strerror(error),
&c->repinfo.remote_addr, c->repinfo.remote_addrlen);
#else /* USE_WINSOCK */
/* examine error */
if(error == WSAEINPROGRESS)
return 1;
else if(error == WSAEWOULDBLOCK) {
ub_winsock_tcp_wouldblock(c->ev->ev, UB_EV_WRITE);
return 1;
} else if(error != 0 && verbosity < 2)
return 0;
else if(error != 0) {
log_err_addr("http connect", wsa_strerror(error),
&c->repinfo.remote_addr, c->repinfo.remote_addrlen);
#endif /* USE_WINSOCK */
return 0;
}
/* keep on processing this socket */
return 2;
}
/** write more data for http (with ssl) */
static int
ssl_http_write_more(struct comm_point* c)
{
#ifdef HAVE_SSL
int r;
log_assert(sldns_buffer_remaining(c->buffer) > 0);
ERR_clear_error();
r = SSL_write(c->ssl, (void*)sldns_buffer_current(c->buffer),
(int)sldns_buffer_remaining(c->buffer));
if(r <= 0) {
int want = SSL_get_error(c->ssl, r);
if(want == SSL_ERROR_ZERO_RETURN) {
return 0; /* closed */
} else if(want == SSL_ERROR_WANT_READ) {
c->ssl_shake_state = comm_ssl_shake_hs_read;
comm_point_listen_for_rw(c, 1, 0);
return 1; /* wait for read condition */
} else if(want == SSL_ERROR_WANT_WRITE) {
return 1; /* write more later */
} else if(want == SSL_ERROR_SYSCALL) {
#ifdef EPIPE
if(errno == EPIPE && verbosity < 2)
return 0; /* silence 'broken pipe' */
#endif
if(errno != 0)
log_err("SSL_write syscall: %s",
strerror(errno));
return 0;
}
- log_crypto_err("could not SSL_write");
+ log_crypto_err_io("could not SSL_write", want);
return 0;
}
sldns_buffer_skip(c->buffer, (ssize_t)r);
return 1;
#else
(void)c;
return 0;
#endif /* HAVE_SSL */
}
/** write more data for http */
static int
http_write_more(int fd, struct comm_point* c)
{
ssize_t r;
log_assert(sldns_buffer_remaining(c->buffer) > 0);
r = send(fd, (void*)sldns_buffer_current(c->buffer),
sldns_buffer_remaining(c->buffer), 0);
if(r == -1) {
#ifndef USE_WINSOCK
if(errno == EINTR || errno == EAGAIN)
return 1;
#else
if(WSAGetLastError() == WSAEINPROGRESS)
return 1;
if(WSAGetLastError() == WSAEWOULDBLOCK) {
ub_winsock_tcp_wouldblock(c->ev->ev, UB_EV_WRITE);
return 1;
}
#endif
log_err_addr("http send r", sock_strerror(errno),
&c->repinfo.remote_addr, c->repinfo.remote_addrlen);
return 0;
}
sldns_buffer_skip(c->buffer, r);
return 1;
}
#ifdef HAVE_NGHTTP2
ssize_t http2_send_cb(nghttp2_session* ATTR_UNUSED(session), const uint8_t* buf,
size_t len, int ATTR_UNUSED(flags), void* cb_arg)
{
ssize_t ret;
struct http2_session* h2_session = (struct http2_session*)cb_arg;
log_assert(h2_session->c->type == comm_http);
log_assert(h2_session->c->h2_session);
#ifdef HAVE_SSL
if(h2_session->c->ssl) {
int r;
ERR_clear_error();
r = SSL_write(h2_session->c->ssl, buf, len);
if(r <= 0) {
int want = SSL_get_error(h2_session->c->ssl, r);
if(want == SSL_ERROR_ZERO_RETURN) {
return NGHTTP2_ERR_CALLBACK_FAILURE;
} else if(want == SSL_ERROR_WANT_READ) {
h2_session->c->ssl_shake_state = comm_ssl_shake_hs_read;
comm_point_listen_for_rw(h2_session->c, 1, 0);
return NGHTTP2_ERR_WOULDBLOCK;
} else if(want == SSL_ERROR_WANT_WRITE) {
return NGHTTP2_ERR_WOULDBLOCK;
} else if(want == SSL_ERROR_SYSCALL) {
#ifdef EPIPE
if(errno == EPIPE && verbosity < 2)
return NGHTTP2_ERR_CALLBACK_FAILURE;
#endif
if(errno != 0)
log_err("SSL_write syscall: %s",
strerror(errno));
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
- log_crypto_err("could not SSL_write");
+ log_crypto_err_io("could not SSL_write", want);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
return r;
}
#endif /* HAVE_SSL */
ret = send(h2_session->c->fd, buf, len, 0);
if(ret == 0) {
return NGHTTP2_ERR_CALLBACK_FAILURE;
} else if(ret < 0) {
#ifndef USE_WINSOCK
if(errno == EINTR || errno == EAGAIN)
return NGHTTP2_ERR_WOULDBLOCK;
#ifdef EPIPE
if(errno == EPIPE && verbosity < 2)
return NGHTTP2_ERR_CALLBACK_FAILURE;
#endif
#ifdef ECONNRESET
if(errno == ECONNRESET && verbosity < 2)
return NGHTTP2_ERR_CALLBACK_FAILURE;
#endif
log_err_addr("could not http2 write: %s", strerror(errno),
&h2_session->c->repinfo.remote_addr,
h2_session->c->repinfo.remote_addrlen);
#else /* USE_WINSOCK */
if(WSAGetLastError() == WSAENOTCONN)
return NGHTTP2_ERR_WOULDBLOCK;
if(WSAGetLastError() == WSAEINPROGRESS)
return NGHTTP2_ERR_WOULDBLOCK;
if(WSAGetLastError() == WSAEWOULDBLOCK) {
ub_winsock_tcp_wouldblock(h2_session->c->ev->ev,
UB_EV_WRITE);
return NGHTTP2_ERR_WOULDBLOCK;
}
if(WSAGetLastError() == WSAECONNRESET && verbosity < 2)
return NGHTTP2_ERR_CALLBACK_FAILURE;
log_err_addr("could not http2 write: %s",
wsa_strerror(WSAGetLastError()),
&h2_session->c->repinfo.remote_addr,
h2_session->c->repinfo.remote_addrlen);
#endif
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
return ret;
}
#endif /* HAVE_NGHTTP2 */
/** Handle http2 writing */
static int
comm_point_http2_handle_write(int ATTR_UNUSED(fd), struct comm_point* c)
{
#ifdef HAVE_NGHTTP2
int ret;
log_assert(c->h2_session);
ret = nghttp2_session_send(c->h2_session->session);
if(ret) {
verbose(VERB_QUERY, "http2: session_send failed, "
"error: %s", nghttp2_strerror(ret));
return 0;
}
if(nghttp2_session_want_read(c->h2_session->session)) {
c->tcp_is_reading = 1;
comm_point_stop_listening(c);
comm_point_start_listening(c, -1, adjusted_tcp_timeout(c));
} else if(!nghttp2_session_want_write(c->h2_session->session))
return 0; /* connection can be closed */
return 1;
#else
(void)c;
return 0;
#endif
}
/**
* Handle http writing callback.
* @param fd: file descriptor of socket.
* @param c: comm point to write buffer out of.
* @return: 0 on error
*/
static int
comm_point_http_handle_write(int fd, struct comm_point* c)
{
log_assert(c->type == comm_http);
log_assert(fd != -1);
/* check pending connect errors, if that fails, we wait for more,
* or we can continue to write contents */
if(c->tcp_check_nb_connect) {
int r = http_check_connect(fd, c);
if(r == 0) return 0;
if(r == 1) return 1;
c->tcp_check_nb_connect = 0;
}
/* if we are in ssl handshake, handle SSL handshake */
#ifdef HAVE_SSL
if(c->ssl && c->ssl_shake_state != comm_ssl_shake_none) {
if(!ssl_handshake(c))
return 0;
if(c->ssl_shake_state != comm_ssl_shake_none)
return 1;
}
#endif /* HAVE_SSL */
if(c->tcp_is_reading)
return 1;
if(c->use_h2) {
return comm_point_http2_handle_write(fd, c);
}
/* http version is <= http/1.1 */
if(c->http_min_version >= http_version_2) {
/* HTTP/2 failed, not allowed to use lower version. */
return 0;
}
/* if we are writing, write more */
if(c->ssl) {
if(!ssl_http_write_more(c))
return 0;
} else {
if(!http_write_more(fd, c))
return 0;
}
/* we write a single buffer contents, that can contain
* the http request, and then flip to read the results */
/* see if write is done */
if(sldns_buffer_remaining(c->buffer) == 0) {
sldns_buffer_clear(c->buffer);
if(c->tcp_do_toggle_rw)
c->tcp_is_reading = 1;
c->tcp_byte_count = 0;
/* switch from listening(write) to listening(read) */
comm_point_stop_listening(c);
comm_point_start_listening(c, -1, -1);
}
return 1;
}
void
comm_point_http_handle_callback(int fd, short event, void* arg)
{
struct comm_point* c = (struct comm_point*)arg;
log_assert(c->type == comm_http);
ub_comm_base_now(c->ev->base);
if(event&UB_EV_TIMEOUT) {
verbose(VERB_QUERY, "http took too long, dropped");
reclaim_http_handler(c);
if(!c->tcp_do_close) {
fptr_ok(fptr_whitelist_comm_point(c->callback));
(void)(*c->callback)(c, c->cb_arg,
NETEVENT_TIMEOUT, NULL);
}
return;
}
if(event&UB_EV_READ) {
if(!comm_point_http_handle_read(fd, c)) {
reclaim_http_handler(c);
if(!c->tcp_do_close) {
fptr_ok(fptr_whitelist_comm_point(
c->callback));
(void)(*c->callback)(c, c->cb_arg,
NETEVENT_CLOSED, NULL);
}
}
return;
}
if(event&UB_EV_WRITE) {
if(!comm_point_http_handle_write(fd, c)) {
reclaim_http_handler(c);
if(!c->tcp_do_close) {
fptr_ok(fptr_whitelist_comm_point(
c->callback));
(void)(*c->callback)(c, c->cb_arg,
NETEVENT_CLOSED, NULL);
}
}
return;
}
log_err("Ignored event %d for httphdl.", event);
}
void comm_point_local_handle_callback(int fd, short event, void* arg)
{
struct comm_point* c = (struct comm_point*)arg;
log_assert(c->type == comm_local);
ub_comm_base_now(c->ev->base);
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,
NULL);
}
return;
}
log_err("Ignored event %d for localhdl.", event);
}
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*
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)
{
struct comm_point* c = (struct comm_point*)calloc(1,
sizeof(struct comm_point));
short evbits;
if(!c)
return NULL;
c->ev = (struct internal_event*)calloc(1,
sizeof(struct internal_event));
if(!c->ev) {
free(c);
return NULL;
}
c->ev->base = base;
c->fd = fd;
c->buffer = buffer;
c->timeout = NULL;
c->tcp_is_reading = 0;
c->tcp_byte_count = 0;
c->tcp_parent = NULL;
c->max_tcp_count = 0;
c->cur_tcp_count = 0;
c->tcp_handlers = NULL;
c->tcp_free = NULL;
c->type = comm_udp;
c->tcp_do_close = 0;
c->do_not_close = 0;
c->tcp_do_toggle_rw = 0;
c->tcp_check_nb_connect = 0;
#ifdef USE_MSG_FASTOPEN
c->tcp_do_fastopen = 0;
#endif
#ifdef USE_DNSCRYPT
c->dnscrypt = 0;
c->dnscrypt_buffer = buffer;
#endif
c->inuse = 0;
c->callback = callback;
c->cb_arg = callback_arg;
c->socket = socket;
c->pp2_enabled = pp2_enabled;
c->pp2_header_state = pp2_header_none;
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 NULL;
}
if(fd!=-1 && ub_event_add(c->ev->ev, c->timeout) != 0 ) {
log_err("could not add udp event");
comm_point_delete(c);
return NULL;
}
c->event_added = 1;
return c;
}
+#if defined(AF_INET6) && defined(IPV6_PKTINFO) && defined(HAVE_RECVMSG)
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)
{
struct comm_point* c = (struct comm_point*)calloc(1,
sizeof(struct comm_point));
short evbits;
if(!c)
return NULL;
c->ev = (struct internal_event*)calloc(1,
sizeof(struct internal_event));
if(!c->ev) {
free(c);
return NULL;
}
c->ev->base = base;
c->fd = fd;
c->buffer = buffer;
c->timeout = NULL;
c->tcp_is_reading = 0;
c->tcp_byte_count = 0;
c->tcp_parent = NULL;
c->max_tcp_count = 0;
c->cur_tcp_count = 0;
c->tcp_handlers = NULL;
c->tcp_free = NULL;
c->type = comm_udp;
c->tcp_do_close = 0;
c->do_not_close = 0;
#ifdef USE_DNSCRYPT
c->dnscrypt = 0;
c->dnscrypt_buffer = buffer;
#endif
c->inuse = 0;
c->tcp_do_toggle_rw = 0;
c->tcp_check_nb_connect = 0;
#ifdef USE_MSG_FASTOPEN
c->tcp_do_fastopen = 0;
#endif
c->callback = callback;
c->cb_arg = callback_arg;
c->socket = socket;
c->pp2_enabled = pp2_enabled;
c->pp2_header_state = pp2_header_none;
evbits = UB_EV_READ | UB_EV_PERSIST;
/* ub_event stuff */
c->ev->ev = ub_event_new(base->eb->base, c->fd, evbits,
comm_point_udp_ancil_callback, c);
if(c->ev->ev == NULL) {
log_err("could not baseset udp event");
comm_point_delete(c);
return NULL;
}
if(fd!=-1 && ub_event_add(c->ev->ev, c->timeout) != 0 ) {
log_err("could not add udp event");
comm_point_delete(c);
return NULL;
}
c->event_added = 1;
return c;
}
+#endif
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)
{
struct comm_point* c = (struct comm_point*)calloc(1,
sizeof(struct comm_point));
short evbits;
if(!c)
return NULL;
c->ev = (struct internal_event*)calloc(1,
sizeof(struct internal_event));
if(!c->ev) {
free(c);
return NULL;
}
c->ev->base = base;
c->fd = -1;
c->buffer = sldns_buffer_new(bufsize);
if(!c->buffer) {
free(c->ev);
free(c);
return NULL;
}
c->timeout = (struct timeval*)malloc(sizeof(struct timeval));
if(!c->timeout) {
sldns_buffer_free(c->buffer);
free(c->ev);
free(c);
return NULL;
}
c->tcp_is_reading = 0;
c->tcp_byte_count = 0;
c->tcp_parent = parent;
c->tcp_timeout_msec = parent->tcp_timeout_msec;
c->tcp_conn_limit = parent->tcp_conn_limit;
c->tcl_addr = NULL;
c->tcp_keepalive = 0;
c->max_tcp_count = 0;
c->cur_tcp_count = 0;
c->tcp_handlers = NULL;
c->tcp_free = NULL;
c->type = comm_tcp;
c->tcp_do_close = 0;
c->do_not_close = 0;
c->tcp_do_toggle_rw = 1;
c->tcp_check_nb_connect = 0;
#ifdef USE_MSG_FASTOPEN
c->tcp_do_fastopen = 0;
#endif
#ifdef USE_DNSCRYPT
c->dnscrypt = 0;
/* We don't know just yet if this is a dnscrypt channel. Allocation
* will be done when handling the callback. */
c->dnscrypt_buffer = c->buffer;
#endif
c->repinfo.c = c;
c->callback = callback;
c->cb_arg = callback_arg;
c->socket = socket;
c->pp2_enabled = parent->pp2_enabled;
c->pp2_header_state = pp2_header_none;
if(spoolbuf) {
c->tcp_req_info = tcp_req_info_create(spoolbuf);
if(!c->tcp_req_info) {
log_err("could not create tcp commpoint");
sldns_buffer_free(c->buffer);
free(c->timeout);
free(c->ev);
free(c);
return NULL;
}
c->tcp_req_info->cp = c;
c->tcp_do_close = 1;
c->tcp_do_toggle_rw = 0;
}
/* add to parent free list */
c->tcp_free = parent->tcp_free;
parent->tcp_free = c;
/* ub_event stuff */
evbits = UB_EV_PERSIST | UB_EV_READ | UB_EV_TIMEOUT;
c->ev->ev = ub_event_new(base->eb->base, c->fd, evbits,
comm_point_tcp_handle_callback, c);
if(c->ev->ev == NULL)
{
log_err("could not basetset tcphdl event");
parent->tcp_free = c->tcp_free;
tcp_req_info_delete(c->tcp_req_info);
sldns_buffer_free(c->buffer);
free(c->timeout);
free(c->ev);
free(c);
return NULL;
}
return c;
}
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,
struct unbound_socket* socket)
{
struct comm_point* c = (struct comm_point*)calloc(1,
sizeof(struct comm_point));
short evbits;
if(!c)
return NULL;
c->ev = (struct internal_event*)calloc(1,
sizeof(struct internal_event));
if(!c->ev) {
free(c);
return NULL;
}
c->ev->base = base;
c->fd = -1;
c->buffer = sldns_buffer_new(bufsize);
if(!c->buffer) {
free(c->ev);
free(c);
return NULL;
}
c->timeout = (struct timeval*)malloc(sizeof(struct timeval));
if(!c->timeout) {
sldns_buffer_free(c->buffer);
free(c->ev);
free(c);
return NULL;
}
c->tcp_is_reading = 0;
c->tcp_byte_count = 0;
c->tcp_parent = parent;
c->tcp_timeout_msec = parent->tcp_timeout_msec;
c->tcp_conn_limit = parent->tcp_conn_limit;
c->tcl_addr = NULL;
c->tcp_keepalive = 0;
c->max_tcp_count = 0;
c->cur_tcp_count = 0;
c->tcp_handlers = NULL;
c->tcp_free = NULL;
c->type = comm_http;
c->tcp_do_close = 1;
c->do_not_close = 0;
c->tcp_do_toggle_rw = 1; /* will be set to 0 after http2 upgrade */
c->tcp_check_nb_connect = 0;
#ifdef USE_MSG_FASTOPEN
c->tcp_do_fastopen = 0;
#endif
#ifdef USE_DNSCRYPT
c->dnscrypt = 0;
c->dnscrypt_buffer = NULL;
#endif
c->repinfo.c = c;
c->callback = callback;
c->cb_arg = callback_arg;
c->socket = socket;
c->pp2_enabled = 0;
c->pp2_header_state = pp2_header_none;
c->http_min_version = http_version_2;
c->http2_stream_max_qbuffer_size = bufsize;
if(harden_large_queries && bufsize > 512)
c->http2_stream_max_qbuffer_size = 512;
c->http2_max_streams = http_max_streams;
if(!(c->http_endpoint = strdup(http_endpoint))) {
log_err("could not strdup http_endpoint");
sldns_buffer_free(c->buffer);
free(c->timeout);
free(c->ev);
free(c);
return NULL;
}
c->use_h2 = 0;
#ifdef HAVE_NGHTTP2
if(!(c->h2_session = http2_session_create(c))) {
log_err("could not create http2 session");
free(c->http_endpoint);
sldns_buffer_free(c->buffer);
free(c->timeout);
free(c->ev);
free(c);
return NULL;
}
if(!(c->h2_session->callbacks = http2_req_callbacks_create())) {
log_err("could not create http2 callbacks");
http2_session_delete(c->h2_session);
free(c->http_endpoint);
sldns_buffer_free(c->buffer);
free(c->timeout);
free(c->ev);
free(c);
return NULL;
}
#endif
/* add to parent free list */
c->tcp_free = parent->tcp_free;
parent->tcp_free = c;
/* ub_event stuff */
evbits = UB_EV_PERSIST | UB_EV_READ | UB_EV_TIMEOUT;
c->ev->ev = ub_event_new(base->eb->base, c->fd, evbits,
comm_point_http_handle_callback, c);
if(c->ev->ev == NULL)
{
log_err("could not set http handler event");
parent->tcp_free = c->tcp_free;
http2_session_delete(c->h2_session);
sldns_buffer_free(c->buffer);
free(c->timeout);
free(c->ev);
free(c);
return NULL;
}
return c;
}
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,
struct tcl_list* tcp_conn_limit, size_t bufsize,
struct sldns_buffer* spoolbuf, enum listen_type port_type,
int pp2_enabled, comm_point_callback_type* callback,
void* callback_arg, struct unbound_socket* socket)
{
struct comm_point* c = (struct comm_point*)calloc(1,
sizeof(struct comm_point));
short evbits;
int i;
/* first allocate the TCP accept listener */
if(!c)
return NULL;
c->ev = (struct internal_event*)calloc(1,
sizeof(struct internal_event));
if(!c->ev) {
free(c);
return NULL;
}
c->ev->base = base;
c->fd = fd;
c->buffer = NULL;
c->timeout = NULL;
c->tcp_is_reading = 0;
c->tcp_byte_count = 0;
c->tcp_timeout_msec = idle_timeout;
c->tcp_conn_limit = tcp_conn_limit;
c->tcl_addr = NULL;
c->tcp_keepalive = 0;
c->tcp_parent = NULL;
c->max_tcp_count = num;
c->cur_tcp_count = 0;
c->tcp_handlers = (struct comm_point**)calloc((size_t)num,
sizeof(struct comm_point*));
if(!c->tcp_handlers) {
free(c->ev);
free(c);
return NULL;
}
c->tcp_free = NULL;
c->type = comm_tcp_accept;
c->tcp_do_close = 0;
c->do_not_close = 0;
c->tcp_do_toggle_rw = 0;
c->tcp_check_nb_connect = 0;
#ifdef USE_MSG_FASTOPEN
c->tcp_do_fastopen = 0;
#endif
#ifdef USE_DNSCRYPT
c->dnscrypt = 0;
c->dnscrypt_buffer = NULL;
#endif
c->callback = NULL;
c->cb_arg = NULL;
c->socket = socket;
c->pp2_enabled = (port_type==listen_type_http?0:pp2_enabled);
c->pp2_header_state = pp2_header_none;
evbits = UB_EV_READ | UB_EV_PERSIST;
/* ub_event stuff */
c->ev->ev = ub_event_new(base->eb->base, c->fd, evbits,
comm_point_tcp_accept_callback, c);
if(c->ev->ev == NULL) {
log_err("could not baseset tcpacc event");
comm_point_delete(c);
return NULL;
}
if (ub_event_add(c->ev->ev, c->timeout) != 0) {
log_err("could not add tcpacc event");
comm_point_delete(c);
return NULL;
}
c->event_added = 1;
/* now prealloc the handlers */
for(i=0; i<num; i++) {
if(port_type == listen_type_tcp ||
port_type == listen_type_ssl ||
port_type == listen_type_tcp_dnscrypt) {
c->tcp_handlers[i] = comm_point_create_tcp_handler(base,
c, bufsize, spoolbuf, callback, callback_arg, socket);
} else if(port_type == listen_type_http) {
c->tcp_handlers[i] = comm_point_create_http_handler(
base, c, bufsize, harden_large_queries,
http_max_streams, http_endpoint,
callback, callback_arg, socket);
}
else {
log_err("could not create tcp handler, unknown listen "
"type");
return NULL;
}
if(!c->tcp_handlers[i]) {
comm_point_delete(c);
return NULL;
}
}
return c;
}
struct comm_point*
comm_point_create_tcp_out(struct comm_base *base, size_t bufsize,
comm_point_callback_type* callback, void* callback_arg)
{
struct comm_point* c = (struct comm_point*)calloc(1,
sizeof(struct comm_point));
short evbits;
if(!c)
return NULL;
c->ev = (struct internal_event*)calloc(1,
sizeof(struct internal_event));
if(!c->ev) {
free(c);
return NULL;
}
c->ev->base = base;
c->fd = -1;
c->buffer = sldns_buffer_new(bufsize);
if(!c->buffer) {
free(c->ev);
free(c);
return NULL;
}
c->timeout = NULL;
c->tcp_is_reading = 0;
c->tcp_byte_count = 0;
c->tcp_timeout_msec = TCP_QUERY_TIMEOUT;
c->tcp_conn_limit = NULL;
c->tcl_addr = NULL;
c->tcp_keepalive = 0;
c->tcp_parent = NULL;
c->max_tcp_count = 0;
c->cur_tcp_count = 0;
c->tcp_handlers = NULL;
c->tcp_free = NULL;
c->type = comm_tcp;
c->tcp_do_close = 0;
c->do_not_close = 0;
c->tcp_do_toggle_rw = 1;
c->tcp_check_nb_connect = 1;
#ifdef USE_MSG_FASTOPEN
c->tcp_do_fastopen = 1;
#endif
#ifdef USE_DNSCRYPT
c->dnscrypt = 0;
c->dnscrypt_buffer = c->buffer;
#endif
c->repinfo.c = c;
c->callback = callback;
c->cb_arg = callback_arg;
c->pp2_enabled = 0;
c->pp2_header_state = pp2_header_none;
evbits = UB_EV_PERSIST | UB_EV_WRITE;
c->ev->ev = ub_event_new(base->eb->base, c->fd, evbits,
comm_point_tcp_handle_callback, c);
if(c->ev->ev == NULL)
{
log_err("could not baseset tcpout event");
sldns_buffer_free(c->buffer);
free(c->ev);
free(c);
return NULL;
}
return c;
}
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)
{
struct comm_point* c = (struct comm_point*)calloc(1,
sizeof(struct comm_point));
short evbits;
if(!c)
return NULL;
c->ev = (struct internal_event*)calloc(1,
sizeof(struct internal_event));
if(!c->ev) {
free(c);
return NULL;
}
c->ev->base = base;
c->fd = -1;
c->buffer = sldns_buffer_new(bufsize);
if(!c->buffer) {
free(c->ev);
free(c);
return NULL;
}
c->timeout = NULL;
c->tcp_is_reading = 0;
c->tcp_byte_count = 0;
c->tcp_parent = NULL;
c->max_tcp_count = 0;
c->cur_tcp_count = 0;
c->tcp_handlers = NULL;
c->tcp_free = NULL;
c->type = comm_http;
c->tcp_do_close = 0;
c->do_not_close = 0;
c->tcp_do_toggle_rw = 1;
c->tcp_check_nb_connect = 1;
c->http_in_headers = 1;
c->http_in_chunk_headers = 0;
c->http_is_chunked = 0;
c->http_temp = temp;
#ifdef USE_MSG_FASTOPEN
c->tcp_do_fastopen = 1;
#endif
#ifdef USE_DNSCRYPT
c->dnscrypt = 0;
c->dnscrypt_buffer = c->buffer;
#endif
c->repinfo.c = c;
c->callback = callback;
c->cb_arg = callback_arg;
c->pp2_enabled = 0;
c->pp2_header_state = pp2_header_none;
evbits = UB_EV_PERSIST | UB_EV_WRITE;
c->ev->ev = ub_event_new(base->eb->base, c->fd, evbits,
comm_point_http_handle_callback, c);
if(c->ev->ev == NULL)
{
log_err("could not baseset tcpout event");
#ifdef HAVE_SSL
SSL_free(c->ssl);
#endif
sldns_buffer_free(c->buffer);
free(c->ev);
free(c);
return NULL;
}
return c;
}
struct comm_point*
comm_point_create_local(struct comm_base *base, int fd, size_t bufsize,
comm_point_callback_type* callback, void* callback_arg)
{
struct comm_point* c = (struct comm_point*)calloc(1,
sizeof(struct comm_point));
short evbits;
if(!c)
return NULL;
c->ev = (struct internal_event*)calloc(1,
sizeof(struct internal_event));
if(!c->ev) {
free(c);
return NULL;
}
c->ev->base = base;
c->fd = fd;
c->buffer = sldns_buffer_new(bufsize);
if(!c->buffer) {
free(c->ev);
free(c);
return NULL;
}
c->timeout = NULL;
c->tcp_is_reading = 1;
c->tcp_byte_count = 0;
c->tcp_parent = NULL;
c->max_tcp_count = 0;
c->cur_tcp_count = 0;
c->tcp_handlers = NULL;
c->tcp_free = NULL;
c->type = comm_local;
c->tcp_do_close = 0;
c->do_not_close = 1;
c->tcp_do_toggle_rw = 0;
c->tcp_check_nb_connect = 0;
#ifdef USE_MSG_FASTOPEN
c->tcp_do_fastopen = 0;
#endif
#ifdef USE_DNSCRYPT
c->dnscrypt = 0;
c->dnscrypt_buffer = c->buffer;
#endif
c->callback = callback;
c->cb_arg = callback_arg;
c->pp2_enabled = 0;
c->pp2_header_state = pp2_header_none;
/* ub_event stuff */
evbits = UB_EV_PERSIST | UB_EV_READ;
c->ev->ev = ub_event_new(base->eb->base, c->fd, evbits,
comm_point_local_handle_callback, c);
if(c->ev->ev == NULL) {
log_err("could not baseset localhdl event");
free(c->ev);
free(c);
return NULL;
}
if (ub_event_add(c->ev->ev, c->timeout) != 0) {
log_err("could not add localhdl event");
ub_event_free(c->ev->ev);
free(c->ev);
free(c);
return NULL;
}
c->event_added = 1;
return c;
}
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,
sizeof(struct comm_point));
short evbits;
if(!c)
return NULL;
c->ev = (struct internal_event*)calloc(1,
sizeof(struct internal_event));
if(!c->ev) {
free(c);
return NULL;
}
c->ev->base = base;
c->fd = fd;
c->buffer = NULL;
c->timeout = NULL;
c->tcp_is_reading = 0;
c->tcp_byte_count = 0;
c->tcp_parent = NULL;
c->max_tcp_count = 0;
c->cur_tcp_count = 0;
c->tcp_handlers = NULL;
c->tcp_free = NULL;
c->type = comm_raw;
c->tcp_do_close = 0;
c->do_not_close = 1;
c->tcp_do_toggle_rw = 0;
c->tcp_check_nb_connect = 0;
#ifdef USE_MSG_FASTOPEN
c->tcp_do_fastopen = 0;
#endif
#ifdef USE_DNSCRYPT
c->dnscrypt = 0;
c->dnscrypt_buffer = c->buffer;
#endif
c->callback = callback;
c->cb_arg = callback_arg;
c->pp2_enabled = 0;
c->pp2_header_state = pp2_header_none;
/* ub_event stuff */
if(writing)
evbits = UB_EV_PERSIST | UB_EV_WRITE;
else evbits = UB_EV_PERSIST | UB_EV_READ;
c->ev->ev = ub_event_new(base->eb->base, c->fd, evbits,
comm_point_raw_handle_callback, c);
if(c->ev->ev == NULL) {
log_err("could not baseset rawhdl event");
free(c->ev);
free(c);
return NULL;
}
if (ub_event_add(c->ev->ev, c->timeout) != 0) {
log_err("could not add rawhdl event");
ub_event_free(c->ev->ev);
free(c->ev);
free(c);
return NULL;
}
c->event_added = 1;
return c;
}
void
comm_point_close(struct comm_point* c)
{
if(!c)
return;
if(c->fd != -1) {
verbose(5, "comm_point_close of %d: event_del", c->fd);
if(c->event_added) {
if(ub_event_del(c->ev->ev) != 0) {
log_err("could not event_del on close");
}
c->event_added = 0;
}
}
tcl_close_connection(c->tcl_addr);
if(c->tcp_req_info)
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) {
#ifdef USE_WINSOCK
if(c->type == comm_tcp || c->type == comm_http) {
/* delete sticky events for the fd, it gets closed */
ub_winsock_tcp_wouldblock(c->ev->ev, UB_EV_READ);
ub_winsock_tcp_wouldblock(c->ev->ev, UB_EV_WRITE);
}
#endif
verbose(VERB_ALGO, "close fd %d", c->fd);
sock_close(c->fd);
}
c->fd = -1;
}
void
comm_point_delete(struct comm_point* c)
{
if(!c)
return;
if((c->type == comm_tcp || c->type == comm_http) && c->ssl) {
#ifdef HAVE_SSL
SSL_shutdown(c->ssl);
SSL_free(c->ssl);
#endif
}
if(c->type == comm_http && c->http_endpoint) {
free(c->http_endpoint);
c->http_endpoint = NULL;
}
comm_point_close(c);
if(c->tcp_handlers) {
int i;
for(i=0; i<c->max_tcp_count; i++)
comm_point_delete(c->tcp_handlers[i]);
free(c->tcp_handlers);
}
free(c->timeout);
if(c->type == comm_tcp || c->type == comm_local || c->type == comm_http) {
sldns_buffer_free(c->buffer);
#ifdef USE_DNSCRYPT
if(c->dnscrypt && c->dnscrypt_buffer != c->buffer) {
sldns_buffer_free(c->dnscrypt_buffer);
}
#endif
if(c->tcp_req_info) {
tcp_req_info_delete(c->tcp_req_info);
}
if(c->h2_session) {
http2_session_delete(c->h2_session);
}
}
ub_event_free(c->ev->ev);
free(c->ev);
free(c);
}
void
comm_point_send_reply(struct comm_reply *repinfo)
{
struct sldns_buffer* buffer;
log_assert(repinfo && repinfo->c);
#ifdef USE_DNSCRYPT
buffer = repinfo->c->dnscrypt_buffer;
if(!dnsc_handle_uncurved_request(repinfo)) {
return;
}
#else
buffer = repinfo->c->buffer;
#endif
if(repinfo->c->type == comm_udp) {
if(repinfo->srctype)
comm_point_send_udp_msg_if(repinfo->c, buffer,
(struct sockaddr*)&repinfo->remote_addr,
repinfo->remote_addrlen, repinfo);
else
comm_point_send_udp_msg(repinfo->c, buffer,
(struct sockaddr*)&repinfo->remote_addr,
repinfo->remote_addrlen, 0);
#ifdef USE_DNSTAP
/*
* sending src (client)/dst (local service) addresses over DNSTAP from udp callback
*/
if(repinfo->c->dtenv != NULL && repinfo->c->dtenv->log_client_response_messages) {
log_addr(VERB_ALGO, "from local addr", (void*)repinfo->c->socket->addr->ai_addr, repinfo->c->socket->addr->ai_addrlen);
log_addr(VERB_ALGO, "response to client", &repinfo->client_addr, repinfo->client_addrlen);
dt_msg_send_client_response(repinfo->c->dtenv, &repinfo->client_addr, (void*)repinfo->c->socket->addr->ai_addr, repinfo->c->type, repinfo->c->buffer);
}
#endif
} else {
#ifdef USE_DNSTAP
/*
* sending src (client)/dst (local service) addresses over DNSTAP from TCP callback
*/
if(repinfo->c->tcp_parent->dtenv != NULL && repinfo->c->tcp_parent->dtenv->log_client_response_messages) {
log_addr(VERB_ALGO, "from local addr", (void*)repinfo->c->socket->addr->ai_addr, repinfo->c->socket->addr->ai_addrlen);
log_addr(VERB_ALGO, "response to client", &repinfo->client_addr, repinfo->client_addrlen);
dt_msg_send_client_response(repinfo->c->tcp_parent->dtenv, &repinfo->client_addr, (void*)repinfo->c->socket->addr->ai_addr, repinfo->c->type,
( repinfo->c->tcp_req_info? repinfo->c->tcp_req_info->spool_buffer: repinfo->c->buffer ));
}
#endif
if(repinfo->c->tcp_req_info) {
tcp_req_info_send_reply(repinfo->c->tcp_req_info);
} else if(repinfo->c->use_h2) {
if(!http2_submit_dns_response(repinfo->c->h2_session)) {
comm_point_drop_reply(repinfo);
return;
}
repinfo->c->h2_stream = NULL;
repinfo->c->tcp_is_reading = 0;
comm_point_stop_listening(repinfo->c);
comm_point_start_listening(repinfo->c, -1,
adjusted_tcp_timeout(repinfo->c));
return;
} else {
comm_point_start_listening(repinfo->c, -1,
adjusted_tcp_timeout(repinfo->c));
}
}
}
void
comm_point_drop_reply(struct comm_reply* repinfo)
{
if(!repinfo)
return;
log_assert(repinfo->c);
log_assert(repinfo->c->type != comm_tcp_accept);
if(repinfo->c->type == comm_udp)
return;
if(repinfo->c->tcp_req_info)
repinfo->c->tcp_req_info->is_drop = 1;
if(repinfo->c->type == comm_http) {
if(repinfo->c->h2_session) {
repinfo->c->h2_session->is_drop = 1;
if(!repinfo->c->h2_session->postpone_drop)
reclaim_http_handler(repinfo->c);
return;
}
reclaim_http_handler(repinfo->c);
return;
}
reclaim_tcp_handler(repinfo->c);
}
void
comm_point_stop_listening(struct comm_point* c)
{
verbose(VERB_ALGO, "comm point stop listening %d", c->fd);
if(c->event_added) {
if(ub_event_del(c->ev->ev) != 0) {
log_err("event_del error to stoplisten");
}
c->event_added = 0;
}
}
void
comm_point_start_listening(struct comm_point* c, int newfd, int 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. */
return;
}
if(c->event_added) {
if(ub_event_del(c->ev->ev) != 0) {
log_err("event_del error to startlisten");
}
c->event_added = 0;
}
if(msec != -1 && msec != 0) {
if(!c->timeout) {
c->timeout = (struct timeval*)malloc(sizeof(
struct timeval));
if(!c->timeout) {
log_err("cpsl: malloc failed. No net read.");
return;
}
}
ub_event_add_bits(c->ev->ev, UB_EV_TIMEOUT);
#ifndef S_SPLINT_S /* splint fails on struct timeval. */
c->timeout->tv_sec = msec/1000;
c->timeout->tv_usec = (msec%1000)*1000;
#endif /* S_SPLINT_S */
} else {
if(msec == 0 || !c->timeout) {
ub_event_del_bits(c->ev->ev, UB_EV_TIMEOUT);
}
}
if(c->type == comm_tcp || c->type == comm_http) {
ub_event_del_bits(c->ev->ev, UB_EV_READ|UB_EV_WRITE);
if(c->tcp_write_and_read) {
verbose(5, "startlistening %d mode rw", (newfd==-1?c->fd:newfd));
ub_event_add_bits(c->ev->ev, UB_EV_READ|UB_EV_WRITE);
} else if(c->tcp_is_reading) {
verbose(5, "startlistening %d mode r", (newfd==-1?c->fd:newfd));
ub_event_add_bits(c->ev->ev, UB_EV_READ);
} else {
verbose(5, "startlistening %d mode w", (newfd==-1?c->fd:newfd));
ub_event_add_bits(c->ev->ev, UB_EV_WRITE);
}
}
if(newfd != -1) {
if(c->fd != -1 && c->fd != newfd) {
verbose(5, "cpsl close of fd %d for %d", c->fd, newfd);
sock_close(c->fd);
}
c->fd = newfd;
ub_event_set_fd(c->ev->ev, c->fd);
}
if(ub_event_add(c->ev->ev, msec==0?NULL:c->timeout) != 0) {
log_err("event_add failed. in cpsl.");
return;
}
c->event_added = 1;
}
void comm_point_listen_for_rw(struct comm_point* c, int rd, int wr)
{
verbose(VERB_ALGO, "comm point listen_for_rw %d %d", c->fd, wr);
if(c->event_added) {
if(ub_event_del(c->ev->ev) != 0) {
log_err("event_del error to cplf");
}
c->event_added = 0;
}
if(!c->timeout) {
ub_event_del_bits(c->ev->ev, UB_EV_TIMEOUT);
}
ub_event_del_bits(c->ev->ev, UB_EV_READ|UB_EV_WRITE);
if(rd) ub_event_add_bits(c->ev->ev, UB_EV_READ);
if(wr) ub_event_add_bits(c->ev->ev, UB_EV_WRITE);
if(ub_event_add(c->ev->ev, c->timeout) != 0) {
log_err("event_add failed. in cplf.");
return;
}
c->event_added = 1;
}
size_t comm_point_get_mem(struct comm_point* c)
{
size_t s;
if(!c)
return 0;
s = sizeof(*c) + sizeof(*c->ev);
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);
#ifdef USE_DNSCRYPT
s += sizeof(*c->dnscrypt_buffer);
if(c->buffer != c->dnscrypt_buffer) {
s += sldns_buffer_capacity(c->dnscrypt_buffer);
}
#endif
}
if(c->type == comm_tcp_accept) {
int i;
for(i=0; i<c->max_tcp_count; i++)
s += comm_point_get_mem(c->tcp_handlers[i]);
}
return s;
}
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,
sizeof(struct internal_timer));
if(!tm) {
log_err("malloc failed");
return NULL;
}
tm->super.ev_timer = tm;
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,
comm_timer_callback, &tm->super);
if(tm->ev == NULL) {
log_err("timer_create: event_base_set failed.");
free(tm);
return NULL;
}
return &tm->super;
}
void
comm_timer_disable(struct comm_timer* timer)
{
if(!timer)
return;
ub_timer_del(timer->ev_timer->ev);
timer->ev_timer->enabled = 0;
}
void
comm_timer_set(struct comm_timer* timer, struct timeval* tv)
{
log_assert(tv);
if(timer->ev_timer->enabled)
comm_timer_disable(timer);
if(ub_timer_add(timer->ev_timer->ev, timer->ev_timer->base->eb->base,
comm_timer_callback, timer, tv) != 0)
log_err("comm_timer_set: evtimer_add failed.");
timer->ev_timer->enabled = 1;
}
void
comm_timer_delete(struct comm_timer* timer)
{
if(!timer)
return;
comm_timer_disable(timer);
/* Free the sub struct timer->ev_timer derived from the super struct timer.
* i.e. assert(timer == timer->ev_timer)
*/
ub_event_free(timer->ev_timer->ev);
free(timer->ev_timer);
}
void
comm_timer_callback(int ATTR_UNUSED(fd), short event, void* arg)
{
struct comm_timer* tm = (struct comm_timer*)arg;
if(!(event&UB_EV_TIMEOUT))
return;
ub_comm_base_now(tm->ev_timer->base);
tm->ev_timer->enabled = 0;
fptr_ok(fptr_whitelist_comm_timer(tm->callback));
(*tm->callback)(tm->cb_arg);
}
int
comm_timer_is_set(struct comm_timer* timer)
{
return (int)timer->ev_timer->enabled;
}
size_t
comm_timer_get_mem(struct comm_timer* ATTR_UNUSED(timer))
{
return sizeof(struct internal_timer);
}
struct comm_signal*
comm_signal_create(struct comm_base* base,
void (*callback)(int, void*), void* cb_arg)
{
struct comm_signal* com = (struct comm_signal*)malloc(
sizeof(struct comm_signal));
if(!com) {
log_err("malloc failed");
return NULL;
}
com->base = base;
com->callback = callback;
com->cb_arg = cb_arg;
com->ev_signal = NULL;
return com;
}
void
comm_signal_callback(int sig, short event, void* arg)
{
struct comm_signal* comsig = (struct comm_signal*)arg;
if(!(event & UB_EV_SIGNAL))
return;
ub_comm_base_now(comsig->base);
fptr_ok(fptr_whitelist_comm_signal(comsig->callback));
(*comsig->callback)(sig, comsig->cb_arg);
}
int
comm_signal_bind(struct comm_signal* comsig, int sig)
{
struct internal_signal* entry = (struct internal_signal*)calloc(1,
sizeof(struct internal_signal));
if(!entry) {
log_err("malloc failed");
return 0;
}
log_assert(comsig);
/* add signal event */
entry->ev = ub_signal_new(comsig->base->eb->base, sig,
comm_signal_callback, comsig);
if(entry->ev == NULL) {
log_err("Could not create signal event");
free(entry);
return 0;
}
if(ub_signal_add(entry->ev, NULL) != 0) {
log_err("Could not add signal handler");
ub_event_free(entry->ev);
free(entry);
return 0;
}
/* link into list */
entry->next = comsig->ev_signal;
comsig->ev_signal = entry;
return 1;
}
void
comm_signal_delete(struct comm_signal* comsig)
{
struct internal_signal* p, *np;
if(!comsig)
return;
p=comsig->ev_signal;
while(p) {
np = p->next;
ub_signal_del(p->ev);
ub_event_free(p->ev);
free(p);
p = np;
}
free(comsig);
}
diff --git a/contrib/unbound/util/proxy_protocol.c b/contrib/unbound/util/proxy_protocol.c
index 757c5141db96..a18804974043 100644
--- a/contrib/unbound/util/proxy_protocol.c
+++ b/contrib/unbound/util/proxy_protocol.c
@@ -1,139 +1,199 @@
/*
* util/proxy_protocol.c - event notification
*
* Copyright (c) 2022, 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file contains PROXY protocol functions.
*/
-#include "config.h"
-#include "util/log.h"
#include "util/proxy_protocol.h"
-int
-pp2_write_to_buf(struct sldns_buffer* buf, struct sockaddr_storage* src,
+/**
+ * Internal struct initialized with function pointers for writing uint16 and
+ * uint32.
+ */
+struct proxy_protocol_data {
+ void (*write_uint16)(void* buf, uint16_t data);
+ void (*write_uint32)(void* buf, uint32_t data);
+};
+struct proxy_protocol_data pp_data;
+
+/**
+ * Internal lookup table; could be further generic like sldns_lookup_table
+ * for all the future generic stuff.
+ */
+struct proxy_protocol_lookup_table {
+ int id;
+ const char *text;
+};
+
+/**
+ * Internal parsing error text; could be exposed with pp_lookup_error.
+ */
+static struct proxy_protocol_lookup_table pp_parse_errors_data[] = {
+ { PP_PARSE_NOERROR, "no parse error" },
+ { PP_PARSE_SIZE, "not enough space for header" },
+ { PP_PARSE_WRONG_HEADERv2, "could not match PROXYv2 header" },
+ { PP_PARSE_UNKNOWN_CMD, "unknown command" },
+ { PP_PARSE_UNKNOWN_FAM_PROT, "unknown family and protocol" },
+};
+
+void
+pp_init(void (*write_uint16)(void* buf, uint16_t data),
+ void (*write_uint32)(void* buf, uint32_t data)) {
+ pp_data.write_uint16 = write_uint16;
+ pp_data.write_uint32 = write_uint32;
+}
+
+const char*
+pp_lookup_error(enum pp_parse_errors error) {
+ return pp_parse_errors_data[error].text;
+}
+
+size_t
+pp2_write_to_buf(uint8_t* buf, size_t buflen,
+#ifdef INET6
+ struct sockaddr_storage* src,
+#else
+ struct sockaddr_in* src,
+#endif
int stream)
{
int af;
+ size_t expected_size;
if(!src) return 0;
af = (int)((struct sockaddr_in*)src)->sin_family;
- if(sldns_buffer_remaining(buf) <
- PP2_HEADER_SIZE + (af==AF_INET?12:36)) {
+ expected_size = PP2_HEADER_SIZE + (af==AF_INET?12:36);
+ if(buflen < expected_size) {
return 0;
}
/* sig */
- sldns_buffer_write(buf, PP2_SIG, PP2_SIG_LEN);
+ memcpy(buf, PP2_SIG, PP2_SIG_LEN);
+ buf += PP2_SIG_LEN;
/* version and command */
- sldns_buffer_write_u8(buf, (PP2_VERSION << 4) | PP2_CMD_PROXY);
- if(af==AF_INET) {
+ *buf = (PP2_VERSION << 4) | PP2_CMD_PROXY;
+ buf++;
+ switch(af) {
+ case AF_INET:
/* family and protocol */
- sldns_buffer_write_u8(buf,
- (PP2_AF_INET<<4) |
- (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM));
+ *buf = (PP2_AF_INET<<4) |
+ (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM);
+ buf++;
/* length */
- sldns_buffer_write_u16(buf, 12);
+ (*pp_data.write_uint16)(buf, 12);
+ buf += 2;
/* src addr */
- sldns_buffer_write(buf,
+ memcpy(buf,
&((struct sockaddr_in*)src)->sin_addr.s_addr, 4);
+ buf += 4;
/* dst addr */
- sldns_buffer_write_u32(buf, 0);
+ (*pp_data.write_uint32)(buf, 0);
+ buf += 4;
/* src port */
- sldns_buffer_write(buf,
+ memcpy(buf,
&((struct sockaddr_in*)src)->sin_port, 2);
+ buf += 2;
+ /* dst addr */
/* dst port */
- sldns_buffer_write_u16(buf, 0);
- } else {
+ (*pp_data.write_uint16)(buf, 12);
+ break;
+#ifdef INET6
+ case AF_INET6:
/* family and protocol */
- sldns_buffer_write_u8(buf,
- (PP2_AF_INET6<<4) |
- (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM));
+ *buf = (PP2_AF_INET6<<4) |
+ (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM);
+ buf++;
/* length */
- sldns_buffer_write_u16(buf, 36);
+ (*pp_data.write_uint16)(buf, 36);
+ buf += 2;
/* src addr */
- sldns_buffer_write(buf,
+ memcpy(buf,
&((struct sockaddr_in6*)src)->sin6_addr, 16);
+ buf += 16;
/* dst addr */
- sldns_buffer_set_at(buf,
- sldns_buffer_position(buf), 0, 16);
- sldns_buffer_skip(buf, 16);
+ memset(buf, 0, 16);
+ buf += 16;
/* src port */
- sldns_buffer_write(buf,
- &((struct sockaddr_in6*)src)->sin6_port, 2);
+ memcpy(buf, &((struct sockaddr_in6*)src)->sin6_port, 2);
+ buf += 2;
/* dst port */
- sldns_buffer_write_u16(buf, 0);
+ (*pp_data.write_uint16)(buf, 0);
+ break;
+#endif /* INET6 */
+ case AF_UNIX:
+ /* fallthrough */
+ default:
+ return 0;
}
- return 1;
+ return expected_size;
}
-struct pp2_header*
-pp2_read_header(struct sldns_buffer* buf)
+int
+pp2_read_header(uint8_t* buf, size_t buflen)
{
size_t size;
- struct pp2_header* header = (struct pp2_header*)sldns_buffer_begin(buf);
+ struct pp2_header* header = (struct pp2_header*)buf;
/* Try to fail all the unsupported cases first. */
- if(sldns_buffer_remaining(buf) < PP2_HEADER_SIZE) {
- log_err("proxy_protocol: not enough space for header");
- return NULL;
+ if(buflen < PP2_HEADER_SIZE) {
+ return PP_PARSE_SIZE;
}
/* Check for PROXYv2 header */
if(memcmp(header, PP2_SIG, PP2_SIG_LEN) != 0 ||
((header->ver_cmd & 0xF0)>>4) != PP2_VERSION) {
- log_err("proxy_protocol: could not match PROXYv2 header");
- return NULL;
+ return PP_PARSE_WRONG_HEADERv2;
}
/* Check the length */
size = PP2_HEADER_SIZE + ntohs(header->len);
- if(sldns_buffer_remaining(buf) < size) {
- log_err("proxy_protocol: not enough space for header");
- return NULL;
+ if(buflen < size) {
+ return PP_PARSE_SIZE;
}
/* Check for supported commands */
if((header->ver_cmd & 0xF) != PP2_CMD_LOCAL &&
(header->ver_cmd & 0xF) != PP2_CMD_PROXY) {
- log_err("proxy_protocol: unsupported command");
- return NULL;
+ return PP_PARSE_UNKNOWN_CMD;
}
/* Check for supported family and protocol */
- if(header->fam_prot != 0x00 /* AF_UNSPEC|UNSPEC */ &&
- header->fam_prot != 0x11 /* AF_INET|STREAM */ &&
- header->fam_prot != 0x12 /* AF_INET|DGRAM */ &&
- header->fam_prot != 0x21 /* AF_INET6|STREAM */ &&
- header->fam_prot != 0x22 /* AF_INET6|DGRAM */) {
- log_err("proxy_protocol: unsupported family and protocol");
- return NULL;
+ if(header->fam_prot != PP2_UNSPEC_UNSPEC &&
+ header->fam_prot != PP2_INET_STREAM &&
+ header->fam_prot != PP2_INET_DGRAM &&
+ header->fam_prot != PP2_INET6_STREAM &&
+ header->fam_prot != PP2_INET6_DGRAM &&
+ header->fam_prot != PP2_UNIX_STREAM &&
+ header->fam_prot != PP2_UNIX_DGRAM) {
+ return PP_PARSE_UNKNOWN_FAM_PROT;
}
/* We have a correct header */
- return header;
+ return PP_PARSE_NOERROR;
}
diff --git a/contrib/unbound/util/proxy_protocol.h b/contrib/unbound/util/proxy_protocol.h
index 13cab9d7438e..ca81065bf49a 100644
--- a/contrib/unbound/util/proxy_protocol.h
+++ b/contrib/unbound/util/proxy_protocol.h
@@ -1,131 +1,177 @@
/*
* util/proxy_protocol.h - PROXY protocol
*
* Copyright (c) 2022, 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file contains PROXY protocol structs and functions.
* Only v2 is supported. TLVs are not currently supported.
*/
#ifndef PROXY_PROTOCOL_H
#define PROXY_PROTOCOL_H
-#include "sldns/sbuffer.h"
+#include "config.h"
/** PROXYv2 minimum header size */
#define PP2_HEADER_SIZE 16
/** PROXYv2 header signature */
#define PP2_SIG "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A"
#define PP2_SIG_LEN 12
-/** PROXYv2 version */
+/** PROXYv2 version (protocol value) */
#define PP2_VERSION 0x2
/**
- * PROXYv2 command.
+ * PROXYv2 command (protocol value).
*/
enum pp2_command {
PP2_CMD_LOCAL = 0x0,
PP2_CMD_PROXY = 0x1
};
/**
- * PROXYv2 address family.
+ * PROXYv2 address family (protocol value).
*/
enum pp2_af {
PP2_AF_UNSPEC = 0x0,
PP2_AF_INET = 0x1,
PP2_AF_INET6 = 0x2,
PP2_AF_UNIX = 0x3
};
/**
- * PROXYv2 protocol.
+ * PROXYv2 protocol (protocol value).
*/
enum pp2_protocol {
PP2_PROT_UNSPEC = 0x0,
PP2_PROT_STREAM = 0x1,
PP2_PROT_DGRAM = 0x2
};
+/**
+ * Expected combinations of address family and protocol values used in checks.
+ */
+enum pp2_af_protocol_combination {
+ PP2_UNSPEC_UNSPEC = (PP2_AF_UNSPEC<<4)|PP2_PROT_UNSPEC,
+ PP2_INET_STREAM = (PP2_AF_INET<<4)|PP2_PROT_STREAM,
+ PP2_INET_DGRAM = (PP2_AF_INET<<4)|PP2_PROT_DGRAM,
+ PP2_INET6_STREAM = (PP2_AF_INET6<<4)|PP2_PROT_STREAM,
+ PP2_INET6_DGRAM = (PP2_AF_INET6<<4)|PP2_PROT_DGRAM,
+ PP2_UNIX_STREAM = (PP2_AF_UNIX<<4)|PP2_PROT_STREAM,
+ PP2_UNIX_DGRAM = (PP2_AF_UNIX<<4)|PP2_PROT_DGRAM
+};
+
/**
* PROXYv2 header.
*/
struct pp2_header {
uint8_t sig[PP2_SIG_LEN];
uint8_t ver_cmd;
uint8_t fam_prot;
uint16_t len;
union {
struct { /* for TCP/UDP over IPv4, len = 12 */
uint32_t src_addr;
uint32_t dst_addr;
uint16_t src_port;
uint16_t dst_port;
} addr4;
struct { /* for TCP/UDP over IPv6, len = 36 */
uint8_t src_addr[16];
uint8_t dst_addr[16];
uint16_t src_port;
uint16_t dst_port;
} addr6;
struct { /* for AF_UNIX sockets, len = 216 */
uint8_t src_addr[108];
uint8_t dst_addr[108];
} addru;
} addr;
};
+/**
+ * PROXY parse errors.
+ */
+enum pp_parse_errors {
+ PP_PARSE_NOERROR = 0,
+ PP_PARSE_SIZE,
+ PP_PARSE_WRONG_HEADERv2,
+ PP_PARSE_UNKNOWN_CMD,
+ PP_PARSE_UNKNOWN_FAM_PROT,
+};
+
+/**
+ * Initialize the internal proxy structure.
+ * @param write_uint16: pointer to a function that can write uint16.
+ * @param write_uint32: pointer to a function that can write uint32.
+ */
+void pp_init(void (*write_uint16)(void* buf, uint16_t data),
+ void (*write_uint32)(void* buf, uint32_t data));
+
+/**
+ * Lookup the parsing error description.
+ * @param error: parsing error from pp2_read_header.
+ * @return the description.
+ */
+const char* pp_lookup_error(enum pp_parse_errors error);
+
/**
* Write a PROXYv2 header at the current position of the buffer.
- * @param buf: the buffer to write to.
+ * @param buf: pointer to the buffer to write data to.
+ * @param buflen: available size on the buffer.
* @param src: the source address.
* @param stream: if the protocol is stream or datagram.
* @return 1 on success, 0 on failure.
*/
-int pp2_write_to_buf(struct sldns_buffer* buf, struct sockaddr_storage* src,
+size_t pp2_write_to_buf(uint8_t* buf, size_t buflen,
+#ifdef INET6
+ struct sockaddr_storage* src,
+#else
+ struct sockaddr_in* src,
+#endif
int stream);
/**
* Read a PROXYv2 header from the current position of the buffer.
* It does initial validation and returns a pointer to the buffer position on
* success.
- * @param buf: the buffer to read from.
- * @return the pointer to the buffer position on success, NULL on error.
+ * @param buf: pointer to the buffer data to read from.
+ * @param buflen: available size on the buffer.
+ * @return parsing error, 0 on success.
*/
-struct pp2_header* pp2_read_header(struct sldns_buffer* buf);
+int pp2_read_header(uint8_t* buf, size_t buflen);
#endif /* PROXY_PROTOCOL_H */
diff --git a/contrib/unbound/util/rfc_1982.c b/contrib/unbound/util/rfc_1982.c
index c28deded606b..cf64e21d08fb 100644
--- a/contrib/unbound/util/rfc_1982.c
+++ b/contrib/unbound/util/rfc_1982.c
@@ -1,74 +1,75 @@
/*
* util/rfc_1982.c - RFC 1982 Serial Number Arithmetic
*
* Copyright (c) 2023, 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file contains functions for RFC 1982 serial number arithmetic.
*/
#include "config.h"
+#include "util/rfc_1982.h"
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;
}
}
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;
}
diff --git a/contrib/unbound/util/siphash.c b/contrib/unbound/util/siphash.c
index 0e1b597d0523..32797dff60e5 100644
--- a/contrib/unbound/util/siphash.c
+++ b/contrib/unbound/util/siphash.c
@@ -1,187 +1,192 @@
/*
SipHash reference C implementation
Copyright (c) 2012-2016 Jean-Philippe Aumasson
<jeanphilippe.aumasson@gmail.com>
Copyright (c) 2012-2014 Daniel J. Bernstein <djb@cr.yp.to>
To the extent possible under law, the author(s) have dedicated all copyright
and related and neighboring rights to this software to the public domain
worldwide. This software is distributed without any warranty.
You should have received a copy of the CC0 Public Domain Dedication along
with
this software. If not, see
<http://creativecommons.org/publicdomain/zero/1.0/>.
*/
/**
* Edited slightly for integration in Unbound. Edits are noted with 'EDIT'.
*/
/** EDIT
* \#include <assert.h>
* \#include <stdint.h>
* \#include <stdio.h>
* \#include <string.h>
* Replaced the above includes with Unbound's config.h
*/
#include "config.h"
+/** EDIT
+ * prevent warning from -Wmissing-prototypes
+ */
+#include "util/siphash.h"
+
/* default: SipHash-2-4 */
#define cROUNDS 2
#define dROUNDS 4
#define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
#define U32TO8_LE(p, v) \
(p)[0] = (uint8_t)((v)); \
(p)[1] = (uint8_t)((v) >> 8); \
(p)[2] = (uint8_t)((v) >> 16); \
(p)[3] = (uint8_t)((v) >> 24);
#define U64TO8_LE(p, v) \
U32TO8_LE((p), (uint32_t)((v))); \
U32TO8_LE((p) + 4, (uint32_t)((v) >> 32));
#define U8TO64_LE(p) \
(((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) | \
((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) | \
((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) | \
((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56))
#define SIPROUND \
do { \
v0 += v1; \
v1 = ROTL(v1, 13); \
v1 ^= v0; \
v0 = ROTL(v0, 32); \
v2 += v3; \
v3 = ROTL(v3, 16); \
v3 ^= v2; \
v0 += v3; \
v3 = ROTL(v3, 21); \
v3 ^= v0; \
v2 += v1; \
v1 = ROTL(v1, 17); \
v1 ^= v2; \
v2 = ROTL(v2, 32); \
} while (0)
#ifdef DEBUG
#define TRACE \
do { \
printf("(%3d) v0 %08x %08x\n", (int)inlen, (uint32_t)(v0 >> 32), \
(uint32_t)v0); \
printf("(%3d) v1 %08x %08x\n", (int)inlen, (uint32_t)(v1 >> 32), \
(uint32_t)v1); \
printf("(%3d) v2 %08x %08x\n", (int)inlen, (uint32_t)(v2 >> 32), \
(uint32_t)v2); \
printf("(%3d) v3 %08x %08x\n", (int)inlen, (uint32_t)(v3 >> 32), \
(uint32_t)v3); \
} while (0)
#else
#define TRACE
#endif
int siphash(const uint8_t *in, const size_t inlen, const uint8_t *k,
uint8_t *out, const size_t outlen) {
uint64_t v0 = 0x736f6d6570736575ULL;
uint64_t v1 = 0x646f72616e646f6dULL;
uint64_t v2 = 0x6c7967656e657261ULL;
uint64_t v3 = 0x7465646279746573ULL;
uint64_t k0 = U8TO64_LE(k);
uint64_t k1 = U8TO64_LE(k + 8);
uint64_t m;
int i;
const uint8_t *end = in + inlen - (inlen % sizeof(uint64_t));
const int left = inlen & 7;
uint64_t b = ((uint64_t)inlen) << 56;
/** EDIT
* The following assert moved here from the top for C90 compliance.
*/
assert((outlen == 8) || (outlen == 16));
v3 ^= k1;
v2 ^= k0;
v1 ^= k1;
v0 ^= k0;
if (outlen == 16)
v1 ^= 0xee;
for (; in != end; in += 8) {
m = U8TO64_LE(in);
v3 ^= m;
TRACE;
for (i = 0; i < cROUNDS; ++i)
SIPROUND;
v0 ^= m;
}
switch (left) {
case 7:
b |= ((uint64_t)in[6]) << 48;
/** EDIT annotate case statement fallthrough for gcc */
/* fallthrough */
case 6:
b |= ((uint64_t)in[5]) << 40;
/** EDIT annotate case statement fallthrough for gcc */
/* fallthrough */
case 5:
b |= ((uint64_t)in[4]) << 32;
/** EDIT annotate case statement fallthrough for gcc */
/* fallthrough */
case 4:
b |= ((uint64_t)in[3]) << 24;
/** EDIT annotate case statement fallthrough for gcc */
/* fallthrough */
case 3:
b |= ((uint64_t)in[2]) << 16;
/** EDIT annotate case statement fallthrough for gcc */
/* fallthrough */
case 2:
b |= ((uint64_t)in[1]) << 8;
/** EDIT annotate case statement fallthrough for gcc */
/* fallthrough */
case 1:
b |= ((uint64_t)in[0]);
break;
case 0:
break;
}
v3 ^= b;
TRACE;
for (i = 0; i < cROUNDS; ++i)
SIPROUND;
v0 ^= b;
if (outlen == 16)
v2 ^= 0xee;
else
v2 ^= 0xff;
TRACE;
for (i = 0; i < dROUNDS; ++i)
SIPROUND;
b = v0 ^ v1 ^ v2 ^ v3;
U64TO8_LE(out, b);
if (outlen == 8)
return 0;
v1 ^= 0xdd;
TRACE;
for (i = 0; i < dROUNDS; ++i)
SIPROUND;
b = v0 ^ v1 ^ v2 ^ v3;
U64TO8_LE(out + 8, b);
return 0;
}
diff --git a/contrib/unbound/validator/val_anchor.c b/contrib/unbound/validator/val_anchor.c
index b1a54e1f0195..8466a8923eb1 100644
--- a/contrib/unbound/validator/val_anchor.c
+++ b/contrib/unbound/validator/val_anchor.c
@@ -1,1324 +1,1345 @@
/*
* validator/val_anchor.c - validator trust anchor storage.
*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file contains storage for the trust anchors for the validator.
*/
#include "config.h"
#include <ctype.h>
#include "validator/val_anchor.h"
#include "validator/val_sigcrypt.h"
#include "validator/autotrust.h"
#include "util/data/packed_rrset.h"
#include "util/data/dname.h"
#include "util/log.h"
#include "util/net_help.h"
#include "util/config_file.h"
#include "util/as112.h"
#include "sldns/sbuffer.h"
#include "sldns/rrdef.h"
#include "sldns/str2wire.h"
#ifdef HAVE_GLOB_H
#include <glob.h>
#endif
int
anchor_cmp(const void* k1, const void* k2)
{
int m;
struct trust_anchor* n1 = (struct trust_anchor*)k1;
struct trust_anchor* n2 = (struct trust_anchor*)k2;
/* no need to ntohs(class) because sort order is irrelevant */
if(n1->dclass != n2->dclass) {
if(n1->dclass < n2->dclass)
return -1;
return 1;
}
return dname_lab_cmp(n1->name, n1->namelabs, n2->name, n2->namelabs,
&m);
}
struct val_anchors*
anchors_create(void)
{
struct val_anchors* a = (struct val_anchors*)calloc(1, sizeof(*a));
if(!a)
return NULL;
a->tree = rbtree_create(anchor_cmp);
if(!a->tree) {
anchors_delete(a);
return NULL;
}
a->autr = autr_global_create();
if(!a->autr) {
anchors_delete(a);
return NULL;
}
lock_basic_init(&a->lock);
lock_protect(&a->lock, a, sizeof(*a));
lock_protect(&a->lock, a->autr, sizeof(*a->autr));
return a;
}
/** delete assembled rrset */
static void
assembled_rrset_delete(struct ub_packed_rrset_key* pkey)
{
if(!pkey) return;
if(pkey->entry.data) {
struct packed_rrset_data* pd = (struct packed_rrset_data*)
pkey->entry.data;
free(pd->rr_data);
free(pd->rr_ttl);
free(pd->rr_len);
free(pd);
}
free(pkey->rk.dname);
free(pkey);
}
/** destroy locks in tree and delete autotrust anchors */
static void
anchors_delfunc(rbnode_type* elem, void* ATTR_UNUSED(arg))
{
struct trust_anchor* ta = (struct trust_anchor*)elem;
if(!ta) return;
if(ta->autr) {
autr_point_delete(ta);
} else {
struct ta_key* p, *np;
lock_basic_destroy(&ta->lock);
free(ta->name);
p = ta->keylist;
while(p) {
np = p->next;
free(p->data);
free(p);
p = np;
}
assembled_rrset_delete(ta->ds_rrset);
assembled_rrset_delete(ta->dnskey_rrset);
free(ta);
}
}
void
anchors_delete(struct val_anchors* anchors)
{
if(!anchors)
return;
lock_unprotect(&anchors->lock, anchors->autr);
lock_unprotect(&anchors->lock, anchors);
lock_basic_destroy(&anchors->lock);
if(anchors->tree)
traverse_postorder(anchors->tree, anchors_delfunc, NULL);
free(anchors->tree);
autr_global_delete(anchors->autr);
free(anchors);
}
void
anchors_init_parents_locked(struct val_anchors* anchors)
{
struct trust_anchor* node, *prev = NULL, *p;
int m;
/* nobody else can grab locks because we hold the main lock.
* Thus the previous items, after unlocked, are not deleted */
RBTREE_FOR(node, struct trust_anchor*, anchors->tree) {
lock_basic_lock(&node->lock);
node->parent = NULL;
if(!prev || prev->dclass != node->dclass) {
prev = node;
lock_basic_unlock(&node->lock);
continue;
}
(void)dname_lab_cmp(prev->name, prev->namelabs, node->name,
node->namelabs, &m); /* we know prev is smaller */
/* sort order like: . com. bla.com. zwb.com. net. */
/* find the previous, or parent-parent-parent */
for(p = prev; p; p = p->parent)
/* looking for name with few labels, a parent */
if(p->namelabs <= m) {
/* ==: since prev matched m, this is closest*/
/* <: prev matches more, but is not a parent,
* this one is a (grand)parent */
node->parent = p;
break;
}
lock_basic_unlock(&node->lock);
prev = node;
}
}
/** initialise parent pointers in the tree */
static void
init_parents(struct val_anchors* anchors)
{
lock_basic_lock(&anchors->lock);
anchors_init_parents_locked(anchors);
lock_basic_unlock(&anchors->lock);
}
struct trust_anchor*
anchor_find(struct val_anchors* anchors, uint8_t* name, int namelabs,
size_t namelen, uint16_t dclass)
{
struct trust_anchor key;
rbnode_type* n;
if(!name) return NULL;
key.node.key = &key;
key.name = name;
key.namelabs = namelabs;
key.namelen = namelen;
key.dclass = dclass;
lock_basic_lock(&anchors->lock);
n = rbtree_search(anchors->tree, &key);
if(n) {
lock_basic_lock(&((struct trust_anchor*)n->key)->lock);
}
lock_basic_unlock(&anchors->lock);
if(!n)
return NULL;
return (struct trust_anchor*)n->key;
}
/** create new trust anchor object */
static struct trust_anchor*
anchor_new_ta(struct val_anchors* anchors, uint8_t* name, int namelabs,
size_t namelen, uint16_t dclass, int lockit)
{
#ifdef UNBOUND_DEBUG
rbnode_type* r;
#endif
struct trust_anchor* ta = (struct trust_anchor*)malloc(
sizeof(struct trust_anchor));
if(!ta)
return NULL;
memset(ta, 0, sizeof(*ta));
ta->node.key = ta;
ta->name = memdup(name, namelen);
if(!ta->name) {
free(ta);
return NULL;
}
ta->namelabs = namelabs;
ta->namelen = namelen;
ta->dclass = dclass;
lock_basic_init(&ta->lock);
if(lockit) {
lock_basic_lock(&anchors->lock);
}
#ifdef UNBOUND_DEBUG
r =
#else
(void)
#endif
rbtree_insert(anchors->tree, &ta->node);
if(lockit) {
lock_basic_unlock(&anchors->lock);
}
log_assert(r != NULL);
return ta;
}
/** find trustanchor key by exact data match */
static struct ta_key*
anchor_find_key(struct trust_anchor* ta, uint8_t* rdata, size_t rdata_len,
uint16_t type)
{
struct ta_key* k;
for(k = ta->keylist; k; k = k->next) {
if(k->type == type && k->len == rdata_len &&
memcmp(k->data, rdata, rdata_len) == 0)
return k;
}
return NULL;
}
/** create new trustanchor key */
static struct ta_key*
anchor_new_ta_key(uint8_t* rdata, size_t rdata_len, uint16_t type)
{
struct ta_key* k = (struct ta_key*)malloc(sizeof(*k));
if(!k)
return NULL;
memset(k, 0, sizeof(*k));
k->data = memdup(rdata, rdata_len);
if(!k->data) {
free(k);
return NULL;
}
k->len = rdata_len;
k->type = type;
return k;
}
/**
* This routine adds a new RR to a trust anchor. The trust anchor may not
* exist yet, and is created if not. The RR can be DS or DNSKEY.
* This routine will also remove duplicates; storing them only once.
* @param anchors: anchor storage.
* @param name: name of trust anchor (wireformat)
* @param type: type or RR
* @param dclass: class of RR
* @param rdata: rdata wireformat, starting with rdlength.
* If NULL, nothing is stored, but an entry is created.
* @param rdata_len: length of rdata including rdlength.
* @return: NULL on error, else the trust anchor.
*/
static struct trust_anchor*
anchor_store_new_key(struct val_anchors* anchors, uint8_t* name, uint16_t type,
uint16_t dclass, uint8_t* rdata, size_t rdata_len)
{
struct ta_key* k;
struct trust_anchor* ta;
int namelabs;
size_t namelen;
namelabs = dname_count_size_labels(name, &namelen);
if(type != LDNS_RR_TYPE_DS && type != LDNS_RR_TYPE_DNSKEY) {
log_err("Bad type for trust anchor");
return 0;
}
/* lookup or create trustanchor */
ta = anchor_find(anchors, name, namelabs, namelen, dclass);
if(!ta) {
ta = anchor_new_ta(anchors, name, namelabs, namelen, dclass, 1);
if(!ta)
return NULL;
lock_basic_lock(&ta->lock);
}
if(!rdata) {
lock_basic_unlock(&ta->lock);
return ta;
}
/* look for duplicates */
if(anchor_find_key(ta, rdata, rdata_len, type)) {
lock_basic_unlock(&ta->lock);
return ta;
}
k = anchor_new_ta_key(rdata, rdata_len, type);
if(!k) {
lock_basic_unlock(&ta->lock);
return NULL;
}
/* add new key */
if(type == LDNS_RR_TYPE_DS)
ta->numDS++;
else ta->numDNSKEY++;
k->next = ta->keylist;
ta->keylist = k;
lock_basic_unlock(&ta->lock);
return ta;
}
/**
* Add new RR. It converts ldns RR to wire format.
* @param anchors: anchor storage.
* @param rr: the wirerr.
* @param rl: length of rr.
* @param dl: length of dname.
* @return NULL on error, else the trust anchor.
*/
static struct trust_anchor*
anchor_store_new_rr(struct val_anchors* anchors, uint8_t* rr, size_t rl,
size_t dl)
{
struct trust_anchor* ta;
if(!(ta=anchor_store_new_key(anchors, rr,
sldns_wirerr_get_type(rr, rl, dl),
sldns_wirerr_get_class(rr, rl, dl),
sldns_wirerr_get_rdatawl(rr, rl, dl),
sldns_wirerr_get_rdatalen(rr, rl, dl)+2))) {
return NULL;
}
log_nametypeclass(VERB_QUERY, "adding trusted key",
rr, sldns_wirerr_get_type(rr, rl, dl),
sldns_wirerr_get_class(rr, rl, dl));
return ta;
}
/**
* Insert insecure anchor
* @param anchors: anchor storage.
* @param str: the domain name.
* @return NULL on error, Else last trust anchor point
*/
static struct trust_anchor*
anchor_insert_insecure(struct val_anchors* anchors, const char* str)
{
struct trust_anchor* ta;
size_t dname_len = 0;
uint8_t* nm = sldns_str2wire_dname(str, &dname_len);
if(!nm) {
log_err("parse error in domain name '%s'", str);
return NULL;
}
ta = anchor_store_new_key(anchors, nm, LDNS_RR_TYPE_DS,
LDNS_RR_CLASS_IN, NULL, 0);
free(nm);
return ta;
}
struct trust_anchor*
anchor_store_str(struct val_anchors* anchors, sldns_buffer* buffer,
const char* str)
{
struct trust_anchor* ta;
uint8_t* rr = sldns_buffer_begin(buffer);
size_t len = sldns_buffer_capacity(buffer), dname_len = 0;
int status = sldns_str2wire_rr_buf(str, rr, &len, &dname_len,
0, NULL, 0, NULL, 0);
if(status != 0) {
log_err("error parsing trust anchor %s: at %d: %s",
str, LDNS_WIREPARSE_OFFSET(status),
sldns_get_errorstr_parse(status));
return NULL;
}
if(!(ta=anchor_store_new_rr(anchors, rr, len, dname_len))) {
log_err("out of memory");
return NULL;
}
return ta;
}
/**
* Read a file with trust anchors
* @param anchors: anchor storage.
* @param buffer: parsing buffer.
* @param fname: string.
* @param onlyone: only one trust anchor allowed in file.
* @return NULL on error. Else last trust-anchor point.
*/
static struct trust_anchor*
anchor_read_file(struct val_anchors* anchors, sldns_buffer* buffer,
const char* fname, int onlyone)
{
struct trust_anchor* ta = NULL, *tanew;
struct sldns_file_parse_state pst;
int status;
size_t len, dname_len;
uint8_t* rr = sldns_buffer_begin(buffer);
int ok = 1;
FILE* in = fopen(fname, "r");
if(!in) {
log_err("error opening file %s: %s", fname, strerror(errno));
return 0;
}
memset(&pst, 0, sizeof(pst));
pst.default_ttl = 3600;
pst.lineno = 1;
while(!feof(in)) {
len = sldns_buffer_capacity(buffer);
dname_len = 0;
status = sldns_fp2wire_rr_buf(in, rr, &len, &dname_len, &pst);
if(len == 0) /* empty, $TTL, $ORIGIN */
continue;
if(status != 0) {
log_err("parse error in %s:%d:%d : %s", fname,
pst.lineno, LDNS_WIREPARSE_OFFSET(status),
sldns_get_errorstr_parse(status));
ok = 0;
break;
}
if(sldns_wirerr_get_type(rr, len, dname_len) !=
LDNS_RR_TYPE_DS && sldns_wirerr_get_type(rr, len,
dname_len) != LDNS_RR_TYPE_DNSKEY) {
continue;
}
if(!(tanew=anchor_store_new_rr(anchors, rr, len, dname_len))) {
log_err("mem error at %s line %d", fname, pst.lineno);
ok = 0;
break;
}
if(onlyone && ta && ta != tanew) {
log_err("error at %s line %d: no multiple anchor "
"domains allowed (you can have multiple "
"keys, but they must have the same name).",
fname, pst.lineno);
ok = 0;
break;
}
ta = tanew;
}
fclose(in);
if(!ok) return NULL;
/* empty file is OK when multiple anchors are allowed */
if(!onlyone && !ta) return (struct trust_anchor*)1;
return ta;
}
/** skip file to end of line */
static void
skip_to_eol(FILE* in)
{
int c;
while((c = getc(in)) != EOF ) {
if(c == '\n')
return;
}
}
/** true for special characters in bind configs */
static int
is_bind_special(int c)
{
switch(c) {
case '{':
case '}':
case '"':
case ';':
return 1;
}
return 0;
}
/**
* Read a keyword skipping bind comments; spaces, specials, restkeywords.
* The file is split into the following tokens:
* * special characters, on their own, rdlen=1, { } doublequote ;
* * whitespace becomes a single ' ' or tab. Newlines become spaces.
* * other words ('keywords')
* * comments are skipped if desired
* / / C++ style comment to end of line
* # to end of line
* / * C style comment * /
* @param in: file to read from.
* @param buf: buffer, what is read is stored after current buffer position.
* Space is left in the buffer to write a terminating 0.
* @param line: line number is increased per line, for error reports.
* @param comments: if 0, comments are not possible and become text.
* if 1, comments are skipped entirely.
* In BIND files, this is when reading quoted strings, for example
* " base 64 text with / / in there "
* @return the number of character written to the buffer.
* 0 on end of file.
*/
static int
readkeyword_bindfile(FILE* in, sldns_buffer* buf, int* line, int comments)
{
int c;
int numdone = 0;
while((c = getc(in)) != EOF ) {
if(comments && c == '#') { /* # blabla */
skip_to_eol(in);
(*line)++;
continue;
} else if(comments && c=='/' && numdone>0 && /* /_/ bla*/
sldns_buffer_read_u8_at(buf,
sldns_buffer_position(buf)-1) == '/') {
sldns_buffer_skip(buf, -1);
numdone--;
skip_to_eol(in);
(*line)++;
continue;
} else if(comments && c=='*' && numdone>0 && /* /_* bla *_/ */
sldns_buffer_read_u8_at(buf,
sldns_buffer_position(buf)-1) == '/') {
sldns_buffer_skip(buf, -1);
numdone--;
/* skip to end of comment */
while(c != EOF && (c=getc(in)) != EOF ) {
if(c == '*') {
if((c=getc(in)) == '/')
break;
}
if(c == '\n')
(*line)++;
}
continue;
}
/* not a comment, complete the keyword */
if(numdone > 0) {
/* check same type */
if(isspace((unsigned char)c)) {
ungetc(c, in);
return numdone;
}
if(is_bind_special(c)) {
ungetc(c, in);
return numdone;
}
}
if(c == '\n') {
c = ' ';
(*line)++;
}
/* space for 1 char + 0 string terminator */
if(sldns_buffer_remaining(buf) < 2) {
fatal_exit("trusted-keys, %d, string too long", *line);
}
sldns_buffer_write_u8(buf, (uint8_t)c);
numdone++;
if(isspace((unsigned char)c)) {
/* collate whitespace into ' ' */
while((c = getc(in)) != EOF ) {
if(c == '\n')
(*line)++;
if(!isspace((unsigned char)c)) {
ungetc(c, in);
break;
}
}
return numdone;
}
if(is_bind_special(c))
return numdone;
}
return numdone;
}
/** skip through file to { or ; */
static int
skip_to_special(FILE* in, sldns_buffer* buf, int* line, int spec)
{
int rdlen;
sldns_buffer_clear(buf);
while((rdlen=readkeyword_bindfile(in, buf, line, 1))) {
if(rdlen == 1 && isspace((unsigned char)*sldns_buffer_begin(buf))) {
sldns_buffer_clear(buf);
continue;
}
if(rdlen != 1 || *sldns_buffer_begin(buf) != (uint8_t)spec) {
sldns_buffer_write_u8(buf, 0);
log_err("trusted-keys, line %d, expected %c",
*line, spec);
return 0;
}
return 1;
}
log_err("trusted-keys, line %d, expected %c got EOF", *line, spec);
return 0;
}
/**
* read contents of trusted-keys{ ... ; clauses and insert keys into storage.
* @param anchors: where to store keys
* @param buf: buffer to use
* @param line: line number in file
* @param in: file to read from.
* @return 0 on error.
*/
static int
process_bind_contents(struct val_anchors* anchors, sldns_buffer* buf,
int* line, FILE* in)
{
/* loop over contents, collate strings before ; */
/* contents is (numbered): 0 1 2 3 4 5 6 7 8 */
/* name. 257 3 5 base64 base64 */
/* quoted value: 0 "111" 0 0 0 0 0 0 0 */
/* comments value: 1 "000" 1 1 1 "0 0 0 0" 1 */
int contnum = 0;
int quoted = 0;
int comments = 1;
int rdlen;
char* str = 0;
sldns_buffer_clear(buf);
while((rdlen=readkeyword_bindfile(in, buf, line, comments))) {
if(rdlen == 1 && sldns_buffer_position(buf) == 1
&& isspace((unsigned char)*sldns_buffer_begin(buf))) {
/* starting whitespace is removed */
sldns_buffer_clear(buf);
continue;
} else if(rdlen == 1 && sldns_buffer_current(buf)[-1] == '"') {
/* remove " from the string */
if(contnum == 0) {
quoted = 1;
comments = 0;
}
sldns_buffer_skip(buf, -1);
if(contnum > 0 && quoted) {
if(sldns_buffer_remaining(buf) < 8+1) {
log_err("line %d, too long", *line);
return 0;
}
sldns_buffer_write(buf, " DNSKEY ", 8);
quoted = 0;
comments = 1;
} else if(contnum > 0)
comments = !comments;
continue;
} else if(rdlen == 1 && sldns_buffer_current(buf)[-1] == ';') {
if(contnum < 5) {
sldns_buffer_write_u8(buf, 0);
log_err("line %d, bad key", *line);
return 0;
}
sldns_buffer_skip(buf, -1);
sldns_buffer_write_u8(buf, 0);
str = strdup((char*)sldns_buffer_begin(buf));
if(!str) {
log_err("line %d, allocation failure", *line);
return 0;
}
if(!anchor_store_str(anchors, buf, str)) {
log_err("line %d, bad key", *line);
free(str);
return 0;
}
free(str);
sldns_buffer_clear(buf);
contnum = 0;
quoted = 0;
comments = 1;
continue;
} else if(rdlen == 1 && sldns_buffer_current(buf)[-1] == '}') {
if(contnum > 0) {
sldns_buffer_write_u8(buf, 0);
log_err("line %d, bad key before }", *line);
return 0;
}
return 1;
} else if(rdlen == 1 &&
isspace((unsigned char)sldns_buffer_current(buf)[-1])) {
/* leave whitespace here */
} else {
/* not space or whatnot, so actual content */
contnum ++;
if(contnum == 1 && !quoted) {
if(sldns_buffer_remaining(buf) < 8+1) {
log_err("line %d, too long", *line);
return 0;
}
sldns_buffer_write(buf, " DNSKEY ", 8);
}
}
}
log_err("line %d, EOF before }", *line);
return 0;
}
/**
* Read a BIND9 like file with trust anchors in named.conf format.
* @param anchors: anchor storage.
* @param buffer: parsing buffer.
* @param fname: string.
* @return false on error.
*/
static int
anchor_read_bind_file(struct val_anchors* anchors, sldns_buffer* buffer,
const char* fname)
{
int line_nr = 1;
FILE* in = fopen(fname, "r");
int rdlen = 0;
if(!in) {
log_err("error opening file %s: %s", fname, strerror(errno));
return 0;
}
verbose(VERB_QUERY, "reading in bind-compat-mode: '%s'", fname);
/* scan for trusted-keys keyword, ignore everything else */
sldns_buffer_clear(buffer);
while((rdlen=readkeyword_bindfile(in, buffer, &line_nr, 1)) != 0) {
if(rdlen != 12 || strncmp((char*)sldns_buffer_begin(buffer),
"trusted-keys", 12) != 0) {
sldns_buffer_clear(buffer);
/* ignore everything but trusted-keys */
continue;
}
if(!skip_to_special(in, buffer, &line_nr, '{')) {
log_err("error in trusted key: \"%s\"", fname);
fclose(in);
return 0;
}
/* process contents */
if(!process_bind_contents(anchors, buffer, &line_nr, in)) {
log_err("error in trusted key: \"%s\"", fname);
fclose(in);
return 0;
}
if(!skip_to_special(in, buffer, &line_nr, ';')) {
log_err("error in trusted key: \"%s\"", fname);
fclose(in);
return 0;
}
sldns_buffer_clear(buffer);
}
fclose(in);
return 1;
}
/**
* Read a BIND9 like files with trust anchors in named.conf format.
* Performs wildcard processing of name.
* @param anchors: anchor storage.
* @param buffer: parsing buffer.
* @param pat: pattern string. (can be wildcarded)
* @return false on error.
*/
static int
anchor_read_bind_file_wild(struct val_anchors* anchors, sldns_buffer* buffer,
const char* pat)
{
#ifdef HAVE_GLOB
glob_t g;
size_t i;
int r, flags;
if(!strchr(pat, '*') && !strchr(pat, '?') && !strchr(pat, '[') &&
!strchr(pat, '{') && !strchr(pat, '~')) {
return anchor_read_bind_file(anchors, buffer, pat);
}
verbose(VERB_QUERY, "wildcard found, processing %s", pat);
flags = 0
#ifdef GLOB_ERR
| GLOB_ERR
#endif
#ifdef GLOB_NOSORT
| GLOB_NOSORT
#endif
#ifdef GLOB_BRACE
| GLOB_BRACE
#endif
#ifdef GLOB_TILDE
| GLOB_TILDE
#endif
;
memset(&g, 0, sizeof(g));
r = glob(pat, flags, NULL, &g);
if(r) {
/* some error */
if(r == GLOB_NOMATCH) {
verbose(VERB_QUERY, "trusted-keys-file: "
"no matches for %s", pat);
return 1;
} else if(r == GLOB_NOSPACE) {
log_err("wildcard trusted-keys-file %s: "
"pattern out of memory", pat);
} else if(r == GLOB_ABORTED) {
log_err("wildcard trusted-keys-file %s: expansion "
"aborted (%s)", pat, strerror(errno));
} else {
log_err("wildcard trusted-keys-file %s: expansion "
"failed (%s)", pat, strerror(errno));
}
/* ignore globs that yield no files */
return 1;
}
/* process files found, if any */
for(i=0; i<(size_t)g.gl_pathc; i++) {
if(!anchor_read_bind_file(anchors, buffer, g.gl_pathv[i])) {
log_err("error reading wildcard "
"trusted-keys-file: %s", g.gl_pathv[i]);
globfree(&g);
return 0;
}
}
globfree(&g);
return 1;
#else /* not HAVE_GLOB */
return anchor_read_bind_file(anchors, buffer, pat);
#endif /* HAVE_GLOB */
}
/**
* Assemble an rrset structure for the type
* @param ta: trust anchor.
* @param num: number of items to fetch from list.
* @param type: fetch only items of this type.
* @return rrset or NULL on error.
*/
static struct ub_packed_rrset_key*
assemble_it(struct trust_anchor* ta, size_t num, uint16_t type)
{
struct ub_packed_rrset_key* pkey = (struct ub_packed_rrset_key*)
malloc(sizeof(*pkey));
struct packed_rrset_data* pd;
struct ta_key* tk;
size_t i;
if(!pkey)
return NULL;
memset(pkey, 0, sizeof(*pkey));
pkey->rk.dname = memdup(ta->name, ta->namelen);
if(!pkey->rk.dname) {
free(pkey);
return NULL;
}
pkey->rk.dname_len = ta->namelen;
pkey->rk.type = htons(type);
pkey->rk.rrset_class = htons(ta->dclass);
/* The rrset is build in an uncompressed way. This means it
* cannot be copied in the normal way. */
pd = (struct packed_rrset_data*)malloc(sizeof(*pd));
if(!pd) {
free(pkey->rk.dname);
free(pkey);
return NULL;
}
memset(pd, 0, sizeof(*pd));
pd->count = num;
pd->trust = rrset_trust_ultimate;
pd->rr_len = (size_t*)reallocarray(NULL, num, sizeof(size_t));
if(!pd->rr_len) {
free(pd);
free(pkey->rk.dname);
free(pkey);
return NULL;
}
pd->rr_ttl = (time_t*)reallocarray(NULL, num, sizeof(time_t));
if(!pd->rr_ttl) {
free(pd->rr_len);
free(pd);
free(pkey->rk.dname);
free(pkey);
return NULL;
}
pd->rr_data = (uint8_t**)reallocarray(NULL, num, sizeof(uint8_t*));
if(!pd->rr_data) {
free(pd->rr_ttl);
free(pd->rr_len);
free(pd);
free(pkey->rk.dname);
free(pkey);
return NULL;
}
/* fill in rrs */
i=0;
for(tk = ta->keylist; tk; tk = tk->next) {
if(tk->type != type)
continue;
pd->rr_len[i] = tk->len;
/* reuse data ptr to allocation in talist */
pd->rr_data[i] = tk->data;
pd->rr_ttl[i] = 0;
i++;
}
pkey->entry.data = (void*)pd;
return pkey;
}
/**
* Assemble structures for the trust DS and DNSKEY rrsets.
* @param ta: trust anchor
* @return: false on error.
*/
static int
anchors_assemble(struct trust_anchor* ta)
{
if(ta->numDS > 0) {
ta->ds_rrset = assemble_it(ta, ta->numDS, LDNS_RR_TYPE_DS);
if(!ta->ds_rrset)
return 0;
}
if(ta->numDNSKEY > 0) {
ta->dnskey_rrset = assemble_it(ta, ta->numDNSKEY,
LDNS_RR_TYPE_DNSKEY);
if(!ta->dnskey_rrset)
return 0;
}
return 1;
}
/**
* Check DS algos for support, warn if not.
* @param ta: trust anchor
* @return number of DS anchors with unsupported algorithms.
*/
static size_t
anchors_ds_unsupported(struct trust_anchor* ta)
{
size_t i, num = 0;
for(i=0; i<ta->numDS; i++) {
if(!ds_digest_algo_is_supported(ta->ds_rrset, i) ||
!ds_key_algo_is_supported(ta->ds_rrset, i))
num++;
}
return num;
}
/**
* Check DNSKEY algos for support, warn if not.
* @param ta: trust anchor
* @return number of DNSKEY anchors with unsupported algorithms.
*/
static size_t
anchors_dnskey_unsupported(struct trust_anchor* ta)
{
size_t i, num = 0;
for(i=0; i<ta->numDNSKEY; i++) {
if(!dnskey_algo_is_supported(ta->dnskey_rrset, i) ||
!dnskey_size_is_supported(ta->dnskey_rrset, i))
num++;
}
return num;
}
/**
* Assemble the rrsets in the anchors, ready for use by validator.
* @param anchors: trust anchor storage.
* @return: false on error.
*/
static int
anchors_assemble_rrsets(struct val_anchors* anchors)
{
struct trust_anchor* ta;
struct trust_anchor* next;
size_t nods, nokey;
lock_basic_lock(&anchors->lock);
ta=(struct trust_anchor*)rbtree_first(anchors->tree);
while((rbnode_type*)ta != RBTREE_NULL) {
next = (struct trust_anchor*)rbtree_next(&ta->node);
lock_basic_lock(&ta->lock);
if(ta->autr || (ta->numDS == 0 && ta->numDNSKEY == 0)) {
lock_basic_unlock(&ta->lock);
ta = next; /* skip */
continue;
}
if(!anchors_assemble(ta)) {
log_err("out of memory");
lock_basic_unlock(&ta->lock);
lock_basic_unlock(&anchors->lock);
return 0;
}
nods = anchors_ds_unsupported(ta);
nokey = anchors_dnskey_unsupported(ta);
if(nods) {
log_nametypeclass(NO_VERBOSE, "warning: unsupported "
"algorithm for trust anchor",
ta->name, LDNS_RR_TYPE_DS, ta->dclass);
}
if(nokey) {
log_nametypeclass(NO_VERBOSE, "warning: unsupported "
"algorithm for trust anchor",
ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass);
}
if(nods == ta->numDS && nokey == ta->numDNSKEY) {
char b[257];
dname_str(ta->name, b);
log_warn("trust anchor %s has no supported algorithms,"
" the anchor is ignored (check if you need to"
" upgrade unbound and "
#ifdef HAVE_LIBRESSL
"libressl"
#else
"openssl"
#endif
")", b);
(void)rbtree_delete(anchors->tree, &ta->node);
lock_basic_unlock(&ta->lock);
anchors_delfunc(&ta->node, NULL);
ta = next;
continue;
}
lock_basic_unlock(&ta->lock);
ta = next;
}
lock_basic_unlock(&anchors->lock);
return 1;
}
int
anchors_apply_cfg(struct val_anchors* anchors, struct config_file* cfg)
{
struct config_strlist* f;
const char** zstr;
char* nm;
sldns_buffer* parsebuf = sldns_buffer_new(65535);
if(!parsebuf) {
log_err("malloc error in anchors_apply_cfg.");
return 0;
}
if(cfg->insecure_lan_zones) {
for(zstr = as112_zones; *zstr; zstr++) {
if(!anchor_insert_insecure(anchors, *zstr)) {
log_err("error in insecure-lan-zones: %s", *zstr);
sldns_buffer_free(parsebuf);
return 0;
}
}
}
for(f = cfg->domain_insecure; f; f = f->next) {
if(!f->str || f->str[0] == 0) /* empty "" */
continue;
if(!anchor_insert_insecure(anchors, f->str)) {
log_err("error in domain-insecure: %s", f->str);
sldns_buffer_free(parsebuf);
return 0;
}
}
for(f = cfg->trust_anchor_file_list; f; f = f->next) {
if(!f->str || f->str[0] == 0) /* empty "" */
continue;
nm = f->str;
if(cfg->chrootdir && cfg->chrootdir[0] && strncmp(nm,
cfg->chrootdir, strlen(cfg->chrootdir)) == 0)
nm += strlen(cfg->chrootdir);
if(!anchor_read_file(anchors, parsebuf, nm, 0)) {
log_err("error reading trust-anchor-file: %s", f->str);
sldns_buffer_free(parsebuf);
return 0;
}
}
for(f = cfg->trusted_keys_file_list; f; f = f->next) {
if(!f->str || f->str[0] == 0) /* empty "" */
continue;
nm = f->str;
if(cfg->chrootdir && cfg->chrootdir[0] && strncmp(nm,
cfg->chrootdir, strlen(cfg->chrootdir)) == 0)
nm += strlen(cfg->chrootdir);
if(!anchor_read_bind_file_wild(anchors, parsebuf, nm)) {
log_err("error reading trusted-keys-file: %s", f->str);
sldns_buffer_free(parsebuf);
return 0;
}
}
for(f = cfg->trust_anchor_list; f; f = f->next) {
if(!f->str || f->str[0] == 0) /* empty "" */
continue;
if(!anchor_store_str(anchors, parsebuf, f->str)) {
log_err("error in trust-anchor: \"%s\"", f->str);
sldns_buffer_free(parsebuf);
return 0;
}
}
/* do autr last, so that it sees what anchors are filled by other
* means can can print errors about double config for the name */
for(f = cfg->auto_trust_anchor_file_list; f; f = f->next) {
if(!f->str || f->str[0] == 0) /* empty "" */
continue;
nm = f->str;
if(cfg->chrootdir && cfg->chrootdir[0] && strncmp(nm,
cfg->chrootdir, strlen(cfg->chrootdir)) == 0)
nm += strlen(cfg->chrootdir);
if(!autr_read_file(anchors, nm)) {
log_err("error reading auto-trust-anchor-file: %s",
f->str);
sldns_buffer_free(parsebuf);
return 0;
}
}
/* first assemble, since it may delete useless anchors */
anchors_assemble_rrsets(anchors);
init_parents(anchors);
sldns_buffer_free(parsebuf);
if(verbosity >= VERB_ALGO) autr_debug_print(anchors);
return 1;
}
struct trust_anchor*
anchors_lookup(struct val_anchors* anchors,
uint8_t* qname, size_t qname_len, uint16_t qclass)
{
struct trust_anchor key;
struct trust_anchor* result;
rbnode_type* res = NULL;
key.node.key = &key;
key.name = qname;
key.namelabs = dname_count_labels(qname);
key.namelen = qname_len;
key.dclass = qclass;
lock_basic_lock(&anchors->lock);
if(rbtree_find_less_equal(anchors->tree, &key, &res)) {
/* exact */
result = (struct trust_anchor*)res;
} else {
/* smaller element (or no element) */
int m;
result = (struct trust_anchor*)res;
if(!result || result->dclass != qclass) {
lock_basic_unlock(&anchors->lock);
return NULL;
}
/* count number of labels matched */
(void)dname_lab_cmp(result->name, result->namelabs, key.name,
key.namelabs, &m);
while(result) { /* go up until qname is subdomain of stub */
if(result->namelabs <= m)
break;
result = result->parent;
}
}
if(result) {
lock_basic_lock(&result->lock);
}
lock_basic_unlock(&anchors->lock);
return result;
}
size_t
anchors_get_mem(struct val_anchors* anchors)
{
struct trust_anchor *ta;
size_t s = sizeof(*anchors);
if(!anchors)
return 0;
RBTREE_FOR(ta, struct trust_anchor*, anchors->tree) {
s += sizeof(*ta) + ta->namelen;
/* keys and so on */
}
return s;
}
int
anchors_add_insecure(struct val_anchors* anchors, uint16_t c, uint8_t* nm)
{
struct trust_anchor key;
key.node.key = &key;
key.name = nm;
key.namelabs = dname_count_size_labels(nm, &key.namelen);
key.dclass = c;
lock_basic_lock(&anchors->lock);
if(rbtree_search(anchors->tree, &key)) {
lock_basic_unlock(&anchors->lock);
/* nothing to do, already an anchor or insecure point */
return 1;
}
if(!anchor_new_ta(anchors, nm, key.namelabs, key.namelen, c, 0)) {
log_err("out of memory");
lock_basic_unlock(&anchors->lock);
return 0;
}
/* no other contents in new ta, because it is insecure point */
anchors_init_parents_locked(anchors);
lock_basic_unlock(&anchors->lock);
return 1;
}
void
anchors_delete_insecure(struct val_anchors* anchors, uint16_t c,
uint8_t* nm)
{
struct trust_anchor key;
struct trust_anchor* ta;
key.node.key = &key;
key.name = nm;
key.namelabs = dname_count_size_labels(nm, &key.namelen);
key.dclass = c;
lock_basic_lock(&anchors->lock);
if(!(ta=(struct trust_anchor*)rbtree_search(anchors->tree, &key))) {
lock_basic_unlock(&anchors->lock);
/* nothing there */
return;
}
/* lock it to drive away other threads that use it */
lock_basic_lock(&ta->lock);
/* see if its really an insecure point */
if(ta->keylist || ta->autr || ta->numDS || ta->numDNSKEY) {
lock_basic_unlock(&anchors->lock);
lock_basic_unlock(&ta->lock);
/* its not an insecure point, do not remove it */
return;
}
/* remove from tree */
(void)rbtree_delete(anchors->tree, &ta->node);
anchors_init_parents_locked(anchors);
lock_basic_unlock(&anchors->lock);
/* actual free of data */
lock_basic_unlock(&ta->lock);
anchors_delfunc(&ta->node, NULL);
}
/** compare two keytags, return -1, 0 or 1 */
static int
keytag_compare(const void* x, const void* y)
{
if(*(uint16_t*)x == *(uint16_t*)y)
return 0;
if(*(uint16_t*)x > *(uint16_t*)y)
return 1;
return -1;
}
size_t
anchor_list_keytags(struct trust_anchor* ta, uint16_t* list, size_t num)
{
size_t i, ret = 0;
if(ta->numDS == 0 && ta->numDNSKEY == 0)
return 0; /* insecure point */
if(ta->numDS != 0 && ta->ds_rrset) {
struct packed_rrset_data* d=(struct packed_rrset_data*)
ta->ds_rrset->entry.data;
for(i=0; i<d->count; i++) {
if(ret == num) continue;
list[ret++] = ds_get_keytag(ta->ds_rrset, i);
}
}
if(ta->numDNSKEY != 0 && ta->dnskey_rrset) {
struct packed_rrset_data* d=(struct packed_rrset_data*)
ta->dnskey_rrset->entry.data;
for(i=0; i<d->count; i++) {
if(ret == num) continue;
list[ret++] = dnskey_calc_keytag(ta->dnskey_rrset, i);
}
}
qsort(list, ret, sizeof(*list), keytag_compare);
return ret;
}
int
anchor_has_keytag(struct val_anchors* anchors, uint8_t* name, int namelabs,
size_t namelen, uint16_t dclass, uint16_t keytag)
{
uint16_t* taglist;
uint16_t* tl;
size_t numtag, i;
struct trust_anchor* anchor = anchor_find(anchors,
name, namelabs, namelen, dclass);
if(!anchor)
return 0;
if(!anchor->numDS && !anchor->numDNSKEY) {
lock_basic_unlock(&anchor->lock);
return 0;
}
taglist = calloc(anchor->numDS + anchor->numDNSKEY, sizeof(*taglist));
if(!taglist) {
lock_basic_unlock(&anchor->lock);
return 0;
}
numtag = anchor_list_keytags(anchor, taglist,
anchor->numDS+anchor->numDNSKEY);
lock_basic_unlock(&anchor->lock);
if(!numtag) {
free(taglist);
return 0;
}
tl = taglist;
for(i=0; i<numtag; i++) {
if(*tl == keytag) {
free(taglist);
return 1;
}
tl++;
}
free(taglist);
return 0;
}
+
+struct trust_anchor*
+anchors_find_any_noninsecure(struct val_anchors* anchors)
+{
+ struct trust_anchor* ta, *next;
+ lock_basic_lock(&anchors->lock);
+ ta=(struct trust_anchor*)rbtree_first(anchors->tree);
+ while((rbnode_type*)ta != RBTREE_NULL) {
+ next = (struct trust_anchor*)rbtree_next(&ta->node);
+ lock_basic_lock(&ta->lock);
+ if(ta->numDS != 0 || ta->numDNSKEY != 0) {
+ /* not an insecurepoint */
+ lock_basic_unlock(&anchors->lock);
+ return ta;
+ }
+ lock_basic_unlock(&ta->lock);
+ ta = next;
+ }
+ lock_basic_unlock(&anchors->lock);
+ return NULL;
+}
diff --git a/contrib/unbound/validator/val_anchor.h b/contrib/unbound/validator/val_anchor.h
index 1597a7d62fbe..02e7e17b5210 100644
--- a/contrib/unbound/validator/val_anchor.h
+++ b/contrib/unbound/validator/val_anchor.h
@@ -1,243 +1,251 @@
/*
* validator/val_anchor.h - validator trust anchor storage.
*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file contains storage for the trust anchors for the validator.
*/
#ifndef VALIDATOR_VAL_ANCHOR_H
#define VALIDATOR_VAL_ANCHOR_H
#include "util/rbtree.h"
#include "util/locks.h"
struct trust_anchor;
struct config_file;
struct ub_packed_rrset_key;
struct autr_point_data;
struct autr_global_data;
struct sldns_buffer;
/**
* Trust anchor store.
* The tree must be locked, while no other locks (from trustanchors) are held.
* And then an anchor searched for. Which can be locked or deleted. Then
* the tree can be unlocked again. This means you have to release the lock
* on a trust anchor and look it up again to delete it.
*/
struct val_anchors {
/** lock on trees */
lock_basic_type lock;
/**
* Anchors are store in this tree. Sort order is chosen, so that
* dnames are in nsec-like order. A lookup on class, name will return
* an exact match of the closest match, with the ancestor needed.
* contents of type trust_anchor.
*/
rbtree_type* tree;
/** Autotrust global data, anchors sorted by next probe time */
struct autr_global_data* autr;
};
/**
* Trust anchor key
*/
struct ta_key {
/** next in list */
struct ta_key* next;
/** rdata, in wireformat of the key RR. starts with rdlength. */
uint8_t* data;
/** length of the rdata (including rdlength). */
size_t len;
/** DNS type (host format) of the key, DS or DNSKEY */
uint16_t type;
};
/**
* A trust anchor in the trust anchor store.
* Unique by name, class.
*/
struct trust_anchor {
/** rbtree node, key is this structure */
rbnode_type node;
/** lock on the entire anchor and its keys; for autotrust changes */
lock_basic_type lock;
/** name of this trust anchor */
uint8_t* name;
/** length of name */
size_t namelen;
/** number of labels in name of rrset */
int namelabs;
/** the ancestor in the trustanchor tree */
struct trust_anchor* parent;
/**
* List of DS or DNSKEY rrs that form the trust anchor.
*/
struct ta_key* keylist;
/** Autotrust anchor point data, or NULL */
struct autr_point_data* autr;
/** number of DSs in the keylist */
size_t numDS;
/** number of DNSKEYs in the keylist */
size_t numDNSKEY;
/** the DS RRset */
struct ub_packed_rrset_key* ds_rrset;
/** The DNSKEY RRset */
struct ub_packed_rrset_key* dnskey_rrset;
/** class of the trust anchor */
uint16_t dclass;
};
/**
* Create trust anchor storage
* @return new storage or NULL on error.
*/
struct val_anchors* anchors_create(void);
/**
* Delete trust anchor storage.
* @param anchors: to delete.
*/
void anchors_delete(struct val_anchors* anchors);
/**
* Process trust anchor config.
* @param anchors: struct anchor storage
* @param cfg: config options.
* @return 0 on error.
*/
int anchors_apply_cfg(struct val_anchors* anchors, struct config_file* cfg);
/**
* Recalculate parent pointers. The caller must hold the lock on the
* anchors structure (say after removing an item from the rbtree).
* Caller must not hold any locks on trust anchors.
* After the call is complete the parent pointers are updated and an item
* just removed is no longer referenced in parent pointers.
* @param anchors: the structure to update.
*/
void anchors_init_parents_locked(struct val_anchors* anchors);
/**
* Given a qname/qclass combination, find the trust anchor closest above it.
* Or return NULL if none exists.
*
* @param anchors: struct anchor storage
* @param qname: query name, uncompressed wireformat.
* @param qname_len: length of qname.
* @param qclass: class to query for.
* @return the trust anchor or NULL if none is found. The anchor is locked.
*/
struct trust_anchor* anchors_lookup(struct val_anchors* anchors,
uint8_t* qname, size_t qname_len, uint16_t qclass);
/**
* Find a trust anchor. Exact matching.
* @param anchors: anchor storage.
* @param name: name of trust anchor (wireformat)
* @param namelabs: labels in name
* @param namelen: length of name
* @param dclass: class of trust anchor
* @return NULL if not found. The anchor is locked.
*/
struct trust_anchor* anchor_find(struct val_anchors* anchors,
uint8_t* name, int namelabs, size_t namelen, uint16_t dclass);
/**
* Store one string as trust anchor RR.
* @param anchors: anchor storage.
* @param buffer: parsing buffer, to generate the RR wireformat in.
* @param str: string.
* @return NULL on error.
*/
struct trust_anchor* anchor_store_str(struct val_anchors* anchors,
struct sldns_buffer* buffer, const char* str);
/**
* Get memory in use by the trust anchor storage
* @param anchors: anchor storage.
* @return memory in use in bytes.
*/
size_t anchors_get_mem(struct val_anchors* anchors);
/** compare two trust anchors */
int anchor_cmp(const void* k1, const void* k2);
/**
* Add insecure point trust anchor. For external use (locks and init_parents)
* @param anchors: anchor storage.
* @param c: class.
* @param nm: name of insecure trust point.
* @return false on alloc failure.
*/
int anchors_add_insecure(struct val_anchors* anchors, uint16_t c, uint8_t* nm);
/**
* Delete insecure point trust anchor. Does not remove if no such point.
* For external use (locks and init_parents)
* @param anchors: anchor storage.
* @param c: class.
* @param nm: name of insecure trust point.
*/
void anchors_delete_insecure(struct val_anchors* anchors, uint16_t c,
uint8_t* nm);
/**
* Get a list of keytags for the trust anchor. Zero tags for insecure points.
* @param ta: trust anchor (locked by caller).
* @param list: array of uint16_t.
* @param num: length of array.
* @return number of keytags filled into array. If total number of keytags is
* bigger than the array, it is truncated at num. On errors, less keytags
* are filled in. The array is sorted.
*/
size_t anchor_list_keytags(struct trust_anchor* ta, uint16_t* list, size_t num);
/**
* Check if there is a trust anchor for given zone with this keytag.
*
* @param anchors: anchor storage
* @param name: name of trust anchor (wireformat)
* @param namelabs: labels in name
* @param namelen: length of name
* @param dclass: class of trust anchor
* @param keytag: keytag
* @return 1 if there is a trust anchor in the trustachor store for this zone
* and keytag, else 0.
*/
int anchor_has_keytag(struct val_anchors* anchors, uint8_t* name, int namelabs,
size_t namelen, uint16_t dclass, uint16_t keytag);
+/**
+ * Find an anchor that is not an insecure point, if any, or there are no
+ * DNSSEC verification anchors if none.
+ * @param anchors: anchor storage
+ * @return trust anchor or NULL. It is locked.
+ */
+struct trust_anchor* anchors_find_any_noninsecure(struct val_anchors* anchors);
+
#endif /* VALIDATOR_VAL_ANCHOR_H */
diff --git a/contrib/unbound/validator/validator.c b/contrib/unbound/validator/validator.c
index 9de9d54db27c..6cd15cfc1c7c 100644
--- a/contrib/unbound/validator/validator.c
+++ b/contrib/unbound/validator/validator.c
@@ -1,3016 +1,3027 @@
/*
* validator/validator.c - secure validator DNS query response module
*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file contains a module that performs validation of DNS queries.
* According to RFC 4034.
*/
#include "config.h"
#include <ctype.h>
#include "validator/validator.h"
#include "validator/val_anchor.h"
#include "validator/val_kcache.h"
#include "validator/val_kentry.h"
#include "validator/val_utils.h"
#include "validator/val_nsec.h"
#include "validator/val_nsec3.h"
#include "validator/val_neg.h"
#include "validator/val_sigcrypt.h"
#include "validator/autotrust.h"
#include "services/cache/dns.h"
#include "services/cache/rrset.h"
#include "util/data/dname.h"
#include "util/module.h"
#include "util/log.h"
#include "util/net_help.h"
#include "util/regional.h"
#include "util/config_file.h"
#include "util/fptr_wlist.h"
#include "sldns/rrdef.h"
#include "sldns/wire2str.h"
#include "sldns/str2wire.h"
/* forward decl for cache response and normal super inform calls of a DS */
static void process_ds_response(struct module_qstate* qstate,
struct val_qstate* vq, int id, int rcode, struct dns_msg* msg,
struct query_info* qinfo, struct sock_list* origin);
/* Updates the suplied EDE (RFC8914) code selectively so we don't lose
* a more specific code */
static void
update_reason_bogus(struct reply_info* rep, sldns_ede_code 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;
}
/** fill up nsec3 key iterations config entry */
static int
fill_nsec3_iter(struct val_env* ve, char* s, int c)
{
char* e;
int i;
free(ve->nsec3_keysize);
free(ve->nsec3_maxiter);
ve->nsec3_keysize = (size_t*)calloc(sizeof(size_t), (size_t)c);
ve->nsec3_maxiter = (size_t*)calloc(sizeof(size_t), (size_t)c);
if(!ve->nsec3_keysize || !ve->nsec3_maxiter) {
log_err("out of memory");
return 0;
}
for(i=0; i<c; i++) {
ve->nsec3_keysize[i] = (size_t)strtol(s, &e, 10);
if(s == e) {
log_err("cannot parse: %s", s);
return 0;
}
s = e;
ve->nsec3_maxiter[i] = (size_t)strtol(s, &e, 10);
if(s == e) {
log_err("cannot parse: %s", s);
return 0;
}
s = e;
if(i>0 && ve->nsec3_keysize[i-1] >= ve->nsec3_keysize[i]) {
log_err("nsec3 key iterations not ascending: %d %d",
(int)ve->nsec3_keysize[i-1],
(int)ve->nsec3_keysize[i]);
return 0;
}
verbose(VERB_ALGO, "validator nsec3cfg keysz %d mxiter %d",
(int)ve->nsec3_keysize[i], (int)ve->nsec3_maxiter[i]);
}
return 1;
}
/** apply config settings to validator */
static int
val_apply_cfg(struct module_env* env, struct val_env* val_env,
struct config_file* cfg)
{
int c;
val_env->bogus_ttl = (uint32_t)cfg->bogus_ttl;
if(!env->anchors)
env->anchors = anchors_create();
if(!env->anchors) {
log_err("out of memory");
return 0;
}
if (env->key_cache)
val_env->kcache = env->key_cache;
if(!val_env->kcache)
val_env->kcache = key_cache_create(cfg);
if(!val_env->kcache) {
log_err("out of memory");
return 0;
}
env->key_cache = val_env->kcache;
if(!anchors_apply_cfg(env->anchors, cfg)) {
log_err("validator: error in trustanchors config");
return 0;
}
val_env->date_override = cfg->val_date_override;
val_env->skew_min = cfg->val_sig_skew_min;
val_env->skew_max = cfg->val_sig_skew_max;
val_env->max_restart = cfg->val_max_restart;
c = cfg_count_numbers(cfg->val_nsec3_key_iterations);
if(c < 1 || (c&1)) {
log_err("validator: unparsable or odd nsec3 key "
"iterations: %s", cfg->val_nsec3_key_iterations);
return 0;
}
val_env->nsec3_keyiter_count = c/2;
if(!fill_nsec3_iter(val_env, cfg->val_nsec3_key_iterations, c/2)) {
log_err("validator: cannot apply nsec3 key iterations");
return 0;
}
if (env->neg_cache)
val_env->neg_cache = env->neg_cache;
if(!val_env->neg_cache)
val_env->neg_cache = val_neg_create(cfg,
val_env->nsec3_maxiter[val_env->nsec3_keyiter_count-1]);
if(!val_env->neg_cache) {
log_err("out of memory");
return 0;
}
env->neg_cache = val_env->neg_cache;
return 1;
}
#ifdef USE_ECDSA_EVP_WORKAROUND
void ecdsa_evp_workaround_init(void);
#endif
int
val_init(struct module_env* env, int id)
{
struct val_env* val_env = (struct val_env*)calloc(1,
sizeof(struct val_env));
if(!val_env) {
log_err("malloc failure");
return 0;
}
env->modinfo[id] = (void*)val_env;
env->need_to_validate = 1;
lock_basic_init(&val_env->bogus_lock);
lock_protect(&val_env->bogus_lock, &val_env->num_rrset_bogus,
sizeof(val_env->num_rrset_bogus));
#ifdef USE_ECDSA_EVP_WORKAROUND
ecdsa_evp_workaround_init();
#endif
if(!val_apply_cfg(env, val_env, env->cfg)) {
log_err("validator: could not apply configuration settings.");
return 0;
}
+ if(env->cfg->disable_edns_do) {
+ struct trust_anchor* anchor = anchors_find_any_noninsecure(
+ env->anchors);
+ if(anchor) {
+ char b[LDNS_MAX_DOMAINLEN+2];
+ dname_str(anchor->name, b);
+ log_warn("validator: disable-edns-do is enabled, but there is a trust anchor for '%s'. Since DNSSEC could not work, the disable-edns-do setting is turned off. Continuing without it.", b);
+ lock_basic_unlock(&anchor->lock);
+ env->cfg->disable_edns_do = 0;
+ }
+ }
return 1;
}
void
val_deinit(struct module_env* env, int id)
{
struct val_env* val_env;
if(!env || !env->modinfo[id])
return;
val_env = (struct val_env*)env->modinfo[id];
lock_basic_destroy(&val_env->bogus_lock);
anchors_delete(env->anchors);
env->anchors = NULL;
key_cache_delete(val_env->kcache);
env->key_cache = NULL;
neg_cache_delete(val_env->neg_cache);
env->neg_cache = NULL;
free(val_env->nsec3_keysize);
free(val_env->nsec3_maxiter);
free(val_env);
env->modinfo[id] = NULL;
}
/** fill in message structure */
static struct val_qstate*
val_new_getmsg(struct module_qstate* qstate, struct val_qstate* vq)
{
if(!qstate->return_msg || qstate->return_rcode != LDNS_RCODE_NOERROR) {
/* create a message to verify */
verbose(VERB_ALGO, "constructing reply for validation");
vq->orig_msg = (struct dns_msg*)regional_alloc(qstate->region,
sizeof(struct dns_msg));
if(!vq->orig_msg)
return NULL;
vq->orig_msg->qinfo = qstate->qinfo;
vq->orig_msg->rep = (struct reply_info*)regional_alloc(
qstate->region, sizeof(struct reply_info));
if(!vq->orig_msg->rep)
return NULL;
memset(vq->orig_msg->rep, 0, sizeof(struct reply_info));
vq->orig_msg->rep->flags = (uint16_t)(qstate->return_rcode&0xf)
|BIT_QR|BIT_RA|(qstate->query_flags|(BIT_CD|BIT_RD));
vq->orig_msg->rep->qdcount = 1;
vq->orig_msg->rep->reason_bogus = LDNS_EDE_NONE;
} else {
vq->orig_msg = qstate->return_msg;
}
vq->qchase = qstate->qinfo;
/* chase reply will be an edited (sub)set of the orig msg rrset ptrs */
vq->chase_reply = regional_alloc_init(qstate->region,
vq->orig_msg->rep,
sizeof(struct reply_info) - sizeof(struct rrset_ref));
if(!vq->chase_reply)
return NULL;
if(vq->orig_msg->rep->rrset_count > RR_COUNT_MAX)
return NULL; /* protect against integer overflow */
vq->chase_reply->rrsets = regional_alloc_init(qstate->region,
vq->orig_msg->rep->rrsets, sizeof(struct ub_packed_rrset_key*)
* vq->orig_msg->rep->rrset_count);
if(!vq->chase_reply->rrsets)
return NULL;
vq->rrset_skip = 0;
return vq;
}
/** allocate new validator query state */
static struct val_qstate*
val_new(struct module_qstate* qstate, int id)
{
struct val_qstate* vq = (struct val_qstate*)regional_alloc(
qstate->region, sizeof(*vq));
log_assert(!qstate->minfo[id]);
if(!vq)
return NULL;
memset(vq, 0, sizeof(*vq));
qstate->minfo[id] = vq;
vq->state = VAL_INIT_STATE;
return val_new_getmsg(qstate, vq);
}
/**
* Exit validation with an error status
*
* @param qstate: query state
* @param id: validator id.
* @return false, for use by caller to return to stop processing.
*/
static int
val_error(struct module_qstate* qstate, int id)
{
qstate->ext_state[id] = module_error;
qstate->return_rcode = LDNS_RCODE_SERVFAIL;
return 0;
}
/**
* Check to see if a given response needs to go through the validation
* process. Typical reasons for this routine to return false are: CD bit was
* on in the original request, or the response is a kind of message that
* is unvalidatable (i.e., SERVFAIL, REFUSED, etc.)
*
* @param qstate: query state.
* @param ret_rc: rcode for this message (if noerror - examine ret_msg).
* @param ret_msg: return msg, can be NULL; look at rcode instead.
* @return true if the response could use validation (although this does not
* mean we can actually validate this response).
*/
static int
needs_validation(struct module_qstate* qstate, int ret_rc,
struct dns_msg* ret_msg)
{
int rcode;
/* If the CD bit is on in the original request, then you could think
* that we don't bother to validate anything.
* But this is signalled internally with the valrec flag.
* User queries are validated with BIT_CD to make our cache clean
* so that bogus messages get retried by the upstream also for
* downstream validators that set BIT_CD.
* For DNS64 bit_cd signals no dns64 processing, but we want to
* provide validation there too */
/*
if(qstate->query_flags & BIT_CD) {
verbose(VERB_ALGO, "not validating response due to CD bit");
return 0;
}
*/
if(qstate->is_valrec) {
verbose(VERB_ALGO, "not validating response, is valrec"
"(validation recursion lookup)");
return 0;
}
if(ret_rc != LDNS_RCODE_NOERROR || !ret_msg)
rcode = ret_rc;
else rcode = (int)FLAGS_GET_RCODE(ret_msg->rep->flags);
if(rcode != LDNS_RCODE_NOERROR && rcode != LDNS_RCODE_NXDOMAIN) {
if(verbosity >= VERB_ALGO) {
char rc[16];
rc[0]=0;
(void)sldns_wire2str_rcode_buf(rcode, rc, sizeof(rc));
verbose(VERB_ALGO, "cannot validate non-answer, rcode %s", rc);
}
return 0;
}
/* cannot validate positive RRSIG response. (negatives can) */
if(qstate->qinfo.qtype == LDNS_RR_TYPE_RRSIG &&
rcode == LDNS_RCODE_NOERROR && ret_msg &&
ret_msg->rep->an_numrrsets > 0) {
verbose(VERB_ALGO, "cannot validate RRSIG, no sigs on sigs.");
return 0;
}
return 1;
}
/**
* Check to see if the response has already been validated.
* @param ret_msg: return msg, can be NULL
* @return true if the response has already been validated
*/
static int
already_validated(struct dns_msg* ret_msg)
{
/* validate unchecked, and re-validate bogus messages */
if (ret_msg && ret_msg->rep->security > sec_status_bogus)
{
verbose(VERB_ALGO, "response has already been validated: %s",
sec_status_to_string(ret_msg->rep->security));
return 1;
}
return 0;
}
/**
* Generate a request for DNS data.
*
* @param qstate: query state that is the parent.
* @param id: module id.
* @param name: what name to query for.
* @param namelen: length of name.
* @param qtype: query type.
* @param qclass: query class.
* @param flags: additional flags, such as the CD bit (BIT_CD), or 0.
* @param newq: If the subquery is newly created, it is returned,
* otherwise NULL is returned
* @param detached: true if this qstate should not attach to the subquery
* @return false on alloc failure.
*/
static int
generate_request(struct module_qstate* qstate, int id, uint8_t* name,
size_t namelen, uint16_t qtype, uint16_t qclass, uint16_t flags,
struct module_qstate** newq, int detached)
{
struct val_qstate* vq = (struct val_qstate*)qstate->minfo[id];
struct query_info ask;
int valrec;
ask.qname = name;
ask.qname_len = namelen;
ask.qtype = qtype;
ask.qclass = qclass;
ask.local_alias = NULL;
log_query_info(VERB_ALGO, "generate request", &ask);
/* enable valrec flag to avoid recursion to the same validation
* routine, this lookup is simply a lookup. */
valrec = 1;
fptr_ok(fptr_whitelist_modenv_detect_cycle(qstate->env->detect_cycle));
if((*qstate->env->detect_cycle)(qstate, &ask,
(uint16_t)(BIT_RD|flags), 0, valrec)) {
verbose(VERB_ALGO, "Could not generate request: cycle detected");
return 0;
}
if(detached) {
struct mesh_state* sub = NULL;
fptr_ok(fptr_whitelist_modenv_add_sub(
qstate->env->add_sub));
if(!(*qstate->env->add_sub)(qstate, &ask,
(uint16_t)(BIT_RD|flags), 0, valrec, newq, &sub)){
log_err("Could not generate request: out of memory");
return 0;
}
}
else {
fptr_ok(fptr_whitelist_modenv_attach_sub(
qstate->env->attach_sub));
if(!(*qstate->env->attach_sub)(qstate, &ask,
(uint16_t)(BIT_RD|flags), 0, valrec, newq)){
log_err("Could not generate request: out of memory");
return 0;
}
}
/* newq; validator does not need state created for that
* query, and its a 'normal' for iterator as well */
if(*newq) {
/* add our blacklist to the query blacklist */
sock_list_merge(&(*newq)->blacklist, (*newq)->region,
vq->chain_blacklist);
}
qstate->ext_state[id] = module_wait_subquery;
return 1;
}
/**
* Generate, send and detach key tag signaling query.
*
* @param qstate: query state.
* @param id: module id.
* @param ta: trust anchor, locked.
* @return false on a processing error.
*/
static int
generate_keytag_query(struct module_qstate* qstate, int id,
struct trust_anchor* ta)
{
/* 3 bytes for "_ta", 5 bytes per tag (4 bytes + "-") */
#define MAX_LABEL_TAGS (LDNS_MAX_LABELLEN-3)/5
size_t i, numtag;
uint16_t tags[MAX_LABEL_TAGS];
char tagstr[LDNS_MAX_LABELLEN+1] = "_ta"; /* +1 for NULL byte */
size_t tagstr_left = sizeof(tagstr) - strlen(tagstr);
char* tagstr_pos = tagstr + strlen(tagstr);
uint8_t dnamebuf[LDNS_MAX_DOMAINLEN+1]; /* +1 for label length byte */
size_t dnamebuf_len = sizeof(dnamebuf);
uint8_t* keytagdname;
struct module_qstate* newq = NULL;
enum module_ext_state ext_state = qstate->ext_state[id];
numtag = anchor_list_keytags(ta, tags, MAX_LABEL_TAGS);
if(numtag == 0)
return 0;
for(i=0; i<numtag; i++) {
/* Buffer can't overflow; numtag is limited to tags that fit in
* the buffer. */
snprintf(tagstr_pos, tagstr_left, "-%04x", (unsigned)tags[i]);
tagstr_left -= strlen(tagstr_pos);
tagstr_pos += strlen(tagstr_pos);
}
sldns_str2wire_dname_buf_origin(tagstr, dnamebuf, &dnamebuf_len,
ta->name, ta->namelen);
if(!(keytagdname = (uint8_t*)regional_alloc_init(qstate->region,
dnamebuf, dnamebuf_len))) {
log_err("could not generate key tag query: out of memory");
return 0;
}
log_nametypeclass(VERB_OPS, "generate keytag query", keytagdname,
LDNS_RR_TYPE_NULL, ta->dclass);
if(!generate_request(qstate, id, keytagdname, dnamebuf_len,
LDNS_RR_TYPE_NULL, ta->dclass, 0, &newq, 1)) {
verbose(VERB_ALGO, "failed to generate key tag signaling request");
return 0;
}
/* Not interested in subquery response. Restore the ext_state,
* that might be changed by generate_request() */
qstate->ext_state[id] = ext_state;
return 1;
}
/**
* Get keytag as uint16_t from string
*
* @param start: start of string containing keytag
* @param keytag: pointer where to store the extracted keytag
* @return: 1 if keytag was extracted, else 0.
*/
static int
sentinel_get_keytag(char* start, uint16_t* keytag) {
char* keytag_str;
char* e = NULL;
keytag_str = calloc(1, SENTINEL_KEYTAG_LEN + 1 /* null byte */);
if(!keytag_str)
return 0;
memmove(keytag_str, start, SENTINEL_KEYTAG_LEN);
keytag_str[SENTINEL_KEYTAG_LEN] = '\0';
*keytag = (uint16_t)strtol(keytag_str, &e, 10);
if(!e || *e != '\0') {
free(keytag_str);
return 0;
}
free(keytag_str);
return 1;
}
/**
* Prime trust anchor for use.
* Generate and dispatch a priming query for the given trust anchor.
* The trust anchor can be DNSKEY or DS and does not have to be signed.
*
* @param qstate: query state.
* @param vq: validator query state.
* @param id: module id.
* @param toprime: what to prime.
* @return false on a processing error.
*/
static int
prime_trust_anchor(struct module_qstate* qstate, struct val_qstate* vq,
int id, struct trust_anchor* toprime)
{
struct module_qstate* newq = NULL;
int ret = generate_request(qstate, id, toprime->name, toprime->namelen,
LDNS_RR_TYPE_DNSKEY, toprime->dclass, BIT_CD, &newq, 0);
if(newq && qstate->env->cfg->trust_anchor_signaling &&
!generate_keytag_query(qstate, id, toprime)) {
verbose(VERB_ALGO, "keytag signaling query failed");
return 0;
}
if(!ret) {
verbose(VERB_ALGO, "Could not prime trust anchor");
return 0;
}
/* ignore newq; validator does not need state created for that
* query, and its a 'normal' for iterator as well */
vq->wait_prime_ta = 1; /* to elicit PRIME_RESP_STATE processing
from the validator inform_super() routine */
/* store trust anchor name for later lookup when prime returns */
vq->trust_anchor_name = regional_alloc_init(qstate->region,
toprime->name, toprime->namelen);
vq->trust_anchor_len = toprime->namelen;
vq->trust_anchor_labs = toprime->namelabs;
if(!vq->trust_anchor_name) {
log_err("Could not prime trust anchor: out of memory");
return 0;
}
return 1;
}
/**
* Validate if the ANSWER and AUTHORITY sections contain valid rrsets.
* They must be validly signed with the given key.
* Tries to validate ADDITIONAL rrsets as well, but only to check them.
* Allows unsigned CNAME after a DNAME that expands the DNAME.
*
* Note that by the time this method is called, the process of finding the
* trusted DNSKEY rrset that signs this response must already have been
* completed.
*
* @param qstate: query state.
* @param env: module env for verify.
* @param ve: validator env for verify.
* @param qchase: query that was made.
* @param chase_reply: answer to validate.
* @param key_entry: the key entry, which is trusted, and which matches
* the signer of the answer. The key entry isgood().
* @return false if any of the rrsets in the an or ns sections of the message
* fail to verify. The message is then set to bogus.
*/
static int
validate_msg_signatures(struct module_qstate* qstate, struct module_env* env,
struct val_env* ve, struct query_info* qchase,
struct reply_info* chase_reply, struct key_entry_key* key_entry)
{
uint8_t* sname;
size_t i, slen;
struct ub_packed_rrset_key* s;
enum sec_status sec;
int dname_seen = 0;
char* reason = NULL;
sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
/* validate the ANSWER section */
for(i=0; i<chase_reply->an_numrrsets; i++) {
s = chase_reply->rrsets[i];
/* Skip the CNAME following a (validated) DNAME.
* Because of the normalization routines in the iterator,
* there will always be an unsigned CNAME following a DNAME
* (unless qtype=DNAME). */
if(dname_seen && ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME) {
dname_seen = 0;
/* CNAME was synthesized by our own iterator */
/* since the DNAME verified, mark the CNAME as secure */
((struct packed_rrset_data*)s->entry.data)->security =
sec_status_secure;
((struct packed_rrset_data*)s->entry.data)->trust =
rrset_trust_validated;
continue;
}
/* Verify the answer rrset */
sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason,
&reason_bogus, LDNS_SECTION_ANSWER, qstate);
/* If the (answer) rrset failed to validate, then this
* message is BAD. */
if(sec != sec_status_secure) {
log_nametypeclass(VERB_QUERY, "validator: response "
"has failed ANSWER rrset:", s->rk.dname,
ntohs(s->rk.type), ntohs(s->rk.rrset_class));
errinf_ede(qstate, reason, reason_bogus);
if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME)
errinf(qstate, "for CNAME");
else if(ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME)
errinf(qstate, "for DNAME");
errinf_origin(qstate, qstate->reply_origin);
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, reason_bogus);
return 0;
}
/* Notice a DNAME that should be followed by an unsigned
* CNAME. */
if(qchase->qtype != LDNS_RR_TYPE_DNAME &&
ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME) {
dname_seen = 1;
}
}
/* validate the AUTHORITY section */
for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
chase_reply->ns_numrrsets; i++) {
s = chase_reply->rrsets[i];
sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason,
&reason_bogus, LDNS_SECTION_AUTHORITY, qstate);
/* If anything in the authority section fails to be secure,
* we have a bad message. */
if(sec != sec_status_secure) {
log_nametypeclass(VERB_QUERY, "validator: response "
"has failed AUTHORITY rrset:", s->rk.dname,
ntohs(s->rk.type), ntohs(s->rk.rrset_class));
errinf_ede(qstate, reason, reason_bogus);
errinf_origin(qstate, qstate->reply_origin);
errinf_rrset(qstate, s);
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, reason_bogus);
return 0;
}
}
/* If set, the validator should clean the additional section of
* secure messages. */
if(!env->cfg->val_clean_additional)
return 1;
/* attempt to validate the ADDITIONAL section rrsets */
for(i=chase_reply->an_numrrsets+chase_reply->ns_numrrsets;
i<chase_reply->rrset_count; i++) {
s = chase_reply->rrsets[i];
/* only validate rrs that have signatures with the key */
/* leave others unchecked, those get removed later on too */
val_find_rrset_signer(s, &sname, &slen);
if(sname && query_dname_compare(sname, key_entry->name)==0)
(void)val_verify_rrset_entry(env, ve, s, key_entry,
&reason, NULL, LDNS_SECTION_ADDITIONAL, qstate);
/* the additional section can fail to be secure,
* it is optional, check signature in case we need
* to clean the additional section later. */
}
return 1;
}
/**
* Detect wrong truncated response (say from BIND 9.6.1 that is forwarding
* and saw the NS record without signatures from a referral).
* The positive response has a mangled authority section.
* Remove that authority section and the additional section.
* @param rep: reply
* @return true if a wrongly truncated response.
*/
static int
detect_wrongly_truncated(struct reply_info* rep)
{
size_t i;
/* only NS in authority, and it is bogus */
if(rep->ns_numrrsets != 1 || rep->an_numrrsets == 0)
return 0;
if(ntohs(rep->rrsets[ rep->an_numrrsets ]->rk.type) != LDNS_RR_TYPE_NS)
return 0;
if(((struct packed_rrset_data*)rep->rrsets[ rep->an_numrrsets ]
->entry.data)->security == sec_status_secure)
return 0;
/* answer section is present and secure */
for(i=0; i<rep->an_numrrsets; i++) {
if(((struct packed_rrset_data*)rep->rrsets[ i ]
->entry.data)->security != sec_status_secure)
return 0;
}
verbose(VERB_ALGO, "truncating to minimal response");
return 1;
}
/**
* For messages that are not referrals, if the chase reply contains an
* unsigned NS record in the authority section it could have been
* inserted by a (BIND) forwarder that thinks the zone is insecure, and
* that has an NS record without signatures in cache. Remove the NS
* record since the reply does not hinge on that record (in the authority
* section), but do not remove it if it removes the last record from the
* answer+authority sections.
* @param chase_reply: the chased reply, we have a key for this contents,
* so we should have signatures for these rrsets and not having
* signatures means it will be bogus.
* @param orig_reply: original reply, remove NS from there as well because
* we cannot mark the NS record as DNSSEC valid because it is not
* validated by signatures.
*/
static void
remove_spurious_authority(struct reply_info* chase_reply,
struct reply_info* orig_reply)
{
size_t i, found = 0;
int remove = 0;
/* if no answer and only 1 auth RRset, do not remove that one */
if(chase_reply->an_numrrsets == 0 && chase_reply->ns_numrrsets == 1)
return;
/* search authority section for unsigned NS records */
for(i = chase_reply->an_numrrsets;
i < chase_reply->an_numrrsets+chase_reply->ns_numrrsets; i++) {
struct packed_rrset_data* d = (struct packed_rrset_data*)
chase_reply->rrsets[i]->entry.data;
if(ntohs(chase_reply->rrsets[i]->rk.type) == LDNS_RR_TYPE_NS
&& d->rrsig_count == 0) {
found = i;
remove = 1;
break;
}
}
/* see if we found the entry */
if(!remove) return;
log_rrset_key(VERB_ALGO, "Removing spurious unsigned NS record "
"(likely inserted by forwarder)", chase_reply->rrsets[found]);
/* find rrset in orig_reply */
for(i = orig_reply->an_numrrsets;
i < orig_reply->an_numrrsets+orig_reply->ns_numrrsets; i++) {
if(ntohs(orig_reply->rrsets[i]->rk.type) == LDNS_RR_TYPE_NS
&& query_dname_compare(orig_reply->rrsets[i]->rk.dname,
chase_reply->rrsets[found]->rk.dname) == 0) {
/* remove from orig_msg */
val_reply_remove_auth(orig_reply, i);
break;
}
}
/* remove rrset from chase_reply */
val_reply_remove_auth(chase_reply, found);
}
/**
* Given a "positive" response -- a response that contains an answer to the
* question, and no CNAME chain, validate this response.
*
* The answer and authority RRsets must already be verified as secure.
*
* @param env: module env for verify.
* @param ve: validator env for verify.
* @param qchase: query that was made.
* @param chase_reply: answer to that query to validate.
* @param kkey: the key entry, which is trusted, and which matches
* the signer of the answer. The key entry isgood().
*/
static void
validate_positive_response(struct module_env* env, struct val_env* ve,
struct query_info* qchase, struct reply_info* chase_reply,
struct key_entry_key* kkey)
{
uint8_t* wc = NULL;
size_t wl;
int wc_cached = 0;
int wc_NSEC_ok = 0;
int nsec3s_seen = 0;
size_t i;
struct ub_packed_rrset_key* s;
/* validate the ANSWER section - this will be the answer itself */
for(i=0; i<chase_reply->an_numrrsets; i++) {
s = chase_reply->rrsets[i];
/* Check to see if the rrset is the result of a wildcard
* expansion. If so, an additional check will need to be
* made in the authority section. */
if(!val_rrset_wildcard(s, &wc, &wl)) {
log_nametypeclass(VERB_QUERY, "Positive response has "
"inconsistent wildcard sigs:", s->rk.dname,
ntohs(s->rk.type), ntohs(s->rk.rrset_class));
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
if(wc && !wc_cached && env->cfg->aggressive_nsec) {
rrset_cache_update_wildcard(env->rrset_cache, s, wc, wl,
env->alloc, *env->now);
wc_cached = 1;
}
}
/* validate the AUTHORITY section as well - this will generally be
* the NS rrset (which could be missing, no problem) */
for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
chase_reply->ns_numrrsets; i++) {
s = chase_reply->rrsets[i];
/* If this is a positive wildcard response, and we have a
* (just verified) NSEC record, try to use it to 1) prove
* that qname doesn't exist and 2) that the correct wildcard
* was used. */
if(wc != NULL && ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) {
if(val_nsec_proves_positive_wildcard(s, qchase, wc)) {
wc_NSEC_ok = 1;
}
/* if not, continue looking for proof */
}
/* Otherwise, if this is a positive wildcard response and
* we have NSEC3 records */
if(wc != NULL && ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) {
nsec3s_seen = 1;
}
}
/* If this was a positive wildcard response that we haven't already
* proven, and we have NSEC3 records, try to prove it using the NSEC3
* records. */
if(wc != NULL && !wc_NSEC_ok && nsec3s_seen) {
enum sec_status sec = nsec3_prove_wildcard(env, ve,
chase_reply->rrsets+chase_reply->an_numrrsets,
chase_reply->ns_numrrsets, qchase, kkey, wc);
if(sec == sec_status_insecure) {
verbose(VERB_ALGO, "Positive wildcard response is "
"insecure");
chase_reply->security = sec_status_insecure;
return;
} else if(sec == sec_status_secure)
wc_NSEC_ok = 1;
}
/* If after all this, we still haven't proven the positive wildcard
* response, fail. */
if(wc != NULL && !wc_NSEC_ok) {
verbose(VERB_QUERY, "positive response was wildcard "
"expansion and did not prove original data "
"did not exist");
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
verbose(VERB_ALGO, "Successfully validated positive response");
chase_reply->security = sec_status_secure;
}
/**
* Validate a NOERROR/NODATA signed response -- a response that has a
* NOERROR Rcode but no ANSWER section RRsets. This consists of making
* certain that the authority section NSEC/NSEC3s proves that the qname
* does exist and the qtype doesn't.
*
* The answer and authority RRsets must already be verified as secure.
*
* @param env: module env for verify.
* @param ve: validator env for verify.
* @param qchase: query that was made.
* @param chase_reply: answer to that query to validate.
* @param kkey: the key entry, which is trusted, and which matches
* the signer of the answer. The key entry isgood().
*/
static void
validate_nodata_response(struct module_env* env, struct val_env* ve,
struct query_info* qchase, struct reply_info* chase_reply,
struct key_entry_key* kkey)
{
/* Since we are here, there must be nothing in the ANSWER section to
* validate. */
/* (Note: CNAME/DNAME responses will not directly get here --
* instead, they are chased down into individual CNAME validations,
* and at the end of the cname chain a POSITIVE, or CNAME_NOANSWER
* validation.) */
/* validate the AUTHORITY section */
int has_valid_nsec = 0; /* If true, then the NODATA has been proven.*/
uint8_t* ce = NULL; /* for wildcard nodata responses. This is the
proven closest encloser. */
uint8_t* wc = NULL; /* for wildcard nodata responses. wildcard nsec */
int nsec3s_seen = 0; /* nsec3s seen */
struct ub_packed_rrset_key* s;
size_t i;
for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
chase_reply->ns_numrrsets; i++) {
s = chase_reply->rrsets[i];
/* If we encounter an NSEC record, try to use it to prove
* NODATA.
* This needs to handle the ENT NODATA case. */
if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) {
if(nsec_proves_nodata(s, qchase, &wc)) {
has_valid_nsec = 1;
/* sets wc-encloser if wildcard applicable */
}
if(val_nsec_proves_name_error(s, qchase->qname)) {
ce = nsec_closest_encloser(qchase->qname, s);
}
if(val_nsec_proves_insecuredelegation(s, qchase)) {
verbose(VERB_ALGO, "delegation is insecure");
chase_reply->security = sec_status_insecure;
return;
}
} else if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) {
nsec3s_seen = 1;
}
}
/* check to see if we have a wildcard NODATA proof. */
/* The wildcard NODATA is 1 NSEC proving that qname does not exist
* (and also proving what the closest encloser is), and 1 NSEC
* showing the matching wildcard, which must be *.closest_encloser. */
if(wc && !ce)
has_valid_nsec = 0;
else if(wc && ce) {
if(query_dname_compare(wc, ce) != 0) {
has_valid_nsec = 0;
}
}
if(!has_valid_nsec && nsec3s_seen) {
enum sec_status sec = nsec3_prove_nodata(env, ve,
chase_reply->rrsets+chase_reply->an_numrrsets,
chase_reply->ns_numrrsets, qchase, kkey);
if(sec == sec_status_insecure) {
verbose(VERB_ALGO, "NODATA response is insecure");
chase_reply->security = sec_status_insecure;
return;
} else if(sec == sec_status_secure)
has_valid_nsec = 1;
}
if(!has_valid_nsec) {
verbose(VERB_QUERY, "NODATA response failed to prove NODATA "
"status with NSEC/NSEC3");
if(verbosity >= VERB_ALGO)
log_dns_msg("Failed NODATA", qchase, chase_reply);
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
verbose(VERB_ALGO, "successfully validated NODATA response.");
chase_reply->security = sec_status_secure;
}
/**
* Validate a NAMEERROR signed response -- a response that has a NXDOMAIN
* Rcode.
* This consists of making certain that the authority section NSEC proves
* that the qname doesn't exist and the covering wildcard also doesn't exist..
*
* The answer and authority RRsets must have already been verified as secure.
*
* @param env: module env for verify.
* @param ve: validator env for verify.
* @param qchase: query that was made.
* @param chase_reply: answer to that query to validate.
* @param kkey: the key entry, which is trusted, and which matches
* the signer of the answer. The key entry isgood().
* @param rcode: adjusted RCODE, in case of RCODE/proof mismatch leniency.
*/
static void
validate_nameerror_response(struct module_env* env, struct val_env* ve,
struct query_info* qchase, struct reply_info* chase_reply,
struct key_entry_key* kkey, int* rcode)
{
int has_valid_nsec = 0;
int has_valid_wnsec = 0;
int nsec3s_seen = 0;
struct ub_packed_rrset_key* s;
size_t i;
uint8_t* ce;
int ce_labs = 0;
int prev_ce_labs = 0;
for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
chase_reply->ns_numrrsets; i++) {
s = chase_reply->rrsets[i];
if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) {
if(val_nsec_proves_name_error(s, qchase->qname))
has_valid_nsec = 1;
ce = nsec_closest_encloser(qchase->qname, s);
ce_labs = dname_count_labels(ce);
/* Use longest closest encloser to prove wildcard. */
if(ce_labs > prev_ce_labs ||
(ce_labs == prev_ce_labs &&
has_valid_wnsec == 0)) {
if(val_nsec_proves_no_wc(s, qchase->qname,
qchase->qname_len))
has_valid_wnsec = 1;
else
has_valid_wnsec = 0;
}
prev_ce_labs = ce_labs;
if(val_nsec_proves_insecuredelegation(s, qchase)) {
verbose(VERB_ALGO, "delegation is insecure");
chase_reply->security = sec_status_insecure;
return;
}
} else if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3)
nsec3s_seen = 1;
}
if((!has_valid_nsec || !has_valid_wnsec) && nsec3s_seen) {
/* use NSEC3 proof, both answer and auth rrsets, in case
* NSEC3s end up in the answer (due to qtype=NSEC3 or so) */
chase_reply->security = nsec3_prove_nameerror(env, ve,
chase_reply->rrsets, chase_reply->an_numrrsets+
chase_reply->ns_numrrsets, qchase, kkey);
if(chase_reply->security != sec_status_secure) {
verbose(VERB_QUERY, "NameError response failed nsec, "
"nsec3 proof was %s", sec_status_to_string(
chase_reply->security));
return;
}
has_valid_nsec = 1;
has_valid_wnsec = 1;
}
/* If the message fails to prove either condition, it is bogus. */
if(!has_valid_nsec) {
verbose(VERB_QUERY, "NameError response has failed to prove: "
"qname does not exist");
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
/* Be lenient with RCODE in NSEC NameError responses */
validate_nodata_response(env, ve, qchase, chase_reply, kkey);
if (chase_reply->security == sec_status_secure)
*rcode = LDNS_RCODE_NOERROR;
return;
}
if(!has_valid_wnsec) {
verbose(VERB_QUERY, "NameError response has failed to prove: "
"covering wildcard does not exist");
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
/* Be lenient with RCODE in NSEC NameError responses */
validate_nodata_response(env, ve, qchase, chase_reply, kkey);
if (chase_reply->security == sec_status_secure)
*rcode = LDNS_RCODE_NOERROR;
return;
}
/* Otherwise, we consider the message secure. */
verbose(VERB_ALGO, "successfully validated NAME ERROR response.");
chase_reply->security = sec_status_secure;
}
/**
* Given a referral response, validate rrsets and take least trusted rrset
* as the current validation status.
*
* Note that by the time this method is called, the process of finding the
* trusted DNSKEY rrset that signs this response must already have been
* completed.
*
* @param chase_reply: answer to validate.
*/
static void
validate_referral_response(struct reply_info* chase_reply)
{
size_t i;
enum sec_status s;
/* message security equals lowest rrset security */
chase_reply->security = sec_status_secure;
for(i=0; i<chase_reply->rrset_count; i++) {
s = ((struct packed_rrset_data*)chase_reply->rrsets[i]
->entry.data)->security;
if(s < chase_reply->security)
chase_reply->security = s;
}
verbose(VERB_ALGO, "validated part of referral response as %s",
sec_status_to_string(chase_reply->security));
}
/**
* Given an "ANY" response -- a response that contains an answer to a
* qtype==ANY question, with answers. This does no checking that all
* types are present.
*
* NOTE: it may be possible to get parent-side delegation point records
* here, which won't all be signed. Right now, this routine relies on the
* upstream iterative resolver to not return these responses -- instead
* treating them as referrals.
*
* NOTE: RFC 4035 is silent on this issue, so this may change upon
* clarification. Clarification draft -05 says to not check all types are
* present.
*
* Note that by the time this method is called, the process of finding the
* trusted DNSKEY rrset that signs this response must already have been
* completed.
*
* @param env: module env for verify.
* @param ve: validator env for verify.
* @param qchase: query that was made.
* @param chase_reply: answer to that query to validate.
* @param kkey: the key entry, which is trusted, and which matches
* the signer of the answer. The key entry isgood().
*/
static void
validate_any_response(struct module_env* env, struct val_env* ve,
struct query_info* qchase, struct reply_info* chase_reply,
struct key_entry_key* kkey)
{
/* all answer and auth rrsets already verified */
/* but check if a wildcard response is given, then check NSEC/NSEC3
* for qname denial to see if wildcard is applicable */
uint8_t* wc = NULL;
size_t wl;
int wc_NSEC_ok = 0;
int nsec3s_seen = 0;
size_t i;
struct ub_packed_rrset_key* s;
if(qchase->qtype != LDNS_RR_TYPE_ANY) {
log_err("internal error: ANY validation called for non-ANY");
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
/* validate the ANSWER section - this will be the answer itself */
for(i=0; i<chase_reply->an_numrrsets; i++) {
s = chase_reply->rrsets[i];
/* Check to see if the rrset is the result of a wildcard
* expansion. If so, an additional check will need to be
* made in the authority section. */
if(!val_rrset_wildcard(s, &wc, &wl)) {
log_nametypeclass(VERB_QUERY, "Positive ANY response"
" has inconsistent wildcard sigs:",
s->rk.dname, ntohs(s->rk.type),
ntohs(s->rk.rrset_class));
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
}
/* if it was a wildcard, check for NSEC/NSEC3s in both answer
* and authority sections (NSEC may be moved to the ANSWER section) */
if(wc != NULL)
for(i=0; i<chase_reply->an_numrrsets+chase_reply->ns_numrrsets;
i++) {
s = chase_reply->rrsets[i];
/* If this is a positive wildcard response, and we have a
* (just verified) NSEC record, try to use it to 1) prove
* that qname doesn't exist and 2) that the correct wildcard
* was used. */
if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) {
if(val_nsec_proves_positive_wildcard(s, qchase, wc)) {
wc_NSEC_ok = 1;
}
/* if not, continue looking for proof */
}
/* Otherwise, if this is a positive wildcard response and
* we have NSEC3 records */
if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) {
nsec3s_seen = 1;
}
}
/* If this was a positive wildcard response that we haven't already
* proven, and we have NSEC3 records, try to prove it using the NSEC3
* records. */
if(wc != NULL && !wc_NSEC_ok && nsec3s_seen) {
/* look both in answer and auth section for NSEC3s */
enum sec_status sec = nsec3_prove_wildcard(env, ve,
chase_reply->rrsets,
chase_reply->an_numrrsets+chase_reply->ns_numrrsets,
qchase, kkey, wc);
if(sec == sec_status_insecure) {
verbose(VERB_ALGO, "Positive ANY wildcard response is "
"insecure");
chase_reply->security = sec_status_insecure;
return;
} else if(sec == sec_status_secure)
wc_NSEC_ok = 1;
}
/* If after all this, we still haven't proven the positive wildcard
* response, fail. */
if(wc != NULL && !wc_NSEC_ok) {
verbose(VERB_QUERY, "positive ANY response was wildcard "
"expansion and did not prove original data "
"did not exist");
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
verbose(VERB_ALGO, "Successfully validated positive ANY response");
chase_reply->security = sec_status_secure;
}
/**
* Validate CNAME response, or DNAME+CNAME.
* This is just like a positive proof, except that this is about a
* DNAME+CNAME. Possible wildcard proof.
* Difference with positive proof is that this routine refuses
* wildcarded DNAMEs.
*
* The answer and authority rrsets must already be verified as secure.
*
* @param env: module env for verify.
* @param ve: validator env for verify.
* @param qchase: query that was made.
* @param chase_reply: answer to that query to validate.
* @param kkey: the key entry, which is trusted, and which matches
* the signer of the answer. The key entry isgood().
*/
static void
validate_cname_response(struct module_env* env, struct val_env* ve,
struct query_info* qchase, struct reply_info* chase_reply,
struct key_entry_key* kkey)
{
uint8_t* wc = NULL;
size_t wl;
int wc_NSEC_ok = 0;
int nsec3s_seen = 0;
size_t i;
struct ub_packed_rrset_key* s;
/* validate the ANSWER section - this will be the CNAME (+DNAME) */
for(i=0; i<chase_reply->an_numrrsets; i++) {
s = chase_reply->rrsets[i];
/* Check to see if the rrset is the result of a wildcard
* expansion. If so, an additional check will need to be
* made in the authority section. */
if(!val_rrset_wildcard(s, &wc, &wl)) {
log_nametypeclass(VERB_QUERY, "Cname response has "
"inconsistent wildcard sigs:", s->rk.dname,
ntohs(s->rk.type), ntohs(s->rk.rrset_class));
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
/* Refuse wildcarded DNAMEs rfc 4597.
* Do not follow a wildcarded DNAME because
* its synthesized CNAME expansion is underdefined */
if(qchase->qtype != LDNS_RR_TYPE_DNAME &&
ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME && wc) {
log_nametypeclass(VERB_QUERY, "cannot validate a "
"wildcarded DNAME:", s->rk.dname,
ntohs(s->rk.type), ntohs(s->rk.rrset_class));
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
/* If we have found a CNAME, stop looking for one.
* The iterator has placed the CNAME chain in correct
* order. */
if (ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME) {
break;
}
}
/* AUTHORITY section */
for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
chase_reply->ns_numrrsets; i++) {
s = chase_reply->rrsets[i];
/* If this is a positive wildcard response, and we have a
* (just verified) NSEC record, try to use it to 1) prove
* that qname doesn't exist and 2) that the correct wildcard
* was used. */
if(wc != NULL && ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) {
if(val_nsec_proves_positive_wildcard(s, qchase, wc)) {
wc_NSEC_ok = 1;
}
/* if not, continue looking for proof */
}
/* Otherwise, if this is a positive wildcard response and
* we have NSEC3 records */
if(wc != NULL && ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) {
nsec3s_seen = 1;
}
}
/* If this was a positive wildcard response that we haven't already
* proven, and we have NSEC3 records, try to prove it using the NSEC3
* records. */
if(wc != NULL && !wc_NSEC_ok && nsec3s_seen) {
enum sec_status sec = nsec3_prove_wildcard(env, ve,
chase_reply->rrsets+chase_reply->an_numrrsets,
chase_reply->ns_numrrsets, qchase, kkey, wc);
if(sec == sec_status_insecure) {
verbose(VERB_ALGO, "wildcard CNAME response is "
"insecure");
chase_reply->security = sec_status_insecure;
return;
} else if(sec == sec_status_secure)
wc_NSEC_ok = 1;
}
/* If after all this, we still haven't proven the positive wildcard
* response, fail. */
if(wc != NULL && !wc_NSEC_ok) {
verbose(VERB_QUERY, "CNAME response was wildcard "
"expansion and did not prove original data "
"did not exist");
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
verbose(VERB_ALGO, "Successfully validated CNAME response");
chase_reply->security = sec_status_secure;
}
/**
* Validate CNAME NOANSWER response, no more data after a CNAME chain.
* This can be a NODATA or a NAME ERROR case, but not both at the same time.
* We don't know because the rcode has been set to NOERROR by the CNAME.
*
* The answer and authority rrsets must already be verified as secure.
*
* @param env: module env for verify.
* @param ve: validator env for verify.
* @param qchase: query that was made.
* @param chase_reply: answer to that query to validate.
* @param kkey: the key entry, which is trusted, and which matches
* the signer of the answer. The key entry isgood().
*/
static void
validate_cname_noanswer_response(struct module_env* env, struct val_env* ve,
struct query_info* qchase, struct reply_info* chase_reply,
struct key_entry_key* kkey)
{
int nodata_valid_nsec = 0; /* If true, then NODATA has been proven.*/
uint8_t* ce = NULL; /* for wildcard nodata responses. This is the
proven closest encloser. */
uint8_t* wc = NULL; /* for wildcard nodata responses. wildcard nsec */
int nxdomain_valid_nsec = 0; /* if true, nameerror has been proven */
int nxdomain_valid_wnsec = 0;
int nsec3s_seen = 0; /* nsec3s seen */
struct ub_packed_rrset_key* s;
size_t i;
uint8_t* nsec_ce; /* Used to find the NSEC with the longest ce */
int ce_labs = 0;
int prev_ce_labs = 0;
/* the AUTHORITY section */
for(i=chase_reply->an_numrrsets; i<chase_reply->an_numrrsets+
chase_reply->ns_numrrsets; i++) {
s = chase_reply->rrsets[i];
/* If we encounter an NSEC record, try to use it to prove
* NODATA. This needs to handle the ENT NODATA case.
* Also try to prove NAMEERROR, and absence of a wildcard */
if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) {
if(nsec_proves_nodata(s, qchase, &wc)) {
nodata_valid_nsec = 1;
/* set wc encloser if wildcard applicable */
}
if(val_nsec_proves_name_error(s, qchase->qname)) {
ce = nsec_closest_encloser(qchase->qname, s);
nxdomain_valid_nsec = 1;
}
nsec_ce = nsec_closest_encloser(qchase->qname, s);
ce_labs = dname_count_labels(nsec_ce);
/* Use longest closest encloser to prove wildcard. */
if(ce_labs > prev_ce_labs ||
(ce_labs == prev_ce_labs &&
nxdomain_valid_wnsec == 0)) {
if(val_nsec_proves_no_wc(s, qchase->qname,
qchase->qname_len))
nxdomain_valid_wnsec = 1;
else
nxdomain_valid_wnsec = 0;
}
prev_ce_labs = ce_labs;
if(val_nsec_proves_insecuredelegation(s, qchase)) {
verbose(VERB_ALGO, "delegation is insecure");
chase_reply->security = sec_status_insecure;
return;
}
} else if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC3) {
nsec3s_seen = 1;
}
}
/* check to see if we have a wildcard NODATA proof. */
/* The wildcard NODATA is 1 NSEC proving that qname does not exists
* (and also proving what the closest encloser is), and 1 NSEC
* showing the matching wildcard, which must be *.closest_encloser. */
if(wc && !ce)
nodata_valid_nsec = 0;
else if(wc && ce) {
if(query_dname_compare(wc, ce) != 0) {
nodata_valid_nsec = 0;
}
}
if(nxdomain_valid_nsec && !nxdomain_valid_wnsec) {
/* name error is missing wildcard denial proof */
nxdomain_valid_nsec = 0;
}
if(nodata_valid_nsec && nxdomain_valid_nsec) {
verbose(VERB_QUERY, "CNAMEchain to noanswer proves that name "
"exists and not exists, bogus");
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
if(!nodata_valid_nsec && !nxdomain_valid_nsec && nsec3s_seen) {
int nodata;
enum sec_status sec = nsec3_prove_nxornodata(env, ve,
chase_reply->rrsets+chase_reply->an_numrrsets,
chase_reply->ns_numrrsets, qchase, kkey, &nodata);
if(sec == sec_status_insecure) {
verbose(VERB_ALGO, "CNAMEchain to noanswer response "
"is insecure");
chase_reply->security = sec_status_insecure;
return;
} else if(sec == sec_status_secure) {
if(nodata)
nodata_valid_nsec = 1;
else nxdomain_valid_nsec = 1;
}
}
if(!nodata_valid_nsec && !nxdomain_valid_nsec) {
verbose(VERB_QUERY, "CNAMEchain to noanswer response failed "
"to prove status with NSEC/NSEC3");
if(verbosity >= VERB_ALGO)
log_dns_msg("Failed CNAMEnoanswer", qchase, chase_reply);
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
if(nodata_valid_nsec)
verbose(VERB_ALGO, "successfully validated CNAME chain to a "
"NODATA response.");
else verbose(VERB_ALGO, "successfully validated CNAME chain to a "
"NAMEERROR response.");
chase_reply->security = sec_status_secure;
}
/**
* Process init state for validator.
* Process the INIT state. First tier responses start in the INIT state.
* This is where they are vetted for validation suitability, and the initial
* key search is done.
*
* Currently, events the come through this routine will be either promoted
* to FINISHED/CNAME_RESP (no validation needed), FINDKEY (next step to
* validation), or will be (temporarily) retired and a new priming request
* event will be generated.
*
* @param qstate: query state.
* @param vq: validator query state.
* @param ve: validator shared global environment.
* @param id: module id.
* @return true if the event should be processed further on return, false if
* not.
*/
static int
processInit(struct module_qstate* qstate, struct val_qstate* vq,
struct val_env* ve, int id)
{
uint8_t* lookup_name;
size_t lookup_len;
struct trust_anchor* anchor;
enum val_classification subtype = val_classify_response(
qstate->query_flags, &qstate->qinfo, &vq->qchase,
vq->orig_msg->rep, vq->rrset_skip);
if(vq->restart_count > ve->max_restart) {
verbose(VERB_ALGO, "restart count exceeded");
return val_error(qstate, id);
}
/* correctly initialize reason_bogus */
update_reason_bogus(vq->chase_reply, LDNS_EDE_DNSSEC_BOGUS);
verbose(VERB_ALGO, "validator classification %s",
val_classification_to_string(subtype));
if(subtype == VAL_CLASS_REFERRAL &&
vq->rrset_skip < vq->orig_msg->rep->rrset_count) {
/* referral uses the rrset name as qchase, to find keys for
* that rrset */
vq->qchase.qname = vq->orig_msg->rep->
rrsets[vq->rrset_skip]->rk.dname;
vq->qchase.qname_len = vq->orig_msg->rep->
rrsets[vq->rrset_skip]->rk.dname_len;
vq->qchase.qtype = ntohs(vq->orig_msg->rep->
rrsets[vq->rrset_skip]->rk.type);
vq->qchase.qclass = ntohs(vq->orig_msg->rep->
rrsets[vq->rrset_skip]->rk.rrset_class);
}
lookup_name = vq->qchase.qname;
lookup_len = vq->qchase.qname_len;
/* for type DS look at the parent side for keys/trustanchor */
/* also for NSEC not at apex */
if(vq->qchase.qtype == LDNS_RR_TYPE_DS ||
(vq->qchase.qtype == LDNS_RR_TYPE_NSEC &&
vq->orig_msg->rep->rrset_count > vq->rrset_skip &&
ntohs(vq->orig_msg->rep->rrsets[vq->rrset_skip]->rk.type) ==
LDNS_RR_TYPE_NSEC &&
!(vq->orig_msg->rep->rrsets[vq->rrset_skip]->
rk.flags&PACKED_RRSET_NSEC_AT_APEX))) {
dname_remove_label(&lookup_name, &lookup_len);
}
val_mark_indeterminate(vq->chase_reply, qstate->env->anchors,
qstate->env->rrset_cache, qstate->env);
vq->key_entry = NULL;
vq->empty_DS_name = NULL;
vq->ds_rrset = 0;
anchor = anchors_lookup(qstate->env->anchors,
lookup_name, lookup_len, vq->qchase.qclass);
/* Determine the signer/lookup name */
val_find_signer(subtype, &vq->qchase, vq->orig_msg->rep,
vq->rrset_skip, &vq->signer_name, &vq->signer_len);
if(vq->signer_name != NULL &&
!dname_subdomain_c(lookup_name, vq->signer_name)) {
log_nametypeclass(VERB_ALGO, "this signer name is not a parent "
"of lookupname, omitted", vq->signer_name, 0, 0);
vq->signer_name = NULL;
}
if(vq->signer_name == NULL) {
log_nametypeclass(VERB_ALGO, "no signer, using", lookup_name,
0, 0);
} else {
lookup_name = vq->signer_name;
lookup_len = vq->signer_len;
log_nametypeclass(VERB_ALGO, "signer is", lookup_name, 0, 0);
}
/* for NXDOMAIN it could be signed by a parent of the trust anchor */
if(subtype == VAL_CLASS_NAMEERROR && vq->signer_name &&
anchor && dname_strict_subdomain_c(anchor->name, lookup_name)){
lock_basic_unlock(&anchor->lock);
anchor = anchors_lookup(qstate->env->anchors,
lookup_name, lookup_len, vq->qchase.qclass);
if(!anchor) { /* unsigned parent denies anchor*/
verbose(VERB_QUERY, "unsigned parent zone denies"
" trust anchor, indeterminate");
vq->chase_reply->security = sec_status_indeterminate;
update_reason_bogus(vq->chase_reply, LDNS_EDE_DNSSEC_INDETERMINATE);
vq->state = VAL_FINISHED_STATE;
return 1;
}
verbose(VERB_ALGO, "trust anchor NXDOMAIN by signed parent");
} else if(subtype == VAL_CLASS_POSITIVE &&
qstate->qinfo.qtype == LDNS_RR_TYPE_DNSKEY &&
query_dname_compare(lookup_name, qstate->qinfo.qname) == 0) {
/* is a DNSKEY so lookup a bit higher since we want to
* get it from a parent or from trustanchor */
dname_remove_label(&lookup_name, &lookup_len);
}
if(vq->rrset_skip > 0 || subtype == VAL_CLASS_CNAME ||
subtype == VAL_CLASS_REFERRAL) {
/* extract this part of orig_msg into chase_reply for
* the eventual VALIDATE stage */
val_fill_reply(vq->chase_reply, vq->orig_msg->rep,
vq->rrset_skip, lookup_name, lookup_len,
vq->signer_name);
if(verbosity >= VERB_ALGO)
log_dns_msg("chased extract", &vq->qchase,
vq->chase_reply);
}
vq->key_entry = key_cache_obtain(ve->kcache, lookup_name, lookup_len,
vq->qchase.qclass, qstate->region, *qstate->env->now);
/* there is no key and no trust anchor */
if(vq->key_entry == NULL && anchor == NULL) {
/*response isn't under a trust anchor, so we cannot validate.*/
vq->chase_reply->security = sec_status_indeterminate;
update_reason_bogus(vq->chase_reply, LDNS_EDE_DNSSEC_INDETERMINATE);
/* go to finished state to cache this result */
vq->state = VAL_FINISHED_STATE;
return 1;
}
/* if not key, or if keyentry is *above* the trustanchor, i.e.
* the keyentry is based on another (higher) trustanchor */
else if(vq->key_entry == NULL || (anchor &&
dname_strict_subdomain_c(anchor->name, vq->key_entry->name))) {
/* trust anchor is an 'unsigned' trust anchor */
if(anchor && anchor->numDS == 0 && anchor->numDNSKEY == 0) {
vq->chase_reply->security = sec_status_insecure;
val_mark_insecure(vq->chase_reply, anchor->name,
qstate->env->rrset_cache, qstate->env);
lock_basic_unlock(&anchor->lock);
/* go to finished state to cache this result */
vq->state = VAL_FINISHED_STATE;
return 1;
}
/* fire off a trust anchor priming query. */
verbose(VERB_DETAIL, "prime trust anchor");
if(!prime_trust_anchor(qstate, vq, id, anchor)) {
lock_basic_unlock(&anchor->lock);
return val_error(qstate, id);
}
lock_basic_unlock(&anchor->lock);
/* and otherwise, don't continue processing this event.
* (it will be reactivated when the priming query returns). */
vq->state = VAL_FINDKEY_STATE;
return 0;
}
if(anchor) {
lock_basic_unlock(&anchor->lock);
}
if(key_entry_isnull(vq->key_entry)) {
/* response is under a null key, so we cannot validate
* However, we do set the status to INSECURE, since it is
* essentially proven insecure. */
vq->chase_reply->security = sec_status_insecure;
val_mark_insecure(vq->chase_reply, vq->key_entry->name,
qstate->env->rrset_cache, qstate->env);
/* go to finished state to cache this result */
vq->state = VAL_FINISHED_STATE;
return 1;
} else if(key_entry_isbad(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);
errinf(qstate, "because of a previous");
errinf(qstate, key_entry_get_reason(vq->key_entry));
/* no retries, stop bothering the authority until timeout */
vq->restart_count = ve->max_restart;
vq->chase_reply->security = sec_status_bogus;
update_reason_bogus(vq->chase_reply, ede);
vq->state = VAL_FINISHED_STATE;
return 1;
}
/* otherwise, we have our "closest" cached key -- continue
* processing in the next state. */
vq->state = VAL_FINDKEY_STATE;
return 1;
}
/**
* Process the FINDKEY state. Generally this just calculates the next name
* to query and either issues a DS or a DNSKEY query. It will check to see
* if the correct key has already been reached, in which case it will
* advance the event to the next state.
*
* @param qstate: query state.
* @param vq: validator query state.
* @param id: module id.
* @return true if the event should be processed further on return, false if
* not.
*/
static int
processFindKey(struct module_qstate* qstate, struct val_qstate* vq, int id)
{
uint8_t* target_key_name, *current_key_name;
size_t target_key_len;
int strip_lab;
struct module_qstate* newq = NULL;
log_query_info(VERB_ALGO, "validator: FindKey", &vq->qchase);
/* We know that state.key_entry is not 0 or bad key -- if it were,
* then previous processing should have directed this event to
* a different state.
* It could be an isnull key, which signals the DNSKEY failed
* with retry and has to be looked up again. */
log_assert(vq->key_entry && !key_entry_isbad(vq->key_entry));
if(key_entry_isnull(vq->key_entry)) {
if(!generate_request(qstate, id, vq->ds_rrset->rk.dname,
vq->ds_rrset->rk.dname_len, LDNS_RR_TYPE_DNSKEY,
vq->qchase.qclass, BIT_CD, &newq, 0)) {
verbose(VERB_ALGO, "error generating DNSKEY request");
return val_error(qstate, id);
}
return 0;
}
target_key_name = vq->signer_name;
target_key_len = vq->signer_len;
if(!target_key_name) {
target_key_name = vq->qchase.qname;
target_key_len = vq->qchase.qname_len;
}
current_key_name = vq->key_entry->name;
/* If our current key entry matches our target, then we are done. */
if(query_dname_compare(target_key_name, current_key_name) == 0) {
vq->state = VAL_VALIDATE_STATE;
return 1;
}
if(vq->empty_DS_name) {
/* if the last empty nonterminal/emptyDS name we detected is
* below the current key, use that name to make progress
* along the chain of trust */
if(query_dname_compare(target_key_name,
vq->empty_DS_name) == 0) {
/* do not query for empty_DS_name again */
verbose(VERB_ALGO, "Cannot retrieve DS for signature");
errinf_ede(qstate, "no signatures", LDNS_EDE_RRSIGS_MISSING);
errinf_origin(qstate, qstate->reply_origin);
vq->chase_reply->security = sec_status_bogus;
update_reason_bogus(vq->chase_reply, LDNS_EDE_RRSIGS_MISSING);
vq->state = VAL_FINISHED_STATE;
return 1;
}
current_key_name = vq->empty_DS_name;
}
log_nametypeclass(VERB_ALGO, "current keyname", current_key_name,
LDNS_RR_TYPE_DNSKEY, LDNS_RR_CLASS_IN);
log_nametypeclass(VERB_ALGO, "target keyname", target_key_name,
LDNS_RR_TYPE_DNSKEY, LDNS_RR_CLASS_IN);
/* assert we are walking down the DNS tree */
if(!dname_subdomain_c(target_key_name, current_key_name)) {
verbose(VERB_ALGO, "bad signer name");
vq->chase_reply->security = sec_status_bogus;
vq->state = VAL_FINISHED_STATE;
return 1;
}
/* so this value is >= -1 */
strip_lab = dname_count_labels(target_key_name) -
dname_count_labels(current_key_name) - 1;
log_assert(strip_lab >= -1);
verbose(VERB_ALGO, "striplab %d", strip_lab);
if(strip_lab > 0) {
dname_remove_labels(&target_key_name, &target_key_len,
strip_lab);
}
log_nametypeclass(VERB_ALGO, "next keyname", target_key_name,
LDNS_RR_TYPE_DNSKEY, LDNS_RR_CLASS_IN);
/* The next step is either to query for the next DS, or to query
* for the next DNSKEY. */
if(vq->ds_rrset)
log_nametypeclass(VERB_ALGO, "DS RRset", vq->ds_rrset->rk.dname, LDNS_RR_TYPE_DS, LDNS_RR_CLASS_IN);
else verbose(VERB_ALGO, "No DS RRset");
if(vq->ds_rrset && query_dname_compare(vq->ds_rrset->rk.dname,
vq->key_entry->name) != 0) {
if(!generate_request(qstate, id, vq->ds_rrset->rk.dname,
vq->ds_rrset->rk.dname_len, LDNS_RR_TYPE_DNSKEY,
vq->qchase.qclass, BIT_CD, &newq, 0)) {
verbose(VERB_ALGO, "error generating DNSKEY request");
return val_error(qstate, id);
}
return 0;
}
if(!vq->ds_rrset || query_dname_compare(vq->ds_rrset->rk.dname,
target_key_name) != 0) {
/* check if there is a cache entry : pick up an NSEC if
* there is no DS, check if that NSEC has DS-bit unset, and
* thus can disprove the secure delegation we seek.
* We can then use that NSEC even in the absence of a SOA
* record that would be required by the iterator to supply
* a completely protocol-correct response.
* Uses negative cache for NSEC3 lookup of DS responses. */
/* only if cache not blacklisted, of course */
struct dns_msg* msg;
if(!qstate->blacklist && !vq->chain_blacklist &&
(msg=val_find_DS(qstate->env, target_key_name,
target_key_len, vq->qchase.qclass, qstate->region,
vq->key_entry->name)) ) {
verbose(VERB_ALGO, "Process cached DS response");
process_ds_response(qstate, vq, id, LDNS_RCODE_NOERROR,
msg, &msg->qinfo, NULL);
return 1; /* continue processing ds-response results */
}
if(!generate_request(qstate, id, target_key_name,
target_key_len, LDNS_RR_TYPE_DS, vq->qchase.qclass,
BIT_CD, &newq, 0)) {
verbose(VERB_ALGO, "error generating DS request");
return val_error(qstate, id);
}
return 0;
}
/* Otherwise, it is time to query for the DNSKEY */
if(!generate_request(qstate, id, vq->ds_rrset->rk.dname,
vq->ds_rrset->rk.dname_len, LDNS_RR_TYPE_DNSKEY,
vq->qchase.qclass, BIT_CD, &newq, 0)) {
verbose(VERB_ALGO, "error generating DNSKEY request");
return val_error(qstate, id);
}
return 0;
}
/**
* Process the VALIDATE stage, the init and findkey stages are finished,
* and the right keys are available to validate the response.
* Or, there are no keys available, in order to invalidate the response.
*
* After validation, the status is recorded in the message and rrsets,
* and finished state is started.
*
* @param qstate: query state.
* @param vq: validator query state.
* @param ve: validator shared global environment.
* @param id: module id.
* @return true if the event should be processed further on return, false if
* not.
*/
static int
processValidate(struct module_qstate* qstate, struct val_qstate* vq,
struct val_env* ve, int id)
{
enum val_classification subtype;
int rcode;
if(!vq->key_entry) {
verbose(VERB_ALGO, "validate: no key entry, failed");
return val_error(qstate, id);
}
/* This is the default next state. */
vq->state = VAL_FINISHED_STATE;
/* Unsigned responses must be underneath a "null" key entry.*/
if(key_entry_isnull(vq->key_entry)) {
verbose(VERB_DETAIL, "Verified that %sresponse is INSECURE",
vq->signer_name?"":"unsigned ");
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->env->cfg->val_log_level >= 2);
return 1;
}
if(key_entry_isbad(vq->key_entry)) {
log_nametypeclass(VERB_DETAIL, "Could not establish a chain "
"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,
key_entry_get_reason_bogus(vq->key_entry));
errinf_ede(qstate, "while building chain of trust",
key_entry_get_reason_bogus(vq->key_entry));
if(vq->restart_count >= ve->max_restart)
key_cache_insert(ve->kcache, vq->key_entry,
qstate->env->cfg->val_log_level >= 2);
return 1;
}
/* signerName being null is the indicator that this response was
* unsigned */
if(vq->signer_name == NULL) {
log_query_info(VERB_ALGO, "processValidate: state has no "
"signer name", &vq->qchase);
verbose(VERB_DETAIL, "Could not establish validation of "
"INSECURE status of unsigned response.");
errinf_ede(qstate, "no signatures", LDNS_EDE_RRSIGS_MISSING);
errinf_origin(qstate, qstate->reply_origin);
vq->chase_reply->security = sec_status_bogus;
update_reason_bogus(vq->chase_reply, LDNS_EDE_RRSIGS_MISSING);
return 1;
}
subtype = val_classify_response(qstate->query_flags, &qstate->qinfo,
&vq->qchase, vq->orig_msg->rep, vq->rrset_skip);
if(subtype != VAL_CLASS_REFERRAL)
remove_spurious_authority(vq->chase_reply, vq->orig_msg->rep);
/* check signatures in the message;
* answer and authority must be valid, additional is only checked. */
if(!validate_msg_signatures(qstate, qstate->env, ve, &vq->qchase,
vq->chase_reply, vq->key_entry)) {
/* workaround bad recursor out there that truncates (even
* with EDNS4k) to 512 by removing RRSIG from auth section
* for positive replies*/
if((subtype == VAL_CLASS_POSITIVE || subtype == VAL_CLASS_ANY
|| subtype == VAL_CLASS_CNAME) &&
detect_wrongly_truncated(vq->orig_msg->rep)) {
/* truncate the message some more */
vq->orig_msg->rep->ns_numrrsets = 0;
vq->orig_msg->rep->ar_numrrsets = 0;
vq->orig_msg->rep->rrset_count =
vq->orig_msg->rep->an_numrrsets;
vq->chase_reply->ns_numrrsets = 0;
vq->chase_reply->ar_numrrsets = 0;
vq->chase_reply->rrset_count =
vq->chase_reply->an_numrrsets;
qstate->errinf = NULL;
}
else {
verbose(VERB_DETAIL, "Validate: message contains "
"bad rrsets");
return 1;
}
}
switch(subtype) {
case VAL_CLASS_POSITIVE:
verbose(VERB_ALGO, "Validating a positive response");
validate_positive_response(qstate->env, ve,
&vq->qchase, vq->chase_reply, vq->key_entry);
verbose(VERB_DETAIL, "validate(positive): %s",
sec_status_to_string(
vq->chase_reply->security));
break;
case VAL_CLASS_NODATA:
verbose(VERB_ALGO, "Validating a nodata response");
validate_nodata_response(qstate->env, ve,
&vq->qchase, vq->chase_reply, vq->key_entry);
verbose(VERB_DETAIL, "validate(nodata): %s",
sec_status_to_string(
vq->chase_reply->security));
break;
case VAL_CLASS_NAMEERROR:
rcode = (int)FLAGS_GET_RCODE(vq->orig_msg->rep->flags);
verbose(VERB_ALGO, "Validating a nxdomain response");
validate_nameerror_response(qstate->env, ve,
&vq->qchase, vq->chase_reply, vq->key_entry, &rcode);
verbose(VERB_DETAIL, "validate(nxdomain): %s",
sec_status_to_string(
vq->chase_reply->security));
FLAGS_SET_RCODE(vq->orig_msg->rep->flags, rcode);
FLAGS_SET_RCODE(vq->chase_reply->flags, rcode);
break;
case VAL_CLASS_CNAME:
verbose(VERB_ALGO, "Validating a cname response");
validate_cname_response(qstate->env, ve,
&vq->qchase, vq->chase_reply, vq->key_entry);
verbose(VERB_DETAIL, "validate(cname): %s",
sec_status_to_string(
vq->chase_reply->security));
break;
case VAL_CLASS_CNAMENOANSWER:
verbose(VERB_ALGO, "Validating a cname noanswer "
"response");
validate_cname_noanswer_response(qstate->env, ve,
&vq->qchase, vq->chase_reply, vq->key_entry);
verbose(VERB_DETAIL, "validate(cname_noanswer): %s",
sec_status_to_string(
vq->chase_reply->security));
break;
case VAL_CLASS_REFERRAL:
verbose(VERB_ALGO, "Validating a referral response");
validate_referral_response(vq->chase_reply);
verbose(VERB_DETAIL, "validate(referral): %s",
sec_status_to_string(
vq->chase_reply->security));
break;
case VAL_CLASS_ANY:
verbose(VERB_ALGO, "Validating a positive ANY "
"response");
validate_any_response(qstate->env, ve, &vq->qchase,
vq->chase_reply, vq->key_entry);
verbose(VERB_DETAIL, "validate(positive_any): %s",
sec_status_to_string(
vq->chase_reply->security));
break;
default:
log_err("validate: unhandled response subtype: %d",
subtype);
}
if(vq->chase_reply->security == sec_status_bogus) {
if(subtype == VAL_CLASS_POSITIVE)
errinf(qstate, "wildcard");
else errinf(qstate, val_classification_to_string(subtype));
errinf(qstate, "proof failed");
errinf_origin(qstate, qstate->reply_origin);
}
return 1;
}
/**
* The Finished state. The validation status (good or bad) has been determined.
*
* @param qstate: query state.
* @param vq: validator query state.
* @param ve: validator shared global environment.
* @param id: module id.
* @return true if the event should be processed further on return, false if
* not.
*/
static int
processFinished(struct module_qstate* qstate, struct val_qstate* vq,
struct val_env* ve, int id)
{
enum val_classification subtype = val_classify_response(
qstate->query_flags, &qstate->qinfo, &vq->qchase,
vq->orig_msg->rep, vq->rrset_skip);
/* store overall validation result in orig_msg */
if(vq->rrset_skip == 0) {
vq->orig_msg->rep->security = vq->chase_reply->security;
update_reason_bogus(vq->orig_msg->rep, vq->chase_reply->reason_bogus);
} else if(subtype != VAL_CLASS_REFERRAL ||
vq->rrset_skip < vq->orig_msg->rep->an_numrrsets +
vq->orig_msg->rep->ns_numrrsets) {
/* ignore sec status of additional section if a referral
* type message skips there and
* use the lowest security status as end result. */
if(vq->chase_reply->security < vq->orig_msg->rep->security) {
vq->orig_msg->rep->security =
vq->chase_reply->security;
update_reason_bogus(vq->orig_msg->rep, vq->chase_reply->reason_bogus);
}
}
if(subtype == VAL_CLASS_REFERRAL) {
/* for a referral, move to next unchecked rrset and check it*/
vq->rrset_skip = val_next_unchecked(vq->orig_msg->rep,
vq->rrset_skip);
if(vq->rrset_skip < vq->orig_msg->rep->rrset_count) {
/* and restart for this rrset */
verbose(VERB_ALGO, "validator: go to next rrset");
vq->chase_reply->security = sec_status_unchecked;
vq->state = VAL_INIT_STATE;
return 1;
}
/* referral chase is done */
}
if(vq->chase_reply->security != sec_status_bogus &&
subtype == VAL_CLASS_CNAME) {
/* chase the CNAME; process next part of the message */
if(!val_chase_cname(&vq->qchase, vq->orig_msg->rep,
&vq->rrset_skip)) {
verbose(VERB_ALGO, "validator: failed to chase CNAME");
vq->orig_msg->rep->security = sec_status_bogus;
update_reason_bogus(vq->orig_msg->rep, LDNS_EDE_DNSSEC_BOGUS);
} else {
/* restart process for new qchase at rrset_skip */
log_query_info(VERB_ALGO, "validator: chased to",
&vq->qchase);
vq->chase_reply->security = sec_status_unchecked;
vq->state = VAL_INIT_STATE;
return 1;
}
}
if(vq->orig_msg->rep->security == sec_status_secure) {
/* If the message is secure, check that all rrsets are
* secure (i.e. some inserted RRset for CNAME chain with
* a different signer name). And drop additional rrsets
* that are not secure (if clean-additional option is set) */
/* this may cause the msg to be marked bogus */
val_check_nonsecure(qstate->env, vq->orig_msg->rep);
if(vq->orig_msg->rep->security == sec_status_secure) {
log_query_info(VERB_DETAIL, "validation success",
&qstate->qinfo);
if(!qstate->no_cache_store) {
val_neg_addreply(qstate->env->neg_cache,
vq->orig_msg->rep);
}
}
}
/* if the result is bogus - set message ttl to bogus ttl to avoid
* endless bogus revalidation */
if(vq->orig_msg->rep->security == sec_status_bogus) {
/* see if we can try again to fetch data */
if(vq->restart_count < ve->max_restart) {
int restart_count = vq->restart_count+1;
verbose(VERB_ALGO, "validation failed, "
"blacklist and retry to fetch data");
val_blacklist(&qstate->blacklist, qstate->region,
qstate->reply_origin, 0);
qstate->reply_origin = NULL;
qstate->errinf = NULL;
memset(vq, 0, sizeof(*vq));
vq->restart_count = restart_count;
vq->state = VAL_INIT_STATE;
verbose(VERB_ALGO, "pass back to next module");
qstate->ext_state[id] = module_restart_next;
return 0;
}
vq->orig_msg->rep->ttl = ve->bogus_ttl;
vq->orig_msg->rep->prefetch_ttl =
PREFETCH_TTL_CALC(vq->orig_msg->rep->ttl);
vq->orig_msg->rep->serve_expired_ttl =
vq->orig_msg->rep->ttl + qstate->env->cfg->serve_expired_ttl;
if((qstate->env->cfg->val_log_level >= 1 ||
qstate->env->cfg->log_servfail) &&
!qstate->env->cfg->val_log_squelch) {
if(qstate->env->cfg->val_log_level < 2 &&
!qstate->env->cfg->log_servfail)
log_query_info(NO_VERBOSE, "validation failure",
&qstate->qinfo);
else {
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);
}
}
/*
* If set, the validator will not make messages bogus, instead
* indeterminate is issued, so that no clients receive SERVFAIL.
* This allows an operator to run validation 'shadow' without
* hurting responses to clients.
*/
/* If we are in permissive mode, bogus gets indeterminate */
if(qstate->env->cfg->val_permissive_mode)
vq->orig_msg->rep->security = sec_status_indeterminate;
}
if(vq->orig_msg->rep->security == sec_status_secure &&
qstate->env->cfg->root_key_sentinel &&
(qstate->qinfo.qtype == LDNS_RR_TYPE_A ||
qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA)) {
char* keytag_start;
uint16_t keytag;
if(*qstate->qinfo.qname == strlen(SENTINEL_IS) +
SENTINEL_KEYTAG_LEN &&
dname_lab_startswith(qstate->qinfo.qname, SENTINEL_IS,
&keytag_start)) {
if(sentinel_get_keytag(keytag_start, &keytag) &&
!anchor_has_keytag(qstate->env->anchors,
(uint8_t*)"", 1, 0, vq->qchase.qclass, keytag)) {
vq->orig_msg->rep->security =
sec_status_secure_sentinel_fail;
}
} else if(*qstate->qinfo.qname == strlen(SENTINEL_NOT) +
SENTINEL_KEYTAG_LEN &&
dname_lab_startswith(qstate->qinfo.qname, SENTINEL_NOT,
&keytag_start)) {
if(sentinel_get_keytag(keytag_start, &keytag) &&
anchor_has_keytag(qstate->env->anchors,
(uint8_t*)"", 1, 0, vq->qchase.qclass, keytag)) {
vq->orig_msg->rep->security =
sec_status_secure_sentinel_fail;
}
}
}
/* 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
* to check if from parentNS */
if(!qstate->no_cache_store) {
if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo,
vq->orig_msg->rep, 0, qstate->prefetch_leeway, 0, NULL,
qstate->query_flags, qstate->qstarttime)) {
log_err("out of memory caching validator results");
}
}
} else {
/* for a referral, store the verified RRsets */
/* and this does not get prefetched, so no leeway */
if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo,
vq->orig_msg->rep, 1, 0, 0, NULL,
qstate->query_flags, qstate->qstarttime)) {
log_err("out of memory caching validator results");
}
}
qstate->return_rcode = LDNS_RCODE_NOERROR;
qstate->return_msg = vq->orig_msg;
qstate->ext_state[id] = module_finished;
return 0;
}
/**
* Handle validator state.
* If a method returns true, the next state is started. If false, then
* processing will stop.
* @param qstate: query state.
* @param vq: validator query state.
* @param ve: validator shared global environment.
* @param id: module id.
*/
static void
val_handle(struct module_qstate* qstate, struct val_qstate* vq,
struct val_env* ve, int id)
{
int cont = 1;
while(cont) {
verbose(VERB_ALGO, "val handle processing q with state %s",
val_state_to_string(vq->state));
switch(vq->state) {
case VAL_INIT_STATE:
cont = processInit(qstate, vq, ve, id);
break;
case VAL_FINDKEY_STATE:
cont = processFindKey(qstate, vq, id);
break;
case VAL_VALIDATE_STATE:
cont = processValidate(qstate, vq, ve, id);
break;
case VAL_FINISHED_STATE:
cont = processFinished(qstate, vq, ve, id);
break;
default:
log_warn("validator: invalid state %d",
vq->state);
cont = 0;
break;
}
}
}
void
val_operate(struct module_qstate* qstate, enum module_ev event, int id,
struct outbound_entry* outbound)
{
struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
struct val_qstate* vq = (struct val_qstate*)qstate->minfo[id];
verbose(VERB_QUERY, "validator[module %d] operate: extstate:%s "
"event:%s", id, strextstate(qstate->ext_state[id]),
strmodulevent(event));
log_query_info(VERB_QUERY, "validator operate: query",
&qstate->qinfo);
if(vq && qstate->qinfo.qname != vq->qchase.qname)
log_query_info(VERB_QUERY, "validator operate: chased to",
&vq->qchase);
(void)outbound;
if(event == module_event_new ||
(event == module_event_pass && vq == NULL)) {
/* pass request to next module, to get it */
verbose(VERB_ALGO, "validator: pass to next module");
qstate->ext_state[id] = module_wait_module;
return;
}
if(event == module_event_moddone) {
/* check if validation is needed */
verbose(VERB_ALGO, "validator: nextmodule returned");
if(!needs_validation(qstate, qstate->return_rcode,
qstate->return_msg)) {
/* no need to validate this */
if(qstate->return_msg)
qstate->return_msg->rep->security =
sec_status_indeterminate;
qstate->ext_state[id] = module_finished;
return;
}
if(already_validated(qstate->return_msg)) {
qstate->ext_state[id] = module_finished;
return;
}
/* qclass ANY should have validation result from spawned
* queries. If we get here, it is bogus or an internal error */
if(qstate->qinfo.qclass == LDNS_RR_CLASS_ANY) {
verbose(VERB_ALGO, "cannot validate classANY: bogus");
if(qstate->return_msg) {
qstate->return_msg->rep->security =
sec_status_bogus;
update_reason_bogus(qstate->return_msg->rep, LDNS_EDE_DNSSEC_BOGUS);
}
qstate->ext_state[id] = module_finished;
return;
}
/* create state to start validation */
qstate->ext_state[id] = module_error; /* override this */
if(!vq) {
vq = val_new(qstate, id);
if(!vq) {
log_err("validator: malloc failure");
qstate->ext_state[id] = module_error;
return;
}
} else if(!vq->orig_msg) {
if(!val_new_getmsg(qstate, vq)) {
log_err("validator: malloc failure");
qstate->ext_state[id] = module_error;
return;
}
}
val_handle(qstate, vq, ve, id);
return;
}
if(event == module_event_pass) {
qstate->ext_state[id] = module_error; /* override this */
/* continue processing, since val_env exists */
val_handle(qstate, vq, ve, id);
return;
}
log_err("validator: bad event %s", strmodulevent(event));
qstate->ext_state[id] = module_error;
return;
}
/**
* Evaluate the response to a priming request.
*
* @param dnskey_rrset: DNSKEY rrset (can be NULL if none) in prime reply.
* (this rrset is allocated in the wrong region, not the qstate).
* @param ta: trust anchor.
* @param qstate: qstate that needs key.
* @param id: module id.
* @return new key entry or NULL on allocation failure.
* The key entry will either contain a validated DNSKEY rrset, or
* represent a Null key (query failed, but validation did not), or a
* Bad key (validation failed).
*/
static struct key_entry_key*
primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset,
struct trust_anchor* ta, struct module_qstate* qstate, int id)
{
struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
struct key_entry_key* kkey = NULL;
enum sec_status sec = sec_status_unchecked;
char* reason = NULL;
sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
int downprot = qstate->env->cfg->harden_algo_downgrade;
if(!dnskey_rrset) {
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, 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");
return NULL;
}
return kkey;
}
/* attempt to verify with trust anchor DS and DNSKEY */
kkey = val_verify_new_DNSKEYs_with_ta(qstate->region, qstate->env, ve,
dnskey_rrset, ta->ds_rrset, ta->dnskey_rrset, downprot,
&reason, &reason_bogus, qstate);
if(!kkey) {
log_err("out of memory: verifying prime TA");
return NULL;
}
if(key_entry_isgood(kkey))
sec = sec_status_secure;
else
sec = sec_status_bogus;
verbose(VERB_DETAIL, "validate keys with anchor(DS): %s",
sec_status_to_string(sec));
if(sec != sec_status_secure) {
log_nametypeclass(VERB_OPS, "failed to prime trust anchor -- "
"DNSKEY rrset is not secure",
ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass);
/* NOTE: in this case, we should probably reject the trust
* anchor for longer, perhaps forever. */
if(qstate->env->cfg->harden_dnssec_stripped) {
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");
return NULL;
}
return kkey;
}
log_nametypeclass(VERB_DETAIL, "Successfully primed trust anchor",
ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass);
return kkey;
}
/**
* In inform supers, with the resulting message and rcode and the current
* keyset in the super state, validate the DS response, returning a KeyEntry.
*
* @param qstate: query state that is validating and asked for a DS.
* @param vq: validator query state
* @param id: module id.
* @param rcode: rcode result value.
* @param msg: result message (if rcode is OK).
* @param qinfo: from the sub query state, query info.
* @param ke: the key entry to return. It returns
* is_bad if the DS response fails to validate, is_null if the
* DS response indicated an end to secure space, is_good if the DS
* validated. It returns ke=NULL if the DS response indicated that the
* request wasn't a delegation point.
* @return 0 on servfail error (malloc failure).
*/
static int
ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
int id, int rcode, struct dns_msg* msg, struct query_info* qinfo,
struct key_entry_key** ke)
{
struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
char* reason = NULL;
sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
enum val_classification subtype;
if(rcode != LDNS_RCODE_NOERROR) {
char rc[16];
rc[0]=0;
(void)sldns_wire2str_rcode_buf(rcode, rc, sizeof(rc));
/* errors here pretty much break validation */
verbose(VERB_DETAIL, "DS response was error, thus bogus");
errinf(qstate, rc);
reason = "no DS";
reason_bogus = LDNS_EDE_NETWORK_ERROR;
errinf_ede(qstate, reason, reason_bogus);
goto return_bogus;
}
subtype = val_classify_response(BIT_RD, qinfo, qinfo, msg->rep, 0);
if(subtype == VAL_CLASS_POSITIVE) {
struct ub_packed_rrset_key* ds;
enum sec_status sec;
ds = reply_find_answer_rrset(qinfo, msg->rep);
/* If there was no DS rrset, then we have mis-classified
* this message. */
if(!ds) {
log_warn("internal error: POSITIVE DS response was "
"missing DS.");
reason = "no DS record";
errinf_ede(qstate, reason, reason_bogus);
goto return_bogus;
}
/* Verify only returns BOGUS or SECURE. If the rrset is
* bogus, then we are done. */
sec = val_verify_rrset_entry(qstate->env, ve, ds,
vq->key_entry, &reason, &reason_bogus, LDNS_SECTION_ANSWER, qstate);
if(sec != sec_status_secure) {
verbose(VERB_DETAIL, "DS rrset in DS response did "
"not verify");
errinf_ede(qstate, reason, reason_bogus);
goto return_bogus;
}
/* If the DS rrset validates, we still have to make sure
* that they are usable. */
if(!val_dsset_isusable(ds)) {
/* If they aren't usable, then we treat it like
* there was no DS. */
*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;
}
/* Otherwise, we return the positive response. */
log_query_info(VERB_DETAIL, "validated DS", qinfo);
*ke = key_entry_create_rrset(qstate->region,
qinfo->qname, qinfo->qname_len, qinfo->qclass, ds,
NULL, LDNS_EDE_NONE, NULL, *qstate->env->now);
return (*ke) != NULL;
} else if(subtype == VAL_CLASS_NODATA ||
subtype == VAL_CLASS_NAMEERROR) {
/* NODATA means that the qname exists, but that there was
* no DS. This is a pretty normal case. */
time_t proof_ttl = 0;
enum sec_status sec;
/* make sure there are NSECs or NSEC3s with signatures */
if(!val_has_signed_nsecs(msg->rep, &reason)) {
verbose(VERB_ALGO, "no NSECs: %s", reason);
reason_bogus = LDNS_EDE_NSEC_MISSING;
errinf_ede(qstate, reason, reason_bogus);
goto return_bogus;
}
/* For subtype Name Error.
* attempt ANS 2.8.1.0 compatibility where it sets rcode
* to nxdomain, but really this is an Nodata/Noerror response.
* Find and prove the empty nonterminal in that case */
/* 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, &reason_bogus, qstate);
switch(sec) {
case sec_status_secure:
verbose(VERB_DETAIL, "NSEC RRset for the "
"referral proved no DS.");
*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:
verbose(VERB_DETAIL, "NSEC RRset for the "
"referral proved not a delegation point");
*ke = NULL;
return 1;
case sec_status_bogus:
verbose(VERB_DETAIL, "NSEC RRset for the "
"referral did not prove no DS.");
errinf(qstate, reason);
goto return_bogus;
case sec_status_unchecked:
default:
/* NSEC proof did not work, try next */
break;
}
sec = nsec3_prove_nods(qstate->env, ve,
msg->rep->rrsets + msg->rep->an_numrrsets,
msg->rep->ns_numrrsets, qinfo, vq->key_entry, &reason,
&reason_bogus, qstate);
switch(sec) {
case sec_status_insecure:
/* case insecure also continues to unsigned
* space. If nsec3-iter-count too high or
* optout, then treat below as unsigned */
case sec_status_secure:
verbose(VERB_DETAIL, "NSEC3s for the "
"referral proved no DS.");
*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:
verbose(VERB_DETAIL, "NSEC3s for the "
"referral proved no delegation");
*ke = NULL;
return 1;
case sec_status_bogus:
verbose(VERB_DETAIL, "NSEC3s for the "
"referral did not prove no DS.");
errinf_ede(qstate, reason, reason_bogus);
goto return_bogus;
case sec_status_unchecked:
default:
/* NSEC3 proof did not work */
break;
}
/* Apparently, no available NSEC/NSEC3 proved NODATA, so
* this is BOGUS. */
verbose(VERB_DETAIL, "DS %s ran out of options, so return "
"bogus", val_classification_to_string(subtype));
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) {
/* if the CNAME matches the exact name we want and is signed
* properly, then also, we are sure that no DS exists there,
* much like a NODATA proof */
enum sec_status sec;
struct ub_packed_rrset_key* cname;
cname = reply_find_rrset_section_an(msg->rep, qinfo->qname,
qinfo->qname_len, LDNS_RR_TYPE_CNAME, qinfo->qclass);
if(!cname) {
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) {
reason = "DS got DNAME answer";
} else {
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, &reason_bogus,
LDNS_SECTION_ANSWER, qstate);
if(sec == sec_status_secure) {
verbose(VERB_ALGO, "CNAME validated, "
"proof that DS does not exist");
/* and that it is not a referral point */
*ke = NULL;
return 1;
}
errinf(qstate, "CNAME in DS response was not secure.");
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;
(void)sldns_wire2str_rcode_buf((int)FLAGS_GET_RCODE(
msg->rep->flags), rc, sizeof(rc));
errinf(qstate, rc);
} else errinf(qstate, val_classification_to_string(subtype));
errinf(qstate, "message fails to prove that");
goto return_bogus;
}
return_bogus:
*ke = key_entry_create_bad(qstate->region, qinfo->qname,
qinfo->qname_len, qinfo->qclass, BOGUS_KEY_TTL,
reason_bogus, reason, *qstate->env->now);
return (*ke) != NULL;
}
/**
* Process DS response. Called from inform_supers.
* Because it is in inform_supers, the mesh itself is busy doing callbacks
* for a state that is to be deleted soon; don't touch the mesh; instead
* set a state in the super, as the super will be reactivated soon.
* Perform processing to determine what state to set in the super.
*
* @param qstate: query state that is validating and asked for a DS.
* @param vq: validator query state
* @param id: module id.
* @param rcode: rcode result value.
* @param msg: result message (if rcode is OK).
* @param qinfo: from the sub query state, query info.
* @param origin: the origin of msg.
*/
static void
process_ds_response(struct module_qstate* qstate, struct val_qstate* vq,
int id, int rcode, struct dns_msg* msg, struct query_info* qinfo,
struct sock_list* origin)
{
struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
struct key_entry_key* dske = NULL;
uint8_t* olds = vq->empty_DS_name;
vq->empty_DS_name = NULL;
if(!ds_response_to_ke(qstate, vq, id, rcode, msg, qinfo, &dske)) {
log_err("malloc failure in process_ds_response");
vq->key_entry = NULL; /* make it error */
vq->state = VAL_VALIDATE_STATE;
return;
}
if(dske == NULL) {
vq->empty_DS_name = regional_alloc_init(qstate->region,
qinfo->qname, qinfo->qname_len);
if(!vq->empty_DS_name) {
log_err("malloc failure in empty_DS_name");
vq->key_entry = NULL; /* make it error */
vq->state = VAL_VALIDATE_STATE;
return;
}
vq->empty_DS_len = qinfo->qname_len;
vq->chain_blacklist = NULL;
/* ds response indicated that we aren't on a delegation point.
* Keep the forState.state on FINDKEY. */
} else if(key_entry_isgood(dske)) {
vq->ds_rrset = key_entry_get_rrset(dske, qstate->region);
if(!vq->ds_rrset) {
log_err("malloc failure in process DS");
vq->key_entry = NULL; /* make it error */
vq->state = VAL_VALIDATE_STATE;
return;
}
vq->chain_blacklist = NULL; /* fresh blacklist for next part*/
/* Keep the forState.state on FINDKEY. */
} else if(key_entry_isbad(dske)
&& vq->restart_count < ve->max_restart) {
vq->empty_DS_name = olds;
val_blacklist(&vq->chain_blacklist, qstate->region, origin, 1);
qstate->errinf = NULL;
vq->restart_count++;
} else {
if(key_entry_isbad(dske)) {
errinf_origin(qstate, origin);
errinf_dname(qstate, "for DS", qinfo->qname);
}
/* NOTE: the reason for the DS to be not good (that is,
* either bad or null) should have been logged by
* dsResponseToKE. */
vq->key_entry = dske;
/* The FINDKEY phase has ended, so move on. */
vq->state = VAL_VALIDATE_STATE;
}
}
/**
* Process DNSKEY response. Called from inform_supers.
* Sets the key entry in the state.
* Because it is in inform_supers, the mesh itself is busy doing callbacks
* for a state that is to be deleted soon; don't touch the mesh; instead
* set a state in the super, as the super will be reactivated soon.
* Perform processing to determine what state to set in the super.
*
* @param qstate: query state that is validating and asked for a DNSKEY.
* @param vq: validator query state
* @param id: module id.
* @param rcode: rcode result value.
* @param msg: result message (if rcode is OK).
* @param qinfo: from the sub query state, query info.
* @param origin: the origin of msg.
*/
static void
process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq,
int id, int rcode, struct dns_msg* msg, struct query_info* qinfo,
struct sock_list* origin)
{
struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
struct key_entry_key* old = vq->key_entry;
struct ub_packed_rrset_key* dnskey = NULL;
int downprot;
char* reason = NULL;
sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
if(rcode == LDNS_RCODE_NOERROR)
dnskey = reply_find_answer_rrset(qinfo, msg->rep);
if(dnskey == NULL) {
/* bad response */
verbose(VERB_DETAIL, "Missing DNSKEY RRset in response to "
"DNSKEY query.");
if(vq->restart_count < ve->max_restart) {
val_blacklist(&vq->chain_blacklist, qstate->region,
origin, 1);
qstate->errinf = NULL;
vq->restart_count++;
return;
}
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, 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, reason, reason_bogus);
errinf_origin(qstate, origin);
errinf_dname(qstate, "for key", qinfo->qname);
vq->state = VAL_VALIDATE_STATE;
return;
}
if(!vq->ds_rrset) {
log_err("internal error: no DS rrset for new DNSKEY response");
vq->key_entry = NULL;
vq->state = VAL_VALIDATE_STATE;
return;
}
downprot = qstate->env->cfg->harden_algo_downgrade;
vq->key_entry = val_verify_new_DNSKEYs(qstate->region, qstate->env,
ve, dnskey, vq->ds_rrset, downprot, &reason, &reason_bogus, qstate);
if(!vq->key_entry) {
log_err("out of memory in verify new DNSKEYs");
vq->state = VAL_VALIDATE_STATE;
return;
}
/* If the key entry isBad or isNull, then we can move on to the next
* state. */
if(!key_entry_isgood(vq->key_entry)) {
if(key_entry_isbad(vq->key_entry)) {
if(vq->restart_count < ve->max_restart) {
val_blacklist(&vq->chain_blacklist,
qstate->region, origin, 1);
qstate->errinf = NULL;
vq->restart_count++;
vq->key_entry = old;
return;
}
verbose(VERB_DETAIL, "Did not match a DS to a DNSKEY, "
"thus bogus.");
errinf_ede(qstate, reason, reason_bogus);
errinf_origin(qstate, origin);
errinf_dname(qstate, "for key", qinfo->qname);
}
vq->chain_blacklist = NULL;
vq->state = VAL_VALIDATE_STATE;
return;
}
vq->chain_blacklist = NULL;
qstate->errinf = NULL;
/* The DNSKEY validated, so cache it as a trusted key rrset. */
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);
}
/**
* Process prime response
* Sets the key entry in the state.
*
* @param qstate: query state that is validating and primed a trust anchor.
* @param vq: validator query state
* @param id: module id.
* @param rcode: rcode result value.
* @param msg: result message (if rcode is OK).
* @param origin: the origin of msg.
*/
static void
process_prime_response(struct module_qstate* qstate, struct val_qstate* vq,
int id, int rcode, struct dns_msg* msg, struct sock_list* origin)
{
struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
struct ub_packed_rrset_key* dnskey_rrset = NULL;
struct trust_anchor* ta = anchor_find(qstate->env->anchors,
vq->trust_anchor_name, vq->trust_anchor_labs,
vq->trust_anchor_len, vq->qchase.qclass);
if(!ta) {
/* trust anchor revoked, restart with less anchors */
vq->state = VAL_INIT_STATE;
if(!vq->trust_anchor_name)
vq->state = VAL_VALIDATE_STATE; /* break a loop */
vq->trust_anchor_name = NULL;
return;
}
/* Fetch and validate the keyEntry that corresponds to the
* current trust anchor. */
if(rcode == LDNS_RCODE_NOERROR) {
dnskey_rrset = reply_find_rrset_section_an(msg->rep,
ta->name, ta->namelen, LDNS_RR_TYPE_DNSKEY,
ta->dclass);
}
if(ta->autr) {
if(!autr_process_prime(qstate->env, ve, ta, dnskey_rrset,
qstate)) {
/* trust anchor revoked, restart with less anchors */
vq->state = VAL_INIT_STATE;
vq->trust_anchor_name = NULL;
return;
}
}
vq->key_entry = primeResponseToKE(dnskey_rrset, ta, qstate, id);
lock_basic_unlock(&ta->lock);
if(vq->key_entry) {
if(key_entry_isbad(vq->key_entry)
&& vq->restart_count < ve->max_restart) {
val_blacklist(&vq->chain_blacklist, qstate->region,
origin, 1);
qstate->errinf = NULL;
vq->restart_count++;
vq->key_entry = NULL;
vq->state = VAL_INIT_STATE;
return;
}
vq->chain_blacklist = NULL;
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->env->cfg->val_log_level >= 2);
}
/* If the result of the prime is a null key, skip the FINDKEY state.*/
if(!vq->key_entry || key_entry_isnull(vq->key_entry) ||
key_entry_isbad(vq->key_entry)) {
vq->state = VAL_VALIDATE_STATE;
}
/* the qstate will be reactivated after inform_super is done */
}
/*
* inform validator super.
*
* @param qstate: query state that finished.
* @param id: module id.
* @param super: the qstate to inform.
*/
void
val_inform_super(struct module_qstate* qstate, int id,
struct module_qstate* super)
{
struct val_qstate* vq = (struct val_qstate*)super->minfo[id];
log_query_info(VERB_ALGO, "validator: inform_super, sub is",
&qstate->qinfo);
log_query_info(VERB_ALGO, "super is", &super->qinfo);
if(!vq) {
verbose(VERB_ALGO, "super: has no validator state");
return;
}
if(vq->wait_prime_ta) {
vq->wait_prime_ta = 0;
process_prime_response(super, vq, id, qstate->return_rcode,
qstate->return_msg, qstate->reply_origin);
return;
}
if(qstate->qinfo.qtype == LDNS_RR_TYPE_DS) {
process_ds_response(super, vq, id, qstate->return_rcode,
qstate->return_msg, &qstate->qinfo,
qstate->reply_origin);
return;
} else if(qstate->qinfo.qtype == LDNS_RR_TYPE_DNSKEY) {
process_dnskey_response(super, vq, id, qstate->return_rcode,
qstate->return_msg, &qstate->qinfo,
qstate->reply_origin);
return;
}
log_err("internal error in validator: no inform_supers possible");
}
void
val_clear(struct module_qstate* qstate, int id)
{
if(!qstate)
return;
/* everything is allocated in the region, so assign NULL */
qstate->minfo[id] = NULL;
}
size_t
val_get_mem(struct module_env* env, int id)
{
struct val_env* ve = (struct val_env*)env->modinfo[id];
if(!ve)
return 0;
return sizeof(*ve) + key_cache_get_mem(ve->kcache) +
val_neg_get_mem(ve->neg_cache) +
sizeof(size_t)*2*ve->nsec3_keyiter_count;
}
/**
* The validator function block
*/
static struct module_func_block val_block = {
"validator",
&val_init, &val_deinit, &val_operate, &val_inform_super, &val_clear,
&val_get_mem
};
struct module_func_block*
val_get_funcblock(void)
{
return &val_block;
}
const char*
val_state_to_string(enum val_state state)
{
switch(state) {
case VAL_INIT_STATE: return "VAL_INIT_STATE";
case VAL_FINDKEY_STATE: return "VAL_FINDKEY_STATE";
case VAL_VALIDATE_STATE: return "VAL_VALIDATE_STATE";
case VAL_FINISHED_STATE: return "VAL_FINISHED_STATE";
}
return "UNKNOWN VALIDATOR STATE";
}

File Metadata

Mime Type
application/octet-stream
Expires
Wed, May 22, 4:27 PM (2 d)
Storage Engine
chunks
Storage Format
Chunks
Storage Handle
5f6LzNSn7e0J
Default Alt Text
(4 MB)

Event Timeline