--- ObsoleteFiles.inc.orig +++ ObsoleteFiles.inc @@ -474,6 +474,16 @@ OLD_FILES+=usr/share/dtrace/watch_execve OLD_FILES+=usr/share/dtrace/watch_kill OLD_FILES+=usr/share/dtrace/watch_vop_remove +# 20180512: Rename Unbound tools +OLD_FILES+=usr/sbin/unbound +OLD_FILES+=usr/sbin/unbound-anchor +OLD_FILES+=usr/sbin/unbound-checkconf +OLD_FILES+=usr/sbin/unbound-control +OLD_FILES+=usr/share/man/man5/unbound.conf.5.gz +OLD_FILES+=usr/share/man/man8/unbound-anchor.8.gz +OLD_FILES+=usr/share/man/man8/unbound-checkconf.8.gz +OLD_FILES+=usr/share/man/man8/unbound-control.8.gz +OLD_FILES+=usr/share/man/man8/unbound.8.gz # 20180331: new clang import which bumps version from 5.0.1 to 6.0.0. OLD_FILES+=usr/lib/clang/5.0.1/include/sanitizer/allocator_interface.h OLD_FILES+=usr/lib/clang/5.0.1/include/sanitizer/asan_interface.h --- contrib/unbound/.gitattributes.orig +++ contrib/unbound/.gitattributes @@ -0,0 +1 @@ +testdata/*.[0-9] linguist-documentation --- contrib/unbound/.github/FUNDING.yml.orig +++ contrib/unbound/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: ['https://nlnetlabs.nl/funding/'] --- contrib/unbound/.travis.yml.orig +++ contrib/unbound/.travis.yml @@ -0,0 +1,16 @@ +sudo: false +language: c +compiler: + - gcc +addons: + apt: + packages: + - libssl-dev + - libevent-dev + - libexpat-dev + - clang +script: + - ./configure --enable-debug --disable-flto + - make + - make test + - (cd testdata/clang-analysis.tdir; bash clang-analysis.test) --- contrib/unbound/Makefile.in.orig +++ contrib/unbound/Makefile.in @@ -23,6 +23,8 @@ CHECKLOCK_OBJ=@CHECKLOCK_OBJ@ DNSTAP_SRC=@DNSTAP_SRC@ DNSTAP_OBJ=@DNSTAP_OBJ@ +DNSCRYPT_SRC=@DNSCRYPT_SRC@ +DNSCRYPT_OBJ=@DNSCRYPT_OBJ@ WITH_PYTHONMODULE=@WITH_PYTHONMODULE@ WITH_PYUNBOUND=@WITH_PYUNBOUND@ PY_MAJOR_VERSION=@PY_MAJOR_VERSION@ @@ -55,7 +57,7 @@ CC=@CC@ CPPFLAGS=-I. @CPPFLAGS@ PYTHON_CPPFLAGS=-I. @PYTHON_CPPFLAGS@ -CFLAGS=@CFLAGS@ +CFLAGS=-DSRCDIR=$(srcdir) @CFLAGS@ LDFLAGS=@LDFLAGS@ LIBS=@LIBS@ LIBOBJS=@LIBOBJS@ @@ -81,7 +83,7 @@ # 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" +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)=" INSTALL=$(SHELL) $(srcdir)/install-sh @@ -95,6 +97,12 @@ 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@ 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 \ @@ -101,30 +109,38 @@ 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/localzone.c services/mesh.c services/modstack.c services/view.c \ +services/rpz.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/storage/dnstree.c util/storage/lookup3.c \ -util/storage/lruhash.c util/storage/slabhash.c util/timehist.c util/tube.c \ +util/rtt.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/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 cachedb/cachedb.c $(CHECKLOCK_SRC) \ -$(DNSTAP_SRC) +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/cachedb.c cachedb/redis.c 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 \ +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 locks.lo log.lo mini_event.lo module.lo net_help.lo \ +fptr_wlist.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 timehist.lo tube.lo winsock_event.lo autotrust.lo val_anchor.lo \ +slabhash.lo tcp_conn_limit.lo timehist.lo tube.lo winsock_event.lo \ +autotrust.lo val_anchor.lo rpz.lo \ validator.lo val_kcache.lo val_kentry.lo val_neg.lo val_nsec3.lo val_nsec.lo \ -val_secalgo.lo val_sigcrypt.lo val_utils.lo dns64.lo cachedb.lo \ -$(PYTHONMOD_OBJ) $(CHECKLOCK_OBJ) $(DNSTAP_OBJ) +val_secalgo.lo val_sigcrypt.lo val_utils.lo dns64.lo cachedb.lo redis.lo authzone.lo \ +$(SUBNET_OBJ) $(PYTHONMOD_OBJ) $(CHECKLOCK_OBJ) $(DNSTAP_OBJ) $(DNSCRYPT_OBJ) \ +$(IPSECMOD_OBJ) $(IPSET_OBJ) respip.lo COMMON_OBJ_WITHOUT_UB_EVENT=$(COMMON_OBJ_WITHOUT_NETCALL) netevent.lo listen_dnsport.lo \ outside_network.lo COMMON_OBJ=$(COMMON_OBJ_WITHOUT_UB_EVENT) ub_event.lo @@ -133,7 +149,7 @@ 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_linux.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 \ @@ -145,18 +161,21 @@ 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/unitverify.c testcode/readhex.c testcode/testpkts.c testcode/unitldns.c \ +testcode/unitecs.c testcode/unitauth.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 +readhex.lo testpkts.lo unitldns.lo unitecs.lo unitauth.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 remote.lo stats.lo unbound.lo \ +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@ @@ -170,17 +189,18 @@ $(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) @WIN_HOST_OBJ_LINK@ +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) @WIN_UBANCHOR_OBJ_LINK@ +$(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 \ +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 $(COMMON_OBJ_WITHOUT_NETCALL) ub_event.lo $(SLDNS_OBJ) \ +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 @@ -199,7 +219,7 @@ $(SLDNS_OBJ) ASYNCLOOK_SRC=testcode/asynclook.c ASYNCLOOK_OBJ=asynclook.lo -ASYNCLOOK_OBJ_LINK=$(ASYNCLOOK_OBJ) log.lo locks.lo $(COMPAT_OBJ) +ASYNCLOOK_OBJ_LINK=$(ASYNCLOOK_OBJ) log.lo locks.lo $(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) \ @@ -211,6 +231,8 @@ DELAYER_OBJ=delayer.lo DELAYER_OBJ_LINK=$(DELAYER_OBJ) worker_cb.lo $(COMMON_OBJ) $(COMPAT_OBJ) \ $(SLDNS_OBJ) +IPSET_SRC=@IPSET_SRC@ +IPSET_OBJ=@IPSET_OBJ@ LIBUNBOUND_SRC=libunbound/context.c libunbound/libunbound.c \ libunbound/libworker.c LIBUNBOUND_OBJ=context.lo libunbound.lo libworker.lo ub_event_pluggable.lo @@ -238,8 +260,9 @@ $(MEMSTATS_SRC) $(CHECKCONF_SRC) $(LIBUNBOUND_SRC) $(HOST_SRC) \ $(ASYNCLOOK_SRC) $(STREAMTCP_SRC) $(PERF_SRC) $(DELAYER_SRC) \ $(CONTROL_SRC) $(UBANCHOR_SRC) $(PETAL_SRC) \ - $(PYTHONMOD_SRC) $(PYUNBOUND_SRC) $(WIN_DAEMON_THE_SRC)\ + $(PYTHONMOD_SRC) $(PYUNBOUND_SRC) $(WIN_DAEMON_THE_SRC) \ $(SVCINST_SRC) $(SVCUNINST_SRC) $(ANCHORUPD_SRC) $(SLDNS_SRC) + ALL_OBJ=$(COMMON_OBJ) $(UNITTEST_OBJ) $(DAEMON_OBJ) \ $(TESTBOUND_OBJ) $(LOCKVERIFY_OBJ) $(PKTVIEW_OBJ) \ $(MEMSTATS_OBJ) $(CHECKCONF_OBJ) $(LIBUNBOUND_OBJ) $(HOST_OBJ) \ @@ -250,7 +273,7 @@ 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) $(staticexe) -version-info @LIBUNBOUND_CURRENT@:@LIBUNBOUND_REVISION@:@LIBUNBOUND_AGE@ -no-undefined +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 @@ -292,10 +315,11 @@ test: unittest$(EXEEXT) testbound$(EXEEXT) ./unittest$(EXEEXT) ./testbound$(EXEEXT) -s - for x in testdata/*.rpl; do echo -n "$$x "; if ./testbound$(EXEEXT) -p $$x >/dev/null 2>&1; then echo OK; else echo failed; exit 1; fi done + for x in $(srcdir)/testdata/*.rpl; do echo -n "$$x "; if ./testbound$(EXEEXT) -p $$x >/dev/null 2>&1; then echo OK; else echo failed; 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 @@ -313,7 +337,7 @@ $(LINK) -o $@ $(CONTROL_OBJ_LINK) $(EXTRALINK) $(SSLLIB) $(LIBS) unbound-host$(EXEEXT): $(HOST_OBJ_LINK) libunbound.la - $(LINK) -o $@ $(HOST_OBJ_LINK) -L. -L.libs -lunbound $(LIBS) + $(LINK) -o $@ $(HOST_OBJ_LINK) -L. -L.libs -lunbound $(SSLLIB) $(LIBS) unbound-anchor$(EXEEXT): $(UBANCHOR_OBJ_LINK) libunbound.la $(LINK) -o $@ $(UBANCHOR_OBJ_LINK) -L. -L.libs -lunbound -lexpat $(SSLLIB) $(LIBS) @@ -346,7 +370,7 @@ $(LINK) -o $@ $(MEMSTATS_OBJ_LINK) $(SSLLIB) $(LIBS) asynclook$(EXEEXT): $(ASYNCLOOK_OBJ_LINK) libunbound.la - $(LINK) -o $@ $(ASYNCLOOK_OBJ_LINK) $(LIBS) -L. -L.libs -lunbound + $(LINK) -o $@ $(ASYNCLOOK_OBJ_LINK) -L. -L.libs -lunbound $(SSLLIB) $(LIBS) streamtcp$(EXEEXT): $(STREAMTCP_OBJ_LINK) $(LINK) -o $@ $(STREAMTCP_OBJ_LINK) $(SSLLIB) $(LIBS) @@ -375,10 +399,17 @@ 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=. $(srcdir)/dnstap/dnstap.proto + $(PROTOC_C) --c_out=. --proto_path=$(srcdir) $(srcdir)/dnstap/dnstap.proto dnstap.pb-c.lo dnstap.pb-c.o: dnstap/dnstap.pb-c.c dnstap/dnstap.pb-c.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 \ @@ -404,7 +435,7 @@ # 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 -lunbound + $(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 -lunbound util/config_file.c: util/configparser.h util/configlexer.c: $(srcdir)/util/configlexer.lex util/configparser.h @@ -426,14 +457,19 @@ rm -f _unbound.la libunbound/python/libunbound_wrap.c libunbound/python/unbound.py pythonmod/interface.h pythonmod/unboundmodule.py rm -rf autom4te.cache .libs build doc/html doc/xml -realclean: clean - rm -f config.status config.log config.h.in config.h - rm -f configure config.sub config.guess ltmain.sh aclocal.m4 libtool - rm -f util/configlexer.c util/configparser.c util/configparser.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 +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) $< @@ -456,9 +492,9 @@ 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 2>&1`"; then \ - sphinx-build -b html pythonmod/doc doc/html/pythonmod; \ - sphinx-build -b html libunbound/python/doc doc/html/pyunbound;\ + 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 @@ -511,6 +547,8 @@ $(INSTALL) -m 755 -d $(DESTDIR)$(mandir)/man8 $(INSTALL) -m 755 -d $(DESTDIR)$(mandir)/man5 $(INSTALL) -m 755 -d $(DESTDIR)$(mandir)/man1 + $(INSTALL) -m 755 -d $(DESTDIR)$(libdir)/pkgconfig + $(INSTALL) -m 644 contrib/libunbound.pc $(DESTDIR)$(libdir)/pkgconfig $(LIBTOOL) --mode=install cp -f unbound$(EXEEXT) $(DESTDIR)$(sbindir)/unbound$(EXEEXT) $(LIBTOOL) --mode=install cp -f unbound-checkconf$(EXEEXT) $(DESTDIR)$(sbindir)/unbound-checkconf$(EXEEXT) $(LIBTOOL) --mode=install cp -f unbound-control$(EXEEXT) $(DESTDIR)$(sbindir)/unbound-control$(EXEEXT) @@ -564,7 +602,7 @@ DEPEND_TMP=depend1073.tmp DEPEND_TMP2=depend1074.tmp DEPEND_TARGET=Makefile -DEPEND_TARGET2=Makefile.in +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 @@ -572,7 +610,8 @@ # then, remove srcdir from the (generated) parser and lexer. # and mention the .lo depend: - (cd $(srcdir) ; $(CC) $(DEPFLAG) $(CPPFLAGS) $(CFLAGS) @PTHREAD_CFLAGS_ONLY@ $(ALL_SRC) $(COMPAT_SRC)) | \ + (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' | \ @@ -584,7 +623,12 @@ -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!\(.*\)\.o[ :]*!\1.lo \1.o: !g' \ > $(DEPEND_TMP) cp $(DEPEND_TARGET) $(DEPEND_TMP2) @@ -599,24 +643,30 @@ 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)/validator/val_nsec.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h \ - $(srcdir)/util/locks.h $(srcdir)/services/cache/dns.h $(srcdir)/util/data/msgreply.h \ - $(srcdir)/services/cache/rrset.h $(srcdir)/util/storage/slabhash.h $(srcdir)/util/data/dname.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)/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/dname.h $(srcdir)/util/module.h $(srcdir)/util/data/msgparse.h $(srcdir)/sldns/rrdef.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)/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/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/data/msgreply.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/module.h \ - $(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.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/regional.h $(srcdir)/util/alloc.h + $(srcdir)/util/data/msgreply.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 \ @@ -625,7 +675,8 @@ $(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)/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 \ @@ -633,9 +684,15 @@ $(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)/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/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/packed_rrset.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h \ $(srcdir)/util/data/dname.h $(srcdir)/util/storage/lookup3.h $(srcdir)/util/alloc.h $(srcdir)/util/regional.h \ @@ -648,10 +705,13 @@ $(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)/util/net_help.h $(srcdir)/util/regional.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)/services/mesh.h $(srcdir)/services/modstack.h $(srcdir)/util/config_file.h $(srcdir)/util/random.h \ - $(srcdir)/sldns/wire2str.h $(srcdir)/sldns/str2wire.h $(srcdir)/sldns/parseutil.h $(srcdir)/sldns/sbuffer.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 \ @@ -692,152 +752,267 @@ $(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)/services/cache/dns.h \ - $(srcdir)/services/cache/rrset.h $(srcdir)/util/storage/slabhash.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/netevent.h $(srcdir)/util/tube.h $(srcdir)/services/mesh.h \ - $(srcdir)/services/modstack.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/sbuffer.h $(srcdir)/sldns/str2wire.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)/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)/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)/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)/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)/sldns/str2wire.h $(srcdir)/sldns/rrdef.h $(srcdir)/sldns/sbuffer.h $(srcdir)/util/regional.h \ - $(srcdir)/util/config_file.h $(srcdir)/util/data/dname.h $(srcdir)/util/storage/lruhash.h \ - $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/msgencode.h $(srcdir)/util/net_help.h \ - $(srcdir)/util/netevent.h $(srcdir)/util/data/msgreply.h $(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h \ - $(srcdir)/util/as112.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)/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)/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)/util/net_help.h \ - $(srcdir)/util/regional.h $(srcdir)/util/data/msgencode.h $(srcdir)/util/timehist.h $(srcdir)/util/fptr_wlist.h \ - $(srcdir)/util/tube.h $(srcdir)/util/alloc.h $(srcdir)/util/config_file.h $(srcdir)/sldns/sbuffer.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)/util/tube.h $(srcdir)/services/mesh.h $(srcdir)/util/rbtree.h $(srcdir)/dns64/dns64.h \ - $(srcdir)/iterator/iterator.h $(srcdir)/services/outbound_list.h $(srcdir)/validator/validator.h \ - $(srcdir)/validator/val_utils.h $(PYTHONMOD_HEADER) $(srcdir)/cachedb/cachedb.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)/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)/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/msgparse.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h \ - $(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/msgencode.h \ - $(srcdir)/util/data/dname.h $(srcdir)/util/net_help.h $(srcdir)/util/random.h $(srcdir)/util/fptr_wlist.h \ - $(srcdir)/util/module.h $(srcdir)/util/tube.h $(srcdir)/services/mesh.h $(srcdir)/services/modstack.h \ - $(srcdir)/sldns/sbuffer.h $(srcdir)/dnstap/dnstap.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)/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)/util/module.h $(srcdir)/util/data/msgreply.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/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)/util/tube.h $(srcdir)/services/mesh.h $(srcdir)/util/rbtree.h \ - $(srcdir)/services/modstack.h $(srcdir)/util/data/dname.h $(srcdir)/util/rtt.h $(srcdir)/services/cache/infra.h \ - $(srcdir)/util/storage/dnstree.h $(srcdir)/sldns/wire2str.h $(srcdir)/sldns/parseutil.h \ - $(srcdir)/util/iana_ports.inc + $(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 +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 +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_secalgo.h fptr_wlist.lo fptr_wlist.o: $(srcdir)/util/fptr_wlist.c config.h $(srcdir)/util/fptr_wlist.h \ - $(srcdir)/util/netevent.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)/util/mini_event.h \ - $(srcdir)/util/rbtree.h $(srcdir)/services/outside_network.h \ - $(srcdir)/services/localzone.h $(srcdir)/util/storage/dnstree.h $(srcdir)/services/cache/infra.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.h \ - $(srcdir)/libunbound/worker.h $(srcdir)/sldns/sbuffer.h $(srcdir)/util/config_file.h \ - $(PYTHONMOD_HEADER) $(srcdir)/cachedb/cachedb.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)/util/storage/lruhash.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/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/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 -netevent.lo netevent.o: $(srcdir)/util/netevent.c config.h $(srcdir)/util/netevent.h $(srcdir)/util/ub_event.h \ - $(srcdir)/util/log.h $(srcdir)/util/net_help.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)/sldns/sbuffer.h \ - $(srcdir)/dnstap/dnstap.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 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)/sldns/parseutil.h \ - $(srcdir)/sldns/wire2str.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)/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)/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 +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 +edns.lo edns.o: $(srcdir)/util/edns.c config.h $(srcdir)/util/edns.h $(srcdir)/util/config_file.h \ + $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \ + $(srcdir)/util/regional.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 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)/util/module.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/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)/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)/util/ub_event.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)/util/tube.h $(srcdir)/util/mini_event.h $(srcdir)/util/rbtree.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)/util/log.h $(srcdir)/util/fptr_wlist.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)/util/mini_event.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)/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/pkthdr.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)/services/modstack.h \ - $(srcdir)/services/cache/rrset.h $(srcdir)/util/storage/slabhash.h $(srcdir)/validator/val_kcache.h \ - $(srcdir)/sldns/sbuffer.h $(srcdir)/sldns/wire2str.h $(srcdir)/sldns/str2wire.h $(srcdir)/sldns/keyraw.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)/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 + $(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 \ @@ -845,10 +1020,14 @@ $(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)/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)/util/tube.h $(srcdir)/services/mesh.h $(srcdir)/services/modstack.h \ - $(srcdir)/sldns/wire2str.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 \ @@ -861,7 +1040,7 @@ 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)/util/data/dname.h $(srcdir)/util/data/msgreply.h $(srcdir)/util/net_help.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 \ @@ -873,8 +1052,8 @@ $(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)/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/pkthdr.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 \ @@ -882,36 +1061,55 @@ $(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)/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)/util/data/dname.h $(srcdir)/util/rbtree.h \ - $(srcdir)/util/net_help.h $(srcdir)/util/regional.h $(srcdir)/sldns/keyraw.h $(srcdir)/sldns/sbuffer.h \ - $(srcdir)/sldns/parseutil.h $(srcdir)/sldns/wire2str.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)/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_kentry.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)/sldns/wire2str.h $(srcdir)/sldns/parseutil.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)/util/tube.h $(srcdir)/services/mesh.h $(srcdir)/util/rbtree.h \ - $(srcdir)/services/modstack.h $(srcdir)/util/net_help.h $(srcdir)/util/regional.h -cachedb.lo cachedb.o: $(srcdir)/cachedb/cachedb.c config.h $(srcdir)/cachedb/cachedb.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/regional.h $(srcdir)/util/net_help.h $(srcdir)/util/config_file.h \ - $(srcdir)/util/data/msgencode.h $(srcdir)/services/cache/dns.h $(srcdir)/validator/val_neg.h \ - $(srcdir)/util/rbtree.h $(srcdir)/validator/val_secalgo.h $(srcdir)/iterator/iter_utils.h \ - $(srcdir)/iterator/iter_resptype.h $(srcdir)/sldns/parseutil.h $(srcdir)/sldns/wire2str.h \ - $(srcdir)/sldns/sbuffer.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 +cachedb.lo cachedb.o: $(srcdir)/cachedb/cachedb.c config.h +redis.lo redis.o: $(srcdir)/cachedb/redis.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 @@ -922,9 +1120,13 @@ $(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)/services/cache/infra.h \ - $(srcdir)/util/storage/lruhash.h $(srcdir)/util/storage/dnstree.h $(srcdir)/util/rbtree.h \ - $(srcdir)/util/random.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 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 \ @@ -941,10 +1143,10 @@ $(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)/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/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/validator/val_utils.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 @@ -955,15 +1157,29 @@ $(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 +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 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)/util/regional.h $(srcdir)/util/log.h \ - $(srcdir)/util/config_file.h $(srcdir)/util/net_help.h $(srcdir)/services/localzone.h $(srcdir)/util/locks.h \ - $(srcdir)/sldns/str2wire.h $(srcdir)/sldns/rrdef.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)/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)/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 \ @@ -973,25 +1189,30 @@ $(srcdir)/sldns/wire2str.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)/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)/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)/util/config_file.h $(srcdir)/util/storage/lookup3.h $(srcdir)/util/storage/slabhash.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)/services/listen_dnsport.h $(srcdir)/services/cache/rrset.h $(srcdir)/services/cache/infra.h \ - $(srcdir)/util/rtt.h $(srcdir)/services/localzone.h $(srcdir)/util/random.h $(srcdir)/util/tube.h \ - $(srcdir)/util/net_help.h $(srcdir)/sldns/keyraw.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)/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)/util/module.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/localzone.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 \ @@ -999,51 +1220,62 @@ $(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)/daemon/worker.h $(srcdir)/libunbound/worker.h $(srcdir)/sldns/sbuffer.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)/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/outside_network.h \ - $(srcdir)/services/listen_dnsport.h $(srcdir)/util/config_file.h $(srcdir)/util/tube.h $(srcdir)/util/net_help.h \ - $(srcdir)/validator/validator.h $(srcdir)/validator/val_utils.h $(srcdir)/services/cache/rrset.h \ - $(srcdir)/util/storage/slabhash.h $(srcdir)/services/cache/infra.h $(srcdir)/util/storage/dnstree.h \ - $(srcdir)/util/rtt.h $(srcdir)/validator/val_kcache.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)/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/fptr_wlist.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/net_help.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)/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)/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/remote.h $(srcdir)/daemon/acl_list.h \ - $(srcdir)/util/storage/dnstree.h $(srcdir)/util/rbtree.h $(srcdir)/util/config_file.h $(srcdir)/util/regional.h \ - $(srcdir)/util/storage/slabhash.h $(srcdir)/services/listen_dnsport.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/mesh.h $(srcdir)/services/localzone.h \ - $(srcdir)/util/data/msgencode.h $(srcdir)/util/data/dname.h $(srcdir)/util/fptr_wlist.h $(srcdir)/util/tube.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)/validator/autotrust.h \ - $(srcdir)/validator/val_anchor.h $(srcdir)/libunbound/context.h $(srcdir)/libunbound/unbound.h \ - $(srcdir)/libunbound/libworker.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 testbound.lo testbound.o: $(srcdir)/testcode/testbound.c config.h $(srcdir)/testcode/testpkts.h \ - $(srcdir)/testcode/replay.h $(srcdir)/util/netevent.h $(srcdir)/util/rbtree.h $(srcdir)/testcode/fake_event.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)/util/config_file.h $(srcdir)/sldns/keyraw.h $(srcdir)/daemon/unbound.c \ $(srcdir)/util/log.h $(srcdir)/daemon/daemon.h $(srcdir)/util/locks.h $(srcdir)/util/alloc.h $(srcdir)/services/modstack.h \ $(srcdir)/util/storage/slabhash.h $(srcdir)/util/storage/lruhash.h \ $(srcdir)/services/listen_dnsport.h $(srcdir)/services/cache/rrset.h \ $(srcdir)/util/data/packed_rrset.h $(srcdir)/services/cache/infra.h $(srcdir)/util/storage/dnstree.h \ - $(srcdir)/util/rtt.h $(srcdir)/util/fptr_wlist.h $(srcdir)/util/module.h $(srcdir)/util/data/msgreply.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)/util/net_help.h $(srcdir)/util/ub_event.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 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 @@ -1050,63 +1282,80 @@ 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)/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)/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/remote.h $(srcdir)/daemon/acl_list.h \ - $(srcdir)/util/storage/dnstree.h $(srcdir)/util/rbtree.h $(srcdir)/util/config_file.h $(srcdir)/util/regional.h \ - $(srcdir)/util/storage/slabhash.h $(srcdir)/services/listen_dnsport.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/mesh.h $(srcdir)/services/localzone.h \ - $(srcdir)/util/data/msgencode.h $(srcdir)/util/data/dname.h $(srcdir)/util/fptr_wlist.h $(srcdir)/util/tube.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)/validator/autotrust.h \ - $(srcdir)/validator/val_anchor.h $(srcdir)/libunbound/context.h $(srcdir)/libunbound/unbound.h \ - $(srcdir)/libunbound/libworker.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 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)/util/regional.h $(srcdir)/util/log.h \ - $(srcdir)/util/config_file.h $(srcdir)/util/net_help.h $(srcdir)/services/localzone.h $(srcdir)/util/locks.h \ - $(srcdir)/sldns/str2wire.h $(srcdir)/sldns/rrdef.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)/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)/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)/util/config_file.h $(srcdir)/util/storage/lookup3.h $(srcdir)/util/storage/slabhash.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)/services/listen_dnsport.h $(srcdir)/services/cache/rrset.h $(srcdir)/services/cache/infra.h \ - $(srcdir)/util/rtt.h $(srcdir)/services/localzone.h $(srcdir)/util/random.h $(srcdir)/util/tube.h \ - $(srcdir)/util/net_help.h $(srcdir)/sldns/keyraw.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)/daemon/worker.h $(srcdir)/libunbound/worker.h $(srcdir)/sldns/sbuffer.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)/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/outside_network.h \ - $(srcdir)/services/listen_dnsport.h $(srcdir)/util/config_file.h $(srcdir)/util/tube.h $(srcdir)/util/net_help.h \ - $(srcdir)/validator/validator.h $(srcdir)/validator/val_utils.h $(srcdir)/services/cache/rrset.h \ - $(srcdir)/util/storage/slabhash.h $(srcdir)/services/cache/infra.h $(srcdir)/util/storage/dnstree.h \ - $(srcdir)/util/rtt.h $(srcdir)/validator/val_kcache.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)/testcode/testpkts.h \ - $(srcdir)/util/rbtree.h $(srcdir)/testcode/fake_event.h $(srcdir)/sldns/str2wire.h $(srcdir)/sldns/rrdef.h + $(srcdir)/util/config_file.h $(srcdir)/testcode/replay.h $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \ + $(srcdir)/testcode/testpkts.h $(srcdir)/util/rbtree.h \ + $(srcdir)/testcode/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)/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/config_file.h $(srcdir)/services/listen_dnsport.h \ - $(srcdir)/services/outside_network.h $(srcdir)/util/rbtree.h \ - $(srcdir)/services/cache/infra.h $(srcdir)/util/storage/dnstree.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)/sldns/sbuffer.h \ - $(srcdir)/sldns/wire2str.h $(srcdir)/sldns/str2wire.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/config_file.h $(srcdir)/services/listen_dnsport.h $(srcdir)/services/outside_network.h \ + $(srcdir)/util/rbtree.h $(srcdir)/services/cache/infra.h \ + $(srcdir)/util/storage/dnstree.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 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)/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)/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 \ @@ -1114,10 +1363,14 @@ 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)/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)/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 \ @@ -1125,20 +1378,31 @@ $(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)/sldns/sbuffer.h $(PYTHONMOD_HEADER) + $(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 worker_cb.lo worker_cb.o: $(srcdir)/smallapp/worker_cb.c config.h $(srcdir)/libunbound/context.h \ $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/alloc.h $(srcdir)/util/rbtree.h $(srcdir)/services/modstack.h \ - $(srcdir)/libunbound/unbound.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h \ - $(srcdir)/libunbound/worker.h $(srcdir)/sldns/sbuffer.h $(srcdir)/util/fptr_wlist.h $(srcdir)/util/netevent.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)/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)/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/cache/rrset.h $(srcdir)/util/storage/slabhash.h \ - $(srcdir)/services/cache/infra.h $(srcdir)/util/rtt.h $(srcdir)/sldns/sbuffer.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 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 \ @@ -1146,19 +1410,23 @@ $(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)/services/localzone.h $(srcdir)/util/storage/dnstree.h $(srcdir)/services/cache/infra.h \ - $(srcdir)/util/rtt.h $(srcdir)/services/cache/rrset.h $(srcdir)/util/storage/slabhash.h \ - $(srcdir)/sldns/sbuffer.h + $(srcdir)/services/localzone.h $(srcdir)/util/storage/dnstree.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/worker.h $(srcdir)/sldns/sbuffer.h \ - $(srcdir)/libunbound/unbound-event.h $(srcdir)/services/outside_network.h $(srcdir)/util/netevent.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/localzone.h $(srcdir)/util/storage/dnstree.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/config_file.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 @@ -1166,8 +1434,8 @@ $(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)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h \ - $(srcdir)/sldns/rrdef.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/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 \ @@ -1180,7 +1448,14 @@ 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/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 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 @@ -1187,17 +1462,19 @@ pythonmod_utils.lo pythonmod_utils.o: $(srcdir)/pythonmod/pythonmod_utils.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)/util/netevent.h $(srcdir)/util/net_help.h $(srcdir)/services/cache/dns.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 \ - + $(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)/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)/daemon/remote.h $(srcdir)/util/config_file.h $(srcdir)/util/ub_event.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 @@ -1209,7 +1486,8 @@ 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)/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 @@ -1229,8 +1507,9 @@ 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 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 --- contrib/unbound/README.md.orig +++ contrib/unbound/README.md @@ -0,0 +1,38 @@ +# Unbound + +[![Travis Build Status](https://travis-ci.org/NLnetLabs/unbound.svg?branch=master)](https://travis-ci.org/NLnetLabs/unbound) +[![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) + +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 lean more about Unbound by reading our +[documentation](https://nlnetlabs.nl/documentation/unbound/). + +## Compiling + +Make sure you have the C toolchain, OpenSSL and its include files, and libexpat +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 +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://nlnetlabs.nl/documentation/unbound/). + +An example configuration file is located in +[doc/example.conf](https://github.com/NLnetLabs/unbound/blob/master/doc/example.conf.in). --- contrib/unbound/ac_pkg_swig.m4.orig +++ contrib/unbound/ac_pkg_swig.m4 @@ -103,9 +103,20 @@ if test -z "$available_patch" ; then [available_patch=0] fi - if test $available_major -ne $required_major \ - -o $available_minor -ne $required_minor \ - -o $available_patch -lt $required_patch ; then + [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 AC_MSG_WARN([SWIG version >= $1 is required. You have $swig_version. You should look at http://www.swig.org]) SWIG='echo "Error: SWIG version >= $1 is required. You have '"$swig_version"'. You should look at http://www.swig.org" ; false' else --- contrib/unbound/aclocal.m4.orig +++ contrib/unbound/aclocal.m4 @@ -1,6 +1,6 @@ -# generated automatically by aclocal 1.15 -*- Autoconf -*- +# generated automatically by aclocal 1.16.1 -*- Autoconf -*- -# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# Copyright (C) 1996-2018 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -9044,3 +9044,397 @@ m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])]) m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])]) +# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- +# serial 11 (pkg-config-0.29.1) + +dnl Copyright © 2004 Scott James Remnant . +dnl Copyright © 2012-2015 Dan Nicholson +dnl +dnl This program is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 2 of the License, or +dnl (at your option) any later version. +dnl +dnl This program is distributed in the hope that it will be useful, but +dnl WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +dnl General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with this program; if not, write to the Free Software +dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +dnl 02111-1307, USA. +dnl +dnl As a special exception to the GNU General Public License, if you +dnl distribute this file as part of a program that contains a +dnl configuration script generated by Autoconf, you may include it under +dnl the same distribution terms that you use for the rest of that +dnl program. + +dnl PKG_PREREQ(MIN-VERSION) +dnl ----------------------- +dnl Since: 0.29 +dnl +dnl Verify that the version of the pkg-config macros are at least +dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's +dnl installed version of pkg-config, this checks the developer's version +dnl of pkg.m4 when generating configure. +dnl +dnl To ensure that this macro is defined, also add: +dnl m4_ifndef([PKG_PREREQ], +dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])]) +dnl +dnl See the "Since" comment for each macro you use to see what version +dnl of the macros you require. +m4_defun([PKG_PREREQ], +[m4_define([PKG_MACROS_VERSION], [0.29.1]) +m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, + [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) +])dnl PKG_PREREQ + +dnl PKG_PROG_PKG_CONFIG([MIN-VERSION]) +dnl ---------------------------------- +dnl Since: 0.16 +dnl +dnl Search for the pkg-config tool and set the PKG_CONFIG variable to +dnl first found in the path. Checks that the version of pkg-config found +dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is +dnl used since that's the first version where most current features of +dnl pkg-config existed. +AC_DEFUN([PKG_PROG_PKG_CONFIG], +[m4_pattern_forbid([^_?PKG_[A-Z_]+$]) +m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) +m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) +AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) +AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) +AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) + +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=m4_default([$1], [0.9.0]) + AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + PKG_CONFIG="" + fi +fi[]dnl +])dnl PKG_PROG_PKG_CONFIG + +dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +dnl ------------------------------------------------------------------- +dnl Since: 0.18 +dnl +dnl Check to see whether a particular set of modules exists. Similar to +dnl PKG_CHECK_MODULES(), but does not set variables or print errors. +dnl +dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +dnl only at the first occurence in configure.ac, so if the first place +dnl it's called might be skipped (such as if it is within an "if", you +dnl have to call PKG_CHECK_EXISTS manually +AC_DEFUN([PKG_CHECK_EXISTS], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +if test -n "$PKG_CONFIG" && \ + AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then + m4_default([$2], [:]) +m4_ifvaln([$3], [else + $3])dnl +fi]) + +dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) +dnl --------------------------------------------- +dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting +dnl pkg_failed based on the result. +m4_define([_PKG_CONFIG], +[if test -n "$$1"; then + pkg_cv_[]$1="$$1" + elif test -n "$PKG_CONFIG"; then + PKG_CHECK_EXISTS([$3], + [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes ], + [pkg_failed=yes]) + else + pkg_failed=untried +fi[]dnl +])dnl _PKG_CONFIG + +dnl _PKG_SHORT_ERRORS_SUPPORTED +dnl --------------------------- +dnl Internal check to see if pkg-config supports short errors. +AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi[]dnl +])dnl _PKG_SHORT_ERRORS_SUPPORTED + + +dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], +dnl [ACTION-IF-NOT-FOUND]) +dnl -------------------------------------------------------------- +dnl Since: 0.4.0 +dnl +dnl Note that if there is a possibility the first call to +dnl PKG_CHECK_MODULES might not happen, you should be sure to include an +dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac +AC_DEFUN([PKG_CHECK_MODULES], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl +AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl + +pkg_failed=no +AC_MSG_CHECKING([for $1]) + +_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) +_PKG_CONFIG([$1][_LIBS], [libs], [$2]) + +m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS +and $1[]_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details.]) + +if test $pkg_failed = yes; then + AC_MSG_RESULT([no]) + _PKG_SHORT_ERRORS_SUPPORTED + if test $_pkg_short_errors_supported = yes; then + $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` + else + $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD + + m4_default([$4], [AC_MSG_ERROR( +[Package requirements ($2) were not met: + +$$1_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + +_PKG_TEXT])[]dnl + ]) +elif test $pkg_failed = untried; then + AC_MSG_RESULT([no]) + m4_default([$4], [AC_MSG_FAILURE( +[The pkg-config script could not be found or is too old. Make sure it +is in your PATH or set the PKG_CONFIG environment variable to the full +path to pkg-config. + +_PKG_TEXT + +To get pkg-config, see .])[]dnl + ]) +else + $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS + $1[]_LIBS=$pkg_cv_[]$1[]_LIBS + AC_MSG_RESULT([yes]) + $3 +fi[]dnl +])dnl PKG_CHECK_MODULES + + +dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], +dnl [ACTION-IF-NOT-FOUND]) +dnl --------------------------------------------------------------------- +dnl Since: 0.29 +dnl +dnl Checks for existence of MODULES and gathers its build flags with +dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags +dnl and VARIABLE-PREFIX_LIBS from --libs. +dnl +dnl Note that if there is a possibility the first call to +dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to +dnl include an explicit call to PKG_PROG_PKG_CONFIG in your +dnl configure.ac. +AC_DEFUN([PKG_CHECK_MODULES_STATIC], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +_save_PKG_CONFIG=$PKG_CONFIG +PKG_CONFIG="$PKG_CONFIG --static" +PKG_CHECK_MODULES($@) +PKG_CONFIG=$_save_PKG_CONFIG[]dnl +])dnl PKG_CHECK_MODULES_STATIC + + +dnl PKG_INSTALLDIR([DIRECTORY]) +dnl ------------------------- +dnl Since: 0.27 +dnl +dnl Substitutes the variable pkgconfigdir as the location where a module +dnl should install pkg-config .pc files. By default the directory is +dnl $libdir/pkgconfig, but the default can be changed by passing +dnl DIRECTORY. The user can override through the --with-pkgconfigdir +dnl parameter. +AC_DEFUN([PKG_INSTALLDIR], +[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) +m4_pushdef([pkg_description], + [pkg-config installation directory @<:@]pkg_default[@:>@]) +AC_ARG_WITH([pkgconfigdir], + [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, + [with_pkgconfigdir=]pkg_default) +AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) +m4_popdef([pkg_default]) +m4_popdef([pkg_description]) +])dnl PKG_INSTALLDIR + + +dnl PKG_NOARCH_INSTALLDIR([DIRECTORY]) +dnl -------------------------------- +dnl Since: 0.27 +dnl +dnl Substitutes the variable noarch_pkgconfigdir as the location where a +dnl module should install arch-independent pkg-config .pc files. By +dnl default the directory is $datadir/pkgconfig, but the default can be +dnl changed by passing DIRECTORY. The user can override through the +dnl --with-noarch-pkgconfigdir parameter. +AC_DEFUN([PKG_NOARCH_INSTALLDIR], +[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) +m4_pushdef([pkg_description], + [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) +AC_ARG_WITH([noarch-pkgconfigdir], + [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, + [with_noarch_pkgconfigdir=]pkg_default) +AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) +m4_popdef([pkg_default]) +m4_popdef([pkg_description]) +])dnl PKG_NOARCH_INSTALLDIR + + +dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, +dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +dnl ------------------------------------------- +dnl Since: 0.28 +dnl +dnl Retrieves the value of the pkg-config variable for the given module. +AC_DEFUN([PKG_CHECK_VAR], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl + +_PKG_CONFIG([$1], [variable="][$3]["], [$2]) +AS_VAR_COPY([$1], [pkg_cv_][$1]) + +AS_VAR_IF([$1], [""], [$5], [$4])dnl +])dnl PKG_CHECK_VAR + +dnl PKG_WITH_MODULES(VARIABLE-PREFIX, MODULES, +dnl [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND], +dnl [DESCRIPTION], [DEFAULT]) +dnl ------------------------------------------ +dnl +dnl Prepare a "--with-" configure option using the lowercase +dnl [VARIABLE-PREFIX] name, merging the behaviour of AC_ARG_WITH and +dnl PKG_CHECK_MODULES in a single macro. +AC_DEFUN([PKG_WITH_MODULES], +[ +m4_pushdef([with_arg], m4_tolower([$1])) + +m4_pushdef([description], + [m4_default([$5], [build with ]with_arg[ support])]) + +m4_pushdef([def_arg], [m4_default([$6], [auto])]) +m4_pushdef([def_action_if_found], [AS_TR_SH([with_]with_arg)=yes]) +m4_pushdef([def_action_if_not_found], [AS_TR_SH([with_]with_arg)=no]) + +m4_case(def_arg, + [yes],[m4_pushdef([with_without], [--without-]with_arg)], + [m4_pushdef([with_without],[--with-]with_arg)]) + +AC_ARG_WITH(with_arg, + AS_HELP_STRING(with_without, description[ @<:@default=]def_arg[@:>@]),, + [AS_TR_SH([with_]with_arg)=def_arg]) + +AS_CASE([$AS_TR_SH([with_]with_arg)], + [yes],[PKG_CHECK_MODULES([$1],[$2],$3,$4)], + [auto],[PKG_CHECK_MODULES([$1],[$2], + [m4_n([def_action_if_found]) $3], + [m4_n([def_action_if_not_found]) $4])]) + +m4_popdef([with_arg]) +m4_popdef([description]) +m4_popdef([def_arg]) + +])dnl PKG_WITH_MODULES + +dnl PKG_HAVE_WITH_MODULES(VARIABLE-PREFIX, MODULES, +dnl [DESCRIPTION], [DEFAULT]) +dnl ----------------------------------------------- +dnl +dnl Convenience macro to trigger AM_CONDITIONAL after PKG_WITH_MODULES +dnl check._[VARIABLE-PREFIX] is exported as make variable. +AC_DEFUN([PKG_HAVE_WITH_MODULES], +[ +PKG_WITH_MODULES([$1],[$2],,,[$3],[$4]) + +AM_CONDITIONAL([HAVE_][$1], + [test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"]) +])dnl PKG_HAVE_WITH_MODULES + +dnl PKG_HAVE_DEFINE_WITH_MODULES(VARIABLE-PREFIX, MODULES, +dnl [DESCRIPTION], [DEFAULT]) +dnl ------------------------------------------------------ +dnl +dnl Convenience macro to run AM_CONDITIONAL and AC_DEFINE after +dnl PKG_WITH_MODULES check. HAVE_[VARIABLE-PREFIX] is exported as make +dnl and preprocessor variable. +AC_DEFUN([PKG_HAVE_DEFINE_WITH_MODULES], +[ +PKG_HAVE_WITH_MODULES([$1],[$2],[$3],[$4]) + +AS_IF([test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"], + [AC_DEFINE([HAVE_][$1], 1, [Enable ]m4_tolower([$1])[ support])]) +])dnl PKG_HAVE_DEFINE_WITH_MODULES + +# AM_CONDITIONAL -*- Autoconf -*- + +# Copyright (C) 1997-2018 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_CONDITIONAL(NAME, SHELL-CONDITION) +# ------------------------------------- +# Define a conditional. +AC_DEFUN([AM_CONDITIONAL], +[AC_PREREQ([2.52])dnl + m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], + [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl +AC_SUBST([$1_TRUE])dnl +AC_SUBST([$1_FALSE])dnl +_AM_SUBST_NOTMAKE([$1_TRUE])dnl +_AM_SUBST_NOTMAKE([$1_FALSE])dnl +m4_define([_AM_COND_VALUE_$1], [$2])dnl +if $2; then + $1_TRUE= + $1_FALSE='#' +else + $1_TRUE='#' + $1_FALSE= +fi +AC_CONFIG_COMMANDS_PRE( +[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then + AC_MSG_ERROR([[conditional "$1" was never defined. +Usually this means the macro was only invoked conditionally.]]) +fi])]) + +# Copyright (C) 2006-2018 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_SUBST_NOTMAKE(VARIABLE) +# --------------------------- +# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. +# This macro is traced by Automake. +AC_DEFUN([_AM_SUBST_NOTMAKE]) + +# AM_SUBST_NOTMAKE(VARIABLE) +# -------------------------- +# Public sister of _AM_SUBST_NOTMAKE. +AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) + --- contrib/unbound/acx_nlnetlabs.m4.orig +++ contrib/unbound/acx_nlnetlabs.m4 @@ -688,8 +688,8 @@ # check if -lwsock32 or -lgdi32 are needed. BAKLIBS="$LIBS" BAKSSLLIBS="$LIBSSL_LIBS" - LIBS="$LIBS -lgdi32" - LIBSSL_LIBS="$LIBSSL_LIBS -lgdi32" + LIBS="$LIBS -lgdi32 -lws2_32" + LIBSSL_LIBS="$LIBSSL_LIBS -lgdi32 -lws2_32" AC_MSG_CHECKING([if -lcrypto needs -lgdi32]) AC_TRY_LINK([], [ int HMAC_Update(void); @@ -839,7 +839,11 @@ if test "$ac_cv_header_windows_h" = "yes"; then AC_DEFINE(USE_WINSOCK, 1, [Whether the windows socket API is used]) USE_WINSOCK="1" - LIBS="$LIBS -lws2_32" + if echo $LIBS | grep 'lws2_32' >/dev/null; then + : + else + LIBS="$LIBS -lws2_32" + fi fi ], dnl no quick getaddrinfo, try mingw32 and winsock2 library. --- contrib/unbound/acx_python.m4.orig +++ contrib/unbound/acx_python.m4 @@ -22,8 +22,7 @@ # Check if you have distutils, else fail # AC_MSG_CHECKING([for the distutils Python package]) - ac_distutils_result=`$PYTHON -c "import distutils" 2>&1` - if test -z "$ac_distutils_result"; then + if ac_distutils_result=`$PYTHON -c "import distutils" 2>&1`; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) --- contrib/unbound/cachedb/cachedb.c.orig +++ contrib/unbound/cachedb/cachedb.c @@ -43,6 +43,7 @@ #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" @@ -56,11 +57,39 @@ #include "sldns/wire2str.h" #include "sldns/sbuffer.h" -#define CACHEDB_HASHSIZE 256 /* bit hash */ +/* header file for htobe64 */ +#ifdef HAVE_ENDIAN_H +# include +#endif +#ifdef HAVE_SYS_ENDIAN_H +# include +#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 +# 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 */ @@ -72,14 +101,18 @@ static int testframe_init(struct module_env* env, struct cachedb_env* cachedb_env) { + struct testframe_moddata* d; (void)env; verbose(VERB_ALGO, "testframe_init"); - cachedb_env->backend_data = (void*)calloc(1, + 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; } + lock_basic_init(&d->lock); + lock_protect(&d->lock, d, sizeof(*d)); return 1; } @@ -92,6 +125,7 @@ verbose(VERB_ALGO, "testframe_deinit"); if(!d) return; + lock_basic_destroy(&d->lock); free(d->stored_key); free(d->stored_data); free(d); @@ -105,9 +139,12 @@ 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)) + 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); @@ -114,8 +151,10 @@ 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; } @@ -126,6 +165,7 @@ 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) */ @@ -137,6 +177,7 @@ d->stored_data = memdup(data, data_len); if(!d->stored_data) { + lock_basic_unlock(&d->lock); log_err("out of memory"); return; } @@ -146,8 +187,10 @@ 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 */ } @@ -160,6 +203,10 @@ 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 */ @@ -170,15 +217,13 @@ static int cachedb_apply_cfg(struct cachedb_env* cachedb_env, struct config_file* cfg) { - const char* backend_str = "testframe"; /* TODO get from cfg */ - if(backend_str && backend_str[0]) { - cachedb_env->backend = cachedb_find_backend(backend_str); - if(!cachedb_env->backend) { - log_err("cachedb: cannot find backend name '%s", - backend_str); - return NULL; - } + const char* backend_str = cfg->cachedb_backend; + 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; } @@ -195,6 +240,8 @@ 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 */ @@ -203,9 +250,20 @@ 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) + 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) + 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; } @@ -276,9 +334,9 @@ size_t clen = 0; uint8_t hash[CACHEDB_HASHSIZE/8]; const char* hex = "0123456789ABCDEF"; - const char* secret = "default"; /* TODO: from qstate->env->cfg */ + 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, @@ -299,7 +357,11 @@ /* 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) */ @@ -328,6 +390,13 @@ if(!qstate->return_msg || !qstate->return_msg->rep) return 0; + /* We don't store the reply if its TTL is 0 unless serve-expired is + * enabled. Such a reply won't be reusable and simply be a waste for + * the backend. It's also compatible with the default behavior of + * dns_cache_store_msg(). */ + if(qstate->return_msg->rep->ttl == 0 && + !qstate->env->cfg->serve_expired) + return 0; if(verbosity >= VERB_ALGO) log_dns_msg("cachedb encoding", &qstate->return_msg->qinfo, qstate->return_msg->rep); @@ -368,12 +437,55 @@ &expiry, sizeof(expiry)); expiry = be64toh(expiry); - if((time_t)expiry < *qstate->env->now) + /* 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= 0 && data->rr_ttl[i] > subtract) + data->rr_ttl[i] -= subtract; + else data->rr_ttl[i] = 0; + } +} + +/* Adjust the TTL of a DNS message and its RRs by 'adjust'. If 'adjust' is + * negative, set the TTLs to 0. */ +static void +adjust_msg_ttl(struct dns_msg* msg, time_t adjust) +{ + size_t i; + if(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; irep->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) @@ -420,24 +532,34 @@ 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) + adjust = -1; + else + return 0; /* message expired */ + } verbose(VERB_ALGO, "cachedb msg adjusted down by %d", (int)adjust); - /*adjust_msg(qstate->return_msg, adjust);*/ - /* TODO: - msg->rep->ttl = r->ttl - adjust; - msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl); - for(i=0; icount + d->rrsig_count; i++) { - if(d->rr_ttl[i] < adjust) - d->rr_ttl[i] = 0; - else d->rr_ttl[i] -= adjust; - } - if(d->ttl < adjust) - d->ttl = 0; - else d->ttl -= adjust; - */ - /* TODO */ + adjust_msg_ttl(qstate->return_msg, adjust); - return 0; + /* 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; } /** @@ -497,14 +619,18 @@ 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); - if(!msg && qstate->env->neg_cache) { + qstate->region, qstate->env->scratch, + 1 /* no partial messages with only a CNAME */ + ); + 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->now, 1/*add SOA*/, NULL, + qstate->env->cfg); } if(!msg) return 0; @@ -520,11 +646,15 @@ static void cachedb_intcache_store(struct module_qstate* qstate) { + uint32_t store_flags = qstate->query_flags; + + if(qstate->env->cfg->serve_expired) + store_flags |= DNSCACHE_STORE_ZEROTTL; if(!qstate->return_msg) return; (void)dns_cache_store(qstate->env, &qstate->qinfo, qstate->return_msg->rep, 0, qstate->prefetch_leeway, 0, - qstate->region, qstate->query_flags); + qstate->region, store_flags); } /** @@ -547,19 +677,26 @@ return; } - if(qstate->blacklist) { - /* cache is blacklisted */ + 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 */ + /* lookup inside unbound's internal cache. + * This does not look for expired entries. */ if(cachedb_intcache_lookup(qstate)) { - if(verbosity >= VERB_ALGO) - log_dns_msg("cachedb internal cache lookup", - &qstate->return_msg->qinfo, - qstate->return_msg->rep); + 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; @@ -573,6 +710,19 @@ 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; + } /* we are done with the query */ qstate->ext_state[id] = module_finished; return; @@ -595,8 +745,8 @@ cachedb_handle_response(struct module_qstate* qstate, struct cachedb_qstate* ATTR_UNUSED(iq), struct cachedb_env* ie, int id) { - /* check if we are enabled, and skip if not */ - if(!ie->enabled) { + /* 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; @@ -649,6 +799,11 @@ (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); --- contrib/unbound/cachedb/cachedb.h.orig +++ contrib/unbound/cachedb/cachedb.h @@ -87,6 +87,8 @@ uint8_t*, size_t); }; +#define CACHEDB_HASHSIZE 256 /* bit hash */ + /** Init the cachedb module */ int cachedb_init(struct module_env* env, int id); /** Deinit the cachedb module */ --- contrib/unbound/cachedb/redis.c.orig +++ contrib/unbound/cachedb/redis.c @@ -0,0 +1,283 @@ +/* + * 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 */ + struct timeval timeout; /* timeout for connection setup and commands */ +}; + +static redisContext* +redis_connect(const struct redis_moddata* moddata) +{ + redisContext* ctx; + + 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; + } + return ctx; + + 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_ALGO, "redis_init"); + + moddata = calloc(1, sizeof(struct redis_moddata)); + if(!moddata) { + log_err("out of memory"); + return 0; + } + 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; + } + /* 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->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); + cachedb_env->backend_data = moddata; + return 1; +} + +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_ALGO, "redis_deinit"); + + 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); +} + +/* + * 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) +{ + redisReply* rep; + char cmdbuf[4+(CACHEDB_HASHSIZE/8)*2+3+1]; /* "SET " + key + " %b" */ + int n; + + 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); + 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 */ --- contrib/unbound/cachedb/redis.h.orig +++ contrib/unbound/cachedb/redis.h @@ -0,0 +1,45 @@ +/* + * cachedb/redis.h - 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. + */ + +/** the redis backend definition, contains callable functions + * and name string */ +extern struct cachedb_backend redis_backend; --- contrib/unbound/compat/arc4_lock.c.orig +++ contrib/unbound/compat/arc4_lock.c @@ -33,6 +33,9 @@ */ #include "config.h" #define LOCKRET(func) func +#ifdef ENABLE_LOCK_CHECKS +#undef ENABLE_LOCK_CHECKS +#endif #include "util/locks.h" void _ARC4_LOCK(void); @@ -46,9 +49,13 @@ void _ARC4_UNLOCK(void) { } + +void _ARC4_LOCK_DESTROY(void) +{ +} #else /* !THREADS_DISABLED */ -static lock_quick_t arc4lock; +static lock_quick_type arc4lock; static int arc4lockinit = 0; void _ARC4_LOCK(void) @@ -64,4 +71,12 @@ { lock_quick_unlock(&arc4lock); } + +void _ARC4_LOCK_DESTROY(void) +{ + if(arc4lockinit) { + arc4lockinit = 0; + lock_quick_destroy(&arc4lock); + } +} #endif /* THREADS_DISABLED */ --- contrib/unbound/compat/arc4random.c.orig +++ contrib/unbound/compat/arc4random.c @@ -71,9 +71,76 @@ static inline void _rs_rekey(u_char *dat, size_t datlen); +/* + * Basic sanity checking; wish we could do better. + */ +static int +fallback_gotdata(char *buf, size_t len) +{ + char any_set = 0; + size_t i; + + for (i = 0; i < len; ++i) + any_set |= buf[i]; + if (any_set == 0) + return -1; + return 0; +} + +/* fallback for getentropy in case libc returns failure */ +static int +fallback_getentropy_urandom(void *buf, size_t len) +{ + size_t i; + int fd, flags; + int save_errno = errno; + +start: + + flags = O_RDONLY; +#ifdef O_NOFOLLOW + flags |= O_NOFOLLOW; +#endif +#ifdef O_CLOEXEC + flags |= O_CLOEXEC; +#endif + fd = open("/dev/urandom", flags, 0); + if (fd == -1) { + if (errno == EINTR) + goto start; + goto nodevrandom; + } +#ifndef O_CLOEXEC +# ifdef HAVE_FCNTL + fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); +# endif +#endif + for (i = 0; i < len; ) { + size_t wanted = len - i; + ssize_t ret = read(fd, (char*)buf + i, wanted); + + if (ret == -1) { + if (errno == EAGAIN || errno == EINTR) + continue; + close(fd); + goto nodevrandom; + } + i += ret; + } + close(fd); + if (fallback_gotdata(buf, len) == 0) { + errno = save_errno; + return 0; /* satisfied */ + } +nodevrandom: + errno = EIO; + return -1; +} + static inline void _rs_init(u_char *buf, size_t n) { + assert(buf); if (n < KEYSZ + IVSZ) return; @@ -114,11 +181,14 @@ u_char rnd[KEYSZ + IVSZ]; if (getentropy(rnd, sizeof rnd) == -1) { + if(errno != ENOSYS || + fallback_getentropy_urandom(rnd, sizeof rnd) == -1) { #ifdef SIGKILL - raise(SIGKILL); + raise(SIGKILL); #else - exit(9); /* windows */ + exit(9); /* windows */ #endif + } } if (!rs) --- contrib/unbound/compat/ctime_r.c.orig +++ contrib/unbound/compat/ctime_r.c @@ -6,7 +6,7 @@ #include "util/locks.h" /** the lock for ctime buffer */ -static lock_basic_t ctime_lock; +static lock_basic_type ctime_lock; /** has it been inited */ static int ctime_r_init = 0; --- contrib/unbound/compat/getentropy_freebsd.c.orig +++ contrib/unbound/compat/getentropy_freebsd.c @@ -0,0 +1,62 @@ +/* $OpenBSD: getentropy_freebsd.c,v 1.3 2016/08/07 03:27:21 tb Exp $ */ + +/* + * Copyright (c) 2014 Pawel Jakub Dawidek + * Copyright (c) 2014 Brent Cook + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Emulation of getentropy(2) as documented at: + * http://man.openbsd.org/getentropy.2 + */ + +#include +#include + +#include +#include + +/* + * Derived from lib/libc/gen/arc4random.c from FreeBSD. + */ +static size_t +getentropy_sysctl(u_char *buf, size_t size) +{ + int mib[2]; + size_t len, done; + + mib[0] = CTL_KERN; + mib[1] = KERN_ARND; + done = 0; + + do { + len = size; + if (sysctl(mib, 2, buf, &len, NULL, 0) == -1) + return (done); + done += len; + buf += len; + size -= len; + } while (size > 0); + + return (done); +} + +int +getentropy(void *buf, size_t len) +{ + if (len <= 256 && getentropy_sysctl(buf, len) == len) + return (0); + + errno = EIO; + return (-1); +} --- contrib/unbound/compat/getentropy_linux.c.orig +++ contrib/unbound/compat/getentropy_linux.c @@ -1,4 +1,4 @@ -/* $OpenBSD: getentropy_linux.c,v 1.20 2014/07/12 15:43:49 beck Exp $ */ +/* $OpenBSD: getentropy_linux.c,v 1.46 2018/11/20 08:04:28 deraadt Exp $ */ /* * Copyright (c) 2014 Theo de Raadt @@ -15,12 +15,15 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Emulation of getentropy(2) as documented at: + * http://man.openbsd.org/getentropy.2 */ + #include "config.h" - /* -#define _POSIX_C_SOURCE 199309L -#define _GNU_SOURCE 1 +#define _POSIX_C_SOURCE 199309L +#define _GNU_SOURCE 1 */ #include #include @@ -27,8 +30,8 @@ #include #include #include -#ifdef HAVE_SYS_SYSCTL_H -#include +#ifdef SYS__sysctl +#include #endif #include #include @@ -39,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -46,16 +50,18 @@ #include #include #include - -#if defined(HAVE_SSL) +#ifndef HAVE_NETTLE #include -#elif defined(HAVE_NETTLE) +#else #include +#define SHA512_CTX struct sha512_ctx +#define SHA512_Init(x) sha512_init(x) +#define SHA512_Update(x, b, s) sha512_update(x, s, b) +#define SHA512_Final(r, c) sha512_digest(c, SHA512_DIGEST_SIZE, r) #endif #include #include -#include #ifdef HAVE_GETAUXVAL #include #endif @@ -75,29 +81,13 @@ HD(b); \ } while (0) -#if defined(HAVE_SSL) -#define CRYPTO_SHA512_CTX SHA512_CTX -#define CRYPTO_SHA512_INIT(x) SHA512_Init(x) -#define CRYPTO_SHA512_FINAL(r, c) SHA512_Final(r, c) #define HR(x, l) (SHA512_Update(&ctx, (char *)(x), (l))) #define HD(x) (SHA512_Update(&ctx, (char *)&(x), sizeof (x))) #define HF(x) (SHA512_Update(&ctx, (char *)&(x), sizeof (void*))) -#elif defined(HAVE_NETTLE) -#define CRYPTO_SHA512_CTX struct sha512_ctx -#define CRYPTO_SHA512_INIT(x) sha512_init(x) -#define CRYPTO_SHA512_FINAL(r, c) sha512_digest(c, SHA512_DIGEST_SIZE, r) -#define HR(x, l) (sha512_update(&ctx, (l), (uint8_t *)(x))) -#define HD(x) (sha512_update(&ctx, sizeof (x), (uint8_t *)&(x))) -#define HF(x) (sha512_update(&ctx, sizeof (void*), (uint8_t *)&(x))) -#endif int getentropy(void *buf, size_t len); -#ifdef CAN_REFERENCE_MAIN -extern int main(int, char *argv[]); -#endif -static int gotdata(char *buf, size_t len); -#if defined(SYS_getrandom) && defined(__NR_getrandom) +#if defined(SYS_getrandom) && defined(GRND_NONBLOCK) static int getentropy_getrandom(void *buf, size_t len); #endif static int getentropy_urandom(void *buf, size_t len); @@ -105,6 +95,7 @@ static int getentropy_sysctl(void *buf, size_t len); #endif static int getentropy_fallback(void *buf, size_t len); +static int getentropy_phdr(struct dl_phdr_info *info, size_t size, void *data); int getentropy(void *buf, size_t len) @@ -113,18 +104,21 @@ if (len > 256) { errno = EIO; - return -1; + return (-1); } -#if defined(SYS_getrandom) && defined(__NR_getrandom) +#if defined(SYS_getrandom) && defined(GRND_NONBLOCK) /* - * Try descriptor-less getrandom() + * Try descriptor-less getrandom(), in non-blocking mode. + * + * The design of Linux getrandom is broken. It has an + * uninitialized phase coupled with blocking behaviour, which + * is unacceptable from within a library at boot time without + * possible recovery. See http://bugs.python.org/issue26839#msg267745 */ ret = getentropy_getrandom(buf, len); if (ret != -1) return (ret); - if (errno != ENOSYS) - return (-1); #endif /* @@ -178,7 +172,7 @@ * - Do the best under the circumstances.... * * This code path exists to bring light to the issue that Linux - * does not provide a failsafe API for entropy collection. + * still does not provide a failsafe API for entropy collection. * * We hope this demonstrates that Linux should either retain their * sysctl ABI, or consider providing a new failsafe API which @@ -196,24 +190,8 @@ return (ret); } -/* - * Basic sanity checking; wish we could do better. - */ +#if defined(SYS_getrandom) && defined(GRND_NONBLOCK) static int -gotdata(char *buf, size_t len) -{ - char any_set = 0; - size_t i; - - for (i = 0; i < len; ++i) - any_set |= buf[i]; - if (any_set == 0) - return -1; - return 0; -} - -#if defined(SYS_getrandom) && defined(__NR_getrandom) -static int getentropy_getrandom(void *buf, size_t len) { int pre_errno = errno; @@ -221,7 +199,7 @@ if (len > 256) return (-1); do { - ret = syscall(SYS_getrandom, buf, len, 0); + ret = syscall(SYS_getrandom, buf, len, GRND_NONBLOCK); } while (ret == -1 && errno == EINTR); if (ret != (int)len) @@ -269,7 +247,7 @@ } for (i = 0; i < len; ) { size_t wanted = len - i; - ssize_t ret = read(fd, (char*)buf + i, wanted); + ssize_t ret = read(fd, (char *)buf + i, wanted); if (ret == -1) { if (errno == EAGAIN || errno == EINTR) @@ -280,13 +258,11 @@ i += ret; } close(fd); - if (gotdata(buf, len) == 0) { - errno = save_errno; - return 0; /* satisfied */ - } + errno = save_errno; + return (0); /* satisfied */ nodevrandom: errno = EIO; - return -1; + return (-1); } #ifdef SYS__sysctl @@ -311,17 +287,15 @@ goto sysctlfailed; i += chunk; } - if (gotdata(buf, len) == 0) { - errno = save_errno; - return (0); /* satisfied */ - } + errno = save_errno; + return (0); /* satisfied */ sysctlfailed: errno = EIO; - return -1; + return (-1); } #endif /* SYS__sysctl */ -static int cl[] = { +static const int cl[] = { CLOCK_REALTIME, #ifdef CLOCK_MONOTONIC CLOCK_MONOTONIC, @@ -347,6 +321,15 @@ }; static int +getentropy_phdr(struct dl_phdr_info *info, size_t ATTR_UNUSED(size), void *data) +{ + SHA512_CTX *ctx = data; + + SHA512_Update(ctx, &info->dlpi_addr, sizeof (info->dlpi_addr)); + return (0); +} + +static int getentropy_fallback(void *buf, size_t len) { uint8_t results[SHA512_DIGEST_LENGTH]; @@ -357,7 +340,7 @@ struct rusage ru; sigset_t sigset; struct stat st; - CRYPTO_SHA512_CTX ctx; + SHA512_CTX ctx; static pid_t lastpid; pid_t pid; size_t i, ii, m; @@ -374,7 +357,7 @@ } for (i = 0; i < len; ) { int j; - CRYPTO_SHA512_INIT(&ctx); + SHA512_Init(&ctx); for (j = 0; j < repeat; j++) { HX((e = gettimeofday(&tv, NULL)) == -1, tv); if (e != -1) { @@ -382,6 +365,8 @@ cnt += (int)tv.tv_usec; } + dl_iterate_phdr(getentropy_phdr, &ctx); + for (ii = 0; ii < sizeof(cl)/sizeof(cl[0]); ii++) HX(clock_gettime(cl[ii], &ts) == -1, ts); @@ -401,9 +386,6 @@ HX(sigprocmask(SIG_BLOCK, NULL, &sigset) == -1, sigset); -#ifdef CAN_REFERENCE_MAIN - HF(main); /* an addr in program */ -#endif HF(getentropy); /* an addr in this library */ HF(printf); /* an addr in libc */ p = (char *)&p; @@ -528,33 +510,30 @@ HD(cnt); } #ifdef HAVE_GETAUXVAL -# ifdef AT_RANDOM +#ifdef AT_RANDOM /* Not as random as you think but we take what we are given */ p = (char *) getauxval(AT_RANDOM); if (p) HR(p, 16); -# endif -# ifdef AT_SYSINFO_EHDR +#endif +#ifdef AT_SYSINFO_EHDR p = (char *) getauxval(AT_SYSINFO_EHDR); if (p) HR(p, pgs); -# endif -# ifdef AT_BASE +#endif +#ifdef AT_BASE p = (char *) getauxval(AT_BASE); if (p) HD(p); -# endif -#endif /* HAVE_GETAUXVAL */ +#endif +#endif - CRYPTO_SHA512_FINAL(results, &ctx); - memcpy((char*)buf + i, results, min(sizeof(results), len - i)); + SHA512_Final(results, &ctx); + memcpy((char *)buf + i, results, min(sizeof(results), len - i)); i += min(sizeof(results), len - i); } - memset(results, 0, sizeof results); - if (gotdata(buf, len) == 0) { - errno = save_errno; - return 0; /* satisfied */ - } - errno = EIO; - return -1; + explicit_bzero(&ctx, sizeof ctx); + explicit_bzero(results, sizeof results); + errno = save_errno; + return (0); /* satisfied */ } --- contrib/unbound/compat/getentropy_osx.c.orig +++ contrib/unbound/compat/getentropy_osx.c @@ -1,4 +1,4 @@ -/* $OpenBSD: getentropy_osx.c,v 1.3 2014/07/12 14:48:00 deraadt Exp $ */ +/* $OpenBSD: getentropy_osx.c,v 1.12 2018/11/20 08:04:28 deraadt Exp $ */ /* * Copyright (c) 2014 Theo de Raadt @@ -15,9 +15,12 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Emulation of getentropy(2) as documented at: + * http://man.openbsd.org/getentropy.2 */ -#include "config.h" +#include #include #include #include @@ -43,14 +46,18 @@ #include #include #include +#if TARGET_OS_OSX #include #include +#endif #include #include +#if TARGET_OS_OSX #include #include #include #include +#endif #include #define SHA512_Update(a, b, c) (CC_SHA512_Update((a), (b), (c))) #define SHA512_Init(xxx) (CC_SHA512_Init((xxx))) @@ -75,10 +82,6 @@ int getentropy(void *buf, size_t len); -#ifdef CAN_REFERENCE_MAIN -extern int main(int, char *argv[]); -#endif -static int gotdata(char *buf, size_t len); static int getentropy_urandom(void *buf, size_t len); static int getentropy_fallback(void *buf, size_t len); @@ -89,7 +92,7 @@ if (len > 256) { errno = EIO; - return -1; + return (-1); } /* @@ -138,23 +141,7 @@ return (ret); } -/* - * Basic sanity checking; wish we could do better. - */ static int -gotdata(char *buf, size_t len) -{ - char any_set = 0; - size_t i; - - for (i = 0; i < len; ++i) - any_set |= buf[i]; - if (any_set == 0) - return -1; - return 0; -} - -static int getentropy_urandom(void *buf, size_t len) { struct stat st; @@ -188,7 +175,7 @@ } for (i = 0; i < len; ) { size_t wanted = len - i; - ssize_t ret = read(fd, (char*)buf + i, wanted); + ssize_t ret = read(fd, (char *)buf + i, wanted); if (ret == -1) { if (errno == EAGAIN || errno == EINTR) @@ -199,18 +186,18 @@ i += ret; } close(fd); - if (gotdata(buf, len) == 0) { - errno = save_errno; - return 0; /* satisfied */ - } + errno = save_errno; + return (0); /* satisfied */ nodevrandom: errno = EIO; - return -1; + return (-1); } +#if TARGET_OS_OSX static int tcpmib[] = { CTL_NET, AF_INET, IPPROTO_TCP, TCPCTL_STATS }; static int udpmib[] = { CTL_NET, AF_INET, IPPROTO_UDP, UDPCTL_STATS }; static int ipmib[] = { CTL_NET, AF_INET, IPPROTO_IP, IPCTL_STATS }; +#endif static int kmib[] = { CTL_KERN, KERN_USRSTACK }; static int hwmib[] = { CTL_HW, HW_USERMEM }; @@ -230,9 +217,11 @@ pid_t pid; size_t i, ii, m; char *p; +#if TARGET_OS_OSX struct tcpstat tcpstat; struct udpstat udpstat; struct ipstat ipstat; +#endif u_int64_t mach_time; unsigned int idata; void *addr; @@ -267,6 +256,7 @@ HX(sysctl(hwmib, sizeof(hwmib) / sizeof(hwmib[0]), &idata, &ii, NULL, 0) == -1, idata); +#if TARGET_OS_OSX ii = sizeof(tcpstat); HX(sysctl(tcpmib, sizeof(tcpmib) / sizeof(tcpmib[0]), &tcpstat, &ii, NULL, 0) == -1, tcpstat); @@ -278,6 +268,7 @@ ii = sizeof(ipstat); HX(sysctl(ipmib, sizeof(ipmib) / sizeof(ipmib[0]), &ipstat, &ii, NULL, 0) == -1, ipstat); +#endif HX((pid = getpid()) == -1, pid); HX((pid = getsid(pid)) == -1, pid); @@ -295,9 +286,6 @@ HX(sigprocmask(SIG_BLOCK, NULL, &sigset) == -1, sigset); -#ifdef CAN_REFERENCE_MAIN - HF(main); /* an addr in program */ -#endif HF(getentropy); /* an addr in this library */ HF(printf); /* an addr in libc */ p = (char *)&p; @@ -419,14 +407,11 @@ } SHA512_Final(results, &ctx); - memcpy((char*)buf + i, results, min(sizeof(results), len - i)); + memcpy((char *)buf + i, results, min(sizeof(results), len - i)); i += min(sizeof(results), len - i); } - memset(results, 0, sizeof results); - if (gotdata(buf, len) == 0) { - errno = save_errno; - return 0; /* satisfied */ - } - errno = EIO; - return -1; + explicit_bzero(&ctx, sizeof ctx); + explicit_bzero(results, sizeof results); + errno = save_errno; + return (0); /* satisfied */ } --- contrib/unbound/compat/getentropy_solaris.c.orig +++ contrib/unbound/compat/getentropy_solaris.c @@ -1,4 +1,4 @@ -/* $OpenBSD: getentropy_solaris.c,v 1.3 2014/07/12 14:46:31 deraadt Exp $ */ +/* $OpenBSD: getentropy_solaris.c,v 1.4 2014/07/12 20:41:47 wouter Exp $ */ /* * Copyright (c) 2014 Theo de Raadt @@ -204,7 +204,7 @@ } for (i = 0; i < len; ) { size_t wanted = len - i; - ssize_t ret = read(fd, (char*)buf + i, wanted); + ssize_t ret = read(fd, (char *)buf + i, wanted); if (ret == -1) { if (errno == EAGAIN || errno == EINTR) @@ -428,7 +428,7 @@ HD(cnt); } SHA512_Final(results, &ctx); - memcpy((char*)buf + i, results, min(sizeof(results), len - i)); + memcpy((char *)buf + i, results, min(sizeof(results), len - i)); i += min(sizeof(results), len - i); } memset(results, 0, sizeof results); --- contrib/unbound/compat/getentropy_win.c.orig +++ contrib/unbound/compat/getentropy_win.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: getentropy_win.c,v 1.5 2016/08/07 03:27:21 tb Exp $ */ /* * Copyright (c) 2014, Theo de Raadt @@ -15,6 +15,9 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Emulation of getentropy(2) as documented at: + * http://man.openbsd.org/getentropy.2 */ #include @@ -37,7 +40,7 @@ if (len > 256) { errno = EIO; - return -1; + return (-1); } if (CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL, --- contrib/unbound/compat/malloc.c.orig +++ contrib/unbound/compat/malloc.c @@ -5,7 +5,12 @@ #undef malloc #include +#ifndef USE_WINSOCK void *malloc (); +#else +/* provide a prototype */ +void *malloc (size_t n); +#endif /* Allocate an N-byte block of memory from the heap. If N is zero, allocate a 1-byte block. */ --- contrib/unbound/compat/snprintf.c.orig +++ contrib/unbound/compat/snprintf.c @@ -658,7 +658,7 @@ * are not their own functions. */ /* printout designation: - * conversion specifier: x, d, u, s, c, n, m, p + * conversion specifier: x, d, u, s, c, m, p * flags: # not supported * 0 zeropad (on the left) * - left adjust (right by default) @@ -798,7 +798,10 @@ minw, minus); break; case 'n': - *va_arg(arg, int*) = ret; + /* unsupported to harden against format string + * exploitation, + * handled like an unknown format specifier. */ + /* *va_arg(arg, int*) = ret; */ break; case 'm': print_str(&at, &left, &ret, strerror(errno), --- contrib/unbound/config.guess.orig +++ contrib/unbound/config.guess @@ -1,8 +1,8 @@ -#! /bin/sh +#!/usr/bin/sh # Attempt to guess a canonical system name. -# Copyright 1992-2013 Free Software Foundation, Inc. +# Copyright 1992-2016 Free Software Foundation, Inc. -timestamp='2013-06-10' +timestamp='2016-10-02' # 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 @@ -24,12 +24,12 @@ # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # -# Originally written by Per Bothner. +# Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: -# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess # -# Please send patches with a ChangeLog entry to config-patches@gnu.org. +# Please send patches to . me=`echo "$0" | sed -e 's,.*/,,'` @@ -50,7 +50,7 @@ GNU config.guess ($timestamp) Originally written by Per Bothner. -Copyright 1992-2013 Free Software Foundation, Inc. +Copyright 1992-2016 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." @@ -149,7 +149,7 @@ LIBC=gnu #endif EOF - eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` ;; esac @@ -168,8 +168,10 @@ # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" - UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ - /usr/sbin/$sysctl 2>/dev/null || echo unknown)` + UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ + /sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || \ + echo unknown)` case "${UNAME_MACHINE_ARCH}" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; @@ -176,11 +178,19 @@ 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. + # 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) eval $set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ @@ -197,6 +207,13 @@ 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 @@ -207,13 +224,13 @@ release='-gnu' ;; *) - release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + 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. - echo "${machine}-${os}${release}" + echo "${machine}-${os}${release}${abi}" exit ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` @@ -223,6 +240,10 @@ UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} exit ;; + *:LibertyBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-libertybsd${UNAME_RELEASE} + exit ;; *:ekkoBSD:*:*) echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} exit ;; @@ -235,6 +256,9 @@ *:MirBSD:*:*) echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} exit ;; + *:Sortix:*:*) + echo ${UNAME_MACHINE}-unknown-sortix + exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) @@ -251,35 +275,35 @@ 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" ;; + UNAME_MACHINE=alpha ;; "EV4.5 (21064)") - UNAME_MACHINE="alpha" ;; + UNAME_MACHINE=alpha ;; "LCA4 (21066/21068)") - UNAME_MACHINE="alpha" ;; + UNAME_MACHINE=alpha ;; "EV5 (21164)") - UNAME_MACHINE="alphaev5" ;; + UNAME_MACHINE=alphaev5 ;; "EV5.6 (21164A)") - UNAME_MACHINE="alphaev56" ;; + UNAME_MACHINE=alphaev56 ;; "EV5.6 (21164PC)") - UNAME_MACHINE="alphapca56" ;; + UNAME_MACHINE=alphapca56 ;; "EV5.7 (21164PC)") - UNAME_MACHINE="alphapca57" ;; + UNAME_MACHINE=alphapca57 ;; "EV6 (21264)") - UNAME_MACHINE="alphaev6" ;; + UNAME_MACHINE=alphaev6 ;; "EV6.7 (21264A)") - UNAME_MACHINE="alphaev67" ;; + UNAME_MACHINE=alphaev67 ;; "EV6.8CB (21264C)") - UNAME_MACHINE="alphaev68" ;; + UNAME_MACHINE=alphaev68 ;; "EV6.8AL (21264B)") - UNAME_MACHINE="alphaev68" ;; + UNAME_MACHINE=alphaev68 ;; "EV6.8CX (21264D)") - UNAME_MACHINE="alphaev68" ;; + UNAME_MACHINE=alphaev68 ;; "EV6.9A (21264/EV69A)") - UNAME_MACHINE="alphaev69" ;; + UNAME_MACHINE=alphaev69 ;; "EV7 (21364)") - UNAME_MACHINE="alphaev7" ;; + UNAME_MACHINE=alphaev7 ;; "EV7.9 (21364A)") - UNAME_MACHINE="alphaev79" ;; + UNAME_MACHINE=alphaev79 ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. @@ -286,7 +310,7 @@ # 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. - echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 @@ -359,16 +383,16 @@ exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) eval $set_cc_for_build - SUN_ARCH="i386" + 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 [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if [ "$CC_FOR_BUILD" != no_compiler_found ]; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then - SUN_ARCH="x86_64" + SUN_ARCH=x86_64 fi fi echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` @@ -393,7 +417,7 @@ exit ;; 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 + test "x${UNAME_RELEASE}" = x && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos${UNAME_RELEASE} @@ -579,8 +603,9 @@ else IBM_ARCH=powerpc fi - if [ -x /usr/bin/oslevel ] ; then - IBM_REV=`/usr/bin/oslevel` + if [ -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 @@ -617,13 +642,13 @@ 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 + 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 + 32) HP_ARCH=hppa2.0n ;; + 64) HP_ARCH=hppa2.0w ;; + '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 esac ;; esac fi @@ -662,11 +687,11 @@ exit (0); } EOF - (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + (CCOPTS="" $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac - if [ ${HP_ARCH} = "hppa2.0w" ] + if [ ${HP_ARCH} = hppa2.0w ] then eval $set_cc_for_build @@ -679,12 +704,12 @@ # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 - if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | + if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then - HP_ARCH="hppa2.0w" + HP_ARCH=hppa2.0w else - HP_ARCH="hppa64" + HP_ARCH=hppa64 fi fi echo ${HP_ARCH}-hp-hpux${HPUX_REV} @@ -789,14 +814,14 @@ echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; 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_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 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/ /_/'` + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) @@ -826,7 +851,7 @@ *:MINGW*:*) echo ${UNAME_MACHINE}-pc-mingw32 exit ;; - i*:MSYS*:*) + *:MSYS*:*) echo ${UNAME_MACHINE}-pc-msys exit ;; i*:windows32*:*) @@ -878,7 +903,7 @@ exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland - echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} exit ;; i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix @@ -901,7 +926,7 @@ EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 - if test "$?" = 0 ; then LIBC="gnulibc1" ; fi + if test "$?" = 0 ; then LIBC=gnulibc1 ; fi echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arc:Linux:*:* | arceb:Linux:*:*) @@ -932,6 +957,9 @@ crisv32:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; + e2k:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; frv:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; @@ -944,6 +972,9 @@ ia64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; + k1om:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; m32r*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; @@ -969,10 +1000,13 @@ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } ;; - or1k:Linux:*:*) + mips64el:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; - or32:Linux:*:*) + openrisc*:Linux:*:*) + echo or1k-unknown-linux-${LIBC} + exit ;; + or32:Linux:*:* | or1k*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; padre:Linux:*:*) @@ -1001,6 +1035,9 @@ ppcle:Linux:*:*) echo powerpcle-unknown-linux-${LIBC} exit ;; + riscv32:Linux:*:* | riscv64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux-${LIBC} exit ;; @@ -1020,7 +1057,7 @@ echo ${UNAME_MACHINE}-dec-linux-${LIBC} exit ;; x86_64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + echo ${UNAME_MACHINE}-pc-linux-${LIBC} exit ;; xtensa*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} @@ -1099,7 +1136,7 @@ # 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 configury will decide that + # prints for the "djgpp" host, or else GDB configure will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; @@ -1248,6 +1285,9 @@ SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux${UNAME_RELEASE} exit ;; + SX-ACE:SUPER-UX:*:*) + echo sxace-nec-superux${UNAME_RELEASE} + exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody${UNAME_RELEASE} exit ;; @@ -1260,22 +1300,32 @@ if test "$UNAME_PROCESSOR" = unknown ; then UNAME_PROCESSOR=powerpc fi - if [ "$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 + if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then + if [ "$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 fi + elif test "$UNAME_PROCESSOR" = i386 ; then + # Avoid executing cc on OS X 10.9, as it ships with a stub + # that puts up a graphical alert prompting to install + # developer tools. Any system running Mac OS X 10.7 or + # later (Darwin 11 and later) is required to have a 64-bit + # processor. This is not true of the ARM version of Darwin + # that Apple uses in portable devices. + UNAME_PROCESSOR=x86_64 fi echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` - if test "$UNAME_PROCESSOR" = "x86"; then + if test "$UNAME_PROCESSOR" = x86; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi @@ -1306,7 +1356,7 @@ # "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 + if test "$cputype" = 386; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" @@ -1348,7 +1398,7 @@ echo i386-pc-xenix exit ;; i*86:skyos:*:*) - echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' + echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE} | sed -e 's/ .*$//'` exit ;; i*86:rdos:*:*) echo ${UNAME_MACHINE}-pc-rdos @@ -1359,171 +1409,25 @@ x86_64:VMkernel:*:*) echo ${UNAME_MACHINE}-unknown-esx exit ;; + amd64:Isilon\ OneFS:*:*) + echo x86_64-unknown-onefs + exit ;; esac -eval $set_cc_for_build -cat >$dummy.c < -# include -#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 - printf ("m68k-sony-newsos%s\n", -#ifdef NEWSOS4 - "4" -#else - "" -#endif - ); exit (0); -#endif -#endif - -#if defined (__arm) && defined (__acorn) && defined (__unix) - printf ("arm-acorn-riscix\n"); exit (0); -#endif - -#if defined (hp300) && !defined (hpux) - printf ("m68k-hp-bsd\n"); exit (0); -#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 -# 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 - printf ("vax-dec-ultrix\n"); exit (0); -# 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; } - -# Convex versions that predate uname can use getsysinfo(1) - -if [ -x /usr/convex/getsysinfo ] -then - case `getsysinfo -f cpu_type` in - c1*) - echo c1-convex-bsd - exit ;; - c2*) - if getsysinfo -f scalar_acc - then echo c32-convex-bsd - else echo c2-convex-bsd - fi - exit ;; - c34*) - echo c34-convex-bsd - exit ;; - c38*) - echo c38-convex-bsd - exit ;; - c4*) - echo c4-convex-bsd - exit ;; - esac -fi - cat >&2 < in order to provide the needed -information to handle your system. +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 --- contrib/unbound/config.h.orig +++ contrib/unbound/config.h @@ -1,1160 +0,0 @@ -/* config.h. Generated from config.h.in by configure. */ -/* config.h.in. Generated from configure.ac by autoheader. */ - -/* Directory to chroot to */ -#define CHROOT_DIR "/var/unbound" - -/* Do sha512 definitions in config.h */ -/* #undef COMPAT_SHA512 */ - -/* Pathname to the Unbound configuration file */ -#define CONFIGFILE "/var/unbound/unbound.conf" - -/* 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 */ - -/* 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 `arc4random' function. */ -#define HAVE_ARC4RANDOM 1 - -/* Define to 1 if you have the `arc4random_uniform' function. */ -#define HAVE_ARC4RANDOM_UNIFORM 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_ARPA_INET_H 1 - -/* Whether the C compiler accepts the "format" attribute */ -#define HAVE_ATTR_FORMAT 1 - -/* Whether the C compiler accepts the "unused" attribute */ -#define HAVE_ATTR_UNUSED 1 - -/* Whether the C compiler accepts the "weak" attribute */ -#define HAVE_ATTR_WEAK 1 - -/* Define to 1 if you have the `chown' function. */ -#define HAVE_CHOWN 1 - -/* Define to 1 if you have the `chroot' function. */ -#define HAVE_CHROOT 1 - -/* Define to 1 if you have the `CRYPTO_cleanup_all_ex_data' function. */ -#define HAVE_CRYPTO_CLEANUP_ALL_EX_DATA 1 - -/* Define to 1 if you have the `ctime_r' function. */ -#define HAVE_CTIME_R 1 - -/* Define to 1 if you have the `daemon' function. */ -#define HAVE_DAEMON 1 - -/* 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 `NID_secp384r1', and to 0 if you - don't. */ -#define HAVE_DECL_NID_SECP384R1 1 - -/* Define to 1 if you have the declaration of `NID_X9_62_prime256v1', and to 0 - if you don't. */ -#define HAVE_DECL_NID_X9_62_PRIME256V1 1 - -/* 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 `sk_SSL_COMP_pop_free', and to 0 - if you don't. */ -#define HAVE_DECL_SK_SSL_COMP_POP_FREE 1 - -/* Define to 1 if you have the declaration of - `SSL_COMP_get_compression_methods', and to 0 if you don't. */ -#define HAVE_DECL_SSL_COMP_GET_COMPRESSION_METHODS 1 - -/* Define to 1 if you have the declaration of `SSL_CTX_set_ecdh_auto', and to - 0 if you don't. */ -#define HAVE_DECL_SSL_CTX_SET_ECDH_AUTO 1 - -/* 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. */ -#define HAVE_DECL_XML_STOPPARSER 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_DLFCN_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_ENDIAN_H */ - -/* Define to 1 if you have the `endprotoent' function. */ -#define HAVE_ENDPROTOENT 1 - -/* Define to 1 if you have the `endpwent' function. */ -#define HAVE_ENDPWENT 1 - -/* Define to 1 if you have the `endservent' function. */ -#define HAVE_ENDSERVENT 1 - -/* Define to 1 if you have the `ERR_free_strings' function. */ -#define HAVE_ERR_FREE_STRINGS 1 - -/* Define to 1 if you have the `ERR_load_crypto_strings' function. */ -#define HAVE_ERR_LOAD_CRYPTO_STRINGS 1 - -/* 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 header file. */ -/* #undef HAVE_EVENT_H */ - -/* Define to 1 if you have the `EVP_cleanup' function. */ -#define HAVE_EVP_CLEANUP 1 - -/* 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. */ -#define HAVE_EVP_SHA1 1 - -/* Define to 1 if you have the `EVP_sha256' function. */ -#define HAVE_EVP_SHA256 1 - -/* Define to 1 if you have the `EVP_sha512' function. */ -#define HAVE_EVP_SHA512 1 - -/* 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 header file. */ -#define HAVE_EXPAT_H 1 - -/* Define to 1 if you have the `fcntl' function. */ -#define HAVE_FCNTL 1 - -/* Define to 1 if you have the `FIPS_mode' function. */ -#define HAVE_FIPS_MODE 1 - -/* Define to 1 if you have the `fork' function. */ -#define HAVE_FORK 1 - -/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */ -#define HAVE_FSEEKO 1 - -/* Define to 1 if you have the `fsync' function. */ -#define HAVE_FSYNC 1 - -/* Whether getaddrinfo is available */ -#define HAVE_GETADDRINFO 1 - -/* 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 header file. */ -#define HAVE_GETOPT_H 1 - -/* Define to 1 if you have the `getpwnam' function. */ -#define HAVE_GETPWNAM 1 - -/* Define to 1 if you have the `getrlimit' function. */ -#define HAVE_GETRLIMIT 1 - -/* Define to 1 if you have the `glob' function. */ -#define HAVE_GLOB 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_GLOB_H 1 - -/* Define to 1 if you have the `gmtime_r' function. */ -#define HAVE_GMTIME_R 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_GRP_H 1 - -/* If you have HMAC_Update */ -#define HAVE_HMAC_UPDATE 1 - -/* Define to 1 if you have the `inet_aton' function. */ -#define HAVE_INET_ATON 1 - -/* Define to 1 if you have the `inet_ntop' function. */ -#define HAVE_INET_NTOP 1 - -/* Define to 1 if you have the `inet_pton' function. */ -#define HAVE_INET_PTON 1 - -/* Define to 1 if you have the `initgroups' function. */ -#define HAVE_INITGROUPS 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_INTTYPES_H 1 - -/* if the function 'ioctlsocket' is available */ -/* #undef HAVE_IOCTLSOCKET */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_IPHLPAPI_H */ - -/* Define to 1 if you have the `isblank' function. */ -#define HAVE_ISBLANK 1 - -/* Define to 1 if you have the `kill' function. */ -#define HAVE_KILL 1 - -/* Define if we have LibreSSL */ -/* #undef HAVE_LIBRESSL */ - -/* Define to 1 if you have the `localtime_r' function. */ -#define HAVE_LOCALTIME_R 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_LOGIN_CAP_H 1 - -/* If have GNU libc compatible malloc */ -#define HAVE_MALLOC 1 - -/* Define to 1 if you have the `memmove' function. */ -#define HAVE_MEMMOVE 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_MEMORY_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_NETDB_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_NETINET_IN_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_NETINET_TCP_H 1 - -/* Use libnettle for crypto */ -/* #undef HAVE_NETTLE */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_NETTLE_DSA_COMPAT_H */ - -/* Use libnss for crypto */ -/* #undef HAVE_NSS */ - -/* Define to 1 if you have the `OpenSSL_add_all_digests' function. */ -#define HAVE_OPENSSL_ADD_ALL_DIGESTS 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_OPENSSL_BN_H 1 - -/* Define to 1 if you have the `OPENSSL_config' function. */ -#define HAVE_OPENSSL_CONFIG 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_OPENSSL_CONF_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_OPENSSL_DH_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_OPENSSL_DSA_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_OPENSSL_ENGINE_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_OPENSSL_ERR_H 1 - -/* 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 header file. */ -#define HAVE_OPENSSL_RAND_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_OPENSSL_RSA_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_OPENSSL_SSL_H 1 - -/* Define if you have POSIX threads libraries and header files. */ -#define HAVE_PTHREAD 1 - -/* Have PTHREAD_PRIO_INHERIT. */ -#define HAVE_PTHREAD_PRIO_INHERIT 1 - -/* Define to 1 if the system has the type `pthread_rwlock_t'. */ -#define HAVE_PTHREAD_RWLOCK_T 1 - -/* Define to 1 if the system has the type `pthread_spinlock_t'. */ -#define HAVE_PTHREAD_SPINLOCK_T 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_PWD_H 1 - -/* Define if you have Python libraries and header files. */ -/* #undef HAVE_PYTHON */ - -/* Define to 1 if you have the `random' function. */ -#define HAVE_RANDOM 1 - -/* Define to 1 if you have the `RAND_cleanup' function. */ -#define HAVE_RAND_CLEANUP 1 - -/* Define to 1 if you have the `reallocarray' function. */ -#define HAVE_REALLOCARRAY 1 - -/* Define to 1 if you have the `recvmsg' function. */ -#define HAVE_RECVMSG 1 - -/* define if you have the sbrk() call */ -/* #undef HAVE_SBRK */ - -/* Define to 1 if you have the `sendmsg' function. */ -#define HAVE_SENDMSG 1 - -/* Define to 1 if you have the `setregid' function. */ -/* #undef HAVE_SETREGID */ - -/* Define to 1 if you have the `setresgid' function. */ -#define HAVE_SETRESGID 1 - -/* Define to 1 if you have the `setresuid' function. */ -#define HAVE_SETRESUID 1 - -/* Define to 1 if you have the `setreuid' function. */ -/* #undef HAVE_SETREUID */ - -/* Define to 1 if you have the `setrlimit' function. */ -#define HAVE_SETRLIMIT 1 - -/* Define to 1 if you have the `setsid' function. */ -#define HAVE_SETSID 1 - -/* Define to 1 if you have the `setusercontext' function. */ -#define HAVE_SETUSERCONTEXT 1 - -/* Define to 1 if you have the `SHA512_Update' function. */ -/* #undef HAVE_SHA512_UPDATE */ - -/* Define to 1 if you have the `sigprocmask' function. */ -#define HAVE_SIGPROCMASK 1 - -/* Define to 1 if you have the `sleep' function. */ -#define HAVE_SLEEP 1 - -/* Define to 1 if you have the `snprintf' function. */ -#define HAVE_SNPRINTF 1 - -/* Define to 1 if you have the `socketpair' function. */ -#define HAVE_SOCKETPAIR 1 - -/* Using Solaris threads */ -/* #undef HAVE_SOLARIS_THREADS */ - -/* Define to 1 if you have the `srandom' function. */ -#define HAVE_SRANDOM 1 - -/* Define if you have the SSL libraries installed. */ -#define HAVE_SSL /**/ - -/* Define to 1 if you have the header file. */ -#define HAVE_STDARG_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDBOOL_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDINT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDLIB_H 1 - -/* Define to 1 if you have the `strftime' function. */ -#define HAVE_STRFTIME 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STRINGS_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STRING_H 1 - -/* Define to 1 if you have the `strlcat' function. */ -#define HAVE_STRLCAT 1 - -/* Define to 1 if you have the `strlcpy' function. */ -#define HAVE_STRLCPY 1 - -/* Define to 1 if you have the `strptime' function. */ -#define HAVE_STRPTIME 1 - -/* Define to 1 if you have the `strsep' function. */ -#define HAVE_STRSEP 1 - -/* 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'. */ -#define HAVE_STRUCT_SOCKADDR_UN_SUN_LEN 1 - -/* Define if you have Swig libraries and header files. */ -/* #undef HAVE_SWIG */ - -/* Define to 1 if you have the header file. */ -#define HAVE_SYSLOG_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_PARAM_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_RESOURCE_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_SHA2_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_SOCKET_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_STAT_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_SYSCTL_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_TYPES_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_UIO_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_UN_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_WAIT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_TIME_H 1 - -/* Define to 1 if you have the `tzset' function. */ -#define HAVE_TZSET 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_UNISTD_H 1 - -/* Define to 1 if you have the `usleep' function. */ -#define HAVE_USLEEP 1 - -/* Define to 1 if you have the `vfork' function. */ -#define HAVE_VFORK 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_VFORK_H */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_WINDOWS_H */ - -/* Using Windows threads */ -/* #undef HAVE_WINDOWS_THREADS */ - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_WINSOCK2_H */ - -/* Define to 1 if `fork' works. */ -#define HAVE_WORKING_FORK 1 - -/* Define to 1 if `vfork' works. */ -#define HAVE_WORKING_VFORK 1 - -/* Define to 1 if you have the `writev' function. */ -#define HAVE_WRITEV 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_WS2TCPIP_H */ - -/* Define to 1 if you have the `_beginthreadex' function. */ -/* #undef HAVE__BEGINTHREADEX */ - -/* if lex has yylex_destroy */ -#define LEX_HAS_YYLEX_DESTROY 1 - -/* Define to the sub-directory where libtool stores uninstalled libraries. */ -#define LT_OBJDIR ".libs/" - -/* Define to the maximum message length to pass to syslog. */ -#define MAXSYSLOGMSGLEN 10240 - -/* 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. */ -#define PACKAGE_BUGREPORT "unbound-bugs@nlnetlabs.nl" - -/* Define to the full name of this package. */ -#define PACKAGE_NAME "unbound" - -/* Define to the full name and version of this package. */ -#define PACKAGE_STRING "unbound 1.5.10" - -/* Define to the one symbol short name of this package. */ -#define PACKAGE_TARNAME "unbound" - -/* Define to the home page for this package. */ -#define PACKAGE_URL "" - -/* Define to the version of this package. */ -#define PACKAGE_VERSION "1.5.10" - -/* default pidfile location */ -#define PIDFILE "/var/unbound/unbound.pid" - -/* Define to necessary symbol if this constant uses a non-standard name on - your system. */ -/* #undef PTHREAD_CREATE_JOINABLE */ - -/* Define as the return type of signal handlers (`int' or `void'). */ -#define RETSIGTYPE void - -/* default rootkey location */ -#define ROOT_ANCHOR_FILE "/var/unbound/root.key" - -/* default rootcert location */ -#define ROOT_CERT_FILE "/var/unbound/icannbundle.pem" - -/* version number for resource files */ -#define RSRC_PACKAGE_VERSION 1,5,10,0 - -/* Directory to chdir to */ -#define RUN_DIR "/var/unbound" - -/* Shared data */ -#define SHARE_DIR "/var/unbound" - -/* The size of `time_t', as computed by sizeof. */ -#define SIZEOF_TIME_T 8 - -/* define if (v)snprintf does not return length needed, (but length used) */ -/* #undef SNPRINTF_RET_BROKEN */ - -/* Define to 1 if you have the ANSI C header files. */ -#define STDC_HEADERS 1 - -/* use default strptime. */ -#define STRPTIME_WORKS 1 - -/* Use win32 resources and API */ -/* #undef UB_ON_WINDOWS */ - -/* default username */ -#define UB_USERNAME "unbound" - -/* 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 dnstap support */ -/* #undef USE_DNSTAP */ - -/* Define this to enable DSA support. */ -#define USE_DSA 1 - -/* Define this to enable ECDSA support. */ -#define USE_ECDSA 1 - -/* Define this to enable an EVP workaround for older openssl */ -/* #undef USE_ECDSA_EVP_WORKAROUND */ - -/* Define this to enable GOST support. */ -#define USE_GOST 1 - -/* Define if you want to use internal select based events */ -#define USE_MINI_EVENT 1 - -/* 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 enable SHA256 and SHA512 support. */ -#define USE_SHA2 1 - -/* Enable extensions on AIX 3, Interix. */ -#ifndef _ALL_SOURCE -# define _ALL_SOURCE 1 -#endif -/* Enable GNU extensions on systems that have them. */ -#ifndef _GNU_SOURCE -# define _GNU_SOURCE 1 -#endif -/* Enable threading extensions on Solaris. */ -#ifndef _POSIX_PTHREAD_SEMANTICS -# define _POSIX_PTHREAD_SEMANTICS 1 -#endif -/* Enable extensions on HP NonStop. */ -#ifndef _TANDEM_SOURCE -# define _TANDEM_SOURCE 1 -#endif -/* Enable general extensions on Solaris. */ -#ifndef __EXTENSIONS__ -# define __EXTENSIONS__ 1 -#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 */ -#define WINVER 0x0502 - -/* 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[]'. */ -#define YYTEXT_POINTER 1 - -/* 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 */ - -/* Define to empty if `const' does not conform to ANSI C. */ -/* #undef const */ - -/* Define to `int' if 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 does not define. */ -/* #undef int16_t */ - -/* Define to `int' if does not define. */ -/* #undef int32_t */ - -/* Define to `long long' if does not define. */ -/* #undef int64_t */ - -/* Define to `signed char' if does not define. */ -/* #undef int8_t */ - -/* Define if replacement function should be used. */ -/* #undef malloc */ - -/* Define to `long int' if does not define. */ -/* #undef off_t */ - -/* Define to `int' if does not define. */ -/* #undef pid_t */ - -/* Define to 'int' if not defined */ -/* #undef rlim_t */ - -/* Define to `unsigned int' if does not define. */ -/* #undef size_t */ - -/* Define to 'int' if not defined */ -/* #undef socklen_t */ - -/* Define to `int' if does not define. */ -/* #undef ssize_t */ - -/* Define to 'unsigned char if not defined */ -/* #undef u_char */ - -/* Define to `int' if doesn't define. */ -/* #undef uid_t */ - -/* Define to `unsigned short' if does not define. */ -/* #undef uint16_t */ - -/* Define to `unsigned int' if does not define. */ -/* #undef uint32_t */ - -/* Define to `unsigned long long' if does not define. */ -/* #undef uint64_t */ - -/* Define to `unsigned char' if 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 UNBOUND_DEBUG -# define NDEBUG -#endif - -/** Use small-ldns codebase */ -#define USE_SLDNS 1 -#ifdef HAVE_SSL -# define LDNS_BUILD_CONFIG_HAVE_SSL 1 -#endif - -#include -#include -#include -#include - -#if STDC_HEADERS -#include -#include -#endif - -#ifdef HAVE_STDARG_H -#include -#endif - -#ifdef HAVE_STDINT_H -#include -#endif - -#include - -#if HAVE_SYS_PARAM_H -#include -#endif - -#ifdef HAVE_SYS_SOCKET_H -#include -#endif - -#ifdef HAVE_SYS_UIO_H -#include -#endif - -#ifdef HAVE_NETINET_IN_H -#include -#endif - -#ifdef HAVE_NETINET_TCP_H -#include -#endif - -#ifdef HAVE_ARPA_INET_H -#include -#endif - -#ifdef HAVE_WINSOCK2_H -#include -#endif - -#ifdef HAVE_WS2TCPIP_H -#include -#endif - -#ifndef USE_WINSOCK -#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 -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 - -#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 - -#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 -# if !HAVE_DECL_REALLOCARRAY -void *reallocarray(void *ptr, size_t nmemb, size_t size); -# endif -#endif /* HAVE_LIBRESSL */ -#ifndef HAVE_ARC4RANDOM -void explicit_bzero(void* buf, size_t len); -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); -#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 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 - - --- contrib/unbound/config.h.in.orig +++ contrib/unbound/config.h.in @@ -1,11 +1,23 @@ /* 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 @@ -27,6 +39,9 @@ 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 @@ -39,6 +54,9 @@ /* 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 @@ -45,6 +63,15 @@ /* 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 header file. */ +#undef HAVE_BSD_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_BSD_STRING_H + /* Define to 1 if you have the `chown' function. */ #undef HAVE_CHOWN @@ -54,6 +81,9 @@ /* 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 @@ -68,6 +98,26 @@ 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 `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 @@ -80,6 +130,10 @@ 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 @@ -107,6 +161,9 @@ /* Define to 1 if you have the 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 header file. */ #undef HAVE_ENDIAN_H @@ -125,6 +182,9 @@ /* 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 @@ -140,9 +200,21 @@ /* Define to 1 if you have the 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_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_MD_CTX_new' function. */ #undef HAVE_EVP_MD_CTX_NEW @@ -164,6 +236,9 @@ /* Define to 1 if you have the 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 @@ -209,9 +284,18 @@ /* Define to 1 if you have the header file. */ #undef HAVE_GRP_H +/* Define to 1 if you have the header file. */ +#undef HAVE_HIREDIS_HIREDIS_H + +/* Define to 1 if you have the `HMAC_Init_ex' function. */ +#undef HAVE_HMAC_INIT_EX + /* If you have HMAC_Update */ #undef HAVE_HMAC_UPDATE +/* If we have htobe64 */ +#undef HAVE_HTOBE64 + /* Define to 1 if you have the `inet_aton' function. */ #undef HAVE_INET_ATON @@ -239,6 +323,12 @@ /* 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 header file. */ +#undef HAVE_LIBKERN_OSBYTEORDER_H + /* Define if we have LibreSSL */ #undef HAVE_LIBRESSL @@ -272,6 +362,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_NETTLE_DSA_COMPAT_H +/* Define to 1 if you have the header file. */ +#undef HAVE_NETTLE_EDDSA_H + /* Use libnss for crypto */ #undef HAVE_NSS @@ -338,15 +431,12 @@ /* Define to 1 if you have the `RAND_cleanup' function. */ #undef HAVE_RAND_CLEANUP -/* Define to 1 if you have the `reallocarray' function. */ +/* If we have reallocarray(3) */ #undef HAVE_REALLOCARRAY /* Define to 1 if you have the `recvmsg' function. */ #undef HAVE_RECVMSG -/* define if you have the sbrk() call */ -#undef HAVE_SBRK - /* Define to 1 if you have the `sendmsg' function. */ #undef HAVE_SENDMSG @@ -374,6 +464,9 @@ /* 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 @@ -395,6 +488,21 @@ /* Define if you have the SSL libraries installed. */ #undef HAVE_SSL +/* 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_cb' function. */ +#undef HAVE_SSL_CTX_SET_TLSEXT_TICKET_KEY_CB + +/* Define to 1 if you have the `SSL_get0_peername' function. */ +#undef HAVE_SSL_GET0_PEERNAME + +/* Define to 1 if you have the `SSL_set1_host' function. */ +#undef HAVE_SSL_SET1_HOST + /* Define to 1 if you have the header file. */ #undef HAVE_STDARG_H @@ -440,6 +548,15 @@ /* Define to 1 if you have the header file. */ #undef HAVE_SYSLOG_H +/* Define to 1 if systemd should be used */ +#undef HAVE_SYSTEMD + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_ENDIAN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_IPC_H + /* Define to 1 if you have the header file. */ #undef HAVE_SYS_PARAM_H @@ -449,6 +566,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SHA2_H +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SHM_H + /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SOCKET_H @@ -509,9 +629,15 @@ /* Define to 1 if you have the 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 @@ -586,6 +712,9 @@ /* Define as the return type of signal handlers (`int' or `void'). */ #undef RETSIGTYPE +/* if REUSEPORT is enabled by default */ +#undef REUSEPORT_DEFAULT + /* default rootkey location */ #undef ROOT_ANCHOR_FILE @@ -601,6 +730,9 @@ /* Shared data */ #undef SHARE_DIR +/* 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 @@ -607,6 +739,9 @@ /* define if (v)snprintf does not return length needed, (but length used) */ #undef SNPRINTF_RET_BROKEN +/* Define to 1 if libsodium supports sodium_set_misuse_handler */ +#undef SODIUM_MISUSE_HANDLER + /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS @@ -616,6 +751,9 @@ /* 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 @@ -634,6 +772,12 @@ /* 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 @@ -646,9 +790,21 @@ /* 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 want to use internal select based events */ #undef USE_MINI_EVENT @@ -658,6 +814,12 @@ /* 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 @@ -840,8 +1002,14 @@ +#ifndef _OPENBSD_SOURCE +#define _OPENBSD_SOURCE 1 +#endif + #ifndef UNBOUND_DEBUG +# ifndef NDEBUG # define NDEBUG +# endif #endif /** Use small-ldns codebase */ @@ -1055,6 +1223,19 @@ 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; @@ -1061,6 +1242,15 @@ 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 +#include +#endif + #ifdef HAVE_LIBRESSL # if !HAVE_DECL_STRLCPY size_t strlcpy(char *dst, const char *src, size_t siz); @@ -1074,17 +1264,14 @@ # if !HAVE_DECL_ARC4RANDOM_UNIFORM && defined(HAVE_ARC4RANDOM_UNIFORM) uint32_t arc4random_uniform(uint32_t upper_bound); # endif -# if !HAVE_DECL_REALLOCARRAY -void *reallocarray(void *ptr, size_t nmemb, size_t size); -# endif #endif /* HAVE_LIBRESSL */ #ifndef HAVE_ARC4RANDOM -void explicit_bzero(void* buf, size_t len); 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); @@ -1150,6 +1337,8 @@ /** 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 unbound control traffic, registered port with IANA, ub-dns-control 8953/tcp unbound dns nameserver control */ #define UNBOUND_CONTROL_PORT 8953 --- contrib/unbound/config.sub.orig +++ contrib/unbound/config.sub @@ -1,8 +1,8 @@ -#! /bin/sh +#!/usr/bin/sh # Configuration validation subroutine script. -# Copyright 1992-2013 Free Software Foundation, Inc. +# Copyright 1992-2016 Free Software Foundation, Inc. -timestamp='2013-08-10' +timestamp='2016-09-05' # 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 @@ -25,7 +25,7 @@ # of the GNU General Public License, version 3 ("GPLv3"). -# Please send patches with a ChangeLog entry to config-patches@gnu.org. +# Please send patches to . # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. @@ -33,7 +33,7 @@ # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: -# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases @@ -53,8 +53,7 @@ me=`echo "$0" | sed -e 's,.*/,,'` usage="\ -Usage: $0 [OPTION] CPU-MFR-OPSYS - $0 [OPTION] ALIAS +Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS Canonicalize a configuration name. @@ -68,7 +67,7 @@ version="\ GNU config.sub ($timestamp) -Copyright 1992-2013 Free Software Foundation, Inc. +Copyright 1992-2016 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." @@ -117,8 +116,8 @@ case $maybe_os in nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ - knetbsd*-gnu* | netbsd*-gnu* | \ - kopensolaris*-gnu* | \ + knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \ + kopensolaris*-gnu* | cloudabi*-eabi* | \ storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` @@ -255,16 +254,18 @@ | arc | arceb \ | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ | avr | avr32 \ + | ba \ | be32 | be64 \ | bfin \ | c4x | c8051 | clipper \ | d10v | d30v | dlx | dsp16xx \ - | epiphany \ - | fido | fr30 | frv \ + | e2k | epiphany \ + | fido | fr30 | frv | ft32 \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | hexagon \ | i370 | i860 | i960 | ia64 \ | ip2k | iq2000 \ + | k1om \ | le32 | le64 \ | lm32 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ @@ -282,8 +283,10 @@ | mips64vr5900 | mips64vr5900el \ | mipsisa32 | mipsisa32el \ | mipsisa32r2 | mipsisa32r2el \ + | mipsisa32r6 | mipsisa32r6el \ | mipsisa64 | mipsisa64el \ | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64r6 | mipsisa64r6el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ | mipsr5900 | mipsr5900el \ @@ -295,14 +298,14 @@ | nds32 | nds32le | nds32be \ | nios | nios2 | nios2eb | nios2el \ | ns16k | ns32k \ - | open8 \ - | or1k | or32 \ + | open8 | or1k | or1knd | or32 \ | pdp10 | pdp11 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle \ | pyramid \ + | riscv32 | riscv64 \ | rl78 | rx \ | score \ - | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ + | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ @@ -310,6 +313,7 @@ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ | ubicom32 \ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ + | visium \ | we32k \ | x86 | xc16x | xstormy16 | xtensa \ | z8k | z80) @@ -324,7 +328,10 @@ c6x) basic_machine=tic6x-unknown ;; - m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip) + leon|leon[3-9]) + basic_machine=sparc-$basic_machine + ;; + m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) basic_machine=$basic_machine-unknown os=-none ;; @@ -369,12 +376,13 @@ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* | avr32-* \ + | ba-* \ | be32-* | be64-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* \ | c8051-* | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ - | elxsi-* \ + | e2k-* | elxsi-* \ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ @@ -381,6 +389,7 @@ | hexagon-* \ | i*86-* | i860-* | i960-* | ia64-* \ | ip2k-* | iq2000-* \ + | k1om-* \ | le32-* | le64-* \ | lm32-* \ | m32c-* | m32r-* | m32rle-* \ @@ -400,8 +409,10 @@ | mips64vr5900-* | mips64vr5900el-* \ | mipsisa32-* | mipsisa32el-* \ | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa32r6-* | mipsisa32r6el-* \ | mipsisa64-* | mipsisa64el-* \ | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64r6-* | mipsisa64r6el-* \ | mipsisa64sb1-* | mipsisa64sb1el-* \ | mipsisa64sr71k-* | mipsisa64sr71kel-* \ | mipsr5900-* | mipsr5900el-* \ @@ -413,16 +424,18 @@ | nios-* | nios2-* | nios2eb-* | nios2el-* \ | none-* | np1-* | ns16k-* | ns32k-* \ | open8-* \ + | or1k*-* \ | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ | pyramid-* \ + | riscv32-* | riscv64-* \ | rl78-* | romp-* | rs6000-* | rx-* \ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ | sparclite-* \ - | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \ | tahoe-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ | tile*-* \ @@ -430,6 +443,7 @@ | ubicom32-* \ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ | vax-* \ + | visium-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* \ | xstormy16-* | xtensa*-* \ @@ -506,6 +520,9 @@ basic_machine=i386-pc os=-aros ;; + asmjs) + basic_machine=asmjs-unknown + ;; aux) basic_machine=m68k-apple os=-aux @@ -626,6 +643,14 @@ basic_machine=m68k-bull os=-sysv3 ;; + e500v[12]) + basic_machine=powerpc-unknown + os=$os"spe" + ;; + e500v[12]-*) + basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + os=$os"spe" + ;; ebmon29k) basic_machine=a29k-amd os=-ebmon @@ -767,6 +792,9 @@ basic_machine=m68k-isi os=-sysv ;; + leon-*|leon[3-9]-*) + basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'` + ;; m68knommu) basic_machine=m68k-unknown os=-linux @@ -822,6 +850,10 @@ basic_machine=powerpc-unknown os=-morphos ;; + moxiebox) + basic_machine=moxie-unknown + os=-moxiebox + ;; msdos) basic_machine=i386-pc os=-msdos @@ -998,7 +1030,7 @@ ppc-* | ppcbe-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` ;; - ppcle | powerpclittle | ppc-le | powerpc-little) + ppcle | powerpclittle) basic_machine=powerpcle-unknown ;; ppcle-* | powerpclittle-*) @@ -1006,9 +1038,9 @@ ;; ppc64) basic_machine=powerpc64-unknown ;; - ppc64-* | ppc64p7-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; - ppc64le | powerpc64little | ppc64-le | powerpc64-little) + ppc64le | powerpc64little) basic_machine=powerpc64le-unknown ;; ppc64le-* | powerpc64little-*) @@ -1354,11 +1386,11 @@ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ | -sym* | -kopensolaris* | -plan9* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ - | -aos* | -aros* \ + | -aos* | -aros* | -cloudabi* | -sortix* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ - | -bitrig* | -openbsd* | -solidbsd* \ + | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ @@ -1365,9 +1397,9 @@ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ | -chorusos* | -chorusrdb* | -cegcc* \ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ - | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ + | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ | -linux-newlib* | -linux-musl* | -linux-uclibc* \ - | -uxpv* | -beos* | -mpeix* | -udk* \ + | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ @@ -1374,7 +1406,8 @@ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ - | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*) + | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \ + | -onefs* | -tirtos* | -phoenix*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) @@ -1506,6 +1539,8 @@ ;; -nacl*) ;; + -ios) + ;; -none) ;; *) @@ -1592,9 +1627,6 @@ mips*-*) os=-elf ;; - or1k-*) - os=-elf - ;; or32-*) os=-coff ;; --- contrib/unbound/configure.orig +++ contrib/unbound/configure @@ -1,8 +1,8 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for unbound 1.5.10. +# Generated by GNU Autoconf 2.69 for unbound 1.10.1. # -# Report bugs to . +# Report bugs to . # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -275,10 +275,11 @@ $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 about your system, including -$0: any error possibly output before this message. Then -$0: install a modern shell, or manually run the script -$0: under such a shell if you do have one." +$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 @@ -590,9 +591,9 @@ # Identity of this package. PACKAGE_NAME='unbound' PACKAGE_TARNAME='unbound' -PACKAGE_VERSION='1.5.10' -PACKAGE_STRING='unbound 1.5.10' -PACKAGE_BUGREPORT='unbound-bugs@nlnetlabs.nl' +PACKAGE_VERSION='1.10.1' +PACKAGE_STRING='unbound 1.10.1' +PACKAGE_BUGREPORT='unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues' PACKAGE_URL='' # Factoring default headers for most tests. @@ -638,6 +639,14 @@ ALLTARGET SOURCEFILE SOURCEDETERMINE +IPSET_OBJ +IPSET_SRC +IPSECMOD_HEADER +IPSECMOD_OBJ +DNSCRYPT_OBJ +DNSCRYPT_SRC +ENABLE_DNSCRYPT +ENABLE_DNSCRYPT_XCHACHA20 DNSTAP_OBJ DNSTAP_SRC opt_dnstap_socket_path @@ -659,10 +668,15 @@ 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 NETBSD_LINTFLAGS PYUNBOUND_UNINSTALL @@ -678,6 +692,7 @@ swig SWIG_LIB SWIG +PC_PY_DEPENDENCY PY_MAJOR_VERSION PYTHON_SITE_PKG PYTHON_LDFLAGS @@ -689,8 +704,19 @@ 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 @@ -739,6 +765,9 @@ UNBOUND_RUN_DIR ub_conf_dir ub_conf_file +UNBOUND_LOCALSTATE_DIR +UNBOUND_SYSCONF_DIR +UNBOUND_SBIN_DIR EGREP GREP CPP @@ -819,26 +848,36 @@ 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_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 +enable_ed25519 +enable_ed448 enable_event_api enable_tfo_client enable_tfo_server with_libevent with_libexpat +with_libhiredis enable_static_exe +enable_fully_static enable_lock_checks enable_allsymbols enable_dnstap @@ -845,7 +884,12 @@ with_dnstap_socket_path with_protobuf_c with_libfstrm +enable_dnscrypt +with_libsodium enable_cachedb +enable_ipsecmod +enable_ipset +with_libmnl with_libunbound_only ' ac_precious_vars='build_alias @@ -860,6 +904,13 @@ 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' @@ -1401,7 +1452,7 @@ # 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.5.10 to adapt to many kinds of systems. +\`configure' configures unbound 1.10.1 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1466,7 +1517,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of unbound 1.5.10:";; + short | recursive ) echo "Configuration of unbound 1.10.1:";; esac cat <<\_ACEOF @@ -1488,6 +1539,7 @@ --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 @@ -1496,16 +1548,25 @@ 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) libs, for debug purposes + (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 @@ -1512,8 +1573,12 @@ to it, smaller install size but libunbound export table is polluted by internal symbols --enable-dnstap Enable dnstap support (requires fstrm, 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 Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] @@ -1547,6 +1612,8 @@ --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-pyunbound build PyUnbound, or --without-pyunbound to skip it. (default=no) --with-pythonmodule build Python module, or --without-pythonmodule to @@ -1556,6 +1623,7 @@ --with-ssl=pathname enable SSL (will check /usr/local/ssl /usr/lib/ssl /usr/ssl /usr/pkg /usr/local /opt/local /usr/sfw /usr) + --with-libbsd Use portable libbsd functions --with-libevent=pathname use libevent (will check /usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr or you can specify @@ -1562,10 +1630,13 @@ 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-dnstap-socket-path=pathname set default dnstap socket path --with-protobuf-c=path Path where protobuf-c is installed, for dnstap --with-libfstrm=path Path where libfstrm 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: @@ -1585,6 +1656,19 @@ 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 @@ -1593,7 +1677,7 @@ 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 . +Report bugs to . _ACEOF ac_status=$? fi @@ -1656,7 +1740,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -unbound configure 1.5.10 +unbound configure 1.10.1 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1815,9 +1899,9 @@ $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 ## -## ---------------------------------------- ##" +( $as_echo "## --------------------------------------------------------------------------------------- ## +## Report this to unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues ## +## --------------------------------------------------------------------------------------- ##" ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac @@ -2365,7 +2449,7 @@ 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.5.10, which was +It was created by unbound $as_me 1.10.1, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2715,14 +2799,14 @@ UNBOUND_VERSION_MAJOR=1 -UNBOUND_VERSION_MINOR=5 +UNBOUND_VERSION_MINOR=10 -UNBOUND_VERSION_MICRO=10 +UNBOUND_VERSION_MICRO=1 -LIBUNBOUND_CURRENT=6 -LIBUNBOUND_REVISION=2 -LIBUNBOUND_AGE=4 +LIBUNBOUND_CURRENT=9 +LIBUNBOUND_REVISION=8 +LIBUNBOUND_AGE=1 # 1.0.0 had 0:12:0 # 1.0.1 had 0:13:0 # 1.0.2 had 0:14:0 @@ -2771,6 +2855,32 @@ # 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 # Current -- the number of the binary API that we're implementing # Revision -- which iteration of the implementation of the binary @@ -2786,7 +2896,7 @@ # Current and Age. Set Revision to 0, since this is the first # implementation of the new API. # -# Otherwise, we're changing the binary API and breaking bakward +# Otherwise, we're changing the binary API and breaking backward # compatibility with old binaries. Increment Current. Set Age to 0, # since we're backward compatible with no previous APIs. Set Revision # to 0 too. @@ -2794,6 +2904,14 @@ + +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' @@ -4055,6 +4173,11 @@ prefix="/usr/local" ;; esac +case "$exec_prefix" in + NONE) + exec_prefix="$prefix" + ;; +esac # are we on MinGW? if uname -s 2>&1 | grep MINGW32 >/dev/null; then on_mingw="yes" @@ -4066,10 +4189,16 @@ # # 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 (x86)\\Unbound\\service.conf" + ub_conf_file="C:\\Program Files\\Unbound\\service.conf" fi # Check whether --with-conf_file was given. @@ -4200,7 +4329,7 @@ if test $on_mingw = no; then UNBOUND_ROOTKEY_FILE="$UNBOUND_RUN_DIR/root.key" else - UNBOUND_ROOTKEY_FILE="C:\\Program Files (x86)\\Unbound\\root.key" + UNBOUND_ROOTKEY_FILE="C:\\Program Files\\Unbound\\root.key" fi fi @@ -4222,7 +4351,7 @@ if test $on_mingw = no; then UNBOUND_ROOTCERT_FILE="$UNBOUND_RUN_DIR/icannbundle.pem" else - UNBOUND_ROOTCERT_FILE="C:\\Program Files (x86)\\Unbound\\icannbundle.pem" + UNBOUND_ROOTCERT_FILE="C:\\Program Files\\Unbound\\icannbundle.pem" fi fi @@ -4351,6 +4480,7 @@ 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 @@ -4414,6 +4544,7 @@ fi +default_cflags=yes fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' @@ -5867,6 +5998,10 @@ # 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 : @@ -6001,6 +6136,7 @@ fi +fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 $as_echo_n "checking for inline... " >&6; } @@ -6167,9 +6303,57 @@ $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 +__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 @@ -6176,6 +6360,8 @@ + + for ac_prog in flex lex do # Extract the first word of "$ac_prog", so it can be a program name with args. @@ -6335,6 +6521,7 @@ 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; } @@ -6345,8 +6532,27 @@ { $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 "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 <&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 for ac_prog in 'bison -y' byacc do # Extract the first word of "$ac_prog", so it can be a program name with args. @@ -14386,8 +14592,129 @@ + + + + + + + +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 + # Checks for header files. -for ac_header in stdarg.h stdbool.h netinet/in.h netinet/tcp.h sys/param.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 +for ac_header in stdarg.h stdbool.h netinet/in.h netinet/tcp.h sys/param.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 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 @@ -14750,7 +15077,40 @@ _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. @@ -15797,6 +16157,208 @@ done +# check if we can use SO_REUSEPORT +if echo "$host" | $GREP -i -e linux -e dragonfly >/dev/null; 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 : @@ -15822,6 +16384,10 @@ $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 @@ -16389,7 +16955,9 @@ $as_echo "#define HAVE_PTHREAD 1" >>confdefs.h - LIBS="$PTHREAD_LIBS $LIBS" + if test -n "$PTHREAD_LIBS"; then + LIBS="$PTHREAD_LIBS $LIBS" + fi CFLAGS="$CFLAGS $PTHREAD_CFLAGS" CC="$PTHREAD_CC" ub_have_pthreads=yes @@ -16583,6 +17151,26 @@ 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 PyUnbound # Check whether --with-pyunbound was given. @@ -16682,8 +17270,7 @@ # { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the distutils Python package" >&5 $as_echo_n "checking for the distutils Python package... " >&6; } - ac_distutils_result=`$PYTHON -c "import distutils" 2>&1` - if test -z "$ac_distutils_result"; then + if ac_distutils_result=`$PYTHON -c "import distutils" 2>&1`; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else @@ -16820,13 +17407,38 @@ $as_echo "#define HAVE_PYTHON 1" >>confdefs.h - LIBS="$PYTHON_LDFLAGS $LIBS" - CPPFLAGS="$CPPFLAGS $PYTHON_CPPFLAGS" + if test -n "$LIBS"; then + LIBS="$PYTHON_LDFLAGS $LIBS" + else + LIBS="$PYTHON_LDFLAGS" + fi + if test -n "$CPPFLAGS"; then + CPPFLAGS="$CPPFLAGS $PYTHON_CPPFLAGS" + else + CPPFLAGS="$PYTHON_CPPFLAGS" + 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 @@ -16871,6 +17483,123 @@ { $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; } @@ -16910,9 +17639,20 @@ if test -z "$available_patch" ; then available_patch=0 fi - if test $available_major -ne $required_major \ - -o $available_minor -ne $required_minor \ - -o $available_patch -lt $required_patch ; then + 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' @@ -16931,6 +17671,7 @@ fi + fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking SWIG" >&5 $as_echo_n "checking SWIG... " >&6; } if test ! -x "$SWIG"; then @@ -17023,8 +17764,10 @@ fi LIBS="$LIBS -lnss3 -lnspr4" SSLLIB="" + PC_CRYPTO_DEPENDENCY="nss nspr" + fi @@ -17066,8 +17809,10 @@ fi LIBS="$LIBS -lhogweed -lnettle -lgmp" SSLLIB="" + PC_CRYPTO_DEPENDENCY="hogweed nettle" + fi @@ -17163,8 +17908,8 @@ # check if -lwsock32 or -lgdi32 are needed. BAKLIBS="$LIBS" BAKSSLLIBS="$LIBSSL_LIBS" - LIBS="$LIBS -lgdi32" - LIBSSL_LIBS="$LIBSSL_LIBS -lgdi32" + 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 @@ -17416,6 +18161,9 @@ conftest$ac_exeext conftest.$ac_ext SSLLIB="-lssl" +PC_CRYPTO_DEPENDENCY="libcrypto libssl" + + # check if -lcrypt32 is needed because CAPIENG needs that. (on windows) BAKLIBS="$LIBS" LIBS="-lssl $LIBS" @@ -17506,17 +18254,7 @@ cat >>confdefs.h <<_ACEOF #define HAVE_DECL_ARC4RANDOM_UNIFORM $ac_have_decl _ACEOF -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 - else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } @@ -17535,7 +18273,7 @@ done -for ac_func in OPENSSL_config EVP_sha1 EVP_sha256 EVP_sha512 FIPS_mode EVP_MD_CTX_new OpenSSL_add_all_digests OPENSSL_init_crypto EVP_cleanup ERR_load_crypto_strings CRYPTO_cleanup_all_ex_data ERR_free_strings RAND_cleanup +for ac_func in OPENSSL_config EVP_sha1 EVP_sha256 EVP_sha512 FIPS_mode EVP_MD_CTX_new OpenSSL_add_all_digests OPENSSL_init_crypto EVP_cleanup ERR_load_crypto_strings CRYPTO_cleanup_all_ex_data ERR_free_strings RAND_cleanup DSA_SIG_set0 EVP_dss1 EVP_DigestVerify SSL_CTX_set_tlsext_ticket_key_cb EVP_aes_256_cbc EVP_EncryptInit_ex HMAC_Init_ex CRYPTO_THREADID_set_callback 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" @@ -17551,12 +18289,13 @@ # these check_funcs need -lssl BAKLIBS="$LIBS" LIBS="-lssl $LIBS" -for ac_func in OPENSSL_init_ssl +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 do : - ac_fn_c_check_func "$LINENO" "OPENSSL_init_ssl" "ac_cv_func_OPENSSL_init_ssl" -if test "x$ac_cv_func_OPENSSL_init_ssl" = xyes; then : + 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 HAVE_OPENSSL_INIT_SSL 1 +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi @@ -17655,10 +18394,173 @@ #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 +#endif + +#ifdef HAVE_OPENSSL_RAND_H +#include +#endif + +#ifdef HAVE_OPENSSL_CONF_H +#include +#endif + +#ifdef HAVE_OPENSSL_ENGINE_H +#include +#endif +#include +#include + +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; @@ -17674,6 +18576,25 @@ ;; 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 wether gost also works # Check whether --enable-gost was given. @@ -17925,15 +18846,36 @@ fi use_dsa="no" -case "$enable_ecdsa" in - no) - ;; - *) +case "$enable_dsa" in + yes) # detect if DSA is supported, and turn it off if not. - ac_fn_c_check_func "$LINENO" "EVP_dss1" "ac_cv_func_EVP_dss1" -if test "x$ac_cv_func_EVP_dss1" = xyes; then : + 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 +#endif +#ifdef HAVE_OPENSSL_RAND_H +#include +#endif + +#ifdef HAVE_OPENSSL_CONF_H +#include +#endif + +#ifdef HAVE_OPENSSL_ENGINE_H +#include +#endif + +" +if eval test \"x\$"$as_ac_Type"\" = x"yes"; then : + + cat >>confdefs.h <<_ACEOF #define USE_DSA 1 _ACEOF @@ -17944,10 +18886,129 @@ 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 --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 + +" +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 + +" +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; @@ -18378,6 +19439,37 @@ 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 +#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 @@ -18442,8 +19534,72 @@ _ACEOF -# set static linking if requested +# 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 + +" +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 + +# set static linking for uninstalled libraries if requested + staticexe="" # Check whether --enable-static-exe was given. if test "${enable_static_exe+set}" = set; then : @@ -18455,10 +19611,34 @@ if test "$on_mingw" = yes; then staticexe="-all-static" # for static compile, include gdi32 and zlib here. - LIBS="$LIBS -lgdi32 -lz" + if echo $LIBS | grep 'lgdi32' >/dev/null; then + : + else + LIBS="$LIBS -lgdi32" + fi + LIBS="$LIBS -lz" 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 + LIBS="$LIBS -lz" + fi +fi + # set lock checking if requested # Check whether --enable-lock_checks was given. if test "${enable_lock_checks+set}" = set; then : @@ -18502,7 +19682,11 @@ $as_echo "#define USE_WINSOCK 1" >>confdefs.h USE_WINSOCK="1" - LIBS="$LIBS -lws2_32" + if echo $LIBS | grep 'lws2_32' >/dev/null; then + : + else + LIBS="$LIBS -lws2_32" + fi fi else @@ -18666,7 +19850,7 @@ WINDRES="$ac_cv_prog_WINDRES" fi - LIBS="$LIBS -liphlpapi" + 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" @@ -18844,6 +20028,75 @@ 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 +#ifdef HAVE_ENDIAN_H +# include +#endif +#ifdef HAVE_SYS_ENDIAN_H +# include +#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 +#ifdef HAVE_ENDIAN_H +# include +#endif +#ifdef HAVE_SYS_ENDIAN_H +# include +#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 : @@ -18900,7 +20153,7 @@ 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 +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 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" @@ -18959,39 +20212,77 @@ done -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sbrk" >&5 -$as_echo_n "checking for sbrk... " >&6; } -# catch the warning of deprecated sbrk -old_cflags="$CFLAGS" -CFLAGS="$CFLAGS -Werror" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ +# check if setreuid en setregid fail, on MacOSX10.4(darwin8). +if echo $target_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 +#endif -int main(void) { void* cur = sbrk(0); printf("%u\n", (unsigned)(size_t)((char*)cur - (char*)sbrk(0))); return 0; } +#ifdef HAVE_NETINET_TCP_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif + +#ifdef HAVE_WINSOCK2_H +#include +#endif + +#ifdef HAVE_WS2TCPIP_H +#include +#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 -if ac_fn_c_try_compile "$LINENO"; then : +ac_fn_c_check_decl "$LINENO" "inet_ntop" "ac_cv_have_decl_inet_ntop" " +$ac_includes_default +#ifdef HAVE_NETINET_IN_H +#include +#endif - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } +#ifdef HAVE_NETINET_TCP_H +#include +#endif -$as_echo "#define HAVE_SBRK 1" >>confdefs.h +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef HAVE_WINSOCK2_H +#include +#endif +#ifdef HAVE_WS2TCPIP_H +#include +#endif + +" +if test "x$ac_cv_have_decl_inet_ntop" = xyes; then : + ac_have_decl=1 else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } + ac_have_decl=0 fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -CFLAGS="$old_cflags" -# check if setreuid en setregid fail, on MacOSX10.4(darwin8). -if echo $build_os | grep darwin8 > /dev/null; then +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_INET_NTOP $ac_have_decl +_ACEOF -$as_echo "#define DARWIN_BROKEN_SETREUID 1" >>confdefs.h - -fi 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 @@ -19160,21 +20451,70 @@ 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" -ac_fn_c_check_func "$LINENO" "reallocarray" "ac_cv_func_reallocarray" -if test "x$ac_cv_func_reallocarray" = xyes; then : - $as_echo "#define HAVE_REALLOCARRAY 1" >>confdefs.h +{ $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 +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 - case " $LIBOBJS " in + + { $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" @@ -19207,12 +20547,6 @@ if test "$ac_cv_func_arc4random" = "no"; then case " $LIBOBJS " in - *" explicit_bzero.$ac_objext "* ) ;; - *) LIBOBJS="$LIBOBJS explicit_bzero.$ac_objext" - ;; -esac - - case " $LIBOBJS " in *" arc4_lock.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS arc4_lock.$ac_objext" ;; @@ -19236,8 +20570,8 @@ esac else - case `uname` in - Darwin) + case "$host" in + Darwin|*darwin*) case " $LIBOBJS " in *" getentropy_osx.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS getentropy_osx.$ac_objext" @@ -19245,7 +20579,7 @@ esac ;; - SunOS) + *solaris*|*sunos*|SunOS) case " $LIBOBJS " in *" getentropy_solaris.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS getentropy_solaris.$ac_objext" @@ -19349,8 +20683,16 @@ fi ;; - Linux|*) + *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" ;; @@ -19781,6 +21123,234 @@ 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 : @@ -19787,6 +21357,8 @@ 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) @@ -19798,6 +21370,80 @@ ;; 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"; then + found_libmnl="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; } + 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 + { $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. @@ -19850,10 +21496,19 @@ 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;} @@ -19929,7 +21584,12 @@ fi -LDFLAGS="$LATE_LDFLAGS $LDFLAGS" +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 @@ -19939,12 +21599,12 @@ -version=1.5.10 +version=1.10.1 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 contrib/libunbound.pc" +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" @@ -20057,6 +21717,10 @@ 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 @@ -20454,7 +22118,7 @@ # 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.5.10, which was +This file was extended by unbound $as_me 1.10.1, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -20514,13 +22178,13 @@ Configuration commands: $config_commands -Report bugs to ." +Report bugs to ." _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.5.10 +unbound config.status 1.10.1 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" @@ -20942,7 +22606,11 @@ "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;; --- contrib/unbound/configure.ac.orig +++ contrib/unbound/configure.ac @@ -6,19 +6,20 @@ 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],[5]) -m4_define([VERSION_MICRO],[10]) -AC_INIT(unbound, m4_defn([VERSION_MAJOR]).m4_defn([VERSION_MINOR]).m4_defn([VERSION_MICRO]), unbound-bugs@nlnetlabs.nl, unbound) +m4_define([VERSION_MINOR],[10]) +m4_define([VERSION_MICRO],[1]) +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=6 -LIBUNBOUND_REVISION=2 -LIBUNBOUND_AGE=4 +LIBUNBOUND_CURRENT=9 +LIBUNBOUND_REVISION=8 +LIBUNBOUND_AGE=1 # 1.0.0 had 0:12:0 # 1.0.1 had 0:13:0 # 1.0.2 had 0:14:0 @@ -67,6 +68,32 @@ # 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 # Current -- the number of the binary API that we're implementing # Revision -- which iteration of the implementation of the binary @@ -82,7 +109,7 @@ # Current and Age. Set Revision to 0, since this is the first # implementation of the new API. # -# Otherwise, we're changing the binary API and breaking bakward +# Otherwise, we're changing the binary API and breaking backward # compatibility with old binaries. Increment Current. Set Age to 0, # since we're backward compatible with no previous APIs. Set Revision # to 0 too. @@ -90,6 +117,10 @@ 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_AIX if test "$ac_cv_header_minix_config_h" = "yes"; then @@ -104,6 +135,11 @@ prefix="/usr/local" ;; esac +case "$exec_prefix" in + NONE) + exec_prefix="$prefix" + ;; +esac # are we on MinGW? if uname -s 2>&1 | grep MINGW32 >/dev/null; then on_mingw="yes" @@ -115,10 +151,16 @@ # # 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 (x86)\\Unbound\\service.conf" + ub_conf_file="C:\\Program Files\\Unbound\\service.conf" fi AC_ARG_WITH([conf_file], AC_HELP_STRING([--with-conf-file=path], @@ -188,7 +230,7 @@ if test $on_mingw = no; then UNBOUND_ROOTKEY_FILE="$UNBOUND_RUN_DIR/root.key" else - UNBOUND_ROOTKEY_FILE="C:\\Program Files (x86)\\Unbound\\root.key" + UNBOUND_ROOTKEY_FILE="C:\\Program Files\\Unbound\\root.key" fi ) AC_SUBST(UNBOUND_ROOTKEY_FILE) @@ -202,7 +244,7 @@ if test $on_mingw = no; then UNBOUND_ROOTCERT_FILE="$UNBOUND_RUN_DIR/icannbundle.pem" else - UNBOUND_ROOTCERT_FILE="C:\\Program Files (x86)\\Unbound\\icannbundle.pem" + UNBOUND_ROOTCERT_FILE="C:\\Program Files\\Unbound\\icannbundle.pem" fi ) AC_SUBST(UNBOUND_ROOTCERT_FILE) @@ -225,9 +267,11 @@ AC_C_CONST AC_LANG_C # allow user to override the -g -O2 flags. +default_cflags=no if test "x$CFLAGS" = "x" ; then ACX_CHECK_COMPILER_FLAG(g, [CFLAGS="$CFLAGS -g"]) ACX_CHECK_COMPILER_FLAG(O2, [CFLAGS="$CFLAGS -O2"]) +default_cflags=yes fi AC_PROG_CC ACX_DEPFLAG @@ -251,9 +295,14 @@ # nothing to do. ;; esac -ACX_CHECK_FLTO -ACX_CHECK_PIE -ACX_CHECK_RELRO_NOW +if test "$default_cflags" = "yes"; then + # only when CFLAGS was "" at the start, if the users wants to + # override we shouldn't add default cflags, because they wouldn't + # be able to turn off these options and set the CFLAGS wanted. + ACX_CHECK_FLTO + ACX_CHECK_PIE + ACX_CHECK_RELRO_NOW +fi AC_C_INLINE ACX_CHECK_FORMAT_ATTRIBUTE @@ -277,11 +326,36 @@ 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_TRY_COMPILE( +[ #include +__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 @@ -291,18 +365,39 @@ 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); fi + else AC_MSG_RESULT(no); + LEX=":" + fi ]) +AC_DEFUN([ACX_YYLEX_OPTION], [ + AC_MSG_CHECKING([for lex %option]) + if cat <&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 +if test "$LEX" != "" -a "$LEX" != ":"; then ACX_YYLEX_DESTROY +fi +if test "$LEX" != "" -a "$LEX" != ":"; then +ACX_YYLEX_OPTION +fi AC_PROG_YACC AC_CHECK_PROG(doxygen, doxygen, doxygen) AC_CHECK_TOOL(STRIP, strip) ACX_LIBTOOL_C_ONLY +PKG_PROG_PKG_CONFIG + # Checks for header files. -AC_CHECK_HEADERS([stdarg.h stdbool.h netinet/in.h netinet/tcp.h sys/param.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],,, [AC_INCLUDES_DEFAULT]) +AC_CHECK_HEADERS([stdarg.h stdbool.h netinet/in.h netinet/tcp.h sys/param.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],,, [AC_INCLUDES_DEFAULT]) # check for types. # Using own tests for int64* because autoconf builtin only give 32bit. @@ -339,6 +434,7 @@ # endif #endif ]) +AC_CHECK_SIZEOF(size_t) # add option to disable the evil rpath ACX_ARG_RPATH @@ -383,6 +479,17 @@ ACX_MKDIR_ONE_ARG AC_CHECK_FUNCS([strptime],[AC_CHECK_STRPTIME_WORKS],[AC_LIBOBJ([strptime])]) +# check if we can use SO_REUSEPORT +if echo "$host" | $GREP -i -e linux -e dragonfly >/dev/null; 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, AC_HELP_STRING([--enable-alloc-checks], [ enable to memory allocation statistics, for debug purposes ]), @@ -398,6 +505,10 @@ 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]) @@ -438,7 +549,9 @@ if test x_$withval != x_no; then AX_PTHREAD([ AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]) - LIBS="$PTHREAD_LIBS $LIBS" + if test -n "$PTHREAD_LIBS"; then + LIBS="$PTHREAD_LIBS $LIBS" + fi CFLAGS="$CFLAGS $PTHREAD_CFLAGS" CC="$PTHREAD_CC" ub_have_pthreads=yes @@ -499,6 +612,18 @@ fi # end of non-mingw check of thread libraries +# Check for SYSLOG_FACILITY +AC_ARG_WITH(syslog-facility, AC_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 PyUnbound AC_ARG_WITH(pyunbound, AC_HELP_STRING([--with-pyunbound], @@ -540,13 +665,30 @@ AC_SUBST(PY_MAJOR_VERSION) # Have Python AC_DEFINE(HAVE_PYTHON,1,[Define if you have Python libraries and header files.]) - LIBS="$PYTHON_LDFLAGS $LIBS" - CPPFLAGS="$CPPFLAGS $PYTHON_CPPFLAGS" + if test -n "$LIBS"; then + LIBS="$PYTHON_LDFLAGS $LIBS" + else + LIBS="$PYTHON_LDFLAGS" + fi + if test -n "$CPPFLAGS"; then + CPPFLAGS="$CPPFLAGS $PYTHON_CPPFLAGS" + else + CPPFLAGS="$PYTHON_CPPFLAGS" + 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_PROG_SWIG + AC_ARG_ENABLE(swig-version-check, AC_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_ERROR([failed to find swig tool, install it, or do not build Python module and PyUnbound]) @@ -620,6 +762,8 @@ fi LIBS="$LIBS -lnss3 -lnspr4" SSLLIB="" + PC_CRYPTO_DEPENDENCY="nss nspr" + AC_SUBST(PC_CRYPTO_DEPENDENCY) ] ) @@ -640,6 +784,8 @@ fi LIBS="$LIBS -lhogweed -lnettle -lgmp" SSLLIB="" + PC_CRYPTO_DEPENDENCY="hogweed nettle" + AC_SUBST(PC_CRYPTO_DEPENDENCY) ] ) @@ -649,6 +795,9 @@ ACX_LIB_SSL SSLLIB="-lssl" +PC_CRYPTO_DEPENDENCY="libcrypto libssl" +AC_SUBST(PC_CRYPTO_DEPENDENCY) + # check if -lcrypt32 is needed because CAPIENG needs that. (on windows) BAKLIBS="$LIBS" LIBS="-lssl $LIBS" @@ -668,17 +817,17 @@ 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,reallocarray]) + 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],,, [AC_INCLUDES_DEFAULT]) -AC_CHECK_FUNCS([OPENSSL_config EVP_sha1 EVP_sha256 EVP_sha512 FIPS_mode EVP_MD_CTX_new OpenSSL_add_all_digests OPENSSL_init_crypto EVP_cleanup ERR_load_crypto_strings CRYPTO_cleanup_all_ex_data ERR_free_strings RAND_cleanup]) +AC_CHECK_FUNCS([OPENSSL_config EVP_sha1 EVP_sha256 EVP_sha512 FIPS_mode EVP_MD_CTX_new OpenSSL_add_all_digests OPENSSL_init_crypto EVP_cleanup ERR_load_crypto_strings CRYPTO_cleanup_all_ex_data ERR_free_strings RAND_cleanup DSA_SIG_set0 EVP_dss1 EVP_DigestVerify SSL_CTX_set_tlsext_ticket_key_cb EVP_aes_256_cbc EVP_EncryptInit_ex HMAC_Init_ex CRYPTO_THREADID_set_callback]) # these check_funcs need -lssl BAKLIBS="$LIBS" LIBS="-lssl $LIBS" -AC_CHECK_FUNCS([OPENSSL_init_ssl]) +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]) LIBS="$BAKLIBS" AC_CHECK_DECLS([SSL_COMP_get_compression_methods,sk_SSL_COMP_pop_free,SSL_CTX_set_ecdh_auto], [], [], [ @@ -701,10 +850,69 @@ #include #include ]) + +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 +#endif + +#ifdef HAVE_OPENSSL_RAND_H +#include +#endif + +#ifdef HAVE_OPENSSL_CONF_H +#include +#endif + +#ifdef HAVE_OPENSSL_ENGINE_H +#include +#endif +#include +#include +], [ + 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], AC_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, AC_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, AC_HELP_STRING([--disable-sha2], [Disable SHA256 and SHA512 RRSIG support])) case "$enable_sha2" in no) @@ -714,6 +922,19 @@ ;; esac +AC_ARG_ENABLE(subnet, AC_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 wether gost also works AC_DEFUN([AC_CHECK_GOST_WORKS], [AC_REQUIRE([AC_PROG_CC]) @@ -864,19 +1085,87 @@ AC_ARG_ENABLE(dsa, AC_HELP_STRING([--disable-dsa], [Disable DSA support])) use_dsa="no" -case "$enable_ecdsa" in - no) - ;; - *) +case "$enable_dsa" in + yes) # detect if DSA is supported, and turn it off if not. - AC_CHECK_FUNC(EVP_dss1, [ + 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 +#endif + +#ifdef HAVE_OPENSSL_RAND_H +#include +#endif + +#ifdef HAVE_OPENSSL_CONF_H +#include +#endif + +#ifdef HAVE_OPENSSL_ENGINE_H +#include +#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_ENABLE(ed25519, AC_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 + ]) + 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, AC_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 + ]) + 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, AC_HELP_STRING([--enable-event-api], [Enable (experimental) pluggable event base libunbound API installed to unbound-event.h])) case "$enable_event_api" in yes) @@ -1000,6 +1289,16 @@ 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 +#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 @@ -1033,11 +1332,44 @@ #include ]) -# set static linking if requested +# hiredis (redis C client for cachedb) +AC_ARG_WITH(libhiredis, AC_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_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 + ]) +fi + +# set static linking for uninstalled libraries if requested AC_SUBST(staticexe) staticexe="" AC_ARG_ENABLE(static-exe, AC_HELP_STRING([--enable-static-exe], - [ enable to compile executables statically against (event) libs, for debug purposes ]), + [ enable to compile executables statically against (event) uninstalled libs, for debug purposes ]), , ) if test x_$enable_static_exe = x_yes; then staticexe="-static" @@ -1044,10 +1376,32 @@ if test "$on_mingw" = yes; then staticexe="-all-static" # for static compile, include gdi32 and zlib here. - LIBS="$LIBS -lgdi32 -lz" + if echo $LIBS | grep 'lgdi32' >/dev/null; then + : + else + LIBS="$LIBS -lgdi32" + fi + LIBS="$LIBS -lz" fi fi +# set full static linking if requested +AC_ARG_ENABLE(fully-static, AC_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 + LIBS="$LIBS -lz" + fi +fi + # set lock checking if requested AC_ARG_ENABLE(lock_checks, AC_HELP_STRING([--enable-lock-checks], [ enable to check lock and unlock calls, for debug purposes ]), @@ -1065,7 +1419,7 @@ #include ]) AC_CHECK_TOOL(WINDRES, windres) - LIBS="$LIBS -liphlpapi" + 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" @@ -1137,28 +1491,66 @@ #include #endif ]) + +AC_MSG_CHECKING([for htobe64]) +AC_LINK_IFELSE([AC_LANG_PROGRAM([ +#include +#ifdef HAVE_ENDIAN_H +# include +#endif +#ifdef HAVE_SYS_ENDIAN_H +# include +#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 +#ifdef HAVE_ENDIAN_H +# include +#endif +#ifdef HAVE_SYS_ENDIAN_H +# include +#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]) +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]) AC_CHECK_FUNCS([setresuid],,[AC_CHECK_FUNCS([setreuid])]) AC_CHECK_FUNCS([setresgid],,[AC_CHECK_FUNCS([setregid])]) -AC_MSG_CHECKING([for sbrk]) -# catch the warning of deprecated sbrk -old_cflags="$CFLAGS" -CFLAGS="$CFLAGS -Werror" -AC_COMPILE_IFELSE([AC_LANG_SOURCE(AC_INCLUDES_DEFAULT -[[ -int main(void) { void* cur = sbrk(0); printf("%u\n", (unsigned)(size_t)((char*)cur - (char*)sbrk(0))); return 0; } -]])], [ - AC_MSG_RESULT(yes) - AC_DEFINE(HAVE_SBRK, 1, [define if you have the sbrk() call]) - ], [AC_MSG_RESULT(no)]) -CFLAGS="$old_cflags" - # check if setreuid en setregid fail, on MacOSX10.4(darwin8). -if echo $build_os | grep darwin8 > /dev/null; then +if echo $target_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 +#endif + +#ifdef HAVE_NETINET_TCP_H +#include +#endif + +#ifdef HAVE_ARPA_INET_H +#include +#endif + +#ifdef HAVE_WINSOCK2_H +#include +#endif + +#ifdef HAVE_WS2TCPIP_H +#include +#endif +]) AC_REPLACE_FUNCS(inet_aton) AC_REPLACE_FUNCS(inet_pton) AC_REPLACE_FUNCS(inet_ntop) @@ -1182,25 +1574,43 @@ 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_REPLACE_FUNCS(reallocarray) +AC_MSG_CHECKING([for reallocarray]) +AC_LINK_IFELSE([AC_LANG_SOURCE(AC_INCLUDES_DEFAULT +[[ +#ifndef _OPENBSD_SOURCE +#define _OPENBSD_SOURCE 1 +#endif +#include +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(explicit_bzero) AC_LIBOBJ(arc4_lock) AC_CHECK_FUNCS([getentropy],,[ if test "$USE_WINSOCK" = 1; then AC_LIBOBJ(getentropy_win) else - case `uname` in - Darwin) + case "$host" in + Darwin|*darwin*) AC_LIBOBJ(getentropy_osx) ;; - SunOS) + *solaris*|*sunos*|SunOS) AC_LIBOBJ(getentropy_solaris) AC_CHECK_HEADERS([sys/sha2.h],, [ AC_CHECK_FUNCS([SHA512_Update],,[ @@ -1213,7 +1623,10 @@ fi AC_SEARCH_LIBS([clock_gettime], [rt]) ;; - Linux|*) + *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]) @@ -1284,8 +1697,23 @@ ] ) +# 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, AC_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]) @@ -1295,6 +1723,62 @@ ;; esac +# check for ipsecmod if requested +AC_ARG_ENABLE(ipsecmod, AC_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, AC_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, AC_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"; then + found_libmnl="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) + LIBS="$LIBS -lmnl" + break; + fi + done + if test x_$found_libmnl != x_yes; then + AC_ERROR([Could not find libmnl, libmnl.h]) + fi + ;; + no|*) + # nothing + ;; +esac + 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). @@ -1341,11 +1825,25 @@ INSTALLTARGET="install-lib" fi ]) +if test $ALLTARGET = "alltargets"; then + if test $USE_NSS = "yes"; then + AC_ERROR([--with-nss can only be used in combination with --with-libunbound-only.]) + fi + if test $USE_NETTLE = "yes"; then + AC_ERROR([--with-nettle can only be used in combination with --with-libunbound-only.]) + fi +fi + AC_SUBST(ALLTARGET) AC_SUBST(INSTALLTARGET) ACX_STRIP_EXT_FLAGS -LDFLAGS="$LATE_LDFLAGS $LDFLAGS" +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.]) @@ -1355,8 +1853,14 @@ dnl includes [ +#ifndef _OPENBSD_SOURCE +#define _OPENBSD_SOURCE 1 +#endif + #ifndef UNBOUND_DEBUG +# ifndef NDEBUG # define NDEBUG +# endif #endif /** Use small-ldns codebase */ @@ -1471,6 +1975,19 @@ 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; @@ -1477,6 +1994,15 @@ 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 +#include +#endif + #ifdef HAVE_LIBRESSL # if !HAVE_DECL_STRLCPY size_t strlcpy(char *dst, const char *src, size_t siz); @@ -1490,17 +2016,14 @@ # if !HAVE_DECL_ARC4RANDOM_UNIFORM && defined(HAVE_ARC4RANDOM_UNIFORM) uint32_t arc4random_uniform(uint32_t upper_bound); # endif -# if !HAVE_DECL_REALLOCARRAY -void *reallocarray(void *ptr, size_t nmemb, size_t size); -# endif #endif /* HAVE_LIBRESSL */ #ifndef HAVE_ARC4RANDOM -void explicit_bzero(void* buf, size_t len); 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); @@ -1566,6 +2089,8 @@ /** 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 unbound control traffic, registered port with IANA, ub-dns-control 8953/tcp unbound dns nameserver control */ #define UNBOUND_CONTROL_PORT 8953 @@ -1579,6 +2104,6 @@ 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 contrib/libunbound.pc]) +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_HEADER([config.h]) AC_OUTPUT --- contrib/unbound/contrib/README.orig +++ contrib/unbound/contrib/README @@ -27,5 +27,29 @@ works like the BIND feature (removes AAAA records unless AAAA-only domain). Useful for certain 'broken IPv6 default route' scenarios. Patch from Stephane Lapie for ASAHI Net. -* unbound_smf22.tar.gz: Solaris SMF installation/removal scripts. +* unbound_smf23.tar.gz: Solaris SMF installation/removal scripts. Contributed by Yuri Voinov. +* unbound.socket and unbound.service: systemd files for unbound, install them + in /usr/lib/systemd/system. Contributed by Sami Kerola and Pavel Odintsov. +* unbound_portable.service.in: systemd file for use unbound as portable service, + see comments in the file. Contributed by Frzk. +* redirect-bogus.patch: Return configured address for bogus A and AAAA answers, + instead of SERVFAIL. Contributed by SIDN. +* fastrpz.patch: fastrpz support from Farsight Security. +* libunbound.so.conf: ltrace.conf file, see ltrace.conf(5), for libunbound. +* unbound-querycachedb.py: utility to show data stored in cachedb backend + for a particular query name and type. It requires dnspython and (for + redis backend) redis Python modules. +* unbound-fuzzme.patch: adds unbound-fuzzme program that parses a packet from + stdin. Used with fuzzers, patch from Jacob Hoffman-Andrews. +* 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. +* 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. +* drop2rpz: perl script that converts the Spamhaus DROP-List in RPZ-Format, + contributed by Andreas Schulze. --- contrib/unbound/contrib/aaaa-filter-iterator.patch.orig +++ contrib/unbound/contrib/aaaa-filter-iterator.patch @@ -1,10 +1,10 @@ Index: trunk/doc/unbound.conf.5.in =================================================================== ---- trunk/doc/unbound.conf.5.in (revision 3587) +--- trunk/doc/unbound.conf.5.in (revision 4357) +++ trunk/doc/unbound.conf.5.in (working copy) -@@ -593,6 +593,13 @@ - possible. Best effort approach, full QNAME and original QTYPE will be sent when - upstream replies with a RCODE other than NOERROR. Default is off. +@@ -701,6 +701,13 @@ + 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 off. .TP +.B aaaa\-filter: \fI +Activate behavior similar to BIND's AAAA-filter. @@ -18,7 +18,7 @@ on your private network, and are not allowed to be returned for Index: trunk/iterator/iter_scrub.c =================================================================== ---- trunk/iterator/iter_scrub.c (revision 3587) +--- trunk/iterator/iter_scrub.c (revision 4357) +++ trunk/iterator/iter_scrub.c (working copy) @@ -617,6 +617,32 @@ } @@ -75,10 +75,11 @@ /* 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 -@@ -681,6 +715,24 @@ +@@ -680,6 +714,24 @@ + prev = NULL; rrset = msg->rrset_first; while(rrset) { - ++ + /* ASN: For AAAA records only... */ + if((ie->aaaa_filter) && (rrset->type == LDNS_RR_TYPE_AAAA)) { + /* ASN: If this is not a AAAA query, then remove AAAA @@ -96,13 +97,12 @@ + LDNS_RR_TYPE_AAAA, qinfo->qclass); + } + /* ASN: End of added code */ -+ + /* remove private addresses */ if( (rrset->type == LDNS_RR_TYPE_A || - rrset->type == LDNS_RR_TYPE_AAAA)) { Index: trunk/iterator/iter_utils.c =================================================================== ---- trunk/iterator/iter_utils.c (revision 3587) +--- trunk/iterator/iter_utils.c (revision 4357) +++ trunk/iterator/iter_utils.c (working copy) @@ -175,6 +175,7 @@ } @@ -114,9 +114,9 @@ Index: trunk/iterator/iterator.c =================================================================== ---- trunk/iterator/iterator.c (revision 3587) +--- trunk/iterator/iterator.c (revision 4357) +++ trunk/iterator/iterator.c (working copy) -@@ -1776,6 +1776,53 @@ +@@ -1847,6 +1847,53 @@ return 0; } @@ -170,7 +170,7 @@ /** * This is the request event state where the request will be sent to one of -@@ -1823,6 +1870,13 @@ +@@ -1894,6 +1941,13 @@ return error_response(qstate, id, LDNS_RCODE_SERVFAIL); } @@ -184,7 +184,7 @@ /* Make sure we have a delegation point, otherwise priming failed * or another failure occurred */ if(!iq->dp) { -@@ -2922,6 +2976,61 @@ +@@ -3095,6 +3149,61 @@ return 0; } @@ -244,9 +244,9 @@ +/* ASN: End of added code */ + /* - * Return priming query results to interestes super querystates. + * Return priming query results to interested super querystates. * -@@ -2941,6 +3050,9 @@ +@@ -3114,6 +3223,9 @@ else if(super->qinfo.qtype == LDNS_RR_TYPE_DS && ((struct iter_qstate*) super->minfo[id])->state == DSNS_FIND_STATE) processDSNSResponse(qstate, id, super); @@ -256,7 +256,7 @@ else if(qstate->return_rcode != LDNS_RCODE_NOERROR) error_supers(qstate, id, super); else if(qstate->is_priming) -@@ -2978,6 +3090,9 @@ +@@ -3151,6 +3263,9 @@ case INIT_REQUEST_3_STATE: cont = processInitRequest3(qstate, iq, id); break; @@ -266,7 +266,7 @@ case QUERYTARGETS_STATE: cont = processQueryTargets(qstate, iq, ie, id); break; -@@ -3270,6 +3385,8 @@ +@@ -3460,6 +3575,8 @@ return "INIT REQUEST STATE (stage 2)"; case INIT_REQUEST_3_STATE: return "INIT REQUEST STATE (stage 3)"; @@ -275,7 +275,7 @@ case QUERYTARGETS_STATE : return "QUERY TARGETS STATE"; case PRIME_RESP_STATE : -@@ -3294,6 +3411,7 @@ +@@ -3484,6 +3601,7 @@ case INIT_REQUEST_STATE : case INIT_REQUEST_2_STATE : case INIT_REQUEST_3_STATE : @@ -285,9 +285,9 @@ return 0; Index: trunk/iterator/iterator.h =================================================================== ---- trunk/iterator/iterator.h (revision 3587) +--- trunk/iterator/iterator.h (revision 4357) +++ trunk/iterator/iterator.h (working copy) -@@ -113,6 +113,9 @@ +@@ -130,6 +130,9 @@ */ int* target_fetch_policy; @@ -294,10 +294,10 @@ + /** ASN: AAAA-filter flag */ + int aaaa_filter; + - /** ip6.arpa dname in wireformat, used for qname-minimisation */ - uint8_t* ip6arpa_dname; - }; -@@ -163,6 +166,14 @@ + /** lock on ratelimit counter */ + lock_basic_type queries_ratelimit_lock; + /** number of queries that have been ratelimited */ +@@ -182,6 +185,14 @@ INIT_REQUEST_3_STATE, /** @@ -311,11 +311,12 @@ + /** * Each time a delegation point changes for a given query or a * query times out and/or wakes up, this state is (re)visited. - * This state is reponsible for iterating through a list of -@@ -346,6 +357,13 @@ + * This state is responsible for iterating through a list of +@@ -364,6 +375,13 @@ + * be used when creating the state. A higher one will be attempted. */ int refetch_glue; - ++ + /** + * ASN: This is a flag that, if true, means that this query is + * for fetching A records to populate cache and determine if we must @@ -322,15 +323,14 @@ + * return AAAA records or not. + */ + int fetch_a_for_aaaa; -+ + /** list of pending queries to authoritative servers. */ struct outbound_list outlist; - Index: trunk/pythonmod/interface.i =================================================================== ---- trunk/pythonmod/interface.i (revision 3587) +--- trunk/pythonmod/interface.i (revision 4357) +++ trunk/pythonmod/interface.i (working copy) -@@ -632,6 +632,7 @@ +@@ -851,6 +851,7 @@ int harden_dnssec_stripped; int harden_referral_path; int use_caps_bits_for_id; @@ -340,9 +340,9 @@ size_t unwanted_threshold; Index: trunk/util/config_file.c =================================================================== ---- trunk/util/config_file.c (revision 3587) +--- trunk/util/config_file.c (revision 4357) +++ trunk/util/config_file.c (working copy) -@@ -176,6 +176,7 @@ +@@ -195,6 +195,7 @@ cfg->harden_referral_path = 0; cfg->harden_algo_downgrade = 0; cfg->use_caps_bits_for_id = 0; @@ -352,9 +352,9 @@ cfg->private_domain = NULL; Index: trunk/util/config_file.h =================================================================== ---- trunk/util/config_file.h (revision 3587) +--- trunk/util/config_file.h (revision 4357) +++ trunk/util/config_file.h (working copy) -@@ -179,6 +179,8 @@ +@@ -209,6 +209,8 @@ int harden_algo_downgrade; /** use 0x20 bits in query as random ID bits */ int use_caps_bits_for_id; @@ -365,9 +365,9 @@ /** strip away these private addrs from answers, no DNS Rebinding */ Index: trunk/util/configlexer.lex =================================================================== ---- trunk/util/configlexer.lex (revision 3587) +--- trunk/util/configlexer.lex (revision 4357) +++ trunk/util/configlexer.lex (working copy) -@@ -267,6 +267,7 @@ +@@ -279,6 +279,7 @@ use-caps-for-id{COLON} { YDVAR(1, VAR_USE_CAPS_FOR_ID) } caps-whitelist{COLON} { YDVAR(1, VAR_CAPS_WHITELIST) } unwanted-reply-threshold{COLON} { YDVAR(1, VAR_UNWANTED_REPLY_THRESHOLD) } @@ -377,9 +377,9 @@ prefetch-key{COLON} { YDVAR(1, VAR_PREFETCH_KEY) } Index: trunk/util/configparser.y =================================================================== ---- trunk/util/configparser.y (revision 3587) +--- trunk/util/configparser.y (revision 4357) +++ trunk/util/configparser.y (working copy) -@@ -92,6 +92,7 @@ +@@ -95,6 +95,7 @@ %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 @@ -387,7 +387,7 @@ %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 -@@ -169,6 +170,7 @@ +@@ -203,6 +204,7 @@ server_dlv_anchor_file | server_dlv_anchor | server_neg_cache_size | server_harden_referral_path | server_private_address | server_private_domain | server_extended_statistics | @@ -395,10 +395,12 @@ server_local_data_ptr | server_jostle_timeout | server_unwanted_reply_threshold | server_log_time_ascii | server_domain_insecure | server_val_sig_skew_min | -@@ -893,6 +895,15 @@ +@@ -1183,6 +1185,15 @@ + OUTYY(("P(server_caps_whitelist:%s)\n", $2)); + if(!cfg_strlist_insert(&cfg_parser->cfg->caps_whitelist, $2)) yyerror("out of memory"); - } - ; ++ } ++ ; +server_aaaa_filter: VAR_AAAA_FILTER STRING_ARG + { + OUTYY(("P(server_aaaa_filter:%s)\n", $2)); @@ -406,8 +408,6 @@ + yyerror("expected yes or no."); + else cfg_parser->cfg->aaaa_filter = (strcmp($2, "yes")==0); + free($2); -+ } -+ ; + } + ; server_private_address: VAR_PRIVATE_ADDRESS STRING_ARG - { - OUTYY(("P(server_private_address:%s)\n", $2)); --- contrib/unbound/contrib/create_unbound_ad_servers.sh.orig +++ contrib/unbound/contrib/create_unbound_ad_servers.sh @@ -9,12 +9,13 @@ # Variables dst_dir="/etc/opt/csw/unbound" work_dir="/tmp" -list_addr="http://pgl.yoyo.org/adservers/serverlist.php?hostformat=nohtml&showintro=1&startdate%5Bday%5D=&startdate%5Bmonth%5D=&startdate%5Byear%5D=" +list_addr="https://pgl.yoyo.org/adservers/serverlist.php?hostformat=nohtml&showintro=1&startdate%5Bday%5D=&startdate%5Bmonth%5D=&startdate%5Byear%5D=" # OS commands CAT=`which cat` ECHO=`which echo` WGET=`which wget` +TR=`which tr` # Check Wget installed if [ ! -f $WGET ]; then @@ -22,8 +23,10 @@ exit 1 fi +# remove special characters with tr to protect unbound.conf $WGET -O $work_dir/yoyo_ad_servers "$list_addr" && \ $CAT $work_dir/yoyo_ad_servers | \ +$TR -d '";$\\' | \ while read line ; \ do \ $ECHO "local-zone: \"$line\" redirect" ;\ @@ -36,4 +39,4 @@ # the unbound_ad_servers file: # # include: $dst_dir/unbound_ad_servers -# \ No newline at end of file +# --- contrib/unbound/contrib/drop-tld.diff.orig +++ contrib/unbound/contrib/drop-tld.diff @@ -0,0 +1,82 @@ +diff --git a/daemon/worker.c b/daemon/worker.c +index 263fcdd..f787b70 100644 +--- a/daemon/worker.c ++++ b/daemon/worker.c +@@ -1213,6 +1213,15 @@ worker_handle_request(struct comm_point* c, void* arg, int error, + addr_to_str(&repinfo->addr, repinfo->addrlen, ip, sizeof(ip)); + log_query_in(ip, qinfo.qname, qinfo.qtype, qinfo.qclass); + } ++ ++ if(worker->env.cfg->drop_tld) { ++ int lab = dname_count_labels(qinfo.qname); ++ if (lab == 2) { ++ comm_point_drop_reply(repinfo); ++ verbose(VERB_ALGO, "Dropping one label query."); ++ return 0; ++ } ++ } + if(qinfo.qtype == LDNS_RR_TYPE_AXFR || + qinfo.qtype == LDNS_RR_TYPE_IXFR) { + verbose(VERB_ALGO, "worker request: refused zone transfer."); +diff --git a/util/config_file.h b/util/config_file.h +index b3ef930..2791541 100644 +--- a/util/config_file.h ++++ b/util/config_file.h +@@ -274,6 +274,8 @@ struct config_file { + int prefetch_key; + /** deny queries of type ANY with an empty answer */ + int deny_any; ++ /** Drop TLD queries from clients **/ ++ int drop_tld; + + /** chrootdir, if not "" or chroot will be done */ + char* chrootdir; +diff --git a/util/configlexer.lex b/util/configlexer.lex +index a86ddf5..9bbedbb 100644 +--- a/util/configlexer.lex ++++ b/util/configlexer.lex +@@ -299,6 +299,7 @@ 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) } ++drop-tld{COLON} { YDVAR(1, VAR_DROP_TLD) } + stub-zone{COLON} { YDVAR(0, VAR_STUB_ZONE) } + name{COLON} { YDVAR(1, VAR_NAME) } + stub-addr{COLON} { YDVAR(1, VAR_STUB_ADDR) } +diff --git a/util/configparser.y b/util/configparser.y +index 10227a2..567d68e 100644 +--- a/util/configparser.y ++++ b/util/configparser.y +@@ -164,6 +164,7 @@ extern struct config_parser_state* cfg_parser; + %token VAR_FAST_SERVER_PERMIL VAR_FAST_SERVER_NUM + %token VAR_ALLOW_NOTIFY VAR_TLS_WIN_CERT VAR_TCP_CONNECTION_LIMIT + %token VAR_FORWARD_NO_CACHE VAR_STUB_NO_CACHE VAR_LOG_SERVFAIL VAR_DENY_ANY ++%token VAR_DROP_TLD + %token VAR_UNKNOWN_SERVER_TIME_LIMIT VAR_LOG_TAG_QUERYREPLY + %token VAR_STREAM_WAIT_SIZE VAR_TLS_CIPHERS VAR_TLS_CIPHERSUITES + %token VAR_TLS_SESSION_TICKET_KEYS +@@ -266,6 +267,7 @@ content_server: server_num_threads | server_verbosity | server_port | + 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_drop_tld | + server_unknown_server_time_limit | server_log_tag_queryreply | + server_stream_wait_size | server_tls_ciphers | + server_tls_ciphersuites | server_tls_session_ticket_keys +@@ -1466,6 +1468,16 @@ server_deny_any: VAR_DENY_ANY STRING_ARG + free($2); + } + ; ++ ++server_drop_tld: VAR_DROP_TLD STRING_ARG ++ { ++ OUTYY(("P(server_drop_tld:%s)\n", $2)); ++ if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) ++ yyerror("expected yes or no."); ++ else cfg_parser->cfg->drop_tld = (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)); --- contrib/unbound/contrib/drop2rpz.orig +++ contrib/unbound/contrib/drop2rpz @@ -0,0 +1,39 @@ +#!/usr/bin/perl + +# usage: curl --silent https://www.spamhaus.org/drop/drop.txt | $0 > /path/to/spamhaus-drop.rpz.local +# +# unbound.conf: +# rpz: +# name: "spamhaus-drop.rpz.local." +# zonefile: "/path/tp/spamhaus-drop.rpz.local" +# rpz-log: yes +# rpz-log-name: "spamhaus-drop" +# + +use strict; +use vars qw{$o1 $o2 $o3 $o4 $m}; + +# trailing dots required +my $origin = 'drop.spamhaus.org.rpz.local.'; +my $mname = 'localhost.'; +my $rname = 'root.localhost.'; +my $ns = $mname; + +my $rpz_action = '.'; # return NXDOMAIN +#my $rpz_action = '*.'; # return NODATA +#my $rpz_action = 'rpz-drop.'; # drop the query + +print "$origin SOA $mname $rname 1 43200 7200 2419200 3600\n"; +print "$origin NS $ns\n"; +while(<>) { + if(($o1, $o2, $o3, $o4, $m) = m{(\d+)\.(\d+)\.(\d+)\.(\d+)/(\d+)}) { + print "$m.$o4.$o3.$o2.$o1.rpz-ip.$origin CNAME $rpz_action\n"; + } else { + print "$_"; + } +} + +# add a testpoint: ask for "dns.google" +# print "32.8.8.8.8.rpz-ip.$origin CNAME $rpz_action\n"; + +exit; --- contrib/unbound/contrib/fastrpz.patch.orig +++ contrib/unbound/contrib/fastrpz.patch @@ -0,0 +1,3504 @@ +Description: based on the included patch contrib/fastrpz.patch +Author: fastrpz@farsightsecurity.com +--- +diff --git a/Makefile.in b/Makefile.in +index a20058cc..495779cc 100644 +--- a/Makefile.in ++++ b/Makefile.in +@@ -23,6 +23,8 @@ CHECKLOCK_SRC=testcode/checklocks.c + CHECKLOCK_OBJ=@CHECKLOCK_OBJ@ + DNSTAP_SRC=@DNSTAP_SRC@ + DNSTAP_OBJ=@DNSTAP_OBJ@ ++FASTRPZ_SRC=@FASTRPZ_SRC@ ++FASTRPZ_OBJ=@FASTRPZ_OBJ@ + DNSCRYPT_SRC=@DNSCRYPT_SRC@ + DNSCRYPT_OBJ=@DNSCRYPT_OBJ@ + WITH_PYTHONMODULE=@WITH_PYTHONMODULE@ +@@ -127,7 +129,7 @@ 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/cachedb.c cachedb/redis.c respip/respip.c $(CHECKLOCK_SRC) \ +-$(DNSTAP_SRC) $(DNSCRYPT_SRC) $(IPSECMOD_SRC) $(IPSET_SRC) ++$(DNSTAP_SRC) $(FASTRPZ_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 \ +@@ -140,7 +142,7 @@ autotrust.lo val_anchor.lo rpz.lo \ + validator.lo val_kcache.lo val_kentry.lo val_neg.lo val_nsec3.lo val_nsec.lo \ + val_secalgo.lo val_sigcrypt.lo val_utils.lo dns64.lo cachedb.lo redis.lo authzone.lo \ + $(SUBNET_OBJ) $(PYTHONMOD_OBJ) $(CHECKLOCK_OBJ) $(DNSTAP_OBJ) $(DNSCRYPT_OBJ) \ +-$(IPSECMOD_OBJ) $(IPSET_OBJ) respip.lo ++$(FASTRPZ_OBJ) $(IPSECMOD_OBJ) $(IPSET_OBJ) respip.lo + COMMON_OBJ_WITHOUT_UB_EVENT=$(COMMON_OBJ_WITHOUT_NETCALL) netevent.lo listen_dnsport.lo \ + outside_network.lo + COMMON_OBJ=$(COMMON_OBJ_WITHOUT_UB_EVENT) ub_event.lo +@@ -410,6 +412,11 @@ dnscrypt.lo dnscrypt.o: $(srcdir)/dnscrypt/dnscrypt.c config.h \ + $(srcdir)/util/config_file.h $(srcdir)/util/log.h \ + $(srcdir)/util/netevent.h + ++# fastrpz ++rpz.lo rpz.o: $(srcdir)/fastrpz/rpz.c config.h fastrpz/rpz.h fastrpz/librpz.h \ ++ $(srcdir)/util/config_file.h $(srcdir)/daemon/daemon.h \ ++ $(srcdir)/util/log.h ++ + # Python Module + pythonmod.lo pythonmod.o: $(srcdir)/pythonmod/pythonmod.c config.h \ + pythonmod/interface.h \ +diff --git a/config.h.in b/config.h.in +index 78d47fed..e33073e4 100644 +--- a/config.h.in ++++ b/config.h.in +@@ -1345,4 +1345,11 @@ void *unbound_stat_realloc_log(void *ptr, size_t size, const char* file, + /** the version of unbound-control that this software implements */ + #define UNBOUND_CONTROL_VERSION 1 + +- ++/* have __attribute__s used in librpz.h */ ++#undef LIBRPZ_HAVE_ATTR ++/** fastrpz librpz.so */ ++#undef FASTRPZ_LIBRPZ_PATH ++/** 0=no fastrpz 1=static link 2=dlopen() */ ++#undef FASTRPZ_LIB_OPEN ++/** turn on fastrpz response policy zones */ ++#undef ENABLE_FASTRPZ +diff --git a/configure.ac b/configure.ac +index 2b91dd3c..e6063d17 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -6,6 +6,7 @@ sinclude(ax_pthread.m4) + sinclude(acx_python.m4) + sinclude(ac_pkg_swig.m4) + sinclude(dnstap/dnstap.m4) ++sinclude(fastrpz/rpz.m4) + sinclude(dnscrypt/dnscrypt.m4) + + # must be numbers. ac_defun because of later processing +@@ -1778,6 +1779,9 @@ case "$enable_ipset" in + ;; + esac + ++# check for Fastrpz with fastrpz/rpz.m4 ++ck_FASTRPZ ++ + 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). +diff --git a/daemon/daemon.c b/daemon/daemon.c +index 8b0fc348..7ffb9221 100644 +--- a/daemon/daemon.c ++++ b/daemon/daemon.c +@@ -91,6 +91,9 @@ + #include "sldns/keyraw.h" + #include "respip/respip.h" + #include ++#ifdef ENABLE_FASTRPZ ++#include "fastrpz/rpz.h" ++#endif + + #ifdef HAVE_SYSTEMD + #include +@@ -458,6 +461,14 @@ daemon_create_workers(struct daemon* daemon) + dt_apply_cfg(daemon->dtenv, daemon->cfg); + #else + fatal_exit("dnstap enabled in config but not built with dnstap support"); ++#endif ++ } ++ if(daemon->cfg->rpz_enable) { ++#ifdef ENABLE_FASTRPZ ++ rpz_init(&daemon->rpz_clist, &daemon->rpz_client, daemon->cfg); ++#else ++ fatal_exit("fastrpz enabled in config" ++ " but not built with fastrpz"); + #endif + } + for(i=0; inum; i++) { +@@ -731,6 +742,9 @@ daemon_cleanup(struct daemon* daemon) + #ifdef USE_DNSCRYPT + dnsc_delete(daemon->dnscenv); + daemon->dnscenv = NULL; ++#endif ++#ifdef ENABLE_FASTRPZ ++ rpz_delete(&daemon->rpz_clist, &daemon->rpz_client); + #endif + daemon->cfg = NULL; + } +diff --git a/daemon/daemon.h b/daemon/daemon.h +index 3effbafb..4d4c34da 100644 +--- a/daemon/daemon.h ++++ b/daemon/daemon.h +@@ -138,6 +138,11 @@ struct daemon { + /** the dnscrypt environment */ + struct dnsc_env* dnscenv; + #endif ++#ifdef ENABLE_FASTRPZ ++ /** global opaque rpz handles */ ++ struct librpz_clist *rpz_clist; ++ struct librpz_client *rpz_client; ++#endif + }; + + /** +diff --git a/daemon/worker.c b/daemon/worker.c +index eb7fdf2f..1982228d 100644 +--- a/daemon/worker.c ++++ b/daemon/worker.c +@@ -76,6 +76,9 @@ + #include "libunbound/context.h" + #include "libunbound/libworker.h" + #include "sldns/sbuffer.h" ++#ifdef ENABLE_FASTRPZ ++#include "fastrpz/rpz.h" ++#endif + #include "sldns/wire2str.h" + #include "util/shm_side/shm_main.h" + #include "dnscrypt/dnscrypt.h" +@@ -534,8 +537,27 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo, + /* not secure */ + secure = 0; + break; ++#ifdef ENABLE_FASTRPZ ++ case sec_status_rpz_rewritten: ++ case sec_status_rpz_drop: ++ fatal_exit("impossible cached RPZ sec_status"); ++ break; ++#endif + } + } ++#ifdef ENABLE_FASTRPZ ++ if(repinfo->rpz) { ++ /* Scan the cached answer for RPZ hits. ++ * ret=1 use cache entry ++ * ret=-1 rewritten response already sent or dropped ++ * ret=0 deny a cached entry exists ++ */ ++ int ret = rpz_worker_cache(worker, msg->rep, qinfo, ++ id, flags, edns, repinfo); ++ if(ret != 1) ++ return ret; ++ } ++#endif + /* return this delegation from the cache */ + edns_bak = *edns; + edns->edns_version = EDNS_ADVERTISED_VERSION; +@@ -710,6 +732,23 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo, + *is_secure_answer = 0; + } + } else *is_secure_answer = 0; ++#ifdef ENABLE_FASTRPZ ++ if(repinfo->rpz) { ++ /* Scan the cached answer for RPZ hits. ++ * ret=1 use cache entry ++ * ret=-1 rewritten response already sent or dropped ++ * ret=0 deny a cached entry exists ++ */ ++ int ret = rpz_worker_cache(worker, rep, qinfo, id, flags, edns, ++ repinfo); ++ if(ret != 1) { ++ rrset_array_unlock_touch(worker->env.rrset_cache, ++ worker->scratchpad, rep->ref, ++ rep->rrset_count); ++ return ret; ++ } ++ } ++#endif + + edns_bak = *edns; + edns->edns_version = EDNS_ADVERTISED_VERSION; +@@ -1435,6 +1474,15 @@ worker_handle_request(struct comm_point* c, void* arg, int error, + log_addr(VERB_ALGO, "refused nonrec (cache snoop) query from", + &repinfo->addr, repinfo->addrlen); + goto send_reply; ++#ifdef ENABLE_FASTRPZ ++ } else { ++ /* Start to rewrite for response policy zones. ++ * This can hit a qname trigger and be done. */ ++ if(rpz_start(worker, &qinfo, repinfo, &edns)) { ++ regional_free_all(worker->scratchpad); ++ return 0; ++ } ++#endif + } + + /* If we've found a local alias, replace the qname with the alias +@@ -1485,12 +1533,21 @@ lookup_cache: + 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))) { + /* answer from cache - we have acquired a readlock on it */ +- if(answer_from_cache(worker, &qinfo, ++ ret = answer_from_cache(worker, &qinfo, + cinfo, &need_drop, &is_expired_answer, &is_secure_answer, + &alias_rrset, &partial_rep, (struct reply_info*)e->data, + *(uint16_t*)(void *)sldns_buffer_begin(c->buffer), + sldns_buffer_read_u16_at(c->buffer, 2), repinfo, +- &edns)) { ++ &edns); ++#ifdef ENABLE_FASTRPZ ++ if(ret < 0) { ++ /* RPZ already dropped or sent a response. */ ++ lock_rw_unlock(&e->lock); ++ regional_free_all(worker->scratchpad); ++ return 0; ++ } ++#endif ++ if(ret) { + /* prefetch it if the prefetch TTL expired. + * Note that if there is more than one pass + * its qname must be that used for cache +@@ -1547,11 +1604,19 @@ lookup_cache: + lock_rw_unlock(&e->lock); + } + if(!LDNS_RD_WIRE(sldns_buffer_begin(c->buffer))) { +- if(answer_norec_from_cache(worker, &qinfo, ++ ret = answer_norec_from_cache(worker, &qinfo, + *(uint16_t*)(void *)sldns_buffer_begin(c->buffer), + sldns_buffer_read_u16_at(c->buffer, 2), repinfo, +- &edns)) { ++ &edns); ++ if(ret) { + regional_free_all(worker->scratchpad); ++#ifdef ENABLE_FASTRPZ ++ if(ret < 0) { ++ /* RPZ already dropped ++ * or sent a response. */ ++ return 0; ++ } ++#endif + goto send_reply; + } + verbose(VERB_ALGO, "answer norec from cache -- " +diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in +index 38c2d298..3b07f392 100644 +--- a/doc/unbound.conf.5.in ++++ b/doc/unbound.conf.5.in +@@ -1828,6 +1828,81 @@ 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 "Response Policy Zone Rewriting" ++.LP ++Response policy zone rewriting is controlled with the ++.B rpz ++clause. ++It must contain a ++.B rpz\-enable: ++option, and one or more ++.B rpz\-zone: ++options. ++It will usually also contain ++.B rpz\-option: ++clauses with general rewriting options or specifying dnsrpzd parameters. ++Beneath the surface, the text in ++.B rpz\-zone: \fI<"domain">\fR ++is converted to \fI"zone domain\\n"\fR and added to the configuration string ++given to ++\fIlibrpz\fR(3). ++The text in ++.B rpz-option \fI<"text">\fR ++is also added to that configuration string. ++.LP ++If using chroot, then the chroot directory must contain the \fIdnsrpzd\fR(3) ++command and the shared libraries that it uses. ++Those can be found with the \fIldd\fR(1) command. ++.LP ++Resolver zone and rewriting options and response policy zone triggers and ++actions are described in \fIlibrpz\fR(3). ++The separate control file that specifies the policy zones maintained by ++the dnsrpzd daemon is described in \fIdnsrpzd\fR(8). ++.LP ++Many installations need a local whitelist that exempts local ++domains from rewriting. ++Whitelist records can be in zones transferred by dnsrpzd from ++authorities or in a local zone file. ++.TP ++.B rpz-enable: \fI ++enables Fastrpz. ++If not enabled, the other options in the ++.B rpz: ++clause are ignored. ++.TP ++.B rpz-zone: \fI<"zone and options"> ++specifies a policy zone and optional per-zone rewriting parameters. ++.TP ++.B rpz-option: \fI<"option"> ++specifies general Fastrpz options. ++.LP ++Fastrpz is available only on POSIX compliant UNIX-like systems with the ++\fImmap\fR(2) system call. ++.LP ++Fastrpz in Unbound differs from rpz and fastrpz in BIND by ++.RS 3 ++.HP 4 ++RPZ-CLIENT-IP triggers can only be used in the first policy zone ++specified with ++.B rpz-zone: ++.HP ++Policy zone rewriting is disabled by the DO bit in DNS requests ++even when no DNSSEC signatures are supplied by authorities. ++.HP ++Unbound local zones are not subject to rpz rewriting. ++.HP ++Like Fastrpz with BIND but unlike classic BIND rpz, ++the ADDITIONAL sections of rewritten responses contain the SOA record from ++the policy zone used to rewrite the response. ++.RE ++.P ++.nf ++# example Fastrpz settings for use with chroot on Freebsd ++rpz: ++ rpz-zone: "rpz.example.org" ++ rpz-zone: "other.rpz.example.org ip-as-ns yes" ++ rpz-option: "dnsrpzd ./dnsrpzd" ++.fi + .SS "DNSCrypt Options" + .LP + The +diff --git a/fastrpz/librpz.h b/fastrpz/librpz.h +new file mode 100644 +index 00000000..645279d1 +--- /dev/null ++++ b/fastrpz/librpz.h +@@ -0,0 +1,957 @@ ++/* ++ * Define the interface from a DNS resolver to the Response Policy Zone ++ * library, librpz. ++ * ++ * This file should be included only the interface functions between the ++ * resolver and librpz to avoid name space pollution. ++ * ++ * Copyright (c) 2016-2017 Farsight Security, Inc. ++ * ++ * Licensed under the Apache License, Version 2.0 (the "License"); ++ * you may not use this file except in compliance with the License. ++ * You may obtain a copy of the License at ++ * http://www.apache.org/licenses/LICENSE-2.0 ++ * ++ * Unless required by applicable law or agreed to in writing, software ++ * distributed under the License is distributed on an "AS IS" BASIS, ++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++ * See the License for the specific language governing permissions and ++ * limitations under the License. ++ * ++ * Fastrpz version 1.2.10 ++ */ ++ ++#ifndef LIBRPZ_H ++#define LIBRPZ_H ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++/* ++ * Allow either ordinary or dlopen() linking. ++ */ ++#ifdef LIBRPZ_INTERNAL ++#define LIBDEF(t,s) extern t s; ++#define LIBDEF_F(f) LIBDEF(librpz_##f##_t, librpz_##f) ++#else ++#define LIBDEF(t,s) ++#define LIBDEF_F(f) ++#endif ++ ++/* ++ * Response Policy Zone triggers. ++ * Comparisons of trigger precedences require ++ * LIBRPZ_TRIG_CLIENT_IP < LIBRPZ_TRIG_QNAME < LIBRPZ_TRIG_IP ++ * < LIBRPZ_TRIG_NSDNAME < LIBRPZ_TRIG_NSIP} ++ */ ++typedef enum { ++ LIBRPZ_TRIG_BAD =0, ++ LIBRPZ_TRIG_CLIENT_IP =1, ++ LIBRPZ_TRIG_QNAME =2, ++ LIBRPZ_TRIG_IP =3, ++ LIBRPZ_TRIG_NSDNAME =4, ++ LIBRPZ_TRIG_NSIP =5 ++} librpz_trig_t; ++#define LIBRPZ_TRIG_SIZE 3 /* sizeof librpz_trig_t in bits */ ++typedef uint8_t librpz_tbit_t; /* one bit for each of the TRIGS_NUM ++ * trigger types */ ++ ++ ++/* ++ * Response Policy Zone Actions or policies ++ */ ++typedef enum { ++ LIBRPZ_POLICY_UNDEFINED =0, /* an empty entry or no decision yet */ ++ LIBRPZ_POLICY_DELETED =1, /* placeholder for a deleted policy */ ++ ++ LIBRPZ_POLICY_PASSTHRU =2, /* 'passthru': do not rewrite */ ++ LIBRPZ_POLICY_DROP =3, /* 'drop': do not respond */ ++ LIBRPZ_POLICY_TCP_ONLY =4, /* 'tcp-only': answer UDP with TC=1 */ ++ LIBRPZ_POLICY_NXDOMAIN =5, /* 'nxdomain': answer with NXDOMAIN */ ++ LIBRPZ_POLICY_NODATA =6, /* 'nodata': answer with ANCOUNT=0 */ ++ LIBRPZ_POLICY_RECORD =7, /* rewrite with the policy's RR */ ++ ++ /* only in client configurations to override the zone */ ++ LIBRPZ_POLICY_GIVEN, /* 'given': what policy record says */ ++ LIBRPZ_POLICY_DISABLED, /* at most log */ ++ LIBRPZ_POLICY_CNAME, /* answer with 'cname x' */ ++} librpz_policy_t; ++#define LIBRPZ_POLICY_BITS 4 ++ ++/* ++ * Special policies that appear as targets of CNAMEs ++ * NXDOMAIN is signaled by a CNAME with a "." target. ++ * NODATA is signaled by a CNAME with a "*." target. ++ */ ++#define LIBRPZ_RPZ_PREFIX "rpz-" ++#define LIBRPZ_RPZ_PASSTHRU LIBRPZ_RPZ_PREFIX"passthru" ++#define LIBRPZ_RPZ_DROP LIBRPZ_RPZ_PREFIX"drop" ++#define LIBRPZ_RPZ_TCP_ONLY LIBRPZ_RPZ_PREFIX"tcp-only" ++ ++ ++typedef uint16_t librpz_dznum_t; /* dnsrpzd zone # in [0,DZNUM_MAX] */ ++typedef uint8_t librpz_cznum_t; /* client zone # in [0,CZNUM_MAX] */ ++ ++ ++/* ++ * CIDR block ++ */ ++typedef struct librpz_prefix { ++ union { ++ struct in_addr in; ++ struct in6_addr in6; ++ } addr; ++ uint8_t family; ++ uint8_t len; ++} librpz_prefix_t; ++ ++/* ++ * A domain ++ */ ++typedef uint8_t librpz_dsize_t; ++typedef struct librpz_domain { ++ librpz_dsize_t size; /* of only .d */ ++ uint8_t d[0]; /* variable length wire format */ ++} librpz_domain_t; ++ ++/* ++ * A maximal domain buffer ++ */ ++typedef struct librpz_domain_buf { ++ librpz_dsize_t size; ++ uint8_t d[NS_MAXCDNAME]; ++} librpz_domain_buf_t; ++ ++/* ++ * A resource record without the owner name. ++ * C compilers say that sizeof(librpz_rr_t)=12 instead of 10. ++ */ ++typedef struct { ++ uint16_t type; /* network byte order */ ++ uint16_t class; /* network byte order */ ++ uint32_t ttl; /* network byte order */ ++ uint16_t rdlength; /* network byte order */ ++ uint8_t rdata[0]; /* variable length */ ++} librpz_rr_t; ++ ++/* ++ * The database file might be mapped with different starting addresses ++ * by concurrent clients (resolvers), and so all pointers are offsets. ++ */ ++typedef uint32_t librpz_idx_t; ++#define LIBRPZ_IDX_NULL 0 ++#define LIBRPZ_IDX_MIN 1 ++#define LIBRPZ_IDX_BAD ((librpz_idx_t)-1) ++/** ++ * Partial decoded results of a set of RPZ queries for a single DNS response ++ * or interation through the mapped file. ++ */ ++typedef int16_t librpz_result_id_t; ++typedef struct librpz_result { ++ librpz_idx_t next_rr; ++ librpz_result_id_t hit_id; /* trigger ID from resolver */ ++ librpz_policy_t zpolicy; /* policy from zone */ ++ librpz_policy_t policy; /* adjusted by client configuration */ ++ librpz_dznum_t dznum; /* dnsrpzd zone number */ ++ librpz_cznum_t cznum; /* librpz client zone number */ ++ librpz_trig_t trig:LIBRPZ_TRIG_SIZE; ++ bool log:1; /* log rewrite given librpz_log_level */ ++} librpz_result_t; ++ ++ ++/** ++ * librpz trace or log levels. ++ */ ++typedef enum { ++ LIBRPZ_LOG_FATAL =0, /* always print fatal errors */ ++ LIBRPZ_LOG_ERROR =1, /* errors have this level */ ++ LIBRPZ_LOG_TRACE1 =2, /* big events such as dnsrpzd starts */ ++ LIBRPZ_LOG_TRACE2 =3, /* smaller dnsrpzd zone transfers */ ++ LIBRPZ_LOG_TRACE3 =4, /* librpz hits */ ++ LIBRPZ_LOG_TRACE4 =5, /* librpz lookups */ ++ LIBRPZ_LOG_INVALID =999, ++} librpz_log_level_t; ++typedef librpz_log_level_t (librpz_log_level_val_t)(librpz_log_level_t level); ++LIBDEF_F(log_level_val) ++ ++/** ++ * Logging function that can be supplied by the resolver. ++ * @param level is one of librpz_log_level_t ++ * @param ctx is for use by the resolver's logging system. ++ * NULL mean a context-free message. ++ */ ++typedef void(librpz_log_fnc_t)(librpz_log_level_t level, void *ctx, ++ const char *buf); ++ ++/** ++ * Point librpz logging functions to the resolver's choice. ++ */ ++typedef void (librpz_set_log_t)(librpz_log_fnc_t *new_log, const char *prog_nm); ++LIBDEF_F(set_log) ++ ++ ++/** ++ * librpz error messages are put in these buffers. ++ * Use a structure intead of naked char* to let the compiler check the length. ++ * A function defined with "foo(char buf[120])" can be called with ++ * "char sbuf[2]; foo(sbuf)" and suffer a buffer overrun. ++ */ ++typedef struct { ++ char c[120]; ++} librpz_emsg_t; ++ ++ ++#ifdef LIBRPZ_HAVE_ATTR ++#define LIBRPZ_UNUSED __attribute__((unused)) ++#define LIBRPZ_PF(f,l) __attribute__((format(printf,f,l))) ++#define LIBRPZ_NORET __attribute__((__noreturn__)) ++#else ++#define LIBRPZ_UNUSED ++#define LIBRPZ_PF(f,l) ++#define LIBRPZ_NORET ++#endif ++ ++#ifdef HAVE_BUILTIN_EXPECT ++#define LIBRPZ_LIKELY(c) __builtin_expect(!!(c), 1) ++#define LIBRPZ_UNLIKELY(c) __builtin_expect(!!(c), 0) ++#else ++#define LIBRPZ_LIKELY(c) (c) ++#define LIBRPZ_UNLIKELY(c) (c) ++#endif ++ ++typedef bool (librpz_parse_log_opt_t)(librpz_emsg_t *emsg, const char *arg); ++LIBDEF_F(parse_log_opt) ++ ++typedef void (librpz_vpemsg_t)(librpz_emsg_t *emsg, ++ const char *p, va_list args); ++LIBDEF_F(vpemsg) ++typedef void (librpz_pemsg_t)(librpz_emsg_t *emsg, ++ const char *p, ...) LIBRPZ_PF(2,3); ++LIBDEF_F(pemsg) ++ ++typedef void (librpz_vlog_t)(librpz_log_level_t level, void *ctx, ++ const char *p, va_list args); ++LIBDEF_F(vlog) ++typedef void (librpz_log_t)(librpz_log_level_t level, void *ctx, ++ const char *p, ...) LIBRPZ_PF(3,4); ++LIBDEF_F(log) ++ ++typedef void (librpz_fatal_t)(int ex_code, ++ const char *p, ...) LIBRPZ_PF(2,3); ++extern void librpz_fatal(int ex_code, ++ const char *p, ...) LIBRPZ_PF(2,3) LIBRPZ_NORET; ++ ++typedef void (librpz_rpz_assert_t)(const char *file, unsigned line, ++ const char *p, ...) LIBRPZ_PF(3,4); ++extern void librpz_rpz_assert(const char *file, unsigned line, ++ const char *p, ...) LIBRPZ_PF(3,4) LIBRPZ_NORET; ++ ++typedef void (librpz_rpz_vassert_t)(const char *file, uint line, ++ const char *p, va_list args); ++extern void librpz_rpz_vassert(const char *file, uint line, ++ const char *p, va_list args) LIBRPZ_NORET; ++ ++ ++/* ++ * As far as clients are concerned, all relative pointers or indexes in a ++ * version of the mapped file except trie node parent pointers remain valid ++ * forever. A client must release a version so that it can be garbage ++ * collected by the file system. When dnsrpzd needs to expand the file, ++ * it copies the old file to a new, larger file. Clients can continue ++ * using the old file. ++ * ++ * Versions can also appear in a single file. Old nodes and trie values ++ * within the file are not destroyed until all clients using the version ++ * that contained the old values release the version. ++ * ++ * A client is marked as using version by connecting to the deamon. It is ++ * marked as using all subsequent versions. A client releases all versions ++ * by closing the connection or a range of versions by updating is slot ++ * in the shared memory version table. ++ * ++ * As far as clients are concerned, there are the following possible librpz ++ * failures: ++ * - malloc() or other fatal internal librpz problems indicated by ++ * a failing return from a librpz function ++ * All operations will fail until client handle is destroyed and ++ * recreated with librpz_client_detach() and librpz_client_create(). ++ * - corrupt database detected by librpz code, corrupt database detected ++ * by dnsrpzd, or disconnection from the daemon. ++ * Current operations will fail. ++ * ++ * Clients assume that the file has already been unlinked before ++ * the corrupt flag is set so that they do not race with the server ++ * over the corruption of a single file. A client that finds the ++ * corrupt set knows that dnsrpzd has already crashed with ++ * abort() and is restarting. The client can re-connect to dnsrpzd ++ * and retransmit its configuration, backing off as usual if anything ++ * goes wrong. ++ * ++ * Searchs of the database by a client do not need locks against dnsrpzd or ++ * other clients, but a lock is used to protect changes to the connection ++ * by competing threads in the client. The client provides fuctions ++ * to serialize the conncurrent use of any single client handle. ++ * Functions that do nothing are appropriate for applications that are ++ * not "threaded" or that do not share client handles among threads. ++ * Otherwise, functions must be provided to librpz_clientcreate(). ++ * Something like the following works with pthreads: ++ * ++ * static void ++ * lock(void *mutex) { assert(pthread_mutex_lock(mutex) == 0); } ++ * ++ * static void ++ * unlock(void *mutex) { assert(pthread_mutex_unlock(mutex) == 0); } ++ * ++ * static void ++ * mutex_destroy(void *mutex) { assert(pthread_mutex_destroy(mutex) == 0); } ++ * ++ * ++ * ++ * At every instant, all of the data and pointers in the mapped file are valid. ++ * Changes to trie node or other data are always made so that it and ++ * all pointers in and to it remain valid for a time. Old versions are ++ * eventually discarded. ++ * ++ * Dnsrpzd periodically defines a new version by setting asside all changes ++ * made since the previous version was defined. Subsequent changes ++ * made (only!) by dnsrpzd will be part of the next version. ++ * ++ * To discard an old version, dnsrpzd must know that all clients have stopped ++ * using that version. Clients do that by using part of the mapped file ++ * to tell dnsrpzd the oldest version that each client is using. ++ * Dnsrpzd assigns each connecting client an entry in the cversions array ++ * in the mapped file. The client puts version numbers into that entry ++ * to signal to dnsrpzd which versions that can be discarded. ++ * Dnsrpzd is free, as far as that client is concerned, to discard all ++ * numerically smaller versions. A client can disclaim all versions with ++ * the version number VERSIONS_ALL or 0. ++ * ++ * The race between a client changing its entry and dnsrpzd discarding a ++ * version is resolved by allowing dnsrpzd to discard all versions ++ * smaller or equal to the client's version number. If dnsrpzd is in ++ * the midst of discarding or about to discard version N when the ++ * client asserts N, no harm is done. The client depends only on ++ * the consistency of version N+1. ++ * ++ * This version mechanism depends in part on not being exercised too frequently ++ * Version numbers are 32 bits long and dnsrpzd creates new versions ++ * at most once every 30 seconds. ++ */ ++ ++ ++/* ++ * Lock functions for concurrent use of a single librpz_client_t client handle. ++ */ ++typedef void(librpz_mutex_t)(void *mutex); ++ ++/* ++ * List of connections to dnsrpzd daemons. ++ */ ++typedef struct librpz_clist librpz_clist_t; ++ ++/* ++ * Client's handle on dnsrpzd. ++ */ ++typedef struct librpz_client librpz_client_t; ++ ++/** ++ * Create the list of connections to the dnsrpzd daemon. ++ * @param[out] emsg: error message ++ * @param lock: start exclusive access to the client handle ++ * @param unlock: end exclusive access to the client handle ++ * @param mutex_destroy: release the lock ++ * @param mutex: pointer to the lock for the client handle ++ * @param log_ctx: NULL or resolver's context log messages ++ */ ++typedef librpz_clist_t *(librpz_clist_create_t)(librpz_emsg_t *emsg, ++ librpz_mutex_t *lock, ++ librpz_mutex_t *unlock, ++ librpz_mutex_t *mutex_destroy, ++ void *mutex, void *log_ctx); ++LIBDEF_F(clist_create) ++ ++ ++/** ++ * Release the list of dnsrpzd connections. ++ */ ++typedef void (librpz_clist_detach_t)(librpz_clist_t **clistp); ++LIBDEF_F(clist_detach) ++ ++/** ++ * Create a librpz client handle. ++ * @param[out] emsg: error message ++ * @param: list of dnsrpzd connections ++ * @param cstr: string of configuration settings separated by ';' or '\n' ++ * @param use_expired: true to not ignore expired zones ++ * @return client handle or NULL if the handle could not be created ++ */ ++typedef librpz_client_t *(librpz_client_create_t)(librpz_emsg_t *emsg, ++ librpz_clist_t *clist, ++ const char *cstr, ++ bool use_expired); ++LIBDEF_F(client_create) ++ ++/** ++ * Start (if necessary) dnsrpzd and connect to it. ++ * @param[out] emsg: error message ++ * @param client handle ++ * @param optional: true if it is ok if starting the daemon is not allowed ++ */ ++typedef bool (librpz_connect_t)(librpz_emsg_t *emsg, librpz_client_t *client, ++ bool optional); ++LIBDEF_F(connect) ++ ++/** ++ * Start to destroy a librpz client handle. ++ * It will not be destroyed until the last set of RPZ queries represented ++ * by a librpz_rsp_t ends. ++ * @param client handle to be released ++ * @return false on error ++ */ ++typedef void (librpz_client_detach_t)(librpz_client_t **clientp); ++LIBDEF_F(client_detach) ++ ++/** ++ * State for a set of RPZ queries for a single DNS response ++ * or for listing the database. ++ */ ++typedef struct librpz_rsp librpz_rsp_t; ++ ++/** ++ * Start a set of RPZ queries for a single DNS response. ++ * @param[out] emsg: error message for false return or *rspp=NULL ++ * @param[out] rspp created context or NULL ++ * @param[out] min_ns_dotsp: NULL or pointer to configured MIN-NS-DOTS value ++ * @param client state ++ * @param have_rd: RD=1 in the DNS request ++ * @param have_do: DO=1 in the DNS request ++ * @return false on error ++ */ ++typedef bool (librpz_rsp_create_t)(librpz_emsg_t *emsg, librpz_rsp_t **rspp, ++ int *min_ns_dotsp, librpz_client_t *client, ++ bool have_rd, bool have_do); ++LIBDEF_F(rsp_create) ++ ++/** ++ * Finish RPZ work for a DNS response. ++ */ ++typedef void (librpz_rsp_detach_t)(librpz_rsp_t **rspp); ++LIBDEF_F(rsp_detach) ++ ++/** ++ * Get the final, accumulated result of a set of RPZ queries. ++ * Yield LIBRPZ_POLICY_UNDEFINED if ++ * - there were no hits, ++ * - there was a dispositive hit, be we have not recursed and are required ++ * to recurse so that evil DNS authories will not know we are using RPZ ++ * - we have a hit and have recursed, but later data such as NSIP could ++ * override ++ * @param[out] emsg ++ * @param[out] result describes the hit ++ * or result->policy=LIBRPZ_POLICY_UNDEFINED without a hit ++ * @param[out] result: current policy rewrite values ++ * @param recursed: recursion has now been done even if it was not done ++ * when the hit was found ++ * @param[in,out] rsp state from librpz_itr_start() ++ * @return false on error ++ */ ++typedef bool (librpz_rsp_result_t)(librpz_emsg_t *emsg, librpz_result_t *result, ++ bool recursed, const librpz_rsp_t *rsp); ++LIBDEF_F(rsp_result) ++ ++/** ++ * Might looking for a trigger be worthwhile? ++ * @param trig: look for this type of trigger ++ * @param ipv6: true if trig is LIBRPZ_TRIG_CLIENT_IP, LIBRPZ_TRIG_IP, ++ * or LIBRPZ_TRIG_NSIP and the IP address is IPv6 ++ * @return: true if looking could be worthwhile ++ */ ++typedef bool (librpz_have_trig_t)(librpz_trig_t trig, bool ipv6, ++ const librpz_rsp_t *rsp); ++LIBDEF_F(have_trig) ++ ++/** ++ * Might looking for NSDNAME and NSIP triggers be worthwhile? ++ * @return: true if looking could be worthwhile ++ */ ++typedef bool (librpz_have_ns_trig_t)(const librpz_rsp_t *rsp); ++LIBDEF_F(have_ns_trig) ++ ++/** ++ * Convert the found client IP trie key to a CIDR block ++ * @param[out] emsg ++ * @param[out] prefix trigger ++ * @param[in,out] rsp state from librpz_itr_start() ++ * @return false on error ++ */ ++typedef bool (librpz_rsp_clientip_prefix_t)(librpz_emsg_t *emsg, ++ librpz_prefix_t *prefix, ++ librpz_rsp_t *rsp); ++LIBDEF_F(rsp_clientip_prefix) ++ ++/** ++ * Compute the owner name of the found or result trie key, usually to log it. ++ * An IP address key might be returned as 8.0.0.0.127.rpz-client-ip. ++ * example.com. might be a qname trigger. example.com.rpz-nsdname. could ++ * be an NSDNAME trigger. ++ * @param[out] emsg ++ * @param[out] owner domain ++ * @param[in,out] rsp state from librpz_itr_start() ++ * @return false on error ++ */ ++typedef bool (librpz_rsp_domain_t)(librpz_emsg_t *emsg, ++ librpz_domain_buf_t *owner, ++ librpz_rsp_t *rsp); ++LIBDEF_F(rsp_domain) ++ ++/** ++ * Get the next RR of the LIBRPZ_POLICY_RECORD result after an initial use of ++ * librpz_rsp_result() or librpz_itr_node() or after a previous use of ++ * librpz_rsp_rr(). The RR is in uncompressed wire format including type, ++ * class, ttl and length in network byte order. ++ * @param[out] emsg ++ * @param[out] typep: optional host byte order record type or ns_t_invalid (0) ++ * @param[out] classp: class such as ns_c_in ++ * @param[out] ttlp: TTL ++ * @param[out] rrp: optionall malloc() buffer containting the next RR or ++ * NULL after the last RR ++ * @param[out] result: current policy rewrite values ++ * @param qname: used construct a wildcard CNAME ++ * @param qname_size ++ * @param[in,out] rsp state from librpz_itr_start() ++ * @return false on error ++ */ ++typedef bool (librpz_rsp_rr_t)(librpz_emsg_t *emsg, uint16_t *typep, ++ uint16_t *classp, uint32_t *ttlp, ++ librpz_rr_t **rrp, librpz_result_t *result, ++ const uint8_t *qname, size_t qname_size, ++ librpz_rsp_t *rsp); ++LIBDEF_F(rsp_rr) ++ ++/** ++ * Get the next RR of the LIBRPZ_POLICY_RECORD result. ++ * @param[out] emsg ++ * @param[out] ttlp: TTL ++ * @param[out] rrp: malloc() buffer with SOA RR without owner name ++ * @param[out] result: current policy rewrite values ++ * @param[out] origin: SOA owner name ++ * @param[out] origin_size ++ * @param[in,out] rsp state from librpz_itr_start() ++ * @return false on error ++ */ ++typedef bool (librpz_rsp_soa_t)(librpz_emsg_t *emsg, uint32_t *ttlp, ++ librpz_rr_t **rrp, librpz_domain_buf_t *origin, ++ librpz_result_t *result, librpz_rsp_t *rsp); ++LIBDEF_F(rsp_soa) ++ ++/** ++ * Get the SOA serial number for a policy zone to compare with a known value ++ * to check whether a zone tranfer is complete. ++ */ ++typedef bool (librpz_soa_serial_t)(librpz_emsg_t *emsg, uint32_t *serialp, ++ const char *domain_nm, librpz_rsp_t *rsp); ++LIBDEF_F(soa_serial) ++ ++/** ++ * Save the current policy checking state. ++ * @param[out] emsg ++ * @param[in,out] rsp state from librpz_itr_start() ++ * @return false on error ++ */ ++typedef bool (librpz_rsp_push_t)(librpz_emsg_t *emsg, librpz_rsp_t *rsp); ++LIBDEF_F(rsp_push) ++#define LIBRPZ_RSP_STACK_DEPTH 3 ++ ++/** ++ * Restore the previous policy checking state. ++ * @param[out] emsg ++ * @param[out] result: NULL or restored policy rewrite values ++ * @param[in,out] rsp state from librpz_itr_start() ++ * @return false on error ++ */ ++typedef bool (librpz_rsp_pop_t)(librpz_emsg_t *emsg, librpz_result_t *result, ++ librpz_rsp_t *rsp); ++LIBDEF_F(rsp_pop) ++ ++/** ++ * Discard the most recently save policy checking state. ++ * @param[out] emsg ++ * @param[out] result: NULL or restored policy rewrite values ++ * @return false on error ++ */ ++typedef bool (librpz_rsp_pop_discard_t)(librpz_emsg_t *emsg, librpz_rsp_t *rsp); ++LIBDEF_F(rsp_pop_discard) ++ ++/** ++ * Disable a zone. ++ * @param[out] emsg ++ * @param znum ++ * @param[in,out] rsp state from librpz_itr_start() ++ * @return false on error ++ */ ++typedef bool (librpz_rsp_forget_zone_t)(librpz_emsg_t *emsg, ++ librpz_cznum_t znum, librpz_rsp_t *rsp); ++LIBDEF_F(rsp_forget_zone) ++ ++/** ++ * Apply RPZ to an IP address. ++ * @param[out] emsg ++ * @param addr: address to check ++ * @param ipv6: true for 16 byte IPv6 instead of 4 byte IPv4 ++ * @param trig LIBRPZ_TRIG_CLIENT_IP, LIBRPZ_TRIG_IP, or LIBRPZ_TRIG_NSIP ++ * @param hit_id: caller chosen ++ * @param recursed: recursion has been done ++ * @param[in,out] rsp state from librpz_itr_start() ++ * @return false on error ++ */ ++typedef bool (librpz_ck_ip_t)(librpz_emsg_t *emsg, ++ const void *addr, uint family, ++ librpz_trig_t trig, librpz_result_id_t hit_id, ++ bool recursed, librpz_rsp_t *rsp); ++LIBDEF_F(ck_ip) ++ ++/** ++ * Apply RPZ to a wire-format domain. ++ * @param[out] emsg ++ * @param domain in wire format ++ * @param domain_size ++ * @param trig LIBRPZ_TRIG_QNAME or LIBRPZ_TRIG_NSDNAME ++ * @param hit_id: caller chosen ++ * @param recursed: recursion has been done ++ * @param[in,out] rsp state from librpz_itr_start() ++ * @return false on error ++ */ ++typedef bool (librpz_ck_domain_t)(librpz_emsg_t *emsg, ++ const uint8_t *domain, size_t domain_size, ++ librpz_trig_t trig, librpz_result_id_t hit_id, ++ bool recursed, librpz_rsp_t *rsp); ++LIBDEF_F(ck_domain) ++ ++/** ++ * Ask dnsrpzd to refresh a zone. ++ * @param[out] emsg error message ++ * @param librpz_domain_t domain to refresh ++ * @param client context ++ * @return false after error ++ */ ++typedef bool (librpz_zone_refresh_t)(librpz_emsg_t *emsg, const char *domain, ++ librpz_rsp_t *rsp); ++LIBDEF_F(zone_refresh) ++ ++/** ++ * Get a string describing the the databasse ++ * @param license: include the license ++ * @param cfiles: include the configuration file names ++ * @param listens: include the local notify IP addresses ++ * @param[out] emsg error message if the result is null ++ * @param client context ++ * @return malloc'ed string or NULL after error ++ */ ++typedef char *(librpz_db_info_t)(librpz_emsg_t *emsg, ++ bool license, bool cfiles, bool listens, ++ librpz_rsp_t *rsp); ++LIBDEF_F(db_info) ++ ++/** ++ * Start a context for listing the nodes and/or zones in the mapped file ++ * @param[out] emsg: error message for false return or *rspp=NULL ++ * @param[out[ rspp created context or NULL ++ * @param client context ++ * @return false after error ++ */ ++typedef bool (librpz_itr_start_t)(librpz_emsg_t *emsg, librpz_rsp_t **rspp, ++ librpz_client_t *client); ++LIBDEF_F(itr_start) ++ ++/** ++ * Get mapped file memory allocation statistics. ++ * @param[out] emsg: error message ++ * @param rsp state from librpz_itr_start() ++ * @return malloc'ed string or NULL after error ++ */ ++typedef char *(librpz_mf_stats_t)(librpz_emsg_t *emsg, librpz_rsp_t *rsp); ++LIBDEF_F(mf_stats) ++ ++/** ++ * Get versions currently used by clients. ++ * @param[out] emsg: error message ++ * @param[in,out] rsp: state from librpz_itr_start() ++ * @return malloc'ed string or NULL after error ++ */ ++typedef char *(librpz_vers_stats_t)(librpz_emsg_t *emsg, librpz_rsp_t *rsp); ++LIBDEF_F(vers_stats) ++ ++/** ++ * Allocate a string describing the next zone or "" after the last zone. ++ * @param[out] emsg ++ * @param all_zones to list all instead of only requested zones ++ * @param[in,out] rsp state from librpz_rsp_start() ++ * @return malloc'ed string or NULL after error ++ */ ++typedef char *(librpz_itr_zone_t)(librpz_emsg_t *emsg, bool all_zones, ++ librpz_rsp_t *rsp); ++LIBDEF_F(itr_zone) ++ ++/** ++ * Describe the next trie node while dumping the database. ++ * @param[out] emsg ++ * @param[out] result describes node ++ * or result->policy=LIBRPZ_POLICY_UNDEFINED after the last node. ++ * @param all_zones to list all instead of only requested zones ++ * @param[in,out] rsp state from librpz_itr_start() ++ * @return: false on error ++ */ ++typedef bool (librpz_itr_node_t)(librpz_emsg_t *emsg, librpz_result_t *result, ++ bool all_zones, librpz_rsp_t *rsp); ++LIBDEF_F(itr_node) ++ ++/** ++ * RPZ policy to string with a backup buffer of POLICY2STR_SIZE size ++ */ ++typedef const char *(librpz_policy2str_t)(librpz_policy_t policy, ++ char *buf, size_t buf_size); ++#define POLICY2STR_SIZE sizeof("policy xxxxxx") ++LIBDEF_F(policy2str) ++ ++/** ++ * Trigger type to string. ++ */ ++typedef const char *(librpz_trig2str_t)(librpz_trig_t trig); ++LIBDEF_F(trig2str) ++ ++/** ++ * Convert a number of seconds to a zone file duration string ++ */ ++typedef const char *(librpz_secs2str_t)(time_t secs, ++ char *buf, size_t buf_size); ++#define SECS2STR_SIZE sizeof("1234567w7d24h59m59s") ++LIBDEF_F(secs2str) ++ ++/** ++ * Parse a duration with 's', 'm', 'h', 'd', and 'w' units. ++ */ ++typedef bool (librpz_str2secs_t)(librpz_emsg_t *emsg, time_t *val, ++ const char *str0); ++LIBDEF_F(str2secs) ++ ++/** ++ * Translate selected rtypes to strings ++ */ ++typedef const char *(librpz_rtype2str_t)(uint type, char *buf, size_t buf_size); ++#define RTYPE2STR_SIZE sizeof("type xxxxx") ++LIBDEF_F(rtype2str) ++ ++/** ++ * Local version of ns_name_ntop() for portability. ++ */ ++typedef int (librpz_domain_ntop_t)(const u_char *src, char *dst, size_t dstsiz); ++LIBDEF_F(domain_ntop) ++ ++/** ++ * Local version of ns_name_pton(). ++ */ ++typedef int (librpz_domain_pton2_t)(const char *src, u_char *dst, size_t dstsiz, ++ size_t *dstlen, bool lower); ++LIBDEF_F(domain_pton2) ++ ++typedef union socku socku_t; ++typedef socku_t *(librpz_mk_inet_su_t)(socku_t *su, const struct in_addr *addrp, ++ in_port_t port); ++LIBDEF_F(mk_inet_su) ++ ++typedef socku_t *(librpz_mk_inet6_su_t)(socku_t *su, const ++ struct in6_addr *addrp, ++ uint32_t scope_id, in_port_t port); ++LIBDEF_F(mk_inet6_su) ++ ++typedef bool (librpz_str2su_t)(socku_t *sup, const char *str); ++LIBDEF_F(str2su) ++ ++typedef char *(librpz_su2str_t)(char *str, size_t str_len, const socku_t *su); ++LIBDEF_F(su2str) ++#define SU2STR_SIZE (INET6_ADDRSTRLEN+1+6+1) ++ ++ ++/** ++ * default path to dnsrpzd ++ */ ++const char *librpz_dnsrpzd_path; ++ ++ ++#undef LIBDEF ++ ++/* ++ * This is the dlopen() interface to librpz. ++ */ ++typedef const struct { ++ const char *dnsrpzd_path; ++ const char *version; ++ librpz_parse_log_opt_t *parse_log_opt; ++ librpz_log_level_val_t *log_level_val; ++ librpz_set_log_t *set_log; ++ librpz_vpemsg_t *vpemsg; ++ librpz_pemsg_t *pemsg; ++ librpz_vlog_t *vlog; ++ librpz_log_t *log; ++ librpz_fatal_t *fatal LIBRPZ_NORET; ++ librpz_rpz_assert_t *rpz_assert LIBRPZ_NORET; ++ librpz_rpz_vassert_t *rpz_vassert LIBRPZ_NORET; ++ librpz_clist_create_t *clist_create; ++ librpz_clist_detach_t *clist_detach; ++ librpz_client_create_t *client_create; ++ librpz_connect_t *connect; ++ librpz_client_detach_t *client_detach; ++ librpz_rsp_create_t *rsp_create; ++ librpz_rsp_detach_t *rsp_detach; ++ librpz_rsp_result_t *rsp_result; ++ librpz_have_trig_t *have_trig; ++ librpz_have_ns_trig_t *have_ns_trig; ++ librpz_rsp_clientip_prefix_t *rsp_clientip_prefix; ++ librpz_rsp_domain_t *rsp_domain; ++ librpz_rsp_rr_t *rsp_rr; ++ librpz_rsp_soa_t *rsp_soa; ++ librpz_soa_serial_t *soa_serial; ++ librpz_rsp_push_t *rsp_push; ++ librpz_rsp_pop_t *rsp_pop; ++ librpz_rsp_pop_discard_t *rsp_pop_discard; ++ librpz_rsp_forget_zone_t *rsp_forget_zone; ++ librpz_ck_ip_t *ck_ip; ++ librpz_ck_domain_t *ck_domain; ++ librpz_zone_refresh_t *zone_refresh; ++ librpz_db_info_t *db_info; ++ librpz_itr_start_t *itr_start; ++ librpz_mf_stats_t *mf_stats; ++ librpz_vers_stats_t *vers_stats; ++ librpz_itr_zone_t *itr_zone; ++ librpz_itr_node_t *itr_node; ++ librpz_policy2str_t *policy2str; ++ librpz_trig2str_t *trig2str; ++ librpz_secs2str_t *secs2str; ++ librpz_str2secs_t *str2secs; ++ librpz_rtype2str_t *rtype2str; ++ librpz_domain_ntop_t *domain_ntop; ++ librpz_domain_pton2_t *domain_pton2; ++ librpz_mk_inet_su_t *mk_inet_su; ++ librpz_mk_inet6_su_t *mk_inet6_su; ++ librpz_str2su_t *str2su; ++ librpz_su2str_t *su2str; ++} librpz_0_t; ++extern librpz_0_t librpz_def_0; ++ ++/* ++ * Future versions can be upward compatible by defining LIBRPZ_DEF as ++ * librpz_X_t. ++ */ ++#define LIBRPZ_DEF librpz_def_0 ++#define LIBRPZ_DEF_STR "librpz_def_0" ++ ++typedef librpz_0_t librpz_t; ++extern librpz_t *librpz; ++ ++ ++#if LIBRPZ_LIB_OPEN == 2 ++#include ++ ++/** ++ * link-load librpz ++ * @param[out] emsg: error message ++ * @param[in,out] dl_handle: NULL or pointer to new dlopen handle ++ * @param[in] path: librpz.so path ++ * @return address of interface structure or NULL on failure ++ */ ++static inline librpz_t * ++librpz_lib_open(librpz_emsg_t *emsg, void **dl_handle, const char *path) ++{ ++ void *handle; ++ librpz_t *new_librpz; ++ ++ emsg->c[0] = '\0'; ++ ++ /* ++ * Close a previously opened handle on librpz.so. ++ */ ++ if (dl_handle != NULL && *dl_handle != NULL) { ++ if (dlclose(*dl_handle) != 0) { ++ snprintf(emsg->c, sizeof(librpz_emsg_t), ++ "dlopen(NULL): %s", dlerror()); ++ return (NULL); ++ } ++ *dl_handle = NULL; ++ } ++ ++ /* ++ * First try the main executable of the process in case it was ++ * linked to librpz. ++ * Do not worry if we cannot search the main executable of the process. ++ */ ++ handle = dlopen(NULL, RTLD_NOW | RTLD_LOCAL); ++ if (handle != NULL) { ++ new_librpz = dlsym(handle, LIBRPZ_DEF_STR); ++ if (new_librpz != NULL) { ++ if (dl_handle != NULL) ++ *dl_handle = handle; ++ return (new_librpz); ++ } ++ if (dlclose(handle) != 0) { ++ snprintf(emsg->c, sizeof(librpz_emsg_t), ++ "dlsym(NULL, "LIBRPZ_DEF_STR"): %s", ++ dlerror()); ++ return (NULL); ++ } ++ } ++ ++ if (path == NULL || path[0] == '\0') { ++ snprintf(emsg->c, sizeof(librpz_emsg_t), ++ "librpz not linked and no dlopen() path provided"); ++ return (NULL); ++ } ++ ++ handle = dlopen(path, RTLD_NOW | RTLD_LOCAL); ++ if (handle == NULL) { ++ snprintf(emsg->c, sizeof(librpz_emsg_t), "dlopen(%s): %s", ++ path, dlerror()); ++ return (NULL); ++ } ++ new_librpz = dlsym(handle, LIBRPZ_DEF_STR); ++ if (new_librpz != NULL) { ++ if (dl_handle != NULL) ++ *dl_handle = handle; ++ return (new_librpz); ++ } ++ snprintf(emsg->c, sizeof(librpz_emsg_t), ++ "dlsym(%s, "LIBRPZ_DEF_STR"): %s", ++ path, dlerror()); ++ dlclose(handle); ++ return (NULL); ++} ++ ++#elif defined(LIBRPZ_LIB_OPEN) ++ ++/* ++ * Statically link to the librpz.so DSO on systems without dlopen() ++ */ ++static inline librpz_t * ++librpz_lib_open(librpz_emsg_t *emsg, void **dl_handle, const char *path) ++{ ++ (void)(path); ++ ++ if (dl_handle != NULL) ++ *dl_handle = NULL; ++ ++#if LIBRPZ_LIB_OPEN == 1 ++ emsg->c[0] = '\0'; ++ return (&LIBRPZ_DEF); ++#else ++ snprintf(emsg->c, sizeof(librpz_emsg_t), ++ "librpz not available via ./configure"); ++ return (NULL); ++#endif /* LIBRPZ_LIB_OPEN */ ++} ++#endif /* LIBRPZ_LIB_OPEN */ ++ ++#endif /* LIBRPZ_H */ +diff --git a/fastrpz/rpz.c b/fastrpz/rpz.c +new file mode 100644 +index 00000000..c5ab7801 +--- /dev/null ++++ b/fastrpz/rpz.c +@@ -0,0 +1,1352 @@ ++/* ++ * fastrpz/rpz.c - interface to the fastrpz response policy zone library ++ * ++ * Optimize no-rewrite cases for speed but optimize rewriting for ++ * simplicity and size. ++ */ ++ ++#include "config.h" ++ ++#ifdef ENABLE_FASTRPZ ++#include "daemon/daemon.h" ++#define LIBRPZ_LIB_OPEN FASTRPZ_LIB_OPEN ++#include "fastrpz/rpz.h" ++#include "daemon/worker.h" ++#include "iterator/iter_delegpt.h" ++#include "iterator/iter_utils.h" ++#include "iterator/iterator.h" ++#include "util/data/dname.h" ++#include "util/data/msgencode.h" ++#include "util/data/msgparse.h" ++#include "util/data/msgreply.h" ++#include "util/log.h" ++#include "util/netevent.h" ++#include "util/net_help.h" ++#include "util/regional.h" ++#include "util/storage/slabhash.h" ++#include "services/cache/dns.h" ++#include "services/cache/rrset.h" ++#include "services/mesh.h" ++#include "sldns/sbuffer.h" ++#include "sldns/rrdef.h" ++ ++ ++typedef enum state { ++ /* No more rewriting */ ++ st_off = 1, ++ /* Send SERVFAIL */ ++ st_servfail, ++ /* No dispositive hit yet */ ++ st_unknown, ++ /* Let the iterator resolve a CNAME or get a delegation point. */ ++ st_iterate, ++ /* Let the iterator resolve NS to check NSIP or NSDNAME triggers. */ ++ st_ck_ns, ++ /* We have an answer */ ++ st_rewritten, ++} st_t; ++ ++ ++/* RPZ state pointed to by struct comm_reply */ ++typedef struct commreply_rpz { ++ /* librpz state */ ++ librpz_rsp_t* rsp; ++ /* ID for log messages */ ++ int log_id; ++ ++ /* from configuration */ ++ int min_ns_dots; ++ ++ /* Running in the iterator */ ++ bool iterating; ++ ++ /* current and previous state and librpz result */ ++ st_t st; ++ st_t saved_st[LIBRPZ_RSP_STACK_DEPTH-1]; ++ librpz_result_t result; ++ ++ /* Stop adding CNAMEs to the prepend list before this owner name. */ ++ librpz_domain_buf_t cname_hit; ++ /* It is not the first CNAME */ ++ bool cname_hit_2nd; ++ librpz_result_id_t hit_id; ++} commreply_rpz_t; ++ ++ ++/* Generate an ID for log messages. */ ++static int log_id; ++ ++librpz_t *librpz; ++ ++ ++static void LIBRPZ_NORET ++rpz_assert(const char *s) ++{ ++ fatal_exit("%s", s); ++ exit(1); ++} ++#define RPZ_ASSERT(c) ((c) ? (void)0 : rpz_assert(#c), (void)0) ++ ++/* ++ * librpz client handle locking ++ */ ++static void ++lock_destroy(void* mutex) ++{ ++ lock_basic_destroy(mutex); ++ free(mutex); ++} ++ ++static void ++lock(void* mutex) ++{ ++ lock_basic_lock(mutex); ++} ++ ++static void ++unlock(void* mutex) ++{ ++ lock_basic_unlock(mutex); ++} ++ ++ ++static void ++log_fnc(librpz_log_level_t level, void* ATTR_UNUSED(ctx), const char* buf) ++{ ++ /* Setting librpz_log_level overrides the unbound "verbose" level. */ ++ if(level > LIBRPZ_LOG_TRACE1 && ++ level <= librpz->log_level_val(LIBRPZ_LOG_INVALID)) ++ level = LIBRPZ_LOG_TRACE1; ++ ++ switch(level) { ++ case LIBRPZ_LOG_FATAL: ++ case LIBRPZ_LOG_ERROR: /* errors */ ++ default: ++ log_err("rpz: %s", buf); ++ break; ++ ++ case LIBRPZ_LOG_TRACE1: /* big events such as dnsrpzd starts */ ++ verbose(VERB_OPS, "rpz: %s", buf); ++ break; ++ ++ case LIBRPZ_LOG_TRACE2: /* smaller dnsrpzd zone transfers */ ++ verbose(VERB_DETAIL, "rpz: %s", buf); ++ break; ++ ++ case LIBRPZ_LOG_TRACE3: /* librpz hits */ ++ verbose(VERB_QUERY, "rpz: %s", buf); ++ break; ++ ++ case LIBRPZ_LOG_TRACE4: /* librpz lookups */ ++ verbose(VERB_CLIENT, "rpz: %s", buf); ++ break; ++ } ++} ++ ++ ++/* Release the librpz version. */ ++static void ++rpz_off(commreply_rpz_t* rpz, st_t st) ++{ ++ if(!rpz) ++ return; ++ rpz->st = st; ++ librpz->rsp_detach(&rpz->rsp); ++} ++ ++ ++static void LIBRPZ_PF(2,3) ++log_fail(commreply_rpz_t* rpz, const char* p, ...) ++{ ++ va_list args; ++ ++ if(rpz->st == st_servfail) ++ return; ++ ++ va_start(args, p); ++ librpz->vlog(LIBRPZ_LOG_ERROR, rpz, p, args); ++ va_end(args); ++ if(!rpz) ++ return; ++ rpz_off(rpz, st_servfail); ++} ++ ++ ++/* Announce a rewrite. */ ++static void ++log_rewrite(uint8_t* qname, librpz_policy_t policy, const char* msg, ++ commreply_rpz_t* rpz) ++{ ++ char policy_buf[POLICY2STR_SIZE]; ++ char qname_nm[LDNS_MAX_DOMAINLEN+1]; ++ librpz_domain_buf_t tdomain; ++ char tdomain_nm[LDNS_MAX_DOMAINLEN+1]; ++ librpz_emsg_t emsg; ++ ++ if(rpz->st == st_servfail || !rpz->result.log) ++ return; ++ if(librpz->log_level_val(LIBRPZ_LOG_INVALID) < LIBRPZ_LOG_TRACE1) ++ return; ++ ++ dname_str(qname, qname_nm); ++ ++ if(!librpz->rsp_domain(&emsg, &tdomain, rpz->rsp)) { ++ librpz->log(LIBRPZ_LOG_ERROR, rpz, "%s", emsg.c); ++ return; ++ } ++ dname_str(tdomain.d, tdomain_nm); ++ ++ librpz->log(LIBRPZ_LOG_TRACE3, rpz, "%srewriting %s via %s %s to %s", ++ msg, qname_nm, tdomain_nm, ++ librpz->trig2str(rpz->result.trig), ++ librpz->policy2str(policy, policy_buf, ++ sizeof(policy_buf))); ++} ++ ++ ++/* Connect to and start dnsrpzd if necessary for the unbound daemon. ++ * Require "rpz-conf: path" to specify the rpz configuration file. ++ * The unbound server directory name is the default rpz working ++ * directory. If unbound uses chroot, then the dnsrpzd working ++ * directory must be in the chroot tree. ++ * The database and socket are closed and re-opened. ++ */ ++void ++rpz_init(librpz_clist_t** pclist, librpz_client_t** pclient, ++ const struct config_file* cfg) ++{ ++ lock_basic_type* mutex; ++ librpz_emsg_t emsg; ++ ++ if(!librpz) { ++ librpz = librpz_lib_open(&emsg, NULL, FASTRPZ_LIBRPZ_PATH); ++ if(!librpz) ++ fatal_exit("rpz: %s", emsg.c); ++ } ++ ++ librpz->set_log(&log_fnc, NULL); ++ ++ if(!cfg->rpz_cstr) ++ fatal_exit("rpz: rpz-zone: not set"); ++ ++ librpz->client_detach(pclient); ++ librpz->clist_detach(pclist); ++ ++ mutex = malloc(sizeof(*mutex)); ++ if(!mutex) ++ fatal_exit("rpz: no memory for lock"); ++ lock_basic_init(mutex); ++ ++ *pclist = librpz->clist_create(&emsg, &lock, &unlock, &lock_destroy, ++ mutex, NULL); ++ if(!pclist) ++ fatal_exit("rpz: %s", emsg.c); ++ ++ *pclient = librpz->client_create(&emsg, *pclist, cfg->rpz_cstr, false); ++ if(!*pclient) ++ fatal_exit("rpz: %s", emsg.c); ++ ++ if(!librpz->connect(&emsg, *pclient, true)) ++ fatal_exit("rpz: %s", emsg.c); ++ ++ verbose(VERB_OPS, "rpz: librpz version %s", librpz->version); ++} ++ ++ ++/* Stop using librpz on behalf of a worker thread. */ ++void ++rpz_delete(librpz_clist_t** pclist, librpz_client_t** pclient) ++{ ++ if(librpz) { ++ librpz->client_detach(pclient); ++ librpz->clist_detach(pclist); ++ } ++} ++ ++ ++/* Release the librpz resources held for a DNS client request. */ ++void ++rpz_end(struct comm_reply* commreply) ++{ ++ if(!commreply->rpz) ++ return; ++ rpz_off(commreply->rpz, commreply->rpz->st); ++ free(commreply->rpz); ++ commreply->rpz = NULL; ++} ++ ++ ++static bool ++push_st(commreply_rpz_t* rpz) ++{ ++ librpz_emsg_t emsg; ++ ++ if(rpz->st == st_off || rpz->st == st_servfail) { ++ librpz->log(LIBRPZ_LOG_ERROR, rpz, ++ "state %d in push_st()", rpz->st); ++ return false; ++ } ++ if(!librpz->rsp_push(&emsg, rpz->rsp)) ++ log_fail(rpz, "%s", emsg.c); ++ memmove(&rpz->saved_st[1], &rpz->saved_st[0], ++ sizeof(rpz->saved_st) - sizeof(rpz->saved_st[0])); ++ rpz->saved_st[0] = rpz->st; ++ return rpz->st != st_servfail; ++} ++ ++ ++static bool ++pop_st(commreply_rpz_t* rpz) ++{ ++ librpz_emsg_t emsg; ++ ++ if(rpz->rsp && !librpz->rsp_pop(&emsg, &rpz->result, rpz->rsp)) ++ log_fail(rpz, "%s", emsg.c); ++ if(rpz->st != st_servfail) ++ rpz->st = rpz->saved_st[0]; ++ memmove(&rpz->saved_st[0], &rpz->saved_st[1], ++ sizeof(rpz->saved_st) - sizeof(rpz->saved_st[0])); ++ return rpz->st != st_servfail; ++} ++ ++static bool ++pop_discard_st(commreply_rpz_t* rpz) ++{ ++ librpz_emsg_t emsg; ++ ++ if(rpz->rsp && !librpz->rsp_pop_discard(&emsg, rpz->rsp)) ++ log_fail(rpz, "%s", emsg.c); ++ memmove(&rpz->saved_st[0], &rpz->saved_st[1], ++ sizeof(rpz->saved_st) - sizeof(rpz->saved_st[0])); ++ return rpz->st != st_servfail; ++} ++ ++/* Check a rewrite attempt for errors and a disabled zone. */ ++static bool /* true=repeat the check */ ++ck_after(uint8_t* qname, bool recursed, librpz_trig_t trig, ++ commreply_rpz_t* rpz) ++{ ++ librpz_emsg_t emsg; ++ ++ if(rpz->st == st_servfail) ++ return false; ++ ++ if(!librpz->rsp_result(&emsg, &rpz->result, recursed, rpz->rsp)) { ++ log_fail(rpz, "%s", emsg.c); ++ return false; ++ } ++ ++ if(rpz->result.policy == LIBRPZ_POLICY_DISABLED) { ++ /* Log the hit on the disabled zone, do not try the zone again, ++ * and restore the state from before the check to forget the hit ++ * before trying again. */ ++ log_rewrite(qname, rpz->result.zpolicy, "disabled ", rpz); ++ if(!librpz->rsp_forget_zone(&emsg, rpz->result.cznum, rpz->rsp)) ++ log_fail(rpz, "%s", emsg.c); ++ return pop_st(rpz); ++ } ++ ++ /* Complain about and forget client-IP address hit that is not ++ * dispositive. Client-IP triggers have the highest priority ++ * within a policy zone, but can be overridden by any hit in a policy ++ * earlier in the client's (resolver's) list of zones, including ++ * policies that cannot be hit until after recursion. If we allowed ++ * client-IP triggers in secondary zones, then than two DNS requests ++ * that differ only in DNS client-IP addresses could properly ++ * have differing results. The Unbound iterator treats identical ++ * DNS requests the same regardless of DNS client-IP address. ++ * struct query_info would need to be modified to have an optional ++ * librpz_prefix_t containing the prefix of the client-IP address hit ++ * from librpz->rsp_clientip_prefix(). Adding to struct query_info ++ * would require finding and changing the many and obscure places ++ * including the Unbound tests to memset(0) the struct query_info ++ * that they create. */ ++ if(trig == LIBRPZ_TRIG_CLIENT_IP) { ++ if(rpz->result.cznum != 0) { ++ log_rewrite(qname, rpz->result.policy, ++ "ignore secondary ", rpz); ++ if(!pop_st(rpz)) ++ log_fail(rpz, "%s", emsg.c); ++ return (false); ++ } ++ } ++ ++ /* Forget the state from before the check and keep the new state ++ * if we do not have a hit on a disabled policy zone. */ ++ pop_discard_st(rpz); ++ return false; ++} ++ ++ ++/* Get the next RR from the policy record. */ ++static bool ++next_rr(librpz_rr_t** rrp, const uint8_t* qname, size_t qname_len, ++ commreply_rpz_t* rpz) ++{ ++ librpz_emsg_t emsg; ++ ++ if(!librpz->rsp_rr(&emsg, NULL, NULL, NULL, rrp, &rpz->result, ++ qname, qname_len, rpz->rsp)) { ++ log_fail(rpz, "%s", emsg.c); ++ *rrp = NULL; ++ return false; ++ } ++ return true; ++} ++ ++ ++static bool /* false=fatal error to be logged */ ++add_rr(struct sldns_buffer* pkt, const uint8_t* owner, size_t owner_len, ++ librpz_rr_t* rr, commreply_rpz_t* rpz) ++{ ++ size_t rdlength; ++ ++ rdlength = ntohs(rr->rdlength); ++ ++ if(!sldns_buffer_available(pkt, owner_len + 10 + rdlength)) { ++ log_fail(rpz, "comm_reply buffer exhausted"); ++ free(rr); ++ return false; ++ } ++ sldns_buffer_write(pkt, owner, owner_len); ++ /* sizeof(librpz_rr_t)=12 instead of 10 */ ++ sldns_buffer_write(pkt, rr, 10 + rdlength); ++ return true; ++} ++ ++ ++/* Convert a fake incoming DNS message to an Unbound struct dns_msg */ ++static void ++pkt2dns_msg(struct dns_msg** dnsmsg, struct sldns_buffer* pkt, ++ commreply_rpz_t* rpz, struct regional* region) ++{ ++ struct msg_parse* msgparse; ++ ++ msgparse = regional_alloc(region, sizeof(*msgparse)); ++ if(!msgparse) { ++ log_fail(rpz, "out of memory for msgparse"); ++ *dnsmsg = NULL; ++ return; ++ } ++ memset(msgparse, 0, sizeof(*msgparse)); ++ if(parse_packet(pkt, msgparse, region) != LDNS_RCODE_NOERROR) { ++ log_fail(rpz, "packet parse error"); ++ *dnsmsg = NULL; ++ return; ++ } ++ *dnsmsg = dns_alloc_msg(pkt, msgparse, region); ++ if(!*dnsmsg) { ++ log_fail(rpz, "dns_alloc_msg() failed"); ++ *dnsmsg = NULL; ++ return; ++ } ++ (*dnsmsg)->rep->security = sec_status_rpz_rewritten; ++} ++ ++ ++static bool /* false=SERVFAIL */ ++ck_ip_rrset(const void* vdata, int family, librpz_trig_t trig, ++ uint8_t* qname, commreply_rpz_t* rpz) ++{ ++ const struct packed_rrset_data* data; ++ uint rr_n; ++ size_t len; ++ librpz_emsg_t emsg; ++ ++ data = vdata; ++ ++ /* Loop to ignore disabled zones. */ ++ do { ++ if(!push_st(rpz)) ++ return false; ++ for(rr_n = 0; rr_n < data->count; ++rr_n) { ++ len = data->rr_len[rr_n]; ++ /* Skip bogus including negative placeholding rdata. */ ++ if((family == AF_INET && ++ len != sizeof(struct in_addr)+2) || ++ (family == AF_INET6 && ++ len != sizeof(struct in6_addr)+2)) ++ continue; ++ if(!librpz->ck_ip(&emsg, data->rr_data[rr_n]+2, ++ family, trig, rpz->hit_id, true, ++ rpz->rsp)) { ++ log_fail(rpz, "%s", emsg.c); ++ return false; ++ } ++ } ++ } while(ck_after(qname, true, trig, rpz)); ++ return rpz->st != st_servfail; ++} ++ ++ ++static bool /* false=SERVFAIL */ ++ck_dname(uint8_t* dname, size_t dname_size, librpz_trig_t trig, ++ uint8_t* qname, bool recursed, commreply_rpz_t* rpz) ++{ ++ librpz_emsg_t emsg; ++ ++ /* Refuse to check the root. */ ++ if(dname_is_root(dname)) ++ return rpz->st != st_servfail; ++ ++ /* Loop to ignore disabled zones. */ ++ do { ++ if(!push_st(rpz)) ++ return false; ++ if(!librpz->ck_domain(&emsg, dname, dname_size, trig, ++ rpz->hit_id, recursed, rpz->rsp)) { ++ log_fail(rpz, "%s", emsg.c); ++ return false; ++ } ++ } while(ck_after(qname, recursed, trig, rpz)); ++ ++ return rpz->st != st_servfail; ++} ++ ++ ++/* Check the IPv4 or IPv6 addresses for one NS name. */ ++static bool /* false=st_servfail */ ++ck_1nsip(uint8_t* nsname, size_t nsname_size, int family, int qtype, ++ bool* have_ns, commreply_rpz_t* rpz, struct module_env* env) ++{ ++ struct ub_packed_rrset_key* akey; ++ ++ akey = rrset_cache_lookup(env->rrset_cache, nsname, nsname_size, ++ qtype, LDNS_RR_CLASS_IN, 0, 0, 0); ++ if(akey) { ++ *have_ns = true; ++ ++ if(!ck_ip_rrset(akey->entry.data, family, LIBRPZ_TRIG_NSIP, ++ nsname, rpz)) { ++ lock_rw_unlock(&akey->entry.lock); ++ return false; ++ } ++ lock_rw_unlock(&akey->entry.lock); ++ } ++ return true; ++} ++ ++ ++static bool /* false=st_servfail */ ++ck_qname(uint8_t* qname, size_t qname_len, ++ bool recursed, /* recursion done */ ++ bool wait_ns, /* willing to iterate for NS data */ ++ commreply_rpz_t* rpz, struct module_env* env) ++{ ++ uint8_t* dname; ++ size_t dname_size; ++ int cur_lab; ++ struct ub_packed_rrset_key* nskey; ++ const struct packed_rrset_data* nsdata; ++ uint8_t* nsname; ++ size_t nsname_size; ++ uint rr_n; ++ bool have_ns, tried_ns; ++ ++ if(!ck_dname(qname, qname_len, LIBRPZ_TRIG_QNAME, qname, false, rpz)) ++ return false; ++ ++ /* Do not waste time looking for NSDNAME and NSIP hits when there ++ * are no currently relevant triggers. */ ++ if(!librpz->have_ns_trig(rpz->rsp)) ++ return true; ++ ++ have_ns = false; ++ tried_ns = false; ++ dname = qname; ++ dname_size = qname_len; ++ for(cur_lab = dname_count_labels(dname) - 2; ++ cur_lab > rpz->min_ns_dots; ++ --cur_lab) { ++ tried_ns = true; ++ dname_remove_label(&dname, &dname_size); ++ nskey = rrset_cache_lookup(env->rrset_cache, dname, dname_size, ++ LDNS_RR_TYPE_NS, LDNS_RR_CLASS_IN, ++ 0, 0, 0); ++ if(!nskey) ++ continue; ++ ++ nsdata = (const struct packed_rrset_data*)nskey->entry.data; ++ for(rr_n = 0; ++ rr_n < nsdata->count && rpz->st == st_unknown; ++ ++rr_n) { ++ nsname = nsdata->rr_data[rr_n]+2; ++ nsname_size = nsdata->rr_len[rr_n]; ++ if(nsname_size <= 2) ++ continue; ++ nsname_size -= 2; ++ if(!ck_dname(nsname, nsname_size, LIBRPZ_TRIG_NSDNAME, ++ qname, recursed, rpz)) ++ return false; ++ if(!ck_1nsip(nsname, nsname_size, AF_INET, ++ LDNS_RR_TYPE_A, &have_ns, rpz, env)) ++ return false; ++ if(!ck_1nsip(nsname, nsname_size, AF_INET6, ++ LDNS_RR_TYPE_AAAA, &have_ns, rpz, env)) ++ return false; ++ } ++ lock_rw_unlock(&nskey->entry.lock); ++ } ++ ++ /* If we failed to find NS records, then stop building the response ++ * before a CNAME with this owner name. */ ++ if(!have_ns && tried_ns && (!recursed || wait_ns)) { ++ rpz->cname_hit.size = qname_len; ++ RPZ_ASSERT(rpz->cname_hit.size <= sizeof(rpz->cname_hit.d)); ++ memcpy(rpz->cname_hit.d, qname, qname_len); ++ rpz->result.hit_id = rpz->hit_id; ++ rpz->st = st_ck_ns; ++ } ++ return true; ++} ++ ++ ++/* ++ * Are we ready to rewrite the response? ++ */ ++static bool /* true=send rewritten response */ ++ck_result(uint8_t* qname, bool recursed, ++ commreply_rpz_t* rpz, const struct comm_point* commpoint) ++{ ++ librpz_emsg_t emsg; ++ ++ switch(rpz->st) { ++ case st_off: ++ case st_servfail: ++ case st_rewritten: ++ return false; ++ case st_unknown: ++ break; ++ case st_iterate: ++ return false; ++ case st_ck_ns: ++ /* An NSDNAME or NSIP check failed for lack of cached data. */ ++ return false; ++ default: ++ fatal_exit("impossible RPZ state %d in rpz_worker_cache()", ++ rpz->st); ++ } ++ ++ /* Wait for a trigger. */ ++ if(rpz->result.policy == LIBRPZ_POLICY_UNDEFINED) { ++ if(recursed && ++ rpz->result.zpolicy != LIBRPZ_POLICY_UNDEFINED && ++ !librpz->rsp_result(&emsg, &rpz->result, true, rpz->rsp)) { ++ log_fail(rpz, "%s", emsg.c); ++ return false; ++ } ++ if(rpz->result.policy == LIBRPZ_POLICY_UNDEFINED) ++ return false; ++ } ++ ++ if(rpz->result.policy == LIBRPZ_POLICY_PASSTHRU) { ++ log_rewrite(qname, rpz->result.policy, "", rpz); ++ rpz_off(rpz, st_off); ++ return false; ++ } ++ ++ /* The TCP-only policy answers UDP requests with truncated responses. */ ++ if(rpz->result.policy == LIBRPZ_POLICY_TCP_ONLY && ++ commpoint->type == comm_tcp) { ++ rpz_off(rpz, st_off); ++ return false; ++ } ++ ++ return true; ++} ++ ++ ++/* ++ * Convert an RPZ hit to a struct dns_msg ++ */ ++static void ++get_result_msg(struct dns_msg** dnsmsg, struct query_info* qinfo, ++ uint16_t id, uint16_t flags, bool recursed, commreply_rpz_t* rpz, ++ struct comm_point* commpoint, struct regional* region) ++{ ++ librpz_rr_t* rr; ++ librpz_domain_buf_t origin; ++ struct sldns_buffer* pkt; ++ uint16_t num_rrs; ++ librpz_emsg_t emsg; ++ ++ *dnsmsg = NULL; ++ if(!ck_result(qinfo->qname, recursed, rpz, commpoint)) ++ return; ++ ++ rpz->st = st_rewritten; ++ ++ if(rpz->result.policy == LIBRPZ_POLICY_DROP) { ++ log_rewrite(qinfo->qname, rpz->result.policy, "", rpz); ++ /* Make a fake cached message to carry ++ * sec_status_rpz_drop and be dropped. */ ++ error_encode(commpoint->buffer, LDNS_RCODE_NOERROR, ++ qinfo, id, flags, NULL); ++ pkt2dns_msg(dnsmsg, commpoint->buffer, rpz, region); ++ (*dnsmsg)->rep->security = sec_status_rpz_drop; ++ return; ++ } ++ ++ /* Create a DNS message of the RPZ data. ++ * In many cases that message could be sent directly to the DNS client, ++ * but sometimes iteration must be used to resolve a CNAME. ++ * This need not be fast, because rewriting responses should be rare. ++ * Therefore, use the simpler but slower tactic of generating a ++ * parsed version of the message. */ ++ ++ flags &= ~BIT_AA; ++ flags |= BIT_QR | BIT_RA; ++ rr = NULL; ++ ++ /* The TCP-only policy answers UDP requests with truncated responses. */ ++ if(rpz->result.policy == LIBRPZ_POLICY_TCP_ONLY) { ++ flags |= BIT_TC; ++ ++ } else if(rpz->result.policy == LIBRPZ_POLICY_NXDOMAIN) { ++ flags |= LDNS_RCODE_NXDOMAIN; ++ ++ } else if(rpz->result.policy == LIBRPZ_POLICY_CNAME) { ++ if(!rpz->iterating && ++ qinfo->qtype != LDNS_RR_TYPE_CNAME) { ++ /* The new DNS message would be a CNAME and ++ * the external request was not for a CNAME. ++ * The worker must punt to the iterator so that ++ * the iterator can resolve the CNAME. */ ++ rpz->st = st_iterate; ++ return; ++ } ++ next_rr(&rr, qinfo->qname, qinfo->qname_len, rpz); ++ ++ } else if(rpz->result.policy == LIBRPZ_POLICY_RECORD || ++ rpz->result.policy == LIBRPZ_POLICY_NODATA) { ++ next_rr(&rr, qinfo->qname, qinfo->qname_len, rpz); ++ /* Punt to the iterator if the new DNS message would ++ * be a CNAME that must be resolved. */ ++ if(!rpz->iterating && ++ qinfo->qtype != LDNS_RR_TYPE_CNAME && ++ rr && rr->type == ntohs(LDNS_RR_TYPE_CNAME)) { ++ free(rr); ++ rpz->st = st_iterate; ++ return; ++ } ++ } ++ log_rewrite(qinfo->qname, rpz->result.policy, "", rpz); ++ ++ /* Make a buffer containing a DNS message with the RPZ data. */ ++ pkt = commpoint->buffer; ++ sldns_buffer_clear(pkt); ++ if(sldns_buffer_remaining(pkt) < LDNS_HEADER_SIZE) { ++ log_fail(rpz, "comm_reply buffer too small for header"); ++ if(rr) ++ free(rr); ++ return; ++ } ++ ++ /* Install ID, flags, QDCOUNT=1, ANCOUNT=# of RPZ RRs, NSCOUNT=0, ++ * and ARCOUNT=1 for the RPZ SOA. */ ++ sldns_buffer_write_u16(pkt, id); ++ sldns_buffer_write_u16(pkt, flags); ++ sldns_buffer_write_u16(pkt, 1); /* QDCOUNT */ ++ sldns_buffer_write_u16(pkt, 0); /* ANCOUNT will be set later */ ++ sldns_buffer_write_u16(pkt, 0); /* NSCOUNT */ ++ sldns_buffer_write_u16(pkt, 1); /* ARCOUNT */ ++ ++ /* Install the question with the LDNS_RR_CLASS_RPZ bit to ++ * to distinguish this supposed cache entry from the real deal. */ ++ sldns_buffer_write(pkt, qinfo->qname, qinfo->qname_len); ++ sldns_buffer_write_u16(pkt, qinfo->qtype); ++ sldns_buffer_write_u16(pkt, LDNS_RR_CLASS_IN); ++ ++ /* Install the RPZ RRs in the answer section */ ++ num_rrs = 0; ++ while(rr) { ++ /* Include only the requested RRs. */ ++ if(qinfo->qtype == LDNS_RR_TYPE_ANY || ++ rr->type == htons(qinfo->qtype) || ++ rr->type == htons(LDNS_RR_TYPE_CNAME)) { ++ if(!add_rr(pkt, qinfo->qname, qinfo->qname_len, ++ rr, rpz)) ++ return; ++ ++ ++num_rrs; ++ } ++ free(rr); ++ ++ next_rr(&rr, qinfo->qname, qinfo->qname_len, rpz); ++ } ++ /* Finish ANCOUNT. */ ++ if(num_rrs != 0) ++ sldns_buffer_write_u16_at(pkt, 6, num_rrs); ++ ++ /* All rewritten responses have an identifying SOA record in the ++ * additional section. */ ++ if(!librpz->rsp_soa(&emsg, NULL, &rr, &origin, ++ &rpz->result, rpz->rsp)) { ++ log_fail(rpz, "no soa"); ++ return; ++ } ++ if(!add_rr(pkt, origin.d, origin.size, rr, rpz)) ++ return; ++ free(rr); ++ ++ /* Create a dns_msg representation of the fake incoming message. */ ++ sldns_buffer_flip(pkt); ++ pkt2dns_msg(dnsmsg, pkt, rpz, region); ++} ++ ++ ++/* Check the RRs in the ANSWER section of a reply_info. */ ++static void ++ck_reply(struct reply_info* reply, uint8_t* qname, bool wait_ns, ++ commreply_rpz_t* rpz, struct module_env* env) ++{ ++ struct ub_packed_rrset_key* rrset; ++ enum sldns_enum_rr_type type; ++ uint rrset_n; ++ ++ /* Check the RRs in the ANSWER section. */ ++ rpz->cname_hit.size = 0; ++ rpz->cname_hit_2nd = false; ++ for(rrset_n = 0; rrset_n < reply->an_numrrsets; ++rrset_n) { ++ /* Check all of the RRs before deciding. */ ++ if(rpz->st != st_unknown) ++ return; ++ ++ rrset = reply->rrsets[rrset_n]; ++ if(ntohs(rrset->rk.rrset_class) != LDNS_RR_CLASS_IN) ++ continue; ++ type = ntohs(rrset->rk.type); ++ ++ if(type == LDNS_RR_TYPE_A) { ++ if(!ck_ip_rrset(rrset->entry.data, AF_INET, ++ LIBRPZ_TRIG_IP, qname, rpz)) ++ break; ++ ++ } else if(type == LDNS_RR_TYPE_AAAA) { ++ if(!ck_ip_rrset(rrset->entry.data, AF_INET6, ++ LIBRPZ_TRIG_IP, qname, rpz)) ++ break; ++ ++ } else if(type == LDNS_RR_TYPE_CNAME) { ++ /* Check CNAME owners unless we already have a hit. */ ++ ++rpz->hit_id; ++ if(!ck_qname(rrset->rk.dname, rrset->rk.dname_len, ++ true, wait_ns, rpz, env)) ++ break; ++ ++ /* Do not worry about the CNAME if it did not hit, ++ * but note the miss so that it can be prepended ++ * if we do hit. */ ++ if(rpz->result.hit_id != rpz->hit_id) { ++ rpz->cname_hit_2nd = true; ++ continue; ++ } ++ ++ /* Stop after hitting a CNAME. ++ * The iterator must be used to include CNAMEs before ++ * the CNAME that hit in the rewritten response. */ ++ rpz->cname_hit.size = rrset->rk.dname_len; ++ RPZ_ASSERT(rpz->cname_hit.size <= sizeof(rpz->cname_hit.d)); ++ memcpy(rpz->cname_hit.d, rrset->rk.dname, ++ rpz->cname_hit.size); ++ break; ++ } ++ } ++} ++ ++ ++static void ++worker_servfail(struct worker* worker, struct query_info* qinfo, ++ uint16_t id, uint16_t flags, struct comm_reply* commreply) ++{ ++ error_encode(commreply->c->buffer, LDNS_RCODE_SERVFAIL, ++ qinfo, id, flags, NULL); ++ regional_free_all(worker->scratchpad); ++ comm_point_send_reply(commreply); ++} ++ ++ ++/* Send an RPZ answer before the iterator has started. ++ * @return: 1=continue normal unbound processing ++ * 0=punt to the iterator ++ * -1=rewritten response already sent or dropped. */ ++static int ++worker_send(struct dns_msg* dnsmsg, struct worker* worker, ++ struct query_info* qinfo, uint16_t id, uint16_t flags, ++ struct edns_data* edns, struct comm_reply* commreply) ++{ ++ switch (commreply->rpz->st) { ++ case st_off: ++ return 1; ++ case st_servfail: ++ worker_servfail(worker, qinfo, id, flags, commreply); ++ return -1; ++ case st_unknown: ++ return 1; ++ case st_iterate: ++ case st_ck_ns: ++ return 0; /* punt to the iterator */ ++ case st_rewritten: ++ break; ++ default: ++ fatal_exit("impossible RPZ state %d in worker_send()", ++ commreply->rpz->st); ++ } ++ ++ if(dnsmsg->rep->security == sec_status_rpz_drop) { ++ regional_free_all(worker->scratchpad); ++ comm_point_drop_reply(commreply); ++ return -1; ++ } ++ ++ edns->edns_version = EDNS_ADVERTISED_VERSION; ++ edns->udp_size = EDNS_ADVERTISED_SIZE; ++ edns->ext_rcode = 0; ++ edns->bits = 0; /* rewritten response cannot verify. */ ++ if(!reply_info_answer_encode(qinfo, dnsmsg->rep, ++ id, flags | BIT_QR, ++ commreply->c->buffer, 0, 1, ++ worker->scratchpad, ++ edns->udp_size, edns, 0, 0)) { ++ worker_servfail(worker, qinfo, id, flags, commreply); ++ } else { ++ regional_free_all(worker->scratchpad); ++ comm_point_send_reply(commreply); ++ } ++ return -1; ++} ++ ++ ++/* Set commreply to an RPZ context if the response might be rewritten. ++ * Try to answer now with a hit allowed before recursion (iteration). */ ++bool /* true=response sent or dropped */ ++rpz_start(struct worker* worker, struct query_info* qinfo, ++ struct comm_reply* commreply, struct edns_data* edns) ++{ ++ commreply_rpz_t* rpz; ++ uint16_t id, flags; ++ struct dns_msg* dnsmsg; ++ int family; ++ const void* addr; ++ librpz_emsg_t emsg; ++ ++ /* Quit if rpz not configured. */ ++ if(!worker->daemon->rpz_client) ++ return false; ++ ++ /* Rewrite only the Internet class */ ++ if(qinfo->qclass != LDNS_RR_CLASS_IN) ++ return false; ++ ++ rpz = commreply->rpz; ++ RPZ_ASSERT(!rpz); ++ ++ dnsmsg = NULL; ++ id = htons(sldns_buffer_read_u16_at(commreply->c->buffer, 0)); ++ flags = sldns_buffer_read_u16_at(commreply->c->buffer, 2); ++ ++ rpz = malloc(sizeof(*rpz)); ++ if(!rpz) { ++ librpz->log(LIBRPZ_LOG_ERROR, NULL, "no memory for rpz"); ++ return 0 > worker_send(dnsmsg, worker, qinfo, ++ id, flags, edns, commreply); ++ } ++ memset(rpz, 0, sizeof(*rpz)); ++ rpz->st = st_unknown; ++ commreply->rpz = rpz; ++ ++ /* Make a new ID for log messages */ ++ rpz->log_id = __sync_add_and_fetch(&log_id, 1); ++ ++ /* Get access to the librpz data. */ ++ if(!librpz->rsp_create(&emsg, &rpz->rsp, &rpz->min_ns_dots, ++ worker->daemon->rpz_client, ++ (flags & BIT_RD) != 0, ++ (edns->bits & EDNS_DO) != 0)) { ++ log_fail(rpz, "%s", emsg.c); ++ return false; ++ } ++ /* Quit if benign reasons prevent rewriting. */ ++ if(!rpz->rsp) { ++ rpz->st = st_off; ++ librpz->log(LIBRPZ_LOG_TRACE1, rpz, "%s", emsg.c); ++ return false; ++ } ++ ++ /* Check the client IP address. ++ * Do not use commreply->srctype because it is often 0. */ ++ family = ((struct sockaddr*)&commreply->addr)->sa_family; ++ switch(family) { ++ case AF_INET: ++ addr = &((struct sockaddr_in*)&commreply->addr)->sin_addr; ++ break; ++ case AF_INET6: ++ addr = &((struct sockaddr_in6*)&commreply->addr)->sin6_addr; ++ break; ++ default: ++ /* Maybe the client is on a UNIX domain socket. */ ++ librpz->log(LIBRPZ_LOG_TRACE2, rpz, ++ "unknown client address family %d", family); ++ addr = NULL; ++ break; ++ } ++ /* Loop to ignore disabled zones. */ ++ while(addr) { ++ if(!push_st(rpz)) ++ break; ++ if(!librpz->ck_ip(&emsg, addr, family, LIBRPZ_TRIG_CLIENT_IP, ++ rpz->hit_id, true, rpz->rsp)) { ++ log_fail(rpz, "%s", emsg.c); ++ break; ++ } ++ if(!ck_after(qinfo->qname, false, LIBRPZ_TRIG_CLIENT_IP, rpz)) ++ break; ++ } ++ if(rpz->st == st_servfail) ++ return 0 > worker_send(dnsmsg, worker, qinfo, ++ id, flags, edns, commreply); ++ ++ /* Check the QNAME and possibly replace a client-IP hit. */ ++ ck_qname(qinfo->qname, qinfo->qname_len, false, true, ++ rpz, &worker->env); ++ ++ get_result_msg(&dnsmsg, qinfo, id, flags, false, ++ rpz, commreply->c, worker->scratchpad); ++ return 0 > worker_send(dnsmsg, worker, qinfo, ++ id, flags, edns, commreply); ++} ++ ++ ++/* Check a cached reply before iteration. ++ * @return: 1=use cache entry ++ * 0=deny a cached entry exists in order to punt to the iterator ++ * -1=rewritten response already sent or dropped */ ++int ++rpz_worker_cache(struct worker* worker, struct reply_info* reply, ++ struct query_info* qinfo, uint16_t id, uint16_t flags, ++ struct edns_data* edns, struct comm_reply* commreply) ++{ ++ commreply_rpz_t* rpz; ++ struct dns_msg* dnsmsg; ++ st_t new_st; ++ librpz_rr_t* rr; ++ ++ dnsmsg = NULL; ++ ++ rpz = commreply->rpz; ++ switch(rpz->st) { ++ case st_off: ++ return 1; /* Send the cache entry. */ ++ case st_servfail: ++ return worker_send(dnsmsg, worker, qinfo, id, flags, ++ edns, commreply); ++ case st_unknown: ++ break; ++ case st_iterate: ++ case st_ck_ns: ++ return 0; /* Punt to the iterator. */ ++ case st_rewritten: ++ default: ++ fatal_exit("impossible RPZ state %d in rpz_worker_cache()", ++ rpz->st); ++ } ++ ++ /* Check the RRs in the ANSWER section. */ ++ if(!push_st(rpz)) ++ return worker_send(dnsmsg, worker, qinfo, id, flags, edns, ++ commreply); ++ ++ ck_reply(reply, qinfo->qname, true, rpz, &worker->env); ++ if(!ck_result(qinfo->qname, true, rpz, commreply->c)) ++ return worker_send(dnsmsg, worker, qinfo, id, flags, edns, ++ commreply); ++ ++ if(rpz->cname_hit.size != 0) { ++ /* Punt to the iterator if leading CNAMEs must be ++ * included in the rewritten response. */ ++ rpz->cname_hit.size = 0; ++ new_st = st_iterate; ++ ++ } else if(rpz->result.policy == LIBRPZ_POLICY_CNAME) { ++ /* Punt if the rewritten response is to a CNAME. */ ++ new_st = st_iterate; ++ ++ } else { ++ if(rpz->result.policy == LIBRPZ_POLICY_RECORD) { ++ next_rr(&rr, qinfo->qname, qinfo->qname_len, rpz); ++ if(rr) { ++ /* Punt we are rewriting to a CNAME. */ ++ if(rr->type == ntohs(LDNS_RR_TYPE_CNAME)) { ++ free(rr); ++ rpz->st = st_iterate; ++ } else { ++ free(rr); ++ } ++ } ++ } ++ get_result_msg(&dnsmsg, qinfo, id, flags, true, ++ rpz, commreply->c, worker->scratchpad); ++ new_st = rpz->st; ++ } ++ ++ switch(new_st) { ++ case st_off: ++ case st_servfail: ++ break; ++ case st_unknown: ++ pop_discard_st(rpz); ++ break; ++ case st_iterate: ++ case st_ck_ns: ++ if(pop_st(rpz)) ++ rpz->st = new_st; ++ break; ++ case st_rewritten: ++ pop_discard_st(rpz); ++ break; ++ default: ++ fatal_exit("impossible RPZ state %d in rpz_worker_cache()", ++ rpz->st); ++ } ++ ++ return worker_send(dnsmsg, worker, qinfo, id, flags, edns, commreply); ++} ++ ++ ++/* Check a cache hit or miss for the iterator. ++ * A cache miss can already have a QNAME hit that was ignored before checking ++ * the iterator because of "QNAME-WAIT-RECURSE yes". ++ * Cache hits are treated like responses from authorities. */ ++bool /* false=SERVFAIL */ ++rpz_iter_cache(struct dns_msg** msg, enum response_type* type, ++ struct module_qstate* qstate, struct iter_qstate* iq) ++{ ++ struct comm_reply* commreply; ++ commreply_rpz_t* rpz; ++ struct dns_msg* dnsmsg; ++ ++ commreply = &qstate->mesh_info->reply_list->query_reply; ++ rpz = commreply->rpz; ++ ++ rpz->iterating = true; ++ ++ switch(rpz->st) { ++ case st_off: ++ iq->rpz_rewritten = 1; /* RPZ has nothing to say. */ ++ return true; ++ case st_servfail: ++ return false; ++ case st_unknown: ++ break; ++ case st_iterate: ++ case st_ck_ns: ++ rpz->st = st_unknown; ++ if(!ck_qname(iq->qchase.qname, iq->qchase.qname_len, ++ *msg != NULL, true, rpz, qstate->env)) ++ return false; ++ /* If we must recurse regardless and if NSIP/NSDNAME ++ * checking failed, then delay in the hope that ++ * recursion will also get NS data. */ ++ if(rpz->st == st_ck_ns) ++ return true; ++ break; ++ case st_rewritten: ++ default: ++ fatal_exit("impossible RPZ state %d in rpz_iter_cache()", ++ rpz->st); ++ } ++ ++ push_st(rpz); ++ ++ /* Check the cache hit. */ ++ if(*msg) ++ ck_reply((*msg)->rep, iq->qchase.qname, true, rpz, qstate->env); ++ ++ /* The DNS ID does not matter, because the generated dns_msg ++ * is nominally from an authority and not to the DNS client. */ ++ get_result_msg(&dnsmsg, &iq->qchase, 1, qstate->query_flags, true, ++ rpz, commreply->c, qstate->region); ++ ++ switch(rpz->st) { ++ case st_off: ++ iq->rpz_rewritten = 1; /* RPZ has nothing to say. */ ++ return true; ++ case st_servfail: ++ return false; ++ case st_unknown: ++ /* RPZ has nothing to say yet. Maybe there will be a hit ++ * later in the CNAME chain. */ ++ return pop_discard_st(rpz); ++ case st_ck_ns: ++ /* Try to get NS data for a CNAME found by ck_reply() */ ++ *type = RESPONSE_TYPE_CNAME; ++ return pop_discard_st(rpz); ++ case st_iterate: ++ default: ++ fatal_exit("impossible RPZ state %d in rpz_iter_cache()", ++ rpz->st); ++ case st_rewritten: ++ break; ++ } ++ ++ if(*msg && rpz->cname_hit.size != 0 && rpz->cname_hit_2nd) { ++ /* We hit a CNAME owner in the cached msg after not hitting one ++ * or more CNAME owners. We need to add those leading CNAMEs ++ * to the prepend list. Tell the iterator to treat the cached ++ * message as a RESPONSE_TYPE_CNAME even if it contains answers. ++ * handle_cname_response() will stop prepending CNAMEs before ++ * the triggering CNAME. handle_cname_response() will cause ++ * a restart to resolve the target of the preceding CNAME, ++ * which is the same as the hit CNAME owner. */ ++ rpz->st = st_unknown; ++ *type = RESPONSE_TYPE_CNAME; ++ return pop_discard_st(rpz); ++ } ++ ++ *msg = dnsmsg; ++ iq->rpz_security = dnsmsg->rep->security; ++ ++ if(dnsmsg && dnsmsg->rep->an_numrrsets != 0 && ++ dnsmsg->rep->rrsets[0]->rk.type == htons(LDNS_RR_TYPE_CNAME)) { ++ /* The cached msg triggered a rule that rewrites to a ++ * CNAME that must be resolved. ++ * We have a replacement dns_msg with that CNAME and also ++ * an SOA RR in the ADDITIONAL section that the iterator ++ * will lose as it adds the CNAME to the prepend list. ++ * Save the SOA RR in iq->rpz_soa. */ ++ iq->rpz_soa = dnsmsg->rep->rrsets[1]; ++ iq->rpz_rewritten = 1; ++ *type = RESPONSE_TYPE_CNAME; ++ return true; ++ } ++ ++ /* Otherwise we have rewritten to zero or more non-CNAME RRs. ++ * (DNAMEs are not supported.) ++ * Tell the iterator to send the rewritten message. */ ++ *type = RESPONSE_TYPE_ANSWER; ++ iq->rpz_rewritten = 1; ++ return true; ++} ++ ++ ++/* Check a RESPONSE_TYPE_ANSWER response from an authority in the iterator. */ ++rpz_iter_resp_t ++rpz_iter_resp(struct module_qstate* qstate, struct iter_qstate* iq, ++ struct dns_msg** resp, bool* is_cname) ++{ ++ struct comm_reply* commreply; ++ commreply_rpz_t* rpz; ++ struct reply_info* rep; ++ ++ *is_cname = false; ++ ++ commreply = &qstate->mesh_info->reply_list->query_reply; ++ rpz = commreply->rpz; ++ switch(rpz->st) { ++ case st_off: ++ case st_servfail: ++ case st_iterate: ++ case st_rewritten: ++ default: ++ fatal_exit("impossible RPZ state %d in rpz_iter_resp()", ++ rpz->st); ++ case st_ck_ns: ++ case st_unknown: ++ break; ++ } ++ ++ /* We know !iq->rpz_rewritten and so the response was after a simple ++ * cache miss when the original QNAME did not trigger a response ++ * or after a CNAME whose owner name did hit but was then forgotten ++ * with pop_st(). ++ * In either case, it is necessary to check the QNAME here. ++ * Checking the QNAME will not lose a better hit. */ ++ rpz->st = st_unknown; ++ ck_qname(iq->qchase.qname, iq->qchase.qname_len, true, false, ++ rpz, qstate->env); ++ ++ /* Check the RRs in the ANSWER section. */ ++ if(!push_st(rpz)) ++ return rpz_iter_resp_fail; ++ ck_reply(iq->response->rep, iq->qchase.qname, false, rpz, qstate->env); ++ get_result_msg(resp, &qstate->qinfo, 1, qstate->query_flags, true, ++ rpz, commreply->c, qstate->region); ++ switch(rpz->st) { ++ case st_off: ++ iq->rpz_rewritten = 1; /* Do not come back. */ ++ return rpz_iter_resp_done; ++ case st_servfail: /* Send SERVFAIL */ ++ return rpz_iter_resp_fail; ++ case st_unknown: ++ case st_ck_ns: ++ return rpz_iter_resp_done; /* continue without change */ ++ case st_iterate: ++ default: ++ fatal_exit("impossible RPZ state %d in rpz_iter_resp()", ++ rpz->st); ++ case st_rewritten: ++ /* Tell the iterator to use handle_cname_response() to ++ * prepend any preceding CNAMEs. ++ * We have a replacement dns_msg that also has an SOA RR in the ++ * ADDITIONAL section that the iterator will lose if it is a ++ * CNAME. Save that SOA in that case. */ ++ rep = (*resp)->rep; ++ if(rep->an_numrrsets != 0 && ++ rep->rrsets[0]->rk.type == ntohs(LDNS_RR_TYPE_CNAME)) { ++ *is_cname = true; ++ iq->rpz_soa = rep->rrsets[1]; ++ } ++ return rpz_iter_resp_rewrite; ++ } ++} ++ ++ ++/* Tell handle_cname_response() to stop adding to the answer prepend list ++ * after adding CNAME with a target that hits a QNAME trigger. ++ * Do not change any RPZ state, but expect the call of handle_cname_response() ++ * to try to resolve the CNAME and hit the same QNAME trigger and rewrite ++ * the response. */ ++rpz_cname_t ++rpz_cname(struct module_qstate* qstate, ++ uint8_t* oname, size_t oname_size) ++{ ++ struct mesh_reply* reply_list; ++ struct comm_reply* commreply; ++ commreply_rpz_t* rpz; ++ rpz_cname_t ret; ++ ++ /* Quit if RPZ is off */ ++ reply_list = qstate->mesh_info->reply_list; ++ if(!reply_list) ++ return rpz_cname_prepend; ++ commreply = &reply_list->query_reply; ++ rpz = commreply->rpz; ++ ++ if(!rpz || rpz->st == st_off) ++ return rpz_cname_prepend; ++ ++ /* Stop on a 2nd or later CNAME for rpz_iter_resp(). */ ++ if(rpz->cname_hit.size != 0) { ++ if(!query_dname_compare(rpz->cname_hit.d, oname)) ++ return rpz_cname_stop; ++ return rpz_cname_prepend; ++ } ++ ++ if(rpz->st != st_unknown) ++ fatal_exit("impossible RPZ state %d in rpz_cname()", rpz->st); ++ ++ ret = rpz_cname_prepend; ++ if(!push_st(rpz)) ++ return rpz_cname_fail; ++ /* Stop before prepending a CNAME that would preempt a ++ * rewritten response or before a possible NSDNAME or NSIP trigger. */ ++ ++rpz->hit_id; ++ ck_qname(oname, oname_size, true, true, rpz, qstate->env); ++ if(rpz->st != st_unknown) ++ ret = rpz_cname_stop; ++ if(!pop_st(rpz)) ++ return rpz_cname_fail; ++ return ret; ++} ++ ++#endif /* ENABLE_FASTRPZ */ +diff --git a/fastrpz/rpz.h b/fastrpz/rpz.h +new file mode 100644 +index 00000000..5d7e31c5 +--- /dev/null ++++ b/fastrpz/rpz.h +@@ -0,0 +1,138 @@ ++/* ++ * fastrpz/rpz.h - interface to the fastrpz response policy zone library ++ * ++ * Copyright (c) 2016 Farsight Security, Inc. ++ * ++ * Licensed under the Apache License, Version 2.0 (the "License"); ++ * you may not use this file except in compliance with the License. ++ * You may obtain a copy of the License at ++ * http://www.apache.org/licenses/LICENSE-2.0 ++ * ++ * Unless required by applicable law or agreed to in writing, software ++ * distributed under the License is distributed on an "AS IS" BASIS, ++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++ * See the License for the specific language governing permissions and ++ * limitations under the License. ++ */ ++ ++#ifndef UNBOUND_FASTRPZ_RPZ_H ++#define UNBOUND_FASTRPZ_RPZ_H ++ ++#ifndef PACKAGE_VERSION ++/* Ensure that config.h has been included to correctly set ENABLE_FASTRPZ */ ++#include "config.h" ++#endif ++ ++#ifdef ENABLE_FASTRPZ ++ ++#include "librpz.h" ++ ++#include "daemon/daemon.h" ++#include "util/config_file.h" ++ ++struct comm_point; /* forward references */ ++struct comm_reply; ++struct dns_msg; ++struct edns_data; ++struct iter_qstate; ++struct query_info; ++struct reply_info; ++enum response_type; /* iterator/iter_utils.h */ ++ ++ ++struct commreply_rpz; ++ ++/** ++ * Connect to the librpz database. ++ * @param pclist: future pointer to opaque librpz client data ++ * @param pclient: future pointer to opaque librpz client data ++ * @param cfg: parsed unbound configuration ++ */ ++void rpz_init(librpz_clist_t** pclist, librpz_client_t** pclient, ++ const struct config_file* cfg); ++ ++/** ++ * Disconnect from the librpz database ++ * @param client: opaque librpz client data ++ */ ++void rpz_delete(librpz_clist_t** pclist, librpz_client_t** pclient); ++ ++/** ++ * Start working on a DNS request and check for client IP address triggers. ++ * @param worker: the DNS request context ++ * @param qinfo: the DNS question ++ * @param[in,out] commreply: the answer ++ * @param c: where to send the response ++ * @param[in,out] edns for the DO flag ++ * @return true if response already sent or dropped ++ */ ++bool rpz_start(struct worker* worker, struct query_info* qinfo, ++ struct comm_reply* commreply, struct edns_data* edns); ++ ++/** ++ * Release resources held for a DNS request ++ * @param rspp: pointer to pointer to rpz client context. ++ */ ++void rpz_end(struct comm_reply* comm_rep); ++ ++/** ++ * Check a cached reply for RPZ hits before iteration ++ * @param worker: the DNS request context ++ * @param casheresp: cache reply ++ * @param qinfo: the DNS question ++ * @param id from the DNS request ++ * @param flags from the DNS request ++ * @param[in,out] edns for the DO flag ++ * @param[in,out] commreply: RPZ state ++ * @return 1=use cache entry, -1=rewritten response already sent or dropped, ++ * 0=deny a cached entry exists ++ */ ++int rpz_worker_cache(struct worker* worker, struct reply_info* cacheresp, ++ struct query_info* qinfo, uint16_t id, uint16_t flags, ++ struct edns_data* edns, struct comm_reply* commreply); ++ ++/** ++ * Check for an existing RPZ CNAME rewrite with "QNAME-WAIT-RECURSE no" ++ * that needs to be resolved before resolving the external request. ++ * @param[out] msg: rewritten CNAME response. ++ * @param qstate: query state. ++ * @param iq: iterator query state. ++ * @return false=send SERVFAIL ++ */ ++bool rpz_iter_cache(struct dns_msg** msg, enum response_type* type, ++ struct module_qstate* qstate, struct iter_qstate* iq); ++ ++/** ++ * Check a response from an authority in the iterator. ++ * @param[out] type: of the final response ++ * @param qstate: query state. ++ * @param iq: iterator query state. ++ * @param is_cname: true if the rewritten response is a CNAME ++ * @return one of rpz_resp_t ++ */ ++typedef enum { ++ rpz_iter_resp_fail, /* Send SERVFAIL. */ ++ rpz_iter_resp_rewrite, /* We rewrote the response. */ ++ rpz_iter_resp_done, /* Restart to refetch glue. */ ++} rpz_iter_resp_t; ++rpz_iter_resp_t rpz_iter_resp(struct module_qstate* qstate, ++ struct iter_qstate* iq, struct dns_msg** resp, ++ bool* is_cname); ++ ++/** ++ * Check a CNAME RR ++ * @param qstate: query state. ++ * @param oname: cname owner name ++ * @param oname_size: length of oname ++ * @return: one of rpz_cname_t ++ */ ++typedef enum { ++ rpz_cname_fail, /* send SERVFAIL */ ++ rpz_cname_prepend, /* prepend CNAME as usual */ ++ rpz_cname_stop, /* stop before prepending this CNAME */ ++} rpz_cname_t; ++rpz_cname_t rpz_cname(struct module_qstate* qstate, ++ uint8_t* oname, size_t oname_size); ++ ++#endif /* ENABLE_FASTRPZ */ ++#endif /* UNBOUND_FASTRPZ_RPZ_H */ +diff --git a/fastrpz/rpz.m4 b/fastrpz/rpz.m4 +new file mode 100644 +index 00000000..21235355 +--- /dev/null ++++ b/fastrpz/rpz.m4 +@@ -0,0 +1,64 @@ ++# fastrpz/rpz.m4 ++ ++# ck_FASTRPZ ++# -------------------------------------------------------------------------- ++# check for Fastrpz ++# --enable-fastrpz enable Fastrpz response policy zones ++# --enable-fastrpz-dl Fastrpz delayed link [default=have dlopen] ++# --with-fastrpz-dir directory containing librpz.so ++# ++# Fastrpz can be compiled into Unbound everywhere with a reasonably ++# modern C compiler. It is enabled on systems with dlopen() and librpz.so. ++ ++AC_DEFUN([ck_FASTRPZ], ++[ ++ fastrpz_avail=yes ++ AC_MSG_CHECKING([for librpz __attribute__s]) ++ AC_TRY_COMPILE(,[ ++ extern void f(char *p __attribute__((unused)), ...) ++ __attribute__((format(printf,1,2))) __attribute__((__noreturn__));], ++ librpz_have_attr=yes ++ AC_DEFINE([LIBRPZ_HAVE_ATTR], 1, [have __attribute__s used in librpz.h]) ++ AC_MSG_RESULT([yes]), ++ librpz_have_attr=no ++ AC_MSG_RESULT([no])) ++ ++ AC_SEARCH_LIBS(dlopen, dl) ++ librpz_dl=yes ++ AC_CHECK_FUNCS(dlopen dlclose dlsym,,librpz_dl=no) ++ AC_ARG_ENABLE([fastrpz-dl], ++ [ --enable-fastrpz-dl Fastrpz delayed link [[default=$librpz_dl]]], ++ [enable_librpz_dl="$enableval"], ++ [enable_librpz_dl="$librpz_dl"]) ++ AC_ARG_WITH([fastrpz-dir], ++ [ --with-fastrpz-dir directory containing librpz.so], ++ [librpz_path="$withval/librpz.so"], [librpz_path="librpz.so"]) ++ AC_DEFINE_UNQUOTED([FASTRPZ_LIBRPZ_PATH], ["$librpz_path"], ++ [fastrpz librpz.so]) ++ if test "x$enable_librpz_dl" = "xyes"; then ++ fastrpz_lib_open=2 ++ else ++ fastrpz_lib_open=1 ++ # Add librpz.so to linked libraries if we are not using dlopen() ++ AC_SEARCH_LIBS([librpz_client_create], [rpz], [], ++ [fastrpz_lib_open=0 ++ fastrpz_avail=no]) ++ fi ++ AC_DEFINE_UNQUOTED([FASTRPZ_LIB_OPEN], [$fastrpz_lib_open], ++ [0=no fastrpz 1=static link 2=dlopen()]) ++ ++ AC_ARG_ENABLE([fastrpz], ++ AS_HELP_STRING([--enable-fastrpz],[enable Fastrpz response policy zones]), ++ [enable_fastrpz=$enableval],[enable_fastrpz=$fastrpz_avail]) ++ if test "x$enable_fastrpz" = xyes; then ++ AC_DEFINE([ENABLE_FASTRPZ], [1], [Enable fastrpz]) ++ if test "x$fastrpz_lib_open" = "x0"; then ++ AC_MSG_ERROR([[dlopen and librpz.so needed for fastrpz]]) ++ fi ++ # used in Makefile.in ++ AC_SUBST([FASTRPZ_SRC], [fastrpz/rpz.c]) ++ AC_SUBST([FASTRPZ_OBJ], [rpz.lo]) ++ elif test "x$fastrpz_avail" = "x0"; then ++ AC_MSG_WARN([[dlopen and librpz.so needed for fastrpz]]) ++ fi ++]) +diff --git a/iterator/iterator.c b/iterator/iterator.c +index 1e0113a8..2fcbf547 100644 +--- a/iterator/iterator.c ++++ b/iterator/iterator.c +@@ -68,6 +68,9 @@ + #include "sldns/str2wire.h" + #include "sldns/parseutil.h" + #include "sldns/sbuffer.h" ++#ifdef ENABLE_FASTRPZ ++#include "fastrpz/rpz.h" ++#endif + + /* in msec */ + int UNKNOWN_SERVER_NICENESS = 376; +@@ -555,6 +558,23 @@ handle_cname_response(struct module_qstate* qstate, struct iter_qstate* iq, + 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)) { ++#ifdef ENABLE_FASTRPZ ++ /* Stop adding CNAME rrsets to the prepend list ++ * before defining an RPZ hit. */ ++ if(!iq->rpz_rewritten) { ++ switch (rpz_cname(qstate, *mname, *mname_len)) { ++ case rpz_cname_fail: ++ /* send SERVFAIL */ ++ return 0; ++ case rpz_cname_prepend: ++ /* save the CNAME. */ ++ break; ++ case rpz_cname_stop: ++ /* Pause before adding the CNAME. */ ++ goto stop_short; ++ } ++ } ++#endif + /* Add this relevant CNAME rrset to the prepend list.*/ + if(!iter_add_prepend_answer(qstate, iq, r)) + return 0; +@@ -563,6 +583,9 @@ handle_cname_response(struct module_qstate* qstate, struct iter_qstate* iq, + + /* Other rrsets in the section are ignored. */ + } ++#ifdef ENABLE_FASTRPZ ++stop_short: ; ++#endif + /* add authority rrsets to authority prepend, for wildcarded CNAMEs */ + for(i=msg->rep->an_numrrsets; irep->an_numrrsets + + msg->rep->ns_numrrsets; i++) { +@@ -1199,6 +1222,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, + uint8_t* delname; + size_t delnamelen; + struct dns_msg* msg = NULL; ++ enum response_type type; + + log_query_info(VERB_DETAIL, "resolving", &qstate->qinfo); + /* check effort */ +@@ -1285,8 +1309,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, + } + if(msg) { + /* handle positive cache response */ +- enum response_type type = response_type_from_cache(msg, +- &iq->qchase); ++ type = response_type_from_cache(msg, &iq->qchase); + if(verbosity >= VERB_ALGO) { + log_dns_msg("msg from cache lookup", &msg->qinfo, + msg->rep); +@@ -1294,7 +1317,22 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, + (int)msg->rep->ttl, + (int)msg->rep->prefetch_ttl); + } ++#ifdef ENABLE_FASTRPZ ++ } ++ /* Check for an RPZ hit in the cached DNS message or an existing ++ * RPZ CNAME rewrite that can be resolved now after a hit on the QNAME ++ * or client IP address. This can involve a creating a fake cache ++ * hit. It can also involve overriding an RESPONSE_TYPE_ANSWER ++ * result from response_type_from_cache(). Or it can ignore ++ * the cached result to refetch glue. */ ++ if(!iq->rpz_rewritten && ++ qstate->mesh_info->reply_list && ++ qstate->mesh_info->reply_list->query_reply.rpz && ++ !rpz_iter_cache(&msg, &type, qstate, iq)) ++ return error_response(qstate, id, LDNS_RCODE_SERVFAIL); + ++ if(msg) { ++#endif + if(type == RESPONSE_TYPE_CNAME) { + uint8_t* sname = 0; + size_t slen = 0; +@@ -2718,6 +2756,62 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, + sock_list_insert(&qstate->reply_origin, + &qstate->reply->addr, qstate->reply->addrlen, + qstate->region); ++#ifdef ENABLE_FASTRPZ ++ /* Check the response for an RPZ hit. The response has already ++ * been saved in the cache. This should have the same effect ++ * as finding that response in the cache. ++ * We have already used rpz_iter_cache() at least once. */ ++ if(!iq->rpz_rewritten && ++ qstate->mesh_info->reply_list && ++ qstate->mesh_info->reply_list->query_reply.rpz) { ++ struct dns_msg* resp; ++ bool is_cname; ++ uint8_t* sname; ++ size_t slen; ++ ++ switch (rpz_iter_resp(qstate, iq, &resp, &is_cname)) { ++ case rpz_iter_resp_fail: ++ return error_response(qstate, id, ++ LDNS_RCODE_SERVFAIL); ++ case rpz_iter_resp_rewrite: ++ /* Prepend any initial CNAMEs from the original ++ * response up to a hit. */ ++ if(!handle_cname_response(qstate, iq, ++ iq->response, ++ &sname, &slen)) ++ return error_response(qstate, id, ++ LDNS_RCODE_SERVFAIL); ++ if (resp) { ++ iq->response = resp; ++ iq->rpz_security = resp->rep->security; ++ iq->rpz_rewritten = 1; ++ ++ /* Send the rewritten record if it ++ * is not a CNAME. */ ++ if(!is_cname) ++ break; ++ ++ /* Prepend the new CNAME ++ * and restart to resolve it. */ ++ if(!handle_cname_response(qstate, iq, ++ resp, &sname, &slen)) ++ return error_response(qstate, id, ++ LDNS_RCODE_SERVFAIL); ++ } ++ iq->qchase.qname = sname; ++ iq->qchase.qname_len = slen; ++ iq->dp = NULL; ++ iq->refetch_glue = 0; ++ iq->query_restart_count++; ++ iq->sent_count = 0; ++ iq->state = INIT_REQUEST_STATE; ++ return 1; ++ ++ case rpz_iter_resp_done: ++ break; ++ } ++ } ++#endif + if(iq->minimisation_state != DONOT_MINIMISE_STATE + && !(iq->chase_flags & BIT_RD)) { + if(FLAGS_GET_RCODE(iq->response->rep->flags) != +@@ -3471,12 +3565,44 @@ processFinished(struct module_qstate* qstate, struct iter_qstate* iq, + * 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) { ++#ifdef ENABLE_FASTRPZ ++ /* Do not save RPZ rewritten messages. */ ++ if(!iq->rpz_rewritten) ++#endif + 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); + } + } ++#ifdef ENABLE_FASTRPZ ++ if(iq->rpz_rewritten) { ++ /* Restore RPZ marks on a rewritten response. The marks ++ * are lost if the rewrite is to a CNAME. */ ++ iq->response->rep->security = iq->rpz_security; ++ ++ /* Append the RPZ SOA to rewritten CNAME chains. */ ++ if(iq->rpz_soa) { ++ struct ub_packed_rrset_key** sets; ++ uint n; ++ ++ n = iq->response->rep->rrset_count; ++ sets = regional_alloc(qstate->region, ++ (1+n) * sizeof(*sets)); ++ if(!sets) { ++ log_err("append RPZ SOA: out of memory"); ++ return error_response(qstate, id, ++ LDNS_RCODE_SERVFAIL); ++ } ++ memcpy(sets, iq->response->rep->rrsets, ++ n * sizeof(struct ub_packed_rrset_key*)); ++ sets[n] = iq->rpz_soa; ++ iq->response->rep->rrsets = sets; ++ ++iq->response->rep->rrset_count; ++ ++iq->response->rep->ar_numrrsets; ++ } ++ } ++#endif + qstate->return_rcode = LDNS_RCODE_NOERROR; + qstate->return_msg = iq->response; + return 0; +diff --git a/iterator/iterator.h b/iterator/iterator.h +index a2f1b570..e1e4a738 100644 +--- a/iterator/iterator.h ++++ b/iterator/iterator.h +@@ -386,6 +386,16 @@ struct iter_qstate { + */ + int minimise_count; + ++ ++#ifdef ENABLE_FASTRPZ ++ /** The response has been rewritten by RPZ. */ ++ int rpz_rewritten; ++ /** RPZ SOA RR for the ADDITIONAL section */ ++ struct ub_packed_rrset_key* rpz_soa; ++ /** sec_status_rpz_rewritten or sec_status_rpz_drop if rewritten. */ ++ enum sec_status rpz_security; ++#endif ++ + /** + * Count number of time-outs. Used to prevent resolving failures when + * the QNAME minimisation QTYPE is blocked. */ +diff --git a/services/cache/dns.c b/services/cache/dns.c +index 2a5bca4a..6de8863a 100644 +--- a/services/cache/dns.c ++++ b/services/cache/dns.c +@@ -967,6 +967,14 @@ dns_cache_store(struct module_env* env, struct query_info* msgqinf, + struct regional* region, uint32_t flags) + { + struct reply_info* rep = NULL; ++ ++#ifdef ENABLE_FASTRPZ ++ /* Never save RPZ rewritten data. */ ++ if (msgrep->security == sec_status_rpz_drop || ++ msgrep->security == sec_status_rpz_rewritten) ++ return 1; ++#endif ++ + /* alloc, malloc properly (not in region, like msg is) */ + rep = reply_info_copy(msgrep, env->alloc, NULL); + if(!rep) +diff --git a/services/mesh.c b/services/mesh.c +index 9114ef4c..3dc518e5 100644 +--- a/services/mesh.c ++++ b/services/mesh.c +@@ -61,6 +61,9 @@ + #include "sldns/wire2str.h" + #include "services/localzone.h" + #include "util/data/dname.h" ++#ifdef ENABLE_FASTRPZ ++#include "fastrpz/rpz.h" ++#endif + #include "respip/respip.h" + #include "services/listen_dnsport.h" + +@@ -1195,6 +1198,13 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, + else secure = 0; + if(!rep && rcode == LDNS_RCODE_NOERROR) + rcode = LDNS_RCODE_SERVFAIL; ++#ifdef ENABLE_FASTRPZ ++ /* Drop the response here for LIBRPZ_POLICY_DROP after iteration. */ ++ if(rep && rep->security == sec_status_rpz_drop) { ++ log_query_info(VERB_QUERY, "rpz drop", &m->s.qinfo); ++ secure = 0; ++ } else ++#endif + /* 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 +@@ -1415,6 +1425,7 @@ struct mesh_state* mesh_area_find(struct mesh_area* mesh, + key.s.is_valrec = valrec; + key.s.qinfo = *qinfo; + key.s.query_flags = qflags; ++ key.reply_list = NULL; + /* 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).*/ +@@ -1461,6 +1472,10 @@ int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns, + if(!r) + return 0; + r->query_reply = *rep; ++#ifdef ENABLE_FASTRPZ ++ /* The new reply structure owns the RPZ state. */ ++ rep->rpz = NULL; ++#endif + r->edns = *edns; + if(edns->opt_list) { + r->edns.opt_list = edns_opt_copy_region(edns->opt_list, +diff --git a/util/config_file.c b/util/config_file.c +index 52ca5a18..0660248f 100644 +--- a/util/config_file.c ++++ b/util/config_file.c +@@ -1460,6 +1460,8 @@ config_delete(struct config_file* cfg) + free(cfg->dnstap_socket_path); + free(cfg->dnstap_identity); + free(cfg->dnstap_version); ++ if (cfg->rpz_cstr) ++ free(cfg->rpz_cstr); + config_deldblstrlist(cfg->ratelimit_for_domain); + config_deldblstrlist(cfg->ratelimit_below_domain); + config_delstrlist(cfg->python_script); +diff --git a/util/config_file.h b/util/config_file.h +index 8739ca2a..a2dcf215 100644 +--- a/util/config_file.h ++++ b/util/config_file.h +@@ -499,6 +499,11 @@ struct config_file { + /** true to disable DNSSEC lameness check in iterator */ + int disable_dnssec_lame_check; + ++ /** true to enable RPZ */ ++ int rpz_enable; ++ /** RPZ configuration */ ++ char* rpz_cstr; ++ + /** ratelimit for ip addresses. 0 is off, otherwise qps (unless overridden) */ + int ip_ratelimit; + /** number of slabs for ip_ratelimit cache */ +diff --git a/util/configlexer.lex b/util/configlexer.lex +index deedffa5..301458a3 100644 +--- a/util/configlexer.lex ++++ b/util/configlexer.lex +@@ -446,6 +446,10 @@ 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) } ++rpz{COLON} { YDVAR(0, VAR_RPZ) } ++rpz-enable{COLON} { YDVAR(1, VAR_RPZ_ENABLE) } ++rpz-zone{COLON} { YDVAR(1, VAR_RPZ_ZONE) } ++rpz-option{COLON} { YDVAR(1, VAR_RPZ_OPTION) } + disable-dnssec-lame-check{COLON} { YDVAR(1, VAR_DISABLE_DNSSEC_LAME_CHECK) } + ip-ratelimit{COLON} { YDVAR(1, VAR_IP_RATELIMIT) } + ratelimit{COLON} { YDVAR(1, VAR_RATELIMIT) } +diff --git a/util/configparser.y b/util/configparser.y +index d471babe..cb6b1d63 100644 +--- a/util/configparser.y ++++ b/util/configparser.y +@@ -125,6 +125,7 @@ extern struct config_parser_state* cfg_parser; + %token VAR_DNSTAP_LOG_CLIENT_RESPONSE_MESSAGES + %token VAR_DNSTAP_LOG_FORWARDER_QUERY_MESSAGES + %token VAR_DNSTAP_LOG_FORWARDER_RESPONSE_MESSAGES ++%token VAR_RPZ VAR_RPZ_ENABLE VAR_RPZ_ZONE VAR_RPZ_OPTION + %token VAR_RESPONSE_IP_TAG VAR_RESPONSE_IP VAR_RESPONSE_IP_DATA + %token VAR_HARDEN_ALGO_DOWNGRADE VAR_IP_TRANSPARENT + %token VAR_DISABLE_DNSSEC_LAME_CHECK +@@ -173,7 +174,7 @@ extern struct config_parser_state* cfg_parser; + + %% + toplevelvars: /* empty */ | toplevelvars toplevelvar ; +-toplevelvar: serverstart contents_server | stubstart contents_stub | ++toplevelvar: serverstart contents_server | stubstart contents_stub | rpzstart contents_rpz | + forwardstart contents_forward | pythonstart contents_py | + rcstart contents_rc | dtstart contents_dt | viewstart contents_view | + dnscstart contents_dnsc | cachedbstart contents_cachedb | +@@ -2837,6 +2838,50 @@ dt_dnstap_log_forwarder_response_messages: VAR_DNSTAP_LOG_FORWARDER_RESPONSE_MES + free($2); + } + ; ++rpzstart: VAR_RPZ ++ { ++ OUTYY(("\nP(rpz:)\n")); ++ } ++ ; ++contents_rpz: contents_rpz content_rpz ++ | ; ++content_rpz: rpz_enable | rpz_zone | rpz_option ++ ; ++rpz_enable: VAR_RPZ_ENABLE STRING_ARG ++ { ++ OUTYY(("P(rpz_enable:%s)\n", $2)); ++ if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) ++ yyerror("expected yes or no."); ++ else cfg_parser->cfg->rpz_enable = (strcmp($2, "yes")==0); ++ free($2); ++ } ++ ; ++rpz_zone: VAR_RPZ_ZONE STRING_ARG ++ { ++ char *new_cstr, *old_cstr; ++ ++ OUTYY(("P(rpz_zone:%s)\n", $2)); ++ old_cstr = cfg_parser->cfg->rpz_cstr; ++ if(asprintf(&new_cstr, "%s\nzone %s", old_cstr?old_cstr:"", $2) == -1) {new_cstr = NULL; yyerror("out of memory");} ++ else if(!new_cstr) ++ yyerror("out of memory"); ++ free(old_cstr); ++ cfg_parser->cfg->rpz_cstr = new_cstr; ++ } ++ ; ++rpz_option: VAR_RPZ_OPTION STRING_ARG ++ { ++ char *new_cstr, *old_cstr; ++ ++ OUTYY(("P(rpz_option:%s)\n", $2)); ++ old_cstr = cfg_parser->cfg->rpz_cstr; ++ if(asprintf(&new_cstr, "%s\n%s", old_cstr ? old_cstr : "", $2) == -1) {new_cstr = NULL; yyerror("out of memory");} ++ else if(!new_cstr) ++ yyerror("out of memory"); ++ free(old_cstr); ++ cfg_parser->cfg->rpz_cstr = new_cstr; ++ } ++ ; + pythonstart: VAR_PYTHON + { + OUTYY(("\nP(python:)\n")); +diff --git a/util/data/msgencode.c b/util/data/msgencode.c +index be69f628..f10773aa 100644 +--- a/util/data/msgencode.c ++++ b/util/data/msgencode.c +@@ -592,6 +592,35 @@ insert_section(struct reply_info* rep, size_t num_rrsets, uint16_t* num_rrs, + return RETVAL_OK; + } + ++#ifdef ENABLE_FASTRPZ ++/* Insert the RPZ SOA even with MINIMAL_RESPONSES */ ++static int ++insert_rpz_soa(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, ++ size_t rr_offset) ++{ ++ int r; ++ size_t i, setstart; ++ ++ *num_rrs = 0; ++ for(i=0; irrsets[rrsets_before+i]->rk.type != LDNS_RR_TYPE_SOA) ++ continue; ++ setstart = sldns_buffer_position(pkt); ++ if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i], ++ pkt, num_rrs, timenow, region, ++ 1, 0, tree, LDNS_SECTION_ADDITIONAL, ++ LDNS_RR_TYPE_ANY, 0, rr_offset)) ++ != RETVAL_OK) { ++ sldns_buffer_set_position(pkt, setstart); ++ return r; ++ } ++ } ++ return RETVAL_OK; ++} ++ ++#endif + /** store query section in wireformat buffer, return RETVAL */ + static int + insert_query(struct query_info* qinfo, struct compress_tree_node** tree, +@@ -779,6 +808,19 @@ reply_info_encode(struct query_info* qinfo, struct reply_info* rep, + } + sldns_buffer_write_u16_at(buffer, 10, arcount); + } ++#ifdef ENABLE_FASTRPZ ++ } else if(rep->security == sec_status_rpz_rewritten) { ++ /* Insert the RPZ SOA for rpz even with MINIMAL_RESPONSES */ ++ r = insert_rpz_soa(rep, rep->ar_numrrsets, &arcount, buffer, ++ rep->an_numrrsets + rep->ns_numrrsets, ++ timenow, region, &tree, rr_offset); ++ if(r!= RETVAL_OK) { ++ if(r != RETVAL_TRUNC) ++ return 0; ++ /* no need to set TC bit, this is the additional */ ++ sldns_buffer_write_u16_at(buffer, 10, arcount); ++ } ++#endif + } + sldns_buffer_flip(buffer); + return 1; +diff --git a/util/data/packed_rrset.c b/util/data/packed_rrset.c +index 4b0294f9..3b3838f6 100644 +--- a/util/data/packed_rrset.c ++++ b/util/data/packed_rrset.c +@@ -256,6 +256,10 @@ sec_status_to_string(enum sec_status s) + case sec_status_insecure: return "sec_status_insecure"; + case sec_status_secure_sentinel_fail: return "sec_status_secure_sentinel_fail"; + case sec_status_secure: return "sec_status_secure"; ++#ifdef ENABLE_FASTRPZ ++ case sec_status_rpz_rewritten: return "sec_status_rpz_rewritten"; ++ case sec_status_rpz_drop: return "sec_status_rpz_drop"; ++#endif + } + return "unknown_sec_status_value"; + } +diff --git a/util/data/packed_rrset.h b/util/data/packed_rrset.h +index 729877ba..ccd1a0c2 100644 +--- a/util/data/packed_rrset.h ++++ b/util/data/packed_rrset.h +@@ -193,7 +193,15 @@ enum sec_status { + sec_status_secure_sentinel_fail, + /** SECURE means that the object (RRset or message) validated + * according to local policy. */ +- sec_status_secure ++ sec_status_secure, ++#ifdef ENABLE_FASTRPZ ++ /** RPZ_REWRITTEN means that the response has been rewritten by ++ * rpz and so cannot be verified. */ ++ sec_status_rpz_rewritten, ++ /** RPZ_DROP means that the response has been rewritten by rpz ++ * as silence. */ ++ sec_status_rpz_drop ++#endif + }; + + /** +diff --git a/util/netevent.c b/util/netevent.c +index 9fe5da2d..037e70d1 100644 +--- a/util/netevent.c ++++ b/util/netevent.c +@@ -57,6 +57,9 @@ + #ifdef HAVE_OPENSSL_ERR_H + #include + #endif ++#ifdef ENABLE_FASTRPZ ++#include "fastrpz/rpz.h" ++#endif + + /* -------- Start of local definitions -------- */ + /** if CMSG_ALIGN is not defined on this platform, a workaround */ +@@ -590,6 +593,9 @@ comm_point_udp_ancil_callback(int fd, short event, void* arg) + struct cmsghdr* cmsg; + #endif /* S_SPLINT_S */ + ++#ifdef ENABLE_FASTRPZ ++ rep.rpz = NULL; ++#endif + rep.c = (struct comm_point*)arg; + log_assert(rep.c->type == comm_udp); + +@@ -679,6 +685,9 @@ comm_point_udp_callback(int fd, short event, void* arg) + int i; + struct sldns_buffer *buffer; + ++#ifdef ENABLE_FASTRPZ ++ rep.rpz = NULL; ++#endif + rep.c = (struct comm_point*)arg; + log_assert(rep.c->type == comm_udp); + +@@ -722,6 +731,9 @@ comm_point_udp_callback(int fd, short event, void* arg) + (void)comm_point_send_udp_msg(rep.c, buffer, + (struct sockaddr*)&rep.addr, rep.addrlen); + } ++#ifdef ENABLE_FASTRPZ ++ rpz_end(&rep); ++#endif + 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; +@@ -3192,6 +3204,9 @@ comm_point_send_reply(struct comm_reply *repinfo) + repinfo->c->tcp_timeout_msec); + } + } ++#ifdef ENABLE_FASTRPZ ++ rpz_end(repinfo); ++#endif + } + + void +@@ -3201,6 +3216,9 @@ comm_point_drop_reply(struct comm_reply* repinfo) + return; + log_assert(repinfo->c); + log_assert(repinfo->c->type != comm_tcp_accept); ++#ifdef ENABLE_FASTRPZ ++ rpz_end(repinfo); ++#endif + if(repinfo->c->type == comm_udp) + return; + if(repinfo->c->tcp_req_info) +@@ -3222,6 +3240,9 @@ 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); ++#ifdef ENABLE_FASTRPZ ++ rpz_end(&c->repinfo); ++#endif + if(c->type == comm_tcp_accept && !c->tcp_free) { + /* no use to start listening no free slots. */ + return; +diff --git a/util/netevent.h b/util/netevent.h +index d80c72b3..0233292f 100644 +--- a/util/netevent.h ++++ b/util/netevent.h +@@ -120,6 +120,10 @@ struct comm_reply { + /** return type 0 (none), 4(IP4), 6(IP6) */ + int srctype; + /* DnsCrypt context */ ++#ifdef ENABLE_FASTRPZ ++ /** per-request RPZ state */ ++ struct commreply_rpz* rpz; ++#endif + #ifdef USE_DNSCRYPT + uint8_t client_nonce[crypto_box_HALF_NONCEBYTES]; + uint8_t nmkey[crypto_box_BEFORENMBYTES]; +diff --git a/validator/validator.c b/validator/validator.c +index c3ca0a27..15251988 100644 +--- a/validator/validator.c ++++ b/validator/validator.c +@@ -2761,6 +2761,12 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, + default: + /* NSEC proof did not work, try next */ + break; ++#ifdef ENABLE_FASTRPZ ++ case sec_status_rpz_rewritten: ++ case sec_status_rpz_drop: ++ fatal_exit("impossible RPZ sec_status"); ++ break; ++#endif + } + + sec = nsec3_prove_nods(qstate->env, ve, +@@ -2794,6 +2800,12 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, + default: + /* NSEC3 proof did not work */ + break; ++#ifdef ENABLE_FASTRPZ ++ case sec_status_rpz_rewritten: ++ case sec_status_rpz_drop: ++ fatal_exit("impossible RPZ sec_status"); ++ break; ++#endif + } + + /* Apparently, no available NSEC/NSEC3 proved NODATA, so --- contrib/unbound/contrib/libunbound.pc.in.orig +++ contrib/unbound/contrib/libunbound.pc.in @@ -7,7 +7,8 @@ Description: Library with validating, recursive, and caching DNS resolver URL: http://www.unbound.net Version: @PACKAGE_VERSION@ -Requires: -Libs: -L${libdir} -lunbound @SSLLIB@ @LIBS@ -Libs.private: @LDFLAGS@ -Cflags: -I${includedir} +Requires: @PC_CRYPTO_DEPENDENCY@ @PC_LIBEVENT_DEPENDENCY@ +Requires.private: @PC_PY_DEPENDENCY@ @PC_LIBBSD_DEPENDENCY@ +Libs: -L${libdir} -lunbound +Libs.private: @SSLLIB@ @LIBS@ +Cflags: -I${includedir} --- contrib/unbound/contrib/libunbound.so.conf.orig +++ contrib/unbound/contrib/libunbound.so.conf @@ -0,0 +1,42 @@ +# See ltrace.conf(5) for description of syntax of this file. +typedef ub_type = enum(TYPE_A=1,TYPE_NS=2,TYPE_SOA=6,TYPE_MX=15,TYPE_TXT=16,TYPE_AAAA=28,TYPE_DS=43,TYPE_DNSKEY=48,TYPE_TLSA=52,TYPE_ANY=255); +typedef ub_class = enum(CLASS_IN=1,CLASS_CH=3,CLASS_NONE=254,CLASS_ANY=255); +typedef ub_rcode = enum(RCODE_NOERROR,RCODE_FORMERR,RCODE_SERVFAIL,RCODE_NXDOMAIN,RCODE_NOTIMPL,RCODE_REFUSED,RCODE_YXDOMAIN,RCODE_YXRRSET,RCODE_NXRRSET,RCODE_NOTAUTH,RCODE_NOTZONE); +typedef ub_havedata = enum(no_data, have_data); +typedef ub_nxdomain = enum(name_exists, nxdomain); +typedef ub_secure = enum(not_secure, secure); +typedef ub_bogus = enum(not_bogus, bogus); +typedef ub_result = struct(string, ub_type, ub_class, array(void*,zero)*, array(int,zero)*, string, ub_rcode, void*, int, ub_havedata, ub_nxdomain, ub_secure, ub_bogus, string, int); +typedef ub_ctx = void; +ub_ctx* ub_ctx_create(void); +void ub_ctx_delete(ub_ctx*); +int ub_ctx_set_option(ub_ctx*, string, string); +int ub_ctx_get_option(ub_ctx*, string, +string*); +int ub_ctx_config(ub_ctx*, string); +int ub_ctx_set_fwd(ub_ctx*, string); +int ub_ctx_set_tls(ub_ctx*, bool(int)); +int ub_ctx_set_stub(ub_ctx*, string, string, bool(int)); +int ub_ctx_resolvconf(ub_ctx*, string); +int ub_ctx_hosts(ub_ctx*, string); +int ub_ctx_add_ta(ub_ctx*, string); +int ub_ctx_add_ta_file(ub_ctx*, string); +int ub_ctx_add_ta_autr(ub_ctx*, string); +int ub_ctx_trustedkeys(ub_ctx*, string); +int ub_ctx_debugout(ub_ctx*, void*); +int ub_ctx_debuglevel(ub_ctx*, int); +int ub_ctx_async(ub_ctx*, bool(int)); +int ub_poll(ub_ctx*); +int ub_wait(ub_ctx*); +int ub_fd(ub_ctx*); +int ub_process(ub_ctx*); +int ub_resolve(ub_ctx*, string, ub_type, ub_class, +ub_result**); +int ub_resolve_async(ub_ctx*, string, ub_type, ub_class, void*, void*, +int*); +int ub_cancel(ub_ctx*, int); +void ub_resolve_free(ub_result*); +string ub_strerror(int); +int ub_ctx_print_local_zones(ub_ctx*); +int ub_ctx_zone_add(ub_ctx*, string, string); +int ub_ctx_zone_remove(ub_ctx*, string); +int ub_ctx_data_add(ub_ctx*, string); +int ub_ctx_data_remove(ub_ctx*, string); +string ub_version(void); --- contrib/unbound/contrib/parseunbound.pl.orig +++ contrib/unbound/contrib/parseunbound.pl @@ -91,7 +91,7 @@ $allstats{$inthread}->{outstandingexc} = $4; } elsif ( $line =~ m/info: average recursion processing time ([0-9\.]+) sec/ ) { - $allstats{$inthread}->{recursionavg} = int($1 * 1000); # change sec to milisec. + $allstats{$inthread}->{recursionavg} = int($1 * 1000); # change sec to millisec. } elsif ( $line =~ m/info: histogram of recursion processing times/ ) { next; @@ -103,7 +103,7 @@ } elsif ( $line =~ m/info: lower\(secs\) upper\(secs\) recursions/ ) { # since after this line we're unsure if we get these numbers - # at all, we sould consider this marker as the end of the + # at all, we should consider this marker as the end of the # block. Chances that we're parsing a file halfway written # at this stage are small. Bold statement. $donestats{$inthread} = 1; --- contrib/unbound/contrib/redirect-bogus.patch.orig +++ contrib/unbound/contrib/redirect-bogus.patch @@ -0,0 +1,344 @@ +Index: daemon/worker.c +=================================================================== +--- daemon/worker.c (revision 4191) ++++ daemon/worker.c (working copy) +@@ -663,8 +663,21 @@ + if(!inplace_cb_reply_servfail_call(&worker->env, qinfo, NULL, rep, + LDNS_RCODE_SERVFAIL, edns, worker->scratchpad)) + goto bail_out; +- error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL, +- qinfo, id, flags, edns); ++ if (qinfo->qtype == LDNS_RR_TYPE_A && ++ worker->env.cfg->redirect_bogus_ipv4) { ++ /* BAD cached */ ++ fixed_address_encode(repinfo->c->buffer, ++ LDNS_RCODE_NOERROR, qinfo, id, flags, edns, ++ worker->env.cfg->redirect_bogus_ipv4); ++ } else if (qinfo->qtype == LDNS_RR_TYPE_AAAA && ++ worker->env.cfg->redirect_bogus_ipv6) { ++ fixed_address_encode(repinfo->c->buffer, ++ LDNS_RCODE_NOERROR, qinfo, id, flags, edns, ++ worker->env.cfg->redirect_bogus_ipv6); ++ } else { ++ 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) { +Index: doc/unbound.conf.5.in +=================================================================== +--- doc/unbound.conf.5.in (revision 4191) ++++ doc/unbound.conf.5.in (working copy) +@@ -1244,6 +1244,18 @@ + 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 redirect-bogus-ipv4: \fI ++Set a fixed address for DNSSEC failures that are cached ++Instead of responding to A queries with SERVFAIL, respond ++with NOERROR and the address specified here ++The TTL of the response will be 5 seconds ++.TP 5 ++.B redirect-bogus-ipv6: \fI ++Set a fixed address for DNSSEC failures that are cached ++Instead of responding to AAAA queries with SERVFAIL, respond ++with NOERROR and the address specified here ++The TTL of the response will be 5 seconds + .SS "Remote Control Options" + In the + .B remote\-control: +Index: services/mesh.c +=================================================================== +--- services/mesh.c (revision 4191) ++++ services/mesh.c (working copy) +@@ -1006,6 +1006,7 @@ + struct timeval end_time; + struct timeval duration; + int secure; ++ int bogus_override = 0; + /* Copy the client's EDNS for later restore, to make sure the edns + * compare is with the correct edns options. */ + struct edns_data edns_bak = r->edns; +@@ -1016,6 +1017,7 @@ + rcode = LDNS_RCODE_SERVFAIL; + if(m->s.env->cfg->stat_extended) + m->s.env->mesh->ans_bogus++; ++ bogus_override = 1; + } + if(rep && rep->security == sec_status_secure) + secure = 1; +@@ -1047,17 +1049,34 @@ + } 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, m->s.region)) +- r->edns.opt_list = NULL; +- } else { +- if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep, rcode, +- &r->edns, m->s.region)) +- r->edns.opt_list = NULL; ++ if(bogus_override && m->s.qinfo.qtype == LDNS_RR_TYPE_A && ++ m->s.env->cfg->redirect_bogus_ipv4) { ++ fixed_address_encode(r->query_reply.c->buffer, ++ LDNS_RCODE_NOERROR, &m->s.qinfo, r->qid, ++ r->qflags, &r->edns, ++ m->s.env->cfg->redirect_bogus_ipv4); ++ } else if(bogus_override && ++ m->s.qinfo.qtype == LDNS_RR_TYPE_AAAA && ++ m->s.env->cfg->redirect_bogus_ipv6) { ++ fixed_address_encode(r->query_reply.c->buffer, ++ LDNS_RCODE_NOERROR, &m->s.qinfo, r->qid, ++ r->qflags, &r->edns, ++ m->s.env->cfg->redirect_bogus_ipv6); ++ } else { ++ if(rcode == LDNS_RCODE_SERVFAIL) { ++ if(!inplace_cb_reply_servfail_call(m->s.env, ++ &m->s.qinfo, &m->s, ++ rep, rcode, &r->edns, m->s.region)) ++ r->edns.opt_list = NULL; ++ } else { ++ if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, ++ &m->s, rep, rcode, &r->edns, ++ m->s.region)) ++ r->edns.opt_list = NULL; ++ } ++ error_encode(r->query_reply.c->buffer, rcode, ++ &m->s.qinfo, r->qid, r->qflags, &r->edns); + } +- error_encode(r->query_reply.c->buffer, rcode, &m->s.qinfo, +- r->qid, r->qflags, &r->edns); + comm_point_send_reply(&r->query_reply); + } else { + size_t udp_size = r->edns.udp_size; +Index: util/config_file.c +=================================================================== +--- util/config_file.c (revision 4191) ++++ util/config_file.c (working copy) +@@ -273,6 +273,8 @@ + cfg->ratelimit_factor = 10; + cfg->qname_minimisation = 0; + cfg->qname_minimisation_strict = 0; ++ cfg->redirect_bogus_ipv4 = NULL; ++ cfg->redirect_bogus_ipv6 = NULL; + cfg->shm_enable = 0; + cfg->shm_key = 11777; + cfg->dnscrypt = 0; +@@ -602,6 +604,10 @@ + } + oi[cfg->num_out_ifs++] = d; + cfg->out_ifs = oi; ++ } else if (strcmp(opt, "redirect-bogus-ipv4:") == 0) { ++ cfg->redirect_bogus_ipv4 = strdup(val); ++ } else if (strcmp(opt, "redirect-bogus-ipv6:") == 0) { ++ cfg->redirect_bogus_ipv6 = strdup(val); + } else { + /* unknown or unsupported (from the set_option interface): + * interface, outgoing-interface, access-control, +@@ -1250,6 +1256,12 @@ + free(cfg->dnstap_version); + config_deldblstrlist(cfg->ratelimit_for_domain); + config_deldblstrlist(cfg->ratelimit_below_domain); ++ if (cfg->redirect_bogus_ipv4) { ++ free(cfg->redirect_bogus_ipv4); ++ } ++ if (cfg->redirect_bogus_ipv6) { ++ free(cfg->redirect_bogus_ipv6); ++ } + #ifdef USE_IPSECMOD + free(cfg->ipsecmod_hook); + config_delstrlist(cfg->ipsecmod_whitelist); +Index: util/config_file.h +=================================================================== +--- util/config_file.h (revision 4191) ++++ util/config_file.h (working copy) +@@ -444,6 +444,9 @@ + /** minimise QNAME in strict mode, minimise according to RFC. + * Do not apply fallback */ + int qname_minimisation_strict; ++ /** construct fake responses for DNSSEC failures */ ++ char *redirect_bogus_ipv4; ++ char *redirect_bogus_ipv6; + /** SHM data - true if shm is enabled */ + int shm_enable; + /** SHM data - key for the shm */ +Index: util/configlexer.lex +=================================================================== +--- util/configlexer.lex (revision 4191) ++++ util/configlexer.lex (working copy) +@@ -410,6 +410,8 @@ + 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) } ++redirect-bogus-ipv4{COLON} { YDVAR(1, VAR_REDIRECT_BOGUS_IPV4) } ++redirect-bogus-ipv6{COLON} { YDVAR(1, VAR_REDIRECT_BOGUS_IPV6) } + dnscrypt{COLON} { YDVAR(0, VAR_DNSCRYPT) } + dnscrypt-enable{COLON} { YDVAR(1, VAR_DNSCRYPT_ENABLE) } + dnscrypt-port{COLON} { YDVAR(1, VAR_DNSCRYPT_PORT) } +Index: util/configparser.y +=================================================================== +--- util/configparser.y (revision 4191) ++++ util/configparser.y (working copy) +@@ -44,6 +44,7 @@ + #include + #include + ++#include "sldns/str2wire.h" + #include "util/configyyrename.h" + #include "util/config_file.h" + #include "util/net_help.h" +@@ -141,6 +142,7 @@ + %token VAR_ACCESS_CONTROL_TAG_DATA VAR_VIEW VAR_ACCESS_CONTROL_VIEW + %token VAR_VIEW_FIRST VAR_SERVE_EXPIRED VAR_FAKE_DSA VAR_FAKE_SHA1 + %token VAR_LOG_IDENTITY VAR_HIDE_TRUSTANCHOR VAR_TRUST_ANCHOR_SIGNALING ++%token VAR_REDIRECT_BOGUS_IPV4 VAR_REDIRECT_BOGUS_IPV6 + %token VAR_USE_SYSTEMD VAR_SHM_ENABLE VAR_SHM_KEY + %token VAR_DNSCRYPT VAR_DNSCRYPT_ENABLE VAR_DNSCRYPT_PORT VAR_DNSCRYPT_PROVIDER + %token VAR_DNSCRYPT_SECRET_KEY VAR_DNSCRYPT_PROVIDER_CERT +@@ -228,6 +230,7 @@ + server_access_control_tag_data | server_access_control_view | + server_qname_minimisation_strict | server_serve_expired | + server_fake_dsa | server_log_identity | server_use_systemd | ++ server_redirect_bogus_ipv4 | server_redirect_bogus_ipv6 | + 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 | +@@ -1873,6 +1876,34 @@ + #endif + } + ; ++server_redirect_bogus_ipv4: VAR_REDIRECT_BOGUS_IPV4 STRING_ARG ++ { ++ uint8_t data[4]; ++ size_t data_len = 4; ++ OUTYY(("P(name:%s)\n", $2)); ++ if(cfg_parser->cfg->redirect_bogus_ipv4) { ++ yyerror("redirect-bogus-ipv4, can only use one address"); ++ } ++ if(sldns_str2wire_a_buf($2, data, &data_len) != LDNS_WIREPARSE_ERR_OK) { ++ yyerror("redirect-bogus-ipv4, not a valid IPv4 address"); ++ } ++ free(cfg_parser->cfg->redirect_bogus_ipv4); ++ cfg_parser->cfg->redirect_bogus_ipv4 = $2; ++ } ++server_redirect_bogus_ipv6: VAR_REDIRECT_BOGUS_IPV6 STRING_ARG ++ { ++ uint8_t data[16]; ++ size_t data_len = 16; ++ OUTYY(("P(name:%s)\n", $2)); ++ if(cfg_parser->cfg->redirect_bogus_ipv6) { ++ yyerror("redirect-bogus-ipv6, can only use one address"); ++ } ++ if(sldns_str2wire_aaaa_buf($2, data, &data_len) != LDNS_WIREPARSE_ERR_OK) { ++ yyerror("redirect-bogus-ipv6, not a valid IPv6 address"); ++ } ++ free(cfg_parser->cfg->redirect_bogus_ipv6); ++ cfg_parser->cfg->redirect_bogus_ipv6 = $2; ++ } + stub_name: VAR_NAME STRING_ARG + { + OUTYY(("P(name:%s)\n", $2)); +Index: util/data/msgencode.c +=================================================================== +--- util/data/msgencode.c (revision 4191) ++++ util/data/msgencode.c (working copy) +@@ -48,6 +48,7 @@ + #include "util/regional.h" + #include "util/net_help.h" + #include "sldns/sbuffer.h" ++#include "sldns/str2wire.h" + #include "services/localzone.h" + + /** return code that means the function ran out of memory. negative so it does +@@ -914,3 +915,63 @@ + attach_edns_record(buf, &es); + } + } ++ ++void ++fixed_address_encode(sldns_buffer* buf, int r, struct query_info* qinfo, ++ uint16_t qid, uint16_t qflags, struct edns_data* edns, char* data) ++{ ++ uint16_t flags; ++ uint8_t addr_data[16]; ++ size_t addr_len = 16; ++ if (qinfo->qtype == LDNS_RR_TYPE_A) { ++ sldns_str2wire_a_buf(data, addr_data, &addr_len); ++ } else if (qinfo->qtype == LDNS_RR_TYPE_AAAA) { ++ sldns_str2wire_aaaa_buf(data, addr_data, &addr_len); ++ } else { ++ return error_encode(buf, LDNS_RCODE_NOERROR, qinfo, qid, qflags, edns); ++ } ++ sldns_buffer_clear(buf); ++ sldns_buffer_write(buf, &qid, sizeof(uint16_t)); ++ flags = (uint16_t)(BIT_QR | BIT_RA | r); /* QR and retcode*/ ++ 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); ++ sldns_buffer_write_u16(buf, 1); ++ flags = 0; ++ sldns_buffer_write(buf, &flags, sizeof(uint16_t)); ++ sldns_buffer_write(buf, &flags, sizeof(uint16_t)); ++ if(qinfo) { ++ // query ++ if(sldns_buffer_current(buf) == qinfo->qname) ++ sldns_buffer_skip(buf, (ssize_t)qinfo->qname_len); ++ else sldns_buffer_write(buf, qinfo->qname, qinfo->qname_len); ++ sldns_buffer_write_u16(buf, qinfo->qtype); ++ sldns_buffer_write_u16(buf, qinfo->qclass); ++ // faked answer ++ if(sldns_buffer_current(buf) == qinfo->qname) ++ sldns_buffer_skip(buf, (ssize_t)qinfo->qname_len); ++ else sldns_buffer_write(buf, qinfo->qname, qinfo->qname_len); ++ sldns_buffer_write_u16(buf, qinfo->qtype); ++ sldns_buffer_write_u16(buf, qinfo->qclass); ++ sldns_buffer_write_u16(buf, 0); ++ // TTL. Should we make this configurable too? ++ sldns_buffer_write_u16(buf, 5); ++ sldns_buffer_write_u16(buf, addr_len); ++ sldns_buffer_write(buf, addr_data, addr_len); ++ fflush(stderr); ++ } ++ 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 = 0; ++ es.bits &= EDNS_DO; ++ if(sldns_buffer_limit(buf) + calc_edns_field_size(&es) > ++ edns->udp_size) ++ return; ++ attach_edns_record(buf, &es); ++ } ++} +Index: util/data/msgencode.h +=================================================================== +--- util/data/msgencode.h (revision 4191) ++++ util/data/msgencode.h (working copy) +@@ -128,4 +128,20 @@ + void error_encode(struct sldns_buffer* pkt, int r, struct query_info* qinfo, + uint16_t qid, uint16_t qflags, struct edns_data* edns); + ++/** ++ * Encode a fixed address response. ++ * This is a fake answer to either an A or AAA query ++ * ++ * It will answer with that address ++ * ++ * @param pkt: where to store the packet. ++ * @param r: RCODE value to encode. ++ * @param qinfo: if not NULL, the query is included. ++ * @param qid: query ID to set in packet. network order. ++ * @param qflags: original query flags (to copy RD and CD bits). host order. ++ * @param edns: if not NULL, this is the query edns info, ++ * and an edns reply is attached. Only attached if EDNS record fits reply. ++ */ ++void fixed_address_encode(struct sldns_buffer* pkt, int r, struct query_info* qinfo, ++ uint16_t qid, uint16_t qflags, struct edns_data* edns, char* address); + #endif /* UTIL_DATA_MSGENCODE_H */ --- contrib/unbound/contrib/unbound-fuzzme.patch.orig +++ contrib/unbound/contrib/unbound-fuzzme.patch @@ -0,0 +1,148 @@ +>From cc9b927f8f29d989ddb8415fe6508a538546abca Mon Sep 17 00:00:00 2001 +From: Jacob Hoffman-Andrews +Date: Wed, 2 Jan 2019 22:52:51 -0800 +Subject: [PATCH] Add unbound-fuzzme. + +This is a small program that simply parses a packet provided on stdout, +for the purposes of fuzzing. +--- + .gitignore | 1 + + Makefile.in | 22 ++++++++++++++++++++-- + smallapp/unbound-fuzzme.c | 38 ++++++++++++++++++++++++++++++++++++++ + 3 files changed, 59 insertions(+), 2 deletions(-) + create mode 100644 smallapp/unbound-fuzzme.c + +diff --git a/.gitignore b/.gitignore +index f4527fd8..6163f905 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -24,6 +24,7 @@ + /unbound-checkconf + /unbound-control + /unbound-control-setup ++/unbound-fuzzme + /unbound-host + /unbound.h + /asynclook +diff --git a/Makefile.in b/Makefile.in +index af5b10f6..dacf1ab5 100644 +--- a/Makefile.in ++++ b/Makefile.in +@@ -177,6 +177,10 @@ 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@ ++FUZZME_SRC=smallapp/unbound-fuzzme.c ++FUZZME_OBJ=unbound-fuzzme.lo ++FUZZME_OBJ_LINK=$(FUZZME_OBJ) worker_cb.lo $(COMMON_OBJ_ALL_SYMBOLS) $(SLDNS_OBJ) \ ++$(COMPAT_OBJ) + 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) \ +@@ -252,6 +256,7 @@ RSRC_OBJ=rsrc_svcinst.o rsrc_svcuninst.o rsrc_anchorupd.o rsrc_unbound.o \ + rsrc_unbound_checkconf.o + + ALL_SRC=$(COMMON_SRC) $(UNITTEST_SRC) $(DAEMON_SRC) \ ++ $(FUZZME_SRC) \ + $(TESTBOUND_SRC) $(LOCKVERIFY_SRC) $(PKTVIEW_SRC) \ + $(MEMSTATS_SRC) $(CHECKCONF_SRC) $(LIBUNBOUND_SRC) $(HOST_SRC) \ + $(ASYNCLOOK_SRC) $(STREAMTCP_SRC) $(PERF_SRC) $(DELAYER_SRC) \ +@@ -259,6 +264,7 @@ ALL_SRC=$(COMMON_SRC) $(UNITTEST_SRC) $(DAEMON_SRC) \ + $(PYTHONMOD_SRC) $(PYUNBOUND_SRC) $(WIN_DAEMON_THE_SRC)\ + $(SVCINST_SRC) $(SVCUNINST_SRC) $(ANCHORUPD_SRC) $(SLDNS_SRC) + ALL_OBJ=$(COMMON_OBJ) $(UNITTEST_OBJ) $(DAEMON_OBJ) \ ++ $(FUZZME_OBJ) \ + $(TESTBOUND_OBJ) $(LOCKVERIFY_OBJ) $(PKTVIEW_OBJ) \ + $(MEMSTATS_OBJ) $(CHECKCONF_OBJ) $(LIBUNBOUND_OBJ) $(HOST_OBJ) \ + $(ASYNCLOOK_OBJ) $(STREAMTCP_OBJ) $(PERF_OBJ) $(DELAYER_OBJ) \ +@@ -274,7 +280,7 @@ LINK_LIB=$(LIBTOOL) --tag=CC --mode=link $(CC) $(RUNTIME_PATH) $(CPPFLAGS) $(CFL + + 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) ++alltargets: unbound$(EXEEXT) unbound-checkconf$(EXEEXT) lib unbound-host$(EXEEXT) unbound-control$(EXEEXT) unbound-anchor$(EXEEXT) unbound-control-setup unbound-fuzzme$(EXEEXT) $(WINAPPS) $(PYUNBOUND_TARGET) + + # compat with BSD make, register suffix, and an implicit rule to actualise it. + .SUFFIXES: .lo +@@ -325,6 +331,9 @@ libunbound.la: $(LIBUNBOUND_OBJ_LINK) + unbound$(EXEEXT): $(DAEMON_OBJ_LINK) libunbound.la + $(LINK) -o $@ $(DAEMON_OBJ_LINK) $(EXTRALINK) $(SSLLIB) $(LIBS) + ++unbound-fuzzme$(EXEEXT): $(FUZZME_OBJ_LINK) libunbound.la ++ $(LINK) -o $@ $(FUZZME_OBJ_LINK) $(EXTRALINK) $(SSLLIB) $(LIBS) ++ + unbound-checkconf$(EXEEXT): $(CHECKCONF_OBJ_LINK) libunbound.la + $(LINK) -o $@ $(CHECKCONF_OBJ_LINK) $(EXTRALINK) $(SSLLIB) $(LIBS) + +@@ -447,7 +456,7 @@ util/configparser.c util/configparser.h: $(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 unbound$(EXEEXT) unbound-checkconf$(EXEEXT) unbound-fuzzme$(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 -rf autom4te.cache .libs build doc/html doc/xml +@@ -1183,6 +1192,15 @@ stats.lo stats.o: $(srcdir)/daemon/stats.c config.h $(srcdir)/daemon/stats.h $(s + $(srcdir)/util/storage/slabhash.h $(srcdir)/services/cache/infra.h $(srcdir)/util/storage/dnstree.h \ + $(srcdir)/util/rtt.h $(srcdir)/services/authzone.h $(srcdir)/validator/val_kcache.h \ + $(srcdir)/validator/val_neg.h ++unbound-fuzzme.lo unbound-fuzzme.o: $(srcdir)/smallapp/unbound-fuzzme.c \ ++ $(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)/util/net_help.h $(srcdir)/util/ub_event.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 \ +diff --git a/smallapp/unbound-fuzzme.c b/smallapp/unbound-fuzzme.c +new file mode 100644 +index 00000000..74ae5204 +--- /dev/null ++++ b/smallapp/unbound-fuzzme.c +@@ -0,0 +1,38 @@ ++/* ++ * unbound-fuzzme.c - parse a packet provided on stdin (for fuzzing). ++ * ++ */ ++#include "config.h" ++#include "util/regional.h" ++#include "util/fptr_wlist.h" ++#include "sldns/sbuffer.h" ++ ++#define SZ 10000 ++ ++int main() { ++ char buffer[SZ]; ++ size_t n_read = fread(buffer, 1, SZ, stdin); ++ if (n_read == SZ) { ++ printf("input too big\n"); ++ return 1; ++ } ++ sldns_buffer *pkt = sldns_buffer_new(n_read); ++ sldns_buffer_init_frm_data(pkt, buffer, n_read); ++ ++ struct regional *region = regional_create(); ++ ++ struct msg_parse* prs; ++ struct edns_data edns; ++ prs = (struct msg_parse*)malloc(sizeof(struct msg_parse)); ++ if(!prs) { ++ printf("out of memory on incoming message\n"); ++ return 1; ++ } ++ memset(prs, 0, sizeof(*prs)); ++ memset(&edns, 0, sizeof(edns)); ++ sldns_buffer_set_position(pkt, 0); ++ if(parse_packet(pkt, prs, region) != LDNS_RCODE_NOERROR) { ++ printf("parse error\n"); ++ return 1; ++ } ++} +-- +2.17.1 + --- contrib/unbound/contrib/unbound-querycachedb.py.orig +++ contrib/unbound/contrib/unbound-querycachedb.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python + +import hashlib +import sys +import struct +import socket +import time +from optparse import OptionParser + +import dns.message +import dns.name +import dns.rdataclass +import dns.rdatatype + +def _calc_hashkey(qname, secret, qtype): + qclass = 'IN' # CLASS is fixed for simplicity + hobj = hashlib.sha256() + hobj.update(dns.name.from_text(qname).to_wire()) + hobj.update(struct.pack('HH', + socket.htons(dns.rdatatype.from_text(qtype)), + socket.htons(dns.rdataclass.from_text(qclass)))) + hobj.update(secret) + return hobj.hexdigest().upper() + +def _redis_get(options, key): + import redis + return redis.Redis(options.address, int(options.port)).get(key) + +def _dump_value(options, qname, key, value): + print(';; query=%s/IN/%s' % (qname, options.qtype)) + print(';; key=%s' % key) + if value is None: + print(';; no value') + return + if len(value) < 16: + print(';; broken value, short length: %d' % len(value)) + return + now = int(time.time()) + timestamp = struct.unpack('!Q', value[-16:-8])[0] + expire = struct.unpack('!Q', value[-8:])[0] + print(';; Now=%d, TimeStamp=%d, Expire=%d, TTL=%d' % + (now, timestamp, expire, max(expire - now, 0))) + print(dns.message.from_wire(value[:-16])) + +def main(): + parser = OptionParser(usage='usage: %prog [options] query_name') + parser.add_option("-a", "--address", dest="address", action="store", + default='127.0.0.1', help="backend-server address", + metavar='ADDRESS') + parser.add_option("-b", "--backend", dest="backend", action="store", + default='redis', help="backend name", + metavar='BACKEND') + parser.add_option("-p", "--port", dest="port", action="store", + default='6379', help="backend-server port", + metavar='PORT') + parser.add_option("-s", "--secret", dest="secret", action="store", + default='default', help="secret seed", metavar='SECRET') + parser.add_option("-t", "--qtype", dest="qtype", action="store", + default='A', help="query RR type", metavar='QTYPE') + + (options, args) = parser.parse_args() + if len(args) < 1: + parser.error('qname is missing') + if options.backend == 'redis': + get_func = _redis_get + else: + raise Exception('unknown backend name: %s\n' % options.backend) + key = _calc_hashkey(args[0], options.secret, options.qtype) + value = get_func(options, key) + _dump_value(options, args[0], key, value) + +if __name__ == '__main__': + try: + main() + except Exception as e: + sys.stderr.write('%s\n' % e) + exit(1) --- contrib/unbound/contrib/unbound.init.orig +++ contrib/unbound/contrib/unbound.init @@ -39,13 +39,13 @@ # setup root jail if [ -s /etc/localtime ]; then [ -d ${rootdir}/etc ] || mkdir -p ${rootdir}/etc ; - if [ ! -e ${rootdir}/etc/localtime ] || /usr/bin/cmp -s /etc/localtime ${rootdir}/etc/localtime; then + if [ ! -e ${rootdir}/etc/localtime ] || ! /usr/bin/cmp -s /etc/localtime ${rootdir}/etc/localtime; then cp -fp /etc/localtime ${rootdir}/etc/localtime fi; fi; if [ -s /etc/resolv.conf ]; then [ -d ${rootdir}/etc ] || mkdir -p ${rootdir}/etc ; - if [ ! -e ${rootdir}/etc/resolv.conf ] || /usr/bin/cmp -s /etc/resolv.conf ${rootdir}/etc/resolv.conf; then + if [ ! -e ${rootdir}/etc/resolv.conf ] || ! /usr/bin/cmp -s /etc/resolv.conf ${rootdir}/etc/resolv.conf; then cp -fp /etc/resolv.conf ${rootdir}/etc/resolv.conf fi; fi; @@ -54,10 +54,10 @@ [ -e ${rootdir}/dev/log ] || touch ${rootdir}/dev/log mount --bind -n /dev/log ${rootdir}/dev/log >/dev/null 2>&1; fi; - if ! egrep -q '^/[^[:space:]]+[[:space:]]+'${rootdir}'/dev/random' /proc/mounts; then + if ! egrep -q '^/[^[:space:]]+[[:space:]]+'${rootdir}'/dev/urandom' /proc/mounts; then [ -d ${rootdir}/dev ] || mkdir -p ${rootdir}/dev ; - [ -e ${rootdir}/dev/random ] || touch ${rootdir}/dev/random - mount --bind -n /dev/random ${rootdir}/dev/random >/dev/null 2>&1; + [ -e ${rootdir}/dev/urandom ] || touch ${rootdir}/dev/urandom + mount --bind -n /dev/urandom ${rootdir}/dev/urandom >/dev/null 2>&1; fi; # if not running, start it up here @@ -78,8 +78,8 @@ if egrep -q '^/[^[:space:]]+[[:space:]]+'${rootdir}'/dev/log' /proc/mounts; then umount ${rootdir}/dev/log >/dev/null 2>&1 fi; - if egrep -q '^/[^[:space:]]+[[:space:]]+'${rootdir}'/dev/random' /proc/mounts; then - umount ${rootdir}/dev/random >/dev/null 2>&1 + if egrep -q '^/[^[:space:]]+[[:space:]]+'${rootdir}'/dev/urandom' /proc/mounts; then + umount ${rootdir}/dev/urandom >/dev/null 2>&1 fi; return $retval } --- contrib/unbound/contrib/unbound.init_fedora.orig +++ contrib/unbound/contrib/unbound.init_fedora @@ -42,7 +42,7 @@ cp -fp /etc/localtime ${rootdir}/etc/localtime fi; mount --bind -n /dev/log ${rootdir}/dev/log >/dev/null 2>&1; - mount --bind -n /dev/random ${rootdir}/dev/random >/dev/null 2>&1; + mount --bind -n /dev/urandom ${rootdir}/dev/urandom >/dev/null 2>&1; mount --bind -n /var/run/unbound ${rootdir}/var/run/unbound >/dev/null 2>&1; # if not running, start it up here @@ -58,7 +58,7 @@ killproc -p $pidfile unbound retval=$? [ $retval -eq 0 ] && rm -f $lockfile - for mountfile in /dev/log /dev/random /etc/localtime /etc/resolv.conf /var/run/unbound + for mountfile in /dev/log /dev/urandom /etc/localtime /etc/resolv.conf /var/run/unbound do if egrep -q '^/[^[:space:]]+[[:space:]]+'${rootdir}''${mountfile}'' /proc/mounts; then umount ${rootdir}$mountfile >/dev/null 2>&1 --- contrib/unbound/contrib/unbound.service.in.orig +++ contrib/unbound/contrib/unbound.service.in @@ -0,0 +1,84 @@ +; For further details about the directives used in this unit file, including +; the below, please refer to systemd's official documentation, available at +; https://www.freedesktop.org/software/systemd/man/systemd.exec.html. +; +; +; - `ProtectSystem=strict` implies we mount the entire file system hierarchy +; read-only for the processes invoked by the unit except for the API file +; system subtrees /dev, /proc and /sys (which are protected by +; PrivateDevices=, ProtectKernelTunables=, ProtectControlGroups=). +; +; - `PrivateTmp=yes` secures access to temporary files of the process, and +; makes sharing between processes via /tmp or /var/tmp impossible. +; +; - `ProtectHome=yes` makes the directories /home, /root, and /run/user +; inaccessible and empty for processes invoked by the unit. +; +; - `ProtectControlGroups=yes` makes the Linux Control Groups hierarchies +; (accessible through /sys/fs/cgroup) read-only to all processes invoked by +; the unit. It also implies `MountAPIVFS=yes`. +; +; - `RuntimeDirectory=unbound` creates a /run/unbound directory, owned by the +; unit User and Group with read-write permissions (0755) as soon as the +; unit starts. This allows unbound to store its pidfile. The directory and +; its content are automatically removed by systemd when the unit stops. +; +; - `NoNewPrivileges=yes` ensures that the service process and all its +; children can never gain new privileges through execve(). +; +; - `RestrictSUIDSGID=yes` ensures that any attempts to set the set-user-ID +; (SUID) or set-group-ID (SGID) bits on files or directories will be denied. +; +; - `RestrictRealTime=yes` ensures that any attempts to enable realtime +; scheduling in a process invoked by the unit will be denied. +; +; - `RestrictNamespaces=yes` ensures that access to any kind of namespacing +; is prohibited. +; +; - `LockPersonality=yes` locks down the personality system call so that the +; kernel execution domain may not be changed from the default. +; +; +[Unit] +Description=Validating, recursive, and caching DNS resolver +Documentation=man:unbound(8) +After=network.target +Before=network-online.target nss-lookup.target +Wants=nss-lookup.target + +[Install] +WantedBy=multi-user.target + +[Service] +ExecReload=+/bin/kill -HUP $MAINPID +ExecStart=@UNBOUND_SBIN_DIR@/unbound -d -p +NotifyAccess=main +Type=notify +CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_SETGID CAP_SETUID CAP_SYS_CHROOT CAP_SYS_RESOURCE CAP_NET_RAW +MemoryDenyWriteExecute=true +NoNewPrivileges=true +PrivateDevices=true +PrivateTmp=true +ProtectHome=true +ProtectControlGroups=true +ProtectKernelModules=true +ProtectSystem=strict +RuntimeDirectory=unbound +ConfigurationDirectory=unbound +StateDirectory=unbound +RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX +RestrictRealtime=true +SystemCallArchitectures=native +SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module mount @obsolete @resources +RestrictNamespaces=yes +LockPersonality=yes +RestrictSUIDSGID=yes +ReadWritePaths=@UNBOUND_RUN_DIR@ @UNBOUND_CHROOT_DIR@ + +# Below rules are needed when chroot is enabled (usually it's enabled by default). +# If chroot is disabled like chrooot: "" then they may be safely removed. +TemporaryFileSystem=@UNBOUND_CHROOT_DIR@/dev:ro +TemporaryFileSystem=@UNBOUND_CHROOT_DIR@/run:ro +BindReadOnlyPaths=-/run/systemd/notify:@UNBOUND_CHROOT_DIR@/run/systemd/notify +BindReadOnlyPaths=-/dev/urandom:@UNBOUND_CHROOT_DIR@/dev/urandom +BindPaths=-/dev/log:@UNBOUND_CHROOT_DIR@/dev/log --- contrib/unbound/contrib/unbound.socket.in.orig +++ contrib/unbound/contrib/unbound.socket.in @@ -0,0 +1,6 @@ +[Socket] +ListenDatagram=127.0.0.1:1153 +ListenStream=127.0.0.1:1153 +# ListenStream=@UNBOUND_RUN_DIR@/control +[Install] +WantedBy=sockets.target --- contrib/unbound/contrib/unbound_munin_.orig +++ contrib/unbound/contrib/unbound_munin_ @@ -150,7 +150,7 @@ fi done # try to get it - echo $$ >$lock + if echo $$ >$lock ; then : ; else break; fi done # do not refetch if the file exists and only LEE seconds old if test -f $state; then @@ -242,6 +242,8 @@ p_config "total.num.prefetch" "cache prefetch" "ABSOLUTE" p_config "num.query.tcp" "TCP queries" "ABSOLUTE" p_config "num.query.tcpout" "TCP out queries" "ABSOLUTE" + p_config "num.query.tls" "TLS queries" "ABSOLUTE" + p_config "num.query.tls.resume" "TLS resumes" "ABSOLUTE" p_config "num.query.ipv6" "IPv6 queries" "ABSOLUTE" p_config "unwanted.queries" "queries that failed acl" "ABSOLUTE" p_config "unwanted.replies" "unwanted or unsolicited replies" "ABSOLUTE" @@ -266,7 +268,6 @@ echo "graph_args --base 1024 -l 0" echo "graph_vlabel memory used in bytes" echo "graph_category DNS" - p_config "mem.total.sbrk" "Total memory" "GAUGE" p_config "mem.cache.rrset" "RRset cache memory" "GAUGE" p_config "mem.cache.message" "Message cache memory" "GAUGE" p_config "mem.mod.iterator" "Iterator module memory" "GAUGE" @@ -444,7 +445,8 @@ for x in `grep "^thread[0-9][0-9]*\.num\.queries=" $state | sed -e 's/=.*//'` total.num.queries \ total.num.cachehits total.num.prefetch num.query.tcp \ - num.query.tcpout num.query.ipv6 unwanted.queries \ + num.query.tcpout num.query.tls num.query.tls.resume \ + num.query.ipv6 unwanted.queries \ unwanted.replies; do if grep "^"$x"=" $state >/dev/null 2>&1; then print_value $x @@ -458,20 +460,6 @@ done ;; memory) - mn=`echo mem.total.sbrk | sed $ABBREV | tr . _` - get_value 'mem.total.sbrk' - if test $value -eq 0; then - chk=`echo $ctrl | sed -e 's/-control$/-checkconf/'` - pidf=`$chk -o pidfile $conf 2>&1` - pid=`cat $pidf 2>&1` - value=`ps -p "$pid" -o rss= 2>&1` - if test "`expr $value + 1 - 1 2>&1`" -eq "$value" 2>&1; then - value=`expr $value \* 1024` - else - value=0 - fi - fi - echo "$mn.value" $value for x in mem.cache.rrset mem.cache.message mem.mod.iterator \ mem.mod.validator msg.cache.count rrset.cache.count \ infra.cache.count key.cache.count; do --- contrib/unbound/contrib/unbound_portable.service.in.orig +++ contrib/unbound/contrib/unbound_portable.service.in @@ -0,0 +1,49 @@ +; This unit file is provided to run unbound as portable service. +; https://systemd.io/PORTABLE_SERVICES/ +; +; To use this unit file, please make sure you either compile unbound with the +; following options: +; +; - --with-chroot-dir="" +; +; Or put the following options in your unbound configuration file: +; +; - chroot: "" +; +; +[Unit] +Description=Validating, recursive, and caching DNS resolver +Documentation=man:unbound(8) +After=network.target +Before=network-online.target nss-lookup.target +Wants=nss-lookup.target + +[Install] +WantedBy=multi-user.target + +[Service] +ExecReload=+/bin/kill -HUP $MAINPID +ExecStart=@UNBOUND_SBIN_DIR@/unbound -d -p +NotifyAccess=main +Type=notify +CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_SETGID CAP_SETUID CAP_SYS_CHROOT CAP_SYS_RESOURCE CAP_NET_RAW +MemoryDenyWriteExecute=true +NoNewPrivileges=true +PrivateDevices=true +PrivateTmp=true +ProtectHome=true +ProtectControlGroups=true +ProtectKernelModules=true +ProtectSystem=strict +RuntimeDirectory=unbound +ConfigurationDirectory=unbound +StateDirectory=unbound +RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX +RestrictRealtime=true +SystemCallArchitectures=native +SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module mount @obsolete @resources +RestrictNamespaces=yes +LockPersonality=yes +RestrictSUIDSGID=yes +BindPaths=/run/systemd/notify +BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout --- contrib/unbound/daemon/acl_list.c.orig +++ contrib/unbound/daemon/acl_list.c @@ -111,6 +111,8 @@ control = acl_refuse_non_local; else if(strcmp(s2, "allow_snoop") == 0) control = acl_allow_snoop; + else if(strcmp(s2, "allow_setrd") == 0) + control = acl_allow_setrd; else { log_err("access control type %s unknown", str); return 0; @@ -170,6 +172,23 @@ return 1; } +/** apply acl_view string */ +static int +acl_list_view_cfg(struct acl_list* acl, const char* str, const char* str2, + struct views* vs) +{ + struct acl_addr* node; + if(!(node=acl_find_or_create(acl, str))) + return 0; + node->view = views_find_view(vs, str2, 0 /* get read lock*/); + if(!node->view) { + log_err("no view with name: %s", str2); + return 0; + } + lock_rw_unlock(&node->view->lock); + return 1; +} + /** apply acl_tag_action string */ static int acl_list_tag_action_cfg(struct acl_list* acl, struct config_file* cfg, @@ -210,15 +229,47 @@ /** check wire data parse */ static int -check_data(const char* data) +check_data(const char* data, const struct config_strlist* head) { char buf[65536]; uint8_t rr[LDNS_RR_BUF_SIZE]; size_t len = sizeof(rr); int res; - snprintf(buf, sizeof(buf), "%s %s", "example.com.", data); + /* '.' is sufficient for validation, and it makes the call to + * sldns_wirerr_get_type() simpler below. */ + snprintf(buf, sizeof(buf), "%s %s", ".", data); res = sldns_str2wire_rr_buf(buf, rr, &len, NULL, 3600, NULL, 0, NULL, 0); + + /* Reject it if we would end up having CNAME and other data (including + * another CNAME) for the same tag. */ + if(res == 0 && head) { + const char* err_data = NULL; + + if(sldns_wirerr_get_type(rr, len, 1) == LDNS_RR_TYPE_CNAME) { + /* adding CNAME while other data already exists. */ + err_data = data; + } else { + snprintf(buf, sizeof(buf), "%s %s", ".", head->str); + len = sizeof(rr); + res = sldns_str2wire_rr_buf(buf, rr, &len, NULL, 3600, + NULL, 0, NULL, 0); + if(res != 0) { + /* This should be impossible here as head->str + * has been validated, but we check it just in + * case. */ + return 0; + } + if(sldns_wirerr_get_type(rr, len, 1) == + LDNS_RR_TYPE_CNAME) /* already have CNAME */ + err_data = head->str; + } + if(err_data) { + log_err("redirect tag data '%s' must not coexist with " + "other data.", err_data); + return 0; + } + } if(res == 0) return 1; log_err("rr data [char %d] parse error %s", @@ -258,7 +309,7 @@ } /* check data? */ - if(!check_data(data)) { + if(!check_data(data, node->tag_datas[tagid])) { log_err("cannot parse access-control-tag data: %s %s '%s'", str, tag, data); return 0; @@ -312,6 +363,27 @@ return 1; } +/** read acl view config */ +static int +read_acl_view(struct acl_list* acl, struct config_file* cfg, struct views* v) +{ + struct config_str2list* np, *p = cfg->acl_view; + cfg->acl_view = NULL; + while(p) { + log_assert(p->str && p->str2); + if(!acl_list_view_cfg(acl, p->str, p->str2, v)) { + return 0; + } + /* free the items as we go to free up memory */ + np = p->next; + free(p->str); + free(p->str2); + free(p); + p = np; + } + return 1; +} + /** read acl tag actions config */ static int read_acl_tag_actions(struct acl_list* acl, struct config_file* cfg) @@ -362,12 +434,15 @@ } int -acl_list_apply_cfg(struct acl_list* acl, struct config_file* cfg) +acl_list_apply_cfg(struct acl_list* acl, struct config_file* cfg, + struct views* v) { regional_free_all(acl->region); addr_tree_init(&acl->tree); if(!read_acl_list(acl, cfg)) return 0; + if(!read_acl_view(acl, cfg, v)) + return 0; if(!read_acl_tags(acl, cfg)) return 0; if(!read_acl_tag_actions(acl, cfg)) --- contrib/unbound/daemon/acl_list.h.orig +++ contrib/unbound/daemon/acl_list.h @@ -43,6 +43,7 @@ #ifndef DAEMON_ACL_LIST_H #define DAEMON_ACL_LIST_H #include "util/storage/dnstree.h" +#include "services/view.h" struct config_file; struct regional; @@ -62,7 +63,9 @@ /** allow full access for recursion (+RD) queries */ acl_allow, /** allow full access for all queries, recursion and cache snooping */ - acl_allow_snoop + acl_allow_snoop, + /** allow full access for recursion queries and set RD flag regardless of request */ + acl_allow_setrd }; /** @@ -75,7 +78,7 @@ * Tree of the addresses that are allowed/blocked. * contents of type acl_addr. */ - rbtree_t tree; + rbtree_type tree; }; /** @@ -100,6 +103,8 @@ struct config_strlist** tag_datas; /** size of the tag_datas array */ size_t tag_datas_size; + /* view element, NULL if none */ + struct view* view; }; /** @@ -118,9 +123,11 @@ * Process access control config. * @param acl: where to store. * @param cfg: config options. + * @param v: views structure * @return 0 on error. */ -int acl_list_apply_cfg(struct acl_list* acl, struct config_file* cfg); +int acl_list_apply_cfg(struct acl_list* acl, struct config_file* cfg, + struct views* v); /** * Lookup access control status for acl structure. --- contrib/unbound/daemon/cachedump.c.orig +++ contrib/unbound/daemon/cachedump.c @@ -62,7 +62,7 @@ /** dump one rrset zonefile line */ static int -dump_rrset_line(SSL* ssl, struct ub_packed_rrset_key* k, time_t now, size_t i) +dump_rrset_line(RES* ssl, struct ub_packed_rrset_key* k, time_t now, size_t i) { char s[65535]; if(!packed_rr_to_string(k, i, now, s, sizeof(s))) { @@ -73,12 +73,13 @@ /** dump rrset key and data info */ static int -dump_rrset(SSL* ssl, struct ub_packed_rrset_key* k, +dump_rrset(RES* ssl, struct ub_packed_rrset_key* k, struct packed_rrset_data* d, time_t now) { size_t i; /* rd lock held by caller */ if(!k || !d) return 1; + if(k->id == 0) return 1; /* deleted */ if(d->ttl < now) return 1; /* expired */ /* meta line */ @@ -98,7 +99,7 @@ /** dump lruhash rrset cache */ static int -dump_rrset_lruhash(SSL* ssl, struct lruhash* h, time_t now) +dump_rrset_lruhash(RES* ssl, struct lruhash* h, time_t now) { struct lruhash_entry* e; /* lruhash already locked by caller */ @@ -117,7 +118,7 @@ /** dump rrset cache */ static int -dump_rrset_cache(SSL* ssl, struct worker* worker) +dump_rrset_cache(RES* ssl, struct worker* worker) { struct rrset_cache* r = worker->env.rrset_cache; size_t slab; @@ -136,7 +137,7 @@ /** dump message to rrset reference */ static int -dump_msg_ref(SSL* ssl, struct ub_packed_rrset_key* k) +dump_msg_ref(RES* ssl, struct ub_packed_rrset_key* k) { char* nm, *tp, *cl; nm = sldns_wire2str_dname(k->rk.dname, k->rk.dname_len); @@ -163,7 +164,7 @@ /** dump message entry */ static int -dump_msg(SSL* ssl, struct query_info* k, struct reply_info* d, +dump_msg(RES* ssl, struct query_info* k, struct reply_info* d, time_t now) { size_t i; @@ -245,7 +246,7 @@ /** dump lruhash msg cache */ static int -dump_msg_lruhash(SSL* ssl, struct worker* worker, struct lruhash* h) +dump_msg_lruhash(RES* ssl, struct worker* worker, struct lruhash* h) { struct lruhash_entry* e; struct query_info* k; @@ -273,7 +274,7 @@ /** dump msg cache */ static int -dump_msg_cache(SSL* ssl, struct worker* worker) +dump_msg_cache(RES* ssl, struct worker* worker) { struct slabhash* sh = worker->env.msg_cache; size_t slab; @@ -290,7 +291,7 @@ } int -dump_cache(SSL* ssl, struct worker* worker) +dump_cache(RES* ssl, struct worker* worker) { if(!dump_rrset_cache(ssl, worker)) return 0; @@ -301,7 +302,7 @@ /** read a line from ssl into buffer */ static int -ssl_read_buf(SSL* ssl, sldns_buffer* buf) +ssl_read_buf(RES* ssl, sldns_buffer* buf) { return ssl_read_line(ssl, (char*)sldns_buffer_begin(buf), sldns_buffer_capacity(buf)); @@ -309,7 +310,7 @@ /** check fixed text on line */ static int -read_fixed(SSL* ssl, sldns_buffer* buf, const char* str) +read_fixed(RES* ssl, sldns_buffer* buf, const char* str) { if(!ssl_read_buf(ssl, buf)) return 0; return (strcmp((char*)sldns_buffer_begin(buf), str) == 0); @@ -317,7 +318,7 @@ /** load an RR into rrset */ static int -load_rr(SSL* ssl, sldns_buffer* buf, struct regional* region, +load_rr(RES* ssl, sldns_buffer* buf, struct regional* region, struct ub_packed_rrset_key* rk, struct packed_rrset_data* d, unsigned int i, int is_rrsig, int* go_on, time_t now) { @@ -434,7 +435,7 @@ /** load an rrset entry */ static int -load_rrset(SSL* ssl, sldns_buffer* buf, struct worker* worker) +load_rrset(RES* ssl, sldns_buffer* buf, struct worker* worker) { char* s = (char*)sldns_buffer_begin(buf); struct regional* region = worker->scratchpad; @@ -518,7 +519,7 @@ /** load rrset cache */ static int -load_rrset_cache(SSL* ssl, struct worker* worker) +load_rrset_cache(RES* ssl, struct worker* worker) { sldns_buffer* buf = worker->env.scratch_buffer; if(!read_fixed(ssl, buf, "START_RRSET_CACHE")) return 0; @@ -563,6 +564,7 @@ qinfo->qclass = sldns_wirerr_get_class(rr, rr_len, dname_len); qinfo->qname_len = dname_len; qinfo->qname = (uint8_t*)regional_alloc_init(region, rr, dname_len); + qinfo->local_alias = NULL; if(!qinfo->qname) { log_warn("error out of memory"); return NULL; @@ -573,7 +575,7 @@ /** load a msg rrset reference */ static int -load_ref(SSL* ssl, sldns_buffer* buf, struct worker* worker, +load_ref(RES* ssl, sldns_buffer* buf, struct worker* worker, struct regional *region, struct ub_packed_rrset_key** rrset, int* go_on) { @@ -618,7 +620,7 @@ /** load a msg entry */ static int -load_msg(SSL* ssl, sldns_buffer* buf, struct worker* worker) +load_msg(RES* ssl, sldns_buffer* buf, struct worker* worker) { struct regional* region = worker->scratchpad; struct query_info qinf; @@ -651,6 +653,7 @@ rep.qdcount = (uint16_t)qdcount; rep.ttl = (time_t)ttl; rep.prefetch_ttl = PREFETCH_TTL_CALC(rep.ttl); + rep.serve_expired_ttl = rep.ttl + SERVE_EXPIRED_TTL; rep.security = (enum sec_status)security; if(an > RR_COUNT_MAX || ns > RR_COUNT_MAX || ar > RR_COUNT_MAX) { log_warn("error too many rrsets"); @@ -683,7 +686,7 @@ /** load msg cache */ static int -load_msg_cache(SSL* ssl, struct worker* worker) +load_msg_cache(RES* ssl, struct worker* worker) { sldns_buffer* buf = worker->env.scratch_buffer; if(!read_fixed(ssl, buf, "START_MSG_CACHE")) return 0; @@ -696,7 +699,7 @@ } int -load_cache(SSL* ssl, struct worker* worker) +load_cache(RES* ssl, struct worker* worker) { if(!load_rrset_cache(ssl, worker)) return 0; @@ -707,7 +710,7 @@ /** print details on a delegation point */ static void -print_dp_details(SSL* ssl, struct worker* worker, struct delegpt* dp) +print_dp_details(RES* ssl, struct worker* worker, struct delegpt* dp) { char buf[257]; struct delegpt_addr* a; @@ -783,7 +786,7 @@ /** print main dp info */ static void -print_dp_main(SSL* ssl, struct delegpt* dp, struct dns_msg* msg) +print_dp_main(RES* ssl, struct delegpt* dp, struct dns_msg* msg) { size_t i, n_ns, n_miss, n_addr, n_res, n_avail; @@ -811,7 +814,7 @@ return; } -int print_deleg_lookup(SSL* ssl, struct worker* worker, uint8_t* nm, +int print_deleg_lookup(RES* ssl, struct worker* worker, uint8_t* nm, size_t nmlen, int ATTR_UNUSED(nmlabs)) { /* deep links into the iterator module */ @@ -826,6 +829,7 @@ qinfo.qname_len = nmlen; qinfo.qtype = LDNS_RR_TYPE_A; qinfo.qclass = LDNS_RR_CLASS_IN; + qinfo.local_alias = NULL; dname_str(nm, b); if(!ssl_printf(ssl, "The following name servers are used for lookup " --- contrib/unbound/daemon/cachedump.h.orig +++ contrib/unbound/daemon/cachedump.h @@ -72,6 +72,7 @@ #ifndef DAEMON_DUMPCACHE_H #define DAEMON_DUMPCACHE_H struct worker; +#include "daemon/remote.h" /** * Dump cache(s) to text @@ -80,7 +81,7 @@ * ptrs to the caches. * @return false on ssl print error. */ -int dump_cache(SSL* ssl, struct worker* worker); +int dump_cache(RES* ssl, struct worker* worker); /** * Load cache(s) from text @@ -89,7 +90,7 @@ * ptrs to the caches. * @return false on ssl error. */ -int load_cache(SSL* ssl, struct worker* worker); +int load_cache(RES* ssl, struct worker* worker); /** * Print the delegation used to lookup for this name. @@ -101,7 +102,7 @@ * @param nmlabs: labels in name. * @return false on ssl error. */ -int print_deleg_lookup(SSL* ssl, struct worker* worker, uint8_t* nm, +int print_deleg_lookup(RES* ssl, struct worker* worker, uint8_t* nm, size_t nmlen, int nmlabs); #endif /* DAEMON_DUMPCACHE_H */ --- contrib/unbound/daemon/daemon.c.orig +++ contrib/unbound/daemon/daemon.c @@ -73,20 +73,29 @@ #include "util/log.h" #include "util/config_file.h" #include "util/data/msgreply.h" +#include "util/shm_side/shm_main.h" #include "util/storage/lookup3.h" #include "util/storage/slabhash.h" +#include "util/tcp_conn_limit.h" #include "services/listen_dnsport.h" #include "services/cache/rrset.h" #include "services/cache/infra.h" #include "services/localzone.h" +#include "services/view.h" #include "services/modstack.h" +#include "services/authzone.h" #include "util/module.h" #include "util/random.h" #include "util/tube.h" #include "util/net_help.h" #include "sldns/keyraw.h" +#include "respip/respip.h" #include +#ifdef HAVE_SYSTEMD +#include +#endif + /** How many quit requests happened. */ static int sig_record_quit = 0; /** How many reload requests happened. */ @@ -96,10 +105,8 @@ /** cleaner ssl memory freeup */ static void* comp_meth = NULL; #endif -#ifdef LEX_HAS_YYLEX_DESTROY /** remove buffers for parsing and init */ int ub_c_lex_destroy(void); -#endif /** used when no other sighandling happens, so we don't die * when multiple signals in quick succession are sent to us. @@ -207,12 +214,16 @@ # ifdef HAVE_ERR_LOAD_CRYPTO_STRINGS ERR_load_crypto_strings(); # endif +#if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL) ERR_load_SSL_strings(); +#endif # ifdef USE_GOST (void)sldns_key_EVP_load_gost_id(); # 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 @@ -225,7 +236,7 @@ # if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL) (void)SSL_library_init(); # else - (void)OPENSSL_init_ssl(0, NULL); + (void)OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL); # endif # if defined(HAVE_SSL) && defined(OPENSSL_THREADS) && !defined(THREADS_DISABLED) if(!ub_openssl_lock_init()) @@ -239,8 +250,6 @@ /* init timezone info while we are not chrooted yet */ tzset(); #endif - /* open /dev/random if needed */ - ub_systemseed((unsigned)time(NULL)^(unsigned)getpid()^0xe67); daemon->need_to_exit = 0; modstack_init(&daemon->mods); if(!(daemon->env = (struct module_env*)calloc(1, @@ -248,16 +257,39 @@ free(daemon); return NULL; } + /* init edns_known_options */ + if(!edns_known_options_init(daemon->env)) { + free(daemon->env); + free(daemon); + return NULL; + } alloc_init(&daemon->superalloc, NULL, 0); daemon->acl = acl_list_create(); if(!daemon->acl) { + edns_known_options_delete(daemon->env); free(daemon->env); free(daemon); return NULL; } + daemon->tcl = tcl_list_create(); + if(!daemon->tcl) { + acl_list_delete(daemon->acl); + edns_known_options_delete(daemon->env); + free(daemon->env); + free(daemon); + return NULL; + } if(gettimeofday(&daemon->time_boot, NULL) < 0) log_err("gettimeofday: %s", strerror(errno)); daemon->time_last_stat = daemon->time_boot; + if((daemon->env->auth_zones = auth_zones_create()) == 0) { + acl_list_delete(daemon->acl); + tcl_list_delete(daemon->tcl); + edns_known_options_delete(daemon->env); + free(daemon->env); + free(daemon); + return NULL; + } return daemon; } @@ -347,6 +379,7 @@ daemon->env)) { fatal_exit("failed to setup modules"); } + log_edns_known_options(VERB_ALGO, daemon->env); } /** @@ -394,13 +427,11 @@ int* shufport; log_assert(daemon && daemon->cfg); if(!daemon->rand) { - unsigned int seed = (unsigned int)time(NULL) ^ - (unsigned int)getpid() ^ 0x438; - daemon->rand = ub_initstate(seed, NULL); + daemon->rand = ub_initstate(NULL); if(!daemon->rand) fatal_exit("could not init random generator"); + hash_set_raninit((uint32_t)ub_random(daemon->rand)); } - hash_set_raninit((uint32_t)ub_random(daemon->rand)); shufport = (int*)calloc(65536, sizeof(int)); if(!shufport) fatal_exit("out of memory during daemon init"); @@ -541,17 +572,71 @@ void daemon_fork(struct daemon* daemon) { + int have_view_respip_cfg = 0; +#ifdef HAVE_SYSTEMD + int ret; +#endif + log_assert(daemon); - if(!acl_list_apply_cfg(daemon->acl, daemon->cfg)) + if(!(daemon->views = views_create())) + fatal_exit("Could not create views: out of memory"); + /* create individual views and their localzone/data trees */ + if(!views_apply_cfg(daemon->views, daemon->cfg)) + fatal_exit("Could not set up views"); + + if(!acl_list_apply_cfg(daemon->acl, daemon->cfg, daemon->views)) fatal_exit("Could not setup access control list"); + if(!tcl_list_apply_cfg(daemon->tcl, daemon->cfg)) + fatal_exit("Could not setup TCP connection limits"); + if(daemon->cfg->dnscrypt) { +#ifdef USE_DNSCRYPT + daemon->dnscenv = dnsc_create(); + if (!daemon->dnscenv) + fatal_exit("dnsc_create failed"); + dnsc_apply_cfg(daemon->dnscenv, daemon->cfg); +#else + fatal_exit("dnscrypt enabled in config but unbound was not built with " + "dnscrypt support"); +#endif + } + /* create global local_zones */ if(!(daemon->local_zones = local_zones_create())) fatal_exit("Could not create local zones: out of memory"); if(!local_zones_apply_cfg(daemon->local_zones, daemon->cfg)) fatal_exit("Could not set up local zones"); + /* process raw response-ip configuration data */ + if(!(daemon->respip_set = respip_set_create())) + fatal_exit("Could not create response IP set"); + if(!respip_global_apply_cfg(daemon->respip_set, daemon->cfg)) + fatal_exit("Could not set up response IP set"); + if(!respip_views_apply_cfg(daemon->views, daemon->cfg, + &have_view_respip_cfg)) + fatal_exit("Could not set up per-view response IP sets"); + daemon->use_response_ip = !respip_set_is_empty(daemon->respip_set) || + have_view_respip_cfg; + + /* read auth zonefiles */ + if(!auth_zones_apply_cfg(daemon->env->auth_zones, daemon->cfg, 1, + &daemon->use_rpz)) + fatal_exit("auth_zones could not be setup"); + /* setup modules */ daemon_setup_modules(daemon); + /* response-ip-xxx options don't work as expected without the respip + * module. To avoid run-time operational surprise we reject such + * configuration. */ + if(daemon->use_response_ip && + modstack_find(&daemon->mods, "respip") < 0) + fatal_exit("response-ip options require respip module"); + /* RPZ response ip triggers don't work as expected without the respip + * module. To avoid run-time operational surprise we reject such + * configuration. */ + if(daemon->use_rpz && + modstack_find(&daemon->mods, "respip") < 0) + fatal_exit("RPZ requires the respip module"); + /* first create all the worker structures, so we can pass * them to the newly created threads. */ @@ -578,14 +663,34 @@ #endif signal_handling_playback(daemon->workers[0]); + if (!shm_main_init(daemon)) + log_warn("SHM has failed"); + /* Start resolver service on main thread. */ +#ifdef HAVE_SYSTEMD + ret = sd_notify(0, "READY=1"); + if(ret <= 0 && getenv("NOTIFY_SOCKET")) + fatal_exit("sd_notify failed %s: %s. Make sure that unbound has " + "access/permission to use the socket presented by systemd.", + getenv("NOTIFY_SOCKET"), + (ret==0?"no $NOTIFY_SOCKET": strerror(-ret))); +#endif log_info("start of service (%s).", PACKAGE_STRING); worker_work(daemon->workers[0]); +#ifdef HAVE_SYSTEMD + if (daemon->workers[0]->need_to_exit) + sd_notify(0, "STOPPING=1"); + else + sd_notify(0, "RELOADING=1"); +#endif log_info("service stopped (%s).", PACKAGE_STRING); /* we exited! a signal happened! Stop other threads */ daemon_stop_others(daemon); + /* Shutdown SHM */ + shm_main_shutdown(daemon); + daemon->need_to_exit = daemon->workers[0]->need_to_exit; } @@ -605,6 +710,12 @@ slabhash_clear(daemon->env->msg_cache); local_zones_delete(daemon->local_zones); daemon->local_zones = NULL; + respip_set_delete(daemon->respip_set); + daemon->respip_set = NULL; + views_delete(daemon->views); + daemon->views = NULL; + if(daemon->env->auth_zones) + auth_zones_cleanup(daemon->env->auth_zones); /* key cache is cleared by module desetup during next daemon_fork() */ daemon_remote_clear(daemon->rc); for(i=0; inum; i++) @@ -612,9 +723,15 @@ free(daemon->workers); daemon->workers = NULL; daemon->num = 0; + alloc_clear_special(&daemon->superalloc); #ifdef USE_DNSTAP dt_delete(daemon->dtenv); + daemon->dtenv = NULL; #endif +#ifdef USE_DNSCRYPT + dnsc_delete(daemon->dnscenv); + daemon->dnscenv = NULL; +#endif daemon->cfg = NULL; } @@ -634,22 +751,24 @@ slabhash_delete(daemon->env->msg_cache); rrset_cache_delete(daemon->env->rrset_cache); infra_delete(daemon->env->infra_cache); + edns_known_options_delete(daemon->env); + auth_zones_delete(daemon->env->auth_zones); } ub_randfree(daemon->rand); alloc_clear(&daemon->superalloc); acl_list_delete(daemon->acl); + tcl_list_delete(daemon->tcl); free(daemon->chroot); free(daemon->pidfile); free(daemon->env); #ifdef HAVE_SSL + listen_sslctx_delete_ticket_keys(); SSL_CTX_free((SSL_CTX*)daemon->listen_sslctx); SSL_CTX_free((SSL_CTX*)daemon->connect_sslctx); #endif free(daemon); -#ifdef LEX_HAS_YYLEX_DESTROY /* lex cleanup */ ub_c_lex_destroy(); -#endif /* libcrypto cleanup */ #ifdef HAVE_SSL # if defined(USE_GOST) && defined(HAVE_LDNS_KEY_EVP_UNLOAD_GOST) @@ -664,7 +783,7 @@ # endif # ifdef HAVE_OPENSSL_CONFIG EVP_cleanup(); -# if OPENSSL_VERSION_NUMBER < 0x10100000 +# if (OPENSSL_VERSION_NUMBER < 0x10100000) && !defined(OPENSSL_NO_ENGINE) ENGINE_cleanup(); # endif CONF_modules_free(); @@ -681,6 +800,9 @@ # if defined(HAVE_SSL) && defined(OPENSSL_THREADS) && !defined(THREADS_DISABLED) ub_openssl_lock_delete(); # endif +#ifndef HAVE_ARC4RANDOM + _ARC4_LOCK_DESTROY(); +#endif #elif defined(HAVE_NSS) NSS_Shutdown(); #endif /* HAVE_SSL or HAVE_NSS */ @@ -697,9 +819,8 @@ { daemon->cfg = cfg; config_apply(cfg); - if(!daemon->env->msg_cache || - cfg->msg_cache_size != slabhash_get_size(daemon->env->msg_cache) || - cfg->msg_cache_slabs != daemon->env->msg_cache->size) { + if(!slabhash_is_size(daemon->env->msg_cache, cfg->msg_cache_size, + cfg->msg_cache_slabs)) { slabhash_delete(daemon->env->msg_cache); daemon->env->msg_cache = slabhash_create(cfg->msg_cache_slabs, HASH_DEFAULT_STARTARRAY, cfg->msg_cache_size, --- contrib/unbound/daemon/daemon.h.orig +++ contrib/unbound/daemon/daemon.h @@ -53,8 +53,11 @@ struct rrset_cache; struct acl_list; struct local_zones; +struct views; struct ub_randstate; struct daemon_remote; +struct respip_set; +struct shm_main_info; #include "dnstap/dnstap_config.h" #ifdef USE_DNSTAP @@ -61,6 +64,11 @@ struct dt_env; #endif +#include "dnscrypt/dnscrypt_config.h" +#ifdef USE_DNSCRYPT +struct dnsc_env; +#endif + /** * Structure holding worker list. * Holds globally visible information. @@ -105,6 +113,8 @@ struct module_stack mods; /** access control, which client IPs are allowed to connect */ struct acl_list* acl; + /** TCP connection limit, limit connections from client IPs */ + struct tcl_list* tcl; /** local authority zones */ struct local_zones* local_zones; /** last time of statistics printout */ @@ -111,10 +121,23 @@ struct timeval time_last_stat; /** time when daemon started */ struct timeval time_boot; + /** views structure containing view tree */ + struct views* views; #ifdef USE_DNSTAP /** the dnstap environment master value, copied and changed by threads*/ struct dt_env* dtenv; #endif + struct shm_main_info* shm_info; + /** response-ip set with associated actions and tags. */ + struct respip_set* respip_set; + /** some response-ip tags or actions are configured if true */ + int use_response_ip; + /** some RPZ policies are configured */ + int use_rpz; +#ifdef USE_DNSCRYPT + /** the dnscrypt environment */ + struct dnsc_env* dnscenv; +#endif }; /** --- contrib/unbound/daemon/remote.c.orig +++ contrib/unbound/daemon/remote.c @@ -68,6 +68,8 @@ #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" @@ -124,7 +126,7 @@ /** divide sum of timers to get average */ static void -timeval_divide(struct timeval* avg, const struct timeval* sum, size_t d) +timeval_divide(struct timeval* avg, const struct timeval* sum, long long d) { #ifndef S_SPLINT_S size_t leftover; @@ -141,125 +143,20 @@ #endif } -/* - * The following function was generated using the openssl utility, using - * the command : "openssl dhparam -C 2048" - * (some openssl versions reject DH that is 'too small', eg. 512). - */ -#ifndef S_SPLINT_S -static DH *get_dh2048(void) +static int +remote_setup_ctx(struct daemon_remote* rc, struct config_file* cfg) { - static unsigned char dh2048_p[]={ - 0xE7,0x36,0x28,0x3B,0xE4,0xC3,0x32,0x1C,0x01,0xC3,0x67,0xD6, - 0xF5,0xF3,0xDA,0xDC,0x71,0xC0,0x42,0x8B,0xE6,0xEB,0x8D,0x80, - 0x35,0x7F,0x09,0x45,0x30,0xE5,0xB2,0x92,0x81,0x3F,0x08,0xCD, - 0x36,0x5E,0x19,0x83,0x62,0xCC,0xAE,0x9B,0x81,0x66,0x24,0xEE, - 0x16,0x6F,0xA9,0x9E,0xF4,0x82,0x1B,0xDD,0x46,0xC7,0x33,0x5D, - 0xF4,0xCA,0xE6,0x8F,0xFC,0xD4,0xD8,0x58,0x94,0x24,0x5D,0xFF, - 0x0A,0xE8,0xEF,0x3D,0xCE,0xBB,0x50,0x94,0xE0,0x5F,0xE8,0x41, - 0xC3,0x35,0x30,0x37,0xD5,0xCB,0x8F,0x3D,0x95,0x15,0x1A,0x77, - 0x42,0xB2,0x06,0x86,0xF6,0x09,0x66,0x0E,0x9A,0x25,0x94,0x3E, - 0xD2,0x04,0x25,0x25,0x1D,0x23,0xEB,0xDC,0x4D,0x0C,0x83,0x28, - 0x2E,0x15,0x81,0x2D,0xC1,0xAF,0x8D,0x36,0x64,0xE3,0x9A,0x83, - 0x78,0xC2,0x8D,0xC0,0x9D,0xD9,0x3A,0x1C,0xC5,0x2B,0x50,0x68, - 0x07,0xA9,0x4B,0x8C,0x07,0x57,0xD6,0x15,0x03,0x4E,0x9E,0x01, - 0xF2,0x6F,0x35,0xAC,0x26,0x9C,0x92,0x68,0x61,0x13,0xFB,0x01, - 0xBA,0x22,0x36,0x01,0x55,0xB6,0x62,0xD9,0xB2,0x98,0xCE,0x5D, - 0x4B,0xA5,0x41,0xD6,0xE5,0x70,0x78,0x12,0x1F,0x64,0xB6,0x6F, - 0xB0,0x91,0x51,0x91,0x92,0xC0,0x94,0x3A,0xD1,0x28,0x4D,0x30, - 0x84,0x3E,0xE4,0xE4,0x7F,0x47,0x89,0xB1,0xB6,0x8C,0x8E,0x0E, - 0x26,0xDB,0xCD,0x17,0x07,0x2A,0x21,0x7A,0xCC,0x68,0xE8,0x57, - 0x94,0x9E,0x59,0x61,0xEC,0x20,0x34,0x26,0x0D,0x66,0x44,0xEB, - 0x6F,0x02,0x58,0xE2,0xED,0xF6,0xF3,0x1B,0xBF,0x9E,0x45,0x52, - 0x5A,0x49,0xA1,0x5B, - }; - static unsigned char dh2048_g[]={ - 0x02, - }; - DH *dh = NULL; - BIGNUM *p = NULL, *g = NULL; - - dh = DH_new(); - p = BN_bin2bn(dh2048_p, sizeof(dh2048_p), NULL); - g = BN_bin2bn(dh2048_g, sizeof(dh2048_g), NULL); - if (!dh || !p || !g) - goto err; - -#if OPENSSL_VERSION_NUMBER < 0x10100000 || defined(HAVE_LIBRESSL) - dh->p = p; - dh->g = g; -#else - if (!DH_set0_pqg(dh, p, NULL, g)) - goto err; -#endif - return dh; -err: - if (p) - BN_free(p); - if (g) - BN_free(g); - if (dh) - DH_free(dh); - return NULL; -} -#endif /* SPLINT */ - -struct daemon_remote* -daemon_remote_create(struct config_file* cfg) -{ char* s_cert; char* s_key; - 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; - } rc->ctx = SSL_CTX_new(SSLv23_server_method()); if(!rc->ctx) { log_crypto_err("could not SSL_CTX_new"); - free(rc); - return NULL; + return 0; } - /* no SSLv2, SSLv3 because has defects */ - if((SSL_CTX_set_options(rc->ctx, SSL_OP_NO_SSLv2) & SSL_OP_NO_SSLv2) - != SSL_OP_NO_SSLv2){ - log_crypto_err("could not set SSL_OP_NO_SSLv2"); - daemon_remote_delete(rc); - return NULL; + if(!listen_sslctx_setup(rc->ctx)) { + return 0; } - if((SSL_CTX_set_options(rc->ctx, SSL_OP_NO_SSLv3) & SSL_OP_NO_SSLv3) - != SSL_OP_NO_SSLv3){ - log_crypto_err("could not set SSL_OP_NO_SSLv3"); - daemon_remote_delete(rc); - return NULL; - } - if (cfg->remote_control_use_cert == 0) { - /* No certificates are requested */ - if(!SSL_CTX_set_cipher_list(rc->ctx, "aNULL")) { - log_crypto_err("Failed to set aNULL cipher list"); - daemon_remote_delete(rc); - return NULL; - } - - /* Since we have no certificates and hence no source of - * DH params, let's generate and set them - */ - if(!SSL_CTX_set_tmp_dh(rc->ctx,get_dh2048())) { - log_crypto_err("Wanted to set DH param, but failed"); - daemon_remote_delete(rc); - return NULL; - } - return rc; - } - rc->use_cert = 1; 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) { @@ -282,36 +179,52 @@ log_crypto_err("Error in SSL_CTX check_private_key"); goto setup_error; } -#if HAVE_DECL_SSL_CTX_SET_ECDH_AUTO - if(!SSL_CTX_set_ecdh_auto(rc->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 (rc->ctx, ecdh)) { - log_crypto_err("Error in SSL_CTX_set_tmp_ecdh, not enabling ECDHE"); - } - EC_KEY_free (ecdh); - } - } -#endif + 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); - daemon_remote_delete(rc); - return NULL; + 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; } @@ -363,16 +276,17 @@ struct addrinfo hints; struct addrinfo* res; struct listen_port* n; - int noproto; + 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); + 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 @@ -383,7 +297,7 @@ if (cfg->username && cfg->username[0] && cfg_uid != (uid_t)-1) { if(chown(ip, cfg_uid, cfg_gid) == -1) - log_err("cannot chown %u.%u %s: %s", + verbose(VERB_QUERY, "cannot chown %u.%u %s: %s", (unsigned)cfg_uid, (unsigned)cfg_gid, ip, strerror(errno)); } @@ -415,7 +329,7 @@ /* open fd */ fd = create_tcp_accept_sock(res, 1, &noproto, 0, - cfg->ip_transparent, 0, cfg->ip_freebind); + cfg->ip_transparent, 0, cfg->ip_freebind, cfg->use_systemd); freeaddrinfo(res); } @@ -452,9 +366,9 @@ { struct listen_port* l = NULL; log_assert(cfg->remote_control_enable && cfg->control_port); - if(cfg->control_ifs) { + if(cfg->control_ifs.first) { struct config_strlist* p; - for(p = cfg->control_ifs; p; p = p->next) { + for(p = cfg->control_ifs.first; p; p = p->next) { if(!add_open(p->str, cfg->control_port, &l, 1, cfg)) { listening_ports_free(l); return NULL; @@ -561,6 +475,7 @@ 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); @@ -575,23 +490,27 @@ comm_point_start_listening(n->c, -1, REMOTE_CONTROL_TCP_TIMEOUT); memcpy(&n->c->repinfo.addr, &addr, addrlen); n->c->repinfo.addrlen = addrlen; - 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; + 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; } - SSL_set_accept_state(n->ssl); - (void)SSL_set_mode(n->ssl, 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; - } n->rc = rc; n->next = rc->busy_list; @@ -632,20 +551,38 @@ } int -ssl_print_text(SSL* ssl, const char* text) +ssl_print_text(RES* res, const char* text) { int r; - if(!ssl) + if(!res) return 0; - ERR_clear_error(); - if((r=SSL_write(ssl, text, (int)strlen(text))) <= 0) { - if(SSL_get_error(ssl, r) == SSL_ERROR_ZERO_RETURN) { - verbose(VERB_QUERY, "warning, in SSL_write, peer " - "closed connection"); + 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) { + verbose(VERB_QUERY, "warning, in SSL_write, peer " + "closed connection"); + return 0; + } + log_crypto_err("could not SSL_write"); return 0; } - log_crypto_err("could not SSL_write"); - 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; +#ifndef USE_WINSOCK + log_err("could not send: %s", strerror(errno)); +#else + log_err("could not send: %s", wsa_strerror(WSAGetLastError())); +#endif + return 0; + } + at += r; + } } return 1; } @@ -652,7 +589,7 @@ /** print text over the ssl connection */ static int -ssl_print_vmsg(SSL* ssl, const char* format, va_list args) +ssl_print_vmsg(RES* ssl, const char* format, va_list args) { char msg[1024]; vsnprintf(msg, sizeof(msg), format, args); @@ -660,7 +597,7 @@ } /** printf style printing to the ssl connection */ -int ssl_printf(SSL* ssl, const char* format, ...) +int ssl_printf(RES* ssl, const char* format, ...) { va_list args; int ret; @@ -671,21 +608,42 @@ } int -ssl_read_line(SSL* ssl, char* buf, size_t max) +ssl_read_line(RES* res, char* buf, size_t max) { int r; size_t len = 0; - if(!ssl) + if(!res) return 0; while(len < max) { - ERR_clear_error(); - if((r=SSL_read(ssl, buf+len, 1)) <= 0) { - if(SSL_get_error(ssl, r) == SSL_ERROR_ZERO_RETURN) { - buf[len] = 0; - return 1; + 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) { + buf[len] = 0; + return 1; + } + log_crypto_err("could not SSL_read"); + return 0; } - log_crypto_err("could not SSL_read"); - 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; +#ifndef USE_WINSOCK + log_err("could not recv: %s", strerror(errno)); +#else + log_err("could not recv: %s", wsa_strerror(WSAGetLastError())); +#endif + return 0; + } + break; + } } if(buf[len] == '\n') { /* return string without \n */ @@ -710,7 +668,7 @@ } /** send the OK to the control client */ -static void send_ok(SSL* ssl) +static void send_ok(RES* ssl) { (void)ssl_printf(ssl, "ok\n"); } @@ -717,25 +675,25 @@ /** do the stop command */ static void -do_stop(SSL* ssl, struct daemon_remote* rc) +do_stop(RES* ssl, struct worker* worker) { - rc->worker->need_to_exit = 1; - comm_base_exit(rc->worker->base); + worker->need_to_exit = 1; + comm_base_exit(worker->base); send_ok(ssl); } /** do the reload command */ static void -do_reload(SSL* ssl, struct daemon_remote* rc) +do_reload(RES* ssl, struct worker* worker) { - rc->worker->need_to_exit = 0; - comm_base_exit(rc->worker->base); + worker->need_to_exit = 0; + comm_base_exit(worker->base); send_ok(ssl); } /** do the verbosity command */ static void -do_verbosity(SSL* ssl, char* str) +do_verbosity(RES* ssl, char* str) { int val = atoi(str); if(val == 0 && strcmp(str, "0") != 0) { @@ -748,11 +706,13 @@ /** print stats from statinfo */ static int -print_stats(SSL* ssl, const char* nm, struct stats_info* s) +print_stats(RES* ssl, const char* nm, struct ub_stats_info* s) { - struct timeval avg; + 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.cachehits"SQ"%lu\n", nm, (unsigned long)(s->svr.num_queries - s->svr.num_queries_missed_cache))) return 0; @@ -760,12 +720,24 @@ (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.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/ - (s->svr.num_queries_missed_cache+ + (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; @@ -777,7 +749,11 @@ (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; - timeval_divide(&avg, &s->mesh_replies_sum_wait, s->mesh_replies_sent); +#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, @@ -789,9 +765,9 @@ /** print stats for one thread */ static int -print_thread_stats(SSL* ssl, int i, struct stats_info* s) +print_thread_stats(RES* ssl, int i, struct ub_stats_info* s) { - char nm[16]; + char nm[32]; snprintf(nm, sizeof(nm), "thread%d", i); nm[sizeof(nm)-1]=0; return print_stats(ssl, nm, s); @@ -799,7 +775,7 @@ /** print long number */ static int -print_longnum(SSL* ssl, const char* desc, size_t x) +print_longnum(RES* ssl, const char* desc, size_t x) { if(x > 1024*1024*1024) { /* more than a Gb */ @@ -814,34 +790,38 @@ /** print mem stats */ static int -print_mem(SSL* ssl, struct worker* worker, struct daemon* daemon) +print_mem(RES* ssl, struct worker* worker, struct daemon* daemon, + struct ub_stats_info* s) { - int m; - size_t msg, rrset, val, iter; -#ifdef HAVE_SBRK - extern void* unbound_start_brk; - void* cur = sbrk(0); - if(!print_longnum(ssl, "mem.total.sbrk"SQ, - (size_t)((char*)cur - (char*)unbound_start_brk))) return 0; -#endif /* HAVE_SBRK */ + 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 */ msg = slabhash_get_mem(daemon->env->msg_cache); rrset = slabhash_get_mem(&daemon->env->rrset_cache->table); - val=0; - iter=0; - m = modstack_find(&worker->env.mesh->mods, "validator"); - if(m != -1) { - fptr_ok(fptr_whitelist_mod_get_mem(worker->env.mesh-> - mods.mod[m]->get_mem)); - val = (*worker->env.mesh->mods.mod[m]->get_mem) - (&worker->env, m); + 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, "subnet"); +#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); } - m = modstack_find(&worker->env.mesh->mods, "iterator"); - if(m != -1) { - fptr_ok(fptr_whitelist_mod_get_mem(worker->env.mesh-> - mods.mod[m]->get_mem)); - iter = (*worker->env.mesh->mods.mod[m]->get_mem) - (&worker->env, m); - } +#endif /* USE_DNSCRYPT */ if(!print_longnum(ssl, "mem.cache.rrset"SQ, rrset)) return 0; @@ -851,12 +831,33 @@ 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 */ + if(!print_longnum(ssl, "mem.streamwait"SQ, + (size_t)s->svr.mem_stream_wait)) + return 0; return 1; } /** print uptime stats */ static int -print_uptime(SSL* ssl, struct worker* worker, int reset) +print_uptime(RES* ssl, struct worker* worker, int reset) { struct timeval now = *worker->env.now_tv; struct timeval up, dt; @@ -875,7 +876,7 @@ /** print extended histogram */ static int -print_hist(SSL* ssl, struct stats_info* s) +print_hist(RES* ssl, struct ub_stats_info* s) { struct timehist* hist; size_t i; @@ -903,7 +904,7 @@ /** print extended stats */ static int -print_ext(SSL* ssl, struct stats_info* s) +print_ext(RES* ssl, struct ub_stats_info* s) { int i; char nm[16]; @@ -910,7 +911,7 @@ const sldns_rr_descriptor* desc; const sldns_lookup_table* lt; /* TYPE */ - for(i=0; isvr.qtype[i] == 0) continue; desc = sldns_rr_descript((uint16_t)i); @@ -937,7 +938,7 @@ (unsigned long)s->svr.qtype_big)) return 0; } /* CLASS */ - for(i=0; isvr.qclass[i] == 0) continue; lt = sldns_lookup_by_id(sldns_rr_classes, i); @@ -954,7 +955,7 @@ (unsigned long)s->svr.qclass_big)) return 0; } /* OPCODE */ - for(i=0; isvr.qopcode[i] == 0) continue; lt = sldns_lookup_by_id(sldns_opcodes, i); @@ -971,6 +972,10 @@ (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.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; /* flags */ @@ -996,7 +1001,7 @@ (unsigned long)s->svr.qEDNS_DO)) return 0; /* RCODE */ - for(i=0; i LDNS_RCODE_REFUSED && s->svr.ans_rcode[i] == 0) continue; @@ -1013,6 +1018,9 @@ 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; @@ -1020,6 +1028,10 @@ (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; @@ -1034,21 +1046,52 @@ (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; + /* applied RPZ actions */ + for(i=0; isvr.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 */ return 1; } /** do the stats command */ static void -do_stats(SSL* ssl, struct daemon_remote* rc, int reset) +do_stats(RES* ssl, struct worker* worker, int reset) { - struct daemon* daemon = rc->worker->daemon; - struct stats_info total; - struct stats_info s; + 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; inum; i++) { - server_stats_obtain(rc->worker, daemon->workers[i], &s, reset); + server_stats_obtain(worker, daemon->workers[i], &s, reset); if(!print_thread_stats(ssl, i, &s)) return; if(i == 0) @@ -1059,10 +1102,10 @@ total.mesh_time_median /= (double)daemon->num; if(!print_stats(ssl, "total", &total)) return; - if(!print_uptime(ssl, rc->worker, reset)) + if(!print_uptime(ssl, worker, reset)) return; if(daemon->cfg->stat_extended) { - if(!print_mem(ssl, rc->worker, daemon)) + if(!print_mem(ssl, worker, daemon, &total)) return; if(!print_hist(ssl, &total)) return; @@ -1073,7 +1116,7 @@ /** parse commandline argument domain name */ static int -parse_arg_name(SSL* ssl, char* str, uint8_t** res, size_t* len, int* labs) +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); @@ -1099,7 +1142,7 @@ /** find second argument, modifies string */ static int -find_arg2(SSL* ssl, char* arg, char** arg2) +find_arg2(RES* ssl, char* arg, char** arg2) { char* as = strchr(arg, ' '); char* at = strchr(arg, '\t'); @@ -1123,8 +1166,8 @@ } /** Add a new zone */ -static void -do_zone_add(SSL* ssl, struct worker* worker, char* arg) +static int +perform_zone_add(RES* ssl, struct local_zones* zones, char* arg) { uint8_t* nm; int nmlabs; @@ -1133,16 +1176,16 @@ enum localzone_type t; struct local_zone* z; if(!find_arg2(ssl, arg, &arg2)) - return; + return 0; if(!parse_arg_name(ssl, arg, &nm, &nmlen, &nmlabs)) - return; + return 0; if(!local_zone_str2type(arg2, &t)) { ssl_printf(ssl, "error not a zone type. %s\n", arg2); free(nm); - return; + return 0; } - lock_rw_wrlock(&worker->daemon->local_zones->lock); - if((z=local_zones_find(worker->daemon->local_zones, nm, nmlen, + 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); @@ -1149,70 +1192,328 @@ z->type = t; /* update type anyway */ lock_rw_unlock(&z->lock); free(nm); - lock_rw_unlock(&worker->daemon->local_zones->lock); - send_ok(ssl); - return; + lock_rw_unlock(&zones->lock); + return 1; } - if(!local_zones_add_zone(worker->daemon->local_zones, nm, nmlen, + if(!local_zones_add_zone(zones, nm, nmlen, nmlabs, LDNS_RR_CLASS_IN, t)) { - lock_rw_unlock(&worker->daemon->local_zones->lock); + 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; - } - lock_rw_unlock(&worker->daemon->local_zones->lock); send_ok(ssl); } -/** Remove a zone */ +/** Do the local_zones command */ static void -do_zone_remove(SSL* ssl, struct worker* worker, char* arg) +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(!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; - lock_rw_wrlock(&worker->daemon->local_zones->lock); - if((z=local_zones_find(worker->daemon->local_zones, nm, nmlen, + 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(worker->daemon->local_zones, z); + local_zones_del_zone(zones, z); } - lock_rw_unlock(&worker->daemon->local_zones->lock); + 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(!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); +} + /** Add new RR data */ +static int +perform_data_add(RES* ssl, struct local_zones* zones, char* arg) +{ + 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(SSL* ssl, struct worker* worker, char* arg) +do_data_add(RES* ssl, struct local_zones* zones, char* arg) { - if(!local_zones_add_RR(worker->daemon->local_zones, arg)) { - ssl_printf(ssl,"error in syntax or out of memory, %s\n", arg); + if(!perform_data_add(ssl, zones, arg)) return; - } send_ok(ssl); } -/** Remove RR data */ +/** Do the local_datas command */ static void -do_data_remove(SSL* ssl, struct worker* worker, char* arg) +do_datas_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(!perform_data_add(ssl, zones, buf)) { + if(!ssl_printf(ssl, "error for input line: %s\n", buf)) + return; + } + else + 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; - local_zones_del_data(worker->daemon->local_zones, nm, + 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(!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(SSL* ssl, struct worker* worker, char* arg) +do_lookup(RES* ssl, struct worker* worker, char* arg) { uint8_t* nm; int nmlabs; @@ -1228,7 +1529,7 @@ do_cache_remove(struct worker* worker, uint8_t* nm, size_t nmlen, uint16_t t, uint16_t c) { - hashvalue_t h; + 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) @@ -1238,6 +1539,7 @@ 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) { @@ -1249,7 +1551,7 @@ /** flush a type */ static void -do_flush_type(SSL* ssl, struct worker* worker, char* arg) +do_flush_type(RES* ssl, struct worker* worker, char* arg) { uint8_t* nm; int nmlabs; @@ -1269,7 +1571,7 @@ /** flush statistics */ static void -do_flush_stats(SSL* ssl, struct worker* worker) +do_flush_stats(RES* ssl, struct worker* worker) { worker_stats_clear(worker); send_ok(ssl); @@ -1324,7 +1626,7 @@ /** flush infra cache */ static void -do_flush_infra(SSL* ssl, struct worker* worker, char* arg) +do_flush_infra(RES* ssl, struct worker* worker, char* arg) { struct sockaddr_storage addr; socklen_t len; @@ -1358,7 +1660,7 @@ /** flush requestlist */ static void -do_flush_requestlist(SSL* ssl, struct worker* worker) +do_flush_requestlist(RES* ssl, struct worker* worker) { mesh_delete_all(worker->env.mesh); send_ok(ssl); @@ -1392,6 +1694,8 @@ 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++; } } @@ -1415,7 +1719,7 @@ /** remove all rrsets and keys from zone from cache */ static void -do_flush_zone(SSL* ssl, struct worker* worker, char* arg) +do_flush_zone(RES* ssl, struct worker* worker, char* arg) { uint8_t* nm; int nmlabs; @@ -1493,7 +1797,7 @@ /** remove all bogus rrsets, msgs and keys from cache */ static void -do_flush_bogus(SSL* ssl, struct worker* worker) +do_flush_bogus(RES* ssl, struct worker* worker) { struct del_info inf; /* what we do is to set them all expired */ @@ -1528,7 +1832,7 @@ struct ub_packed_rrset_key* k = (struct ub_packed_rrset_key*)e->key; struct packed_rrset_data* d = (struct packed_rrset_data*)e->data; /* delete the parentside negative cache rrsets, - * these are namerserver rrsets that failed lookup, rdata empty */ + * these are nameserver rrsets that failed lookup, rdata empty */ if((k->rk.flags & PACKED_RRSET_PARENT_SIDE) && d->count == 1 && d->rrsig_count == 0 && d->rr_len[0] == 0) { d->ttl = inf->expired; @@ -1568,7 +1872,7 @@ /** remove all negative(NODATA,NXDOMAIN), and servfail messages from cache */ static void -do_flush_negative(SSL* ssl, struct worker* worker) +do_flush_negative(RES* ssl, struct worker* worker) { struct del_info inf; /* what we do is to set them all expired */ @@ -1596,7 +1900,7 @@ /** remove name rrset from cache */ static void -do_flush_name(SSL* ssl, struct worker* w, char* arg) +do_flush_name(RES* ssl, struct worker* w, char* arg) { uint8_t* nm; int nmlabs; @@ -1620,7 +1924,7 @@ /** printout a delegation point info */ static int -ssl_print_name_dp(SSL* ssl, const char* str, uint8_t* nm, uint16_t dclass, +ssl_print_name_dp(RES* ssl, const char* str, uint8_t* nm, uint16_t dclass, struct delegpt* dp) { char buf[257]; @@ -1654,7 +1958,7 @@ /** print root forwards */ static int -print_root_fwds(SSL* ssl, struct iter_forwards* fwds, uint8_t* root) +print_root_fwds(RES* ssl, struct iter_forwards* fwds, uint8_t* root) { struct delegpt* dp; dp = forwards_lookup(fwds, root, LDNS_RR_CLASS_IN); @@ -1667,7 +1971,7 @@ /** parse args into delegpt */ static struct delegpt* -parse_delegpt(SSL* ssl, char* args, uint8_t* nm, int allow_names) +parse_delegpt(RES* ssl, char* args, uint8_t* nm, int allow_names) { /* parse args and add in */ char* p = args; @@ -1675,6 +1979,7 @@ 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; @@ -1687,7 +1992,7 @@ p = skipwhite(p); /* position at next spot */ } /* parse address */ - if(!extstrtoaddr(todo, &addr, &addrlen)) { + if(!authextstrtoaddr(todo, &addr, &addrlen, &auth_name)) { if(allow_names) { uint8_t* n = NULL; size_t ln; @@ -1714,8 +2019,14 @@ 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)) { + if(!delegpt_add_addr_mlc(dp, &addr, addrlen, 0, 0, + auth_name)) { (void)ssl_printf(ssl, "error out of memory\n"); delegpt_free_mlc(dp); return NULL; @@ -1728,7 +2039,7 @@ /** do the status command */ static void -do_forward(SSL* ssl, struct worker* worker, char* args) +do_forward(RES* ssl, struct worker* worker, char* args) { struct iter_forwards* fwd = worker->env.fwds; uint8_t* root = (uint8_t*)"\000"; @@ -1759,7 +2070,7 @@ } static int -parse_fs_args(SSL* ssl, char* args, uint8_t** nm, struct delegpt** dp, +parse_fs_args(RES* ssl, char* args, uint8_t** nm, struct delegpt** dp, int* insecure, int* prime) { char* zonename; @@ -1804,7 +2115,7 @@ /** do the forward_add command */ static void -do_forward_add(SSL* ssl, struct worker* worker, char* args) +do_forward_add(RES* ssl, struct worker* worker, char* args) { struct iter_forwards* fwd = worker->env.fwds; int insecure = 0; @@ -1832,7 +2143,7 @@ /** do the forward_remove command */ static void -do_forward_remove(SSL* ssl, struct worker* worker, char* args) +do_forward_remove(RES* ssl, struct worker* worker, char* args) { struct iter_forwards* fwd = worker->env.fwds; int insecure = 0; @@ -1849,7 +2160,7 @@ /** do the stub_add command */ static void -do_stub_add(SSL* ssl, struct worker* worker, char* args) +do_stub_add(RES* ssl, struct worker* worker, char* args) { struct iter_forwards* fwd = worker->env.fwds; int insecure = 0, prime = 0; @@ -1890,7 +2201,7 @@ /** do the stub_remove command */ static void -do_stub_remove(SSL* ssl, struct worker* worker, char* args) +do_stub_remove(RES* ssl, struct worker* worker, char* args) { struct iter_forwards* fwd = worker->env.fwds; int insecure = 0; @@ -1908,7 +2219,7 @@ /** do the insecure_add command */ static void -do_insecure_add(SSL* ssl, struct worker* worker, char* arg) +do_insecure_add(RES* ssl, struct worker* worker, char* arg) { size_t nmlen; int nmlabs; @@ -1929,7 +2240,7 @@ /** do the insecure_remove command */ static void -do_insecure_remove(SSL* ssl, struct worker* worker, char* arg) +do_insecure_remove(RES* ssl, struct worker* worker, char* arg) { size_t nmlen; int nmlabs; @@ -1944,7 +2255,7 @@ } static void -do_insecure_list(SSL* ssl, struct worker* worker) +do_insecure_list(RES* ssl, struct worker* worker) { char buf[257]; struct trust_anchor* a; @@ -1960,7 +2271,7 @@ /** do the status command */ static void -do_status(SSL* ssl, struct worker* worker) +do_status(RES* ssl, struct worker* worker) { int i; time_t uptime; @@ -1981,9 +2292,12 @@ 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\n" , + if(!ssl_printf(ssl, "options:%s%s%s%s\n" , (worker->daemon->reuseport?" reuseport":""), - (worker->daemon->rc->accept_list?" control(ssl)":""))) + (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())) @@ -2065,7 +2379,7 @@ /** do the dump_requestlist command */ static void -do_dump_requestlist(SSL* ssl, struct worker* worker) +do_dump_requestlist(RES* ssl, struct worker* worker) { struct mesh_area* mesh; struct mesh_state* m; @@ -2104,7 +2418,7 @@ /** the infra cache */ struct infra_cache* infra; /** the SSL connection */ - SSL* ssl; + RES* ssl; /** the time now */ time_t now; /** ssl failure? stop writing and skip the rest. If the tcp @@ -2121,10 +2435,16 @@ 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) { @@ -2153,7 +2473,7 @@ /** do the dump_infra command */ static void -do_dump_infra(SSL* ssl, struct worker* worker) +do_dump_infra(RES* ssl, struct worker* worker) { struct infra_arg arg; arg.infra = worker->env.infra_cache; @@ -2165,7 +2485,7 @@ /** do the log_reopen command */ static void -do_log_reopen(SSL* ssl, struct worker* worker) +do_log_reopen(RES* ssl, struct worker* worker) { struct config_file* cfg = worker->env.cfg; send_ok(ssl); @@ -2172,9 +2492,62 @@ 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; + 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(!auth_zone_read_zonefile(z, worker->env.cfg)) { + lock_rw_unlock(&z->lock); + (void)ssl_printf(ssl, "error failed to read %s\n", arg); + return; + } + lock_rw_unlock(&z->lock); + 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(SSL* ssl, struct worker* worker, char* arg) +do_set_option(RES* ssl, struct worker* worker, char* arg) { char* arg2; if(!find_arg2(ssl, arg, &arg2)) @@ -2183,6 +2556,14 @@ (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); } @@ -2189,13 +2570,13 @@ /* routine to printout option values over SSL */ void remote_get_opt_ssl(char* line, void* arg) { - SSL* ssl = (SSL*)arg; + RES* ssl = (RES*)arg; (void)ssl_printf(ssl, "%s\n", line); } /** do the get_option command */ static void -do_get_option(SSL* ssl, struct worker* worker, char* arg) +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); @@ -2207,7 +2588,7 @@ /** do the list_forwards command */ static void -do_list_forwards(SSL* ssl, struct worker* worker) +do_list_forwards(RES* ssl, struct worker* worker) { /* since its a per-worker structure no locks needed */ struct iter_forwards* fwds = worker->env.fwds; @@ -2235,7 +2616,7 @@ /** do the list_stubs command */ static void -do_list_stubs(SSL* ssl, struct worker* worker) +do_list_stubs(RES* ssl, struct worker* worker) { struct iter_hints_stub* z; struct trust_anchor* a; @@ -2261,11 +2642,40 @@ } } +/** 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(SSL* ssl, struct worker* worker) +do_list_local_zones(RES* ssl, struct local_zones* zones) { - struct local_zones* zones = worker->daemon->local_zones; struct local_zone* z; char buf[257]; lock_rw_rdlock(&zones->lock); @@ -2286,9 +2696,8 @@ /** do the list_local_data command */ static void -do_list_local_data(SSL* ssl, struct worker* worker) +do_list_local_data(RES* ssl, struct worker* worker, struct local_zones* zones) { - struct local_zones* zones = worker->daemon->local_zones; struct local_zone* z; struct local_data* d; struct local_rrset* p; @@ -2324,12 +2733,44 @@ 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 */ - SSL* ssl; + RES* ssl; /** all or only ratelimited */ int all; /** current time */ @@ -2336,6 +2777,8 @@ time_t now; }; +#define ip_ratelimit_list_arg ratelimit_list_arg + /** list items in the ratelimit table */ static void rate_list(struct lruhash_entry* e, void* arg) @@ -2354,9 +2797,27 @@ 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); + 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(SSL* ssl, struct worker* worker, char* arg) +do_ratelimit_list(RES* ssl, struct worker* worker, char* arg) { struct ratelimit_list_arg a; a.all = 0; @@ -2372,9 +2833,27 @@ 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; + 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); +} + /** tell other processes to execute the command */ static void -distribute_cmd(struct daemon_remote* rc, SSL* ssl, char* cmd) +distribute_cmd(struct daemon_remote* rc, RES* ssl, char* cmd) { int i; if(!cmd || !ssl) @@ -2400,22 +2879,22 @@ /** execute a remote control command */ static void -execute_cmd(struct daemon_remote* rc, SSL* ssl, char* cmd, +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, rc); + do_stop(ssl, worker); return; } else if(cmdcmp(p, "reload", 6)) { - do_reload(ssl, rc); + do_reload(ssl, worker); return; } else if(cmdcmp(p, "stats_noreset", 13)) { - do_stats(ssl, rc, 0); + do_stats(ssl, worker, 0); return; } else if(cmdcmp(p, "stats", 5)) { - do_stats(ssl, rc, 1); + do_stats(ssl, worker, 1); return; } else if(cmdcmp(p, "status", 6)) { do_status(ssl, worker); @@ -2436,14 +2915,32 @@ do_insecure_list(ssl, worker); return; } else if(cmdcmp(p, "list_local_zones", 16)) { - do_list_local_zones(ssl, worker); + do_list_local_zones(ssl, worker->daemon->local_zones); return; } else if(cmdcmp(p, "list_local_data", 15)) { - do_list_local_data(ssl, worker); + 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); @@ -2505,13 +3002,33 @@ if(cmdcmp(p, "verbosity", 9)) { do_verbosity(ssl, skipwhite(p+9)); } else if(cmdcmp(p, "local_zone_remove", 17)) { - do_zone_remove(ssl, worker, skipwhite(p+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, skipwhite(p+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, skipwhite(p+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, skipwhite(p+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)) { @@ -2556,7 +3073,7 @@ /** handle remote control request */ static void -handle_req(struct daemon_remote* rc, struct rc_state* s, SSL* ssl) +handle_req(struct daemon_remote* rc, struct rc_state* s, RES* res) { int r; char pre[10]; @@ -2570,12 +3087,31 @@ fd_set_block(s->c->fd); /* try to read magic UBCT[version]_space_ string */ - ERR_clear_error(); - if((r=SSL_read(ssl, magic, (int)sizeof(magic)-1)) <= 0) { - if(SSL_get_error(ssl, r) == SSL_ERROR_ZERO_RETURN) + 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) + return; + log_crypto_err("could not SSL_read"); return; - log_crypto_err("could not SSL_read"); - 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; +#ifndef USE_WINSOCK + log_err("could not recv: %s", strerror(errno)); +#else + log_err("could not recv: %s", wsa_strerror(WSAGetLastError())); +#endif + return; + } + r = (int)rr; + break; + } } magic[6] = 0; if( r != 6 || strncmp(magic, "UBCT", 4) != 0) { @@ -2585,7 +3121,7 @@ } /* read the command line */ - if(!ssl_read_line(ssl, buf, sizeof(buf))) { + if(!ssl_read_line(res, buf, sizeof(buf))) { return; } snprintf(pre, sizeof(pre), "UBCT%d ", UNBOUND_CONTROL_VERSION); @@ -2592,18 +3128,51 @@ if(strcmp(magic, pre) != 0) { verbose(VERB_QUERY, "control connection had bad " "version %s, cmd: %s", magic, buf); - ssl_printf(ssl, "error version mismatch\n"); + ssl_printf(res, "error version mismatch\n"); return; } verbose(VERB_DETAIL, "control cmd: %s", buf); /* figure out what to do */ - execute_cmd(rc, ssl, buf, rc->worker); + 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.addr, s->c->repinfo.addrlen); + log_crypto_err("remote control failed ssl"); + 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; @@ -2613,38 +3182,16 @@ clean_point(rc, s); return 0; } - /* (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); - 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(1, "failed connection from", - &s->c->repinfo.addr, s->c->repinfo.addrlen); - log_crypto_err("remote control failed ssl"); - 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; } - s->shake_state = rc_none; /* once handshake has completed, check authentication */ if (!rc->use_cert) { @@ -2667,7 +3214,9 @@ } /* if OK start to actually handle the request */ - handle_req(rc, s, s->ssl); + res.ssl = s->ssl; + res.fd = c->fd; + handle_req(rc, s, &res); verbose(VERB_ALGO, "remote control operation completed"); clean_point(rc, s); --- contrib/unbound/daemon/remote.h.orig +++ contrib/unbound/daemon/remote.h @@ -73,6 +73,8 @@ /** the ssl state */ SSL* ssl; #endif + /** file descriptor */ + int fd; /** the rc this is part of */ struct daemon_remote* rc; }; @@ -104,6 +106,19 @@ }; /** + * Connection to print to, either SSL or plain over fd + */ +struct remote_stream { +#ifdef HAVE_SSL + /** SSL structure, nonNULL if using SSL */ + SSL* ssl; +#endif + /** file descriptor for plain transfer */ + int fd; +}; +typedef struct remote_stream RES; + +/** * Create new remote control state for the daemon. * @param cfg: config file with key file settings. * @return new state, or NULL on failure. @@ -166,26 +181,26 @@ * @param text: the text. * @return false on connection failure. */ -int ssl_print_text(SSL* ssl, const char* text); +int ssl_print_text(RES* ssl, const char* text); /** * printf style printing to the ssl connection - * @param ssl: the SSL connection to print to. Blocking. + * @param ssl: the RES connection to print to. Blocking. * @param format: printf style format string. * @return success or false on a network failure. */ -int ssl_printf(SSL* ssl, const char* format, ...) +int ssl_printf(RES* ssl, const char* format, ...) ATTR_FORMAT(printf, 2, 3); /** * Read until \n is encountered - * If SSL signals EOF, the string up to then is returned (without \n). - * @param ssl: the SSL connection to read from. blocking. + * If stream signals EOF, the string up to then is returned (without \n). + * @param ssl: the RES connection to read from. blocking. * @param buf: buffer to read to. * @param max: size of buffer. * @return false on connection failure. */ -int ssl_read_line(SSL* ssl, char* buf, size_t max); +int ssl_read_line(RES* ssl, char* buf, size_t max); #endif /* HAVE_SSL */ #endif /* DAEMON_REMOTE_H */ --- contrib/unbound/daemon/stats.c.orig +++ contrib/unbound/daemon/stats.c @@ -56,72 +56,134 @@ #include "util/timehist.h" #include "util/net_help.h" #include "validator/validator.h" +#include "iterator/iterator.h" #include "sldns/sbuffer.h" #include "services/cache/rrset.h" #include "services/cache/infra.h" +#include "services/authzone.h" #include "validator/val_kcache.h" +#include "validator/val_neg.h" +#ifdef CLIENT_SUBNET +#include "edns-subnet/subnetmod.h" +#endif +#ifdef HAVE_SSL +#include +#endif /** add timers and the values do not overflow or become negative */ static void -timeval_add(struct timeval* d, const struct timeval* add) +stats_timeval_add(long long* d_sec, long long* d_usec, long long add_sec, long long add_usec) { #ifndef S_SPLINT_S - d->tv_sec += add->tv_sec; - d->tv_usec += add->tv_usec; - if(d->tv_usec > 1000000) { - d->tv_usec -= 1000000; - d->tv_sec++; + (*d_sec) += add_sec; + (*d_usec) += add_usec; + if((*d_usec) >= 1000000) { + (*d_usec) -= 1000000; + (*d_sec)++; } #endif } -void server_stats_init(struct server_stats* stats, struct config_file* cfg) +void server_stats_init(struct ub_server_stats* stats, struct config_file* cfg) { memset(stats, 0, sizeof(*stats)); stats->extended = cfg->stat_extended; } -void server_stats_querymiss(struct server_stats* stats, struct worker* worker) +void server_stats_querymiss(struct ub_server_stats* stats, struct worker* worker) { stats->num_queries_missed_cache++; stats->sum_query_list_size += worker->env.mesh->all.count; - if(worker->env.mesh->all.count > stats->max_query_list_size) - stats->max_query_list_size = worker->env.mesh->all.count; + if((long long)worker->env.mesh->all.count > stats->max_query_list_size) + stats->max_query_list_size = (long long)worker->env.mesh->all.count; } -void server_stats_prefetch(struct server_stats* stats, struct worker* worker) +void server_stats_prefetch(struct ub_server_stats* stats, struct worker* worker) { stats->num_queries_prefetch++; /* changes the query list size so account that, like a querymiss */ stats->sum_query_list_size += worker->env.mesh->all.count; - if(worker->env.mesh->all.count > stats->max_query_list_size) - stats->max_query_list_size = worker->env.mesh->all.count; + if((long long)worker->env.mesh->all.count > stats->max_query_list_size) + stats->max_query_list_size = (long long)worker->env.mesh->all.count; } -void server_stats_log(struct server_stats* stats, struct worker* worker, +void server_stats_log(struct ub_server_stats* stats, struct worker* worker, int threadnum) { log_info("server stats for thread %d: %u queries, " - "%u answers from cache, %u recursions, %u prefetch", + "%u answers from cache, %u recursions, %u prefetch, %u rejected by " + "ip ratelimiting", threadnum, (unsigned)stats->num_queries, (unsigned)(stats->num_queries - stats->num_queries_missed_cache), (unsigned)stats->num_queries_missed_cache, - (unsigned)stats->num_queries_prefetch); + (unsigned)stats->num_queries_prefetch, + (unsigned)stats->num_queries_ip_ratelimited); log_info("server stats for thread %d: requestlist max %u avg %g " "exceeded %u jostled %u", threadnum, (unsigned)stats->max_query_list_size, (stats->num_queries_missed_cache+stats->num_queries_prefetch)? (double)stats->sum_query_list_size/ - (stats->num_queries_missed_cache+ + (double)(stats->num_queries_missed_cache+ stats->num_queries_prefetch) : 0.0, (unsigned)worker->env.mesh->stats_dropped, (unsigned)worker->env.mesh->stats_jostled); } + +#ifdef CLIENT_SUBNET +/** Set the EDNS Subnet stats. */ +static void +set_subnet_stats(struct worker* worker, struct ub_server_stats* svr, + int reset) +{ + int m = modstack_find(&worker->env.mesh->mods, "subnet"); + struct subnet_env* sne; + if(m == -1) + return; + sne = (struct subnet_env*)worker->env.modinfo[m]; + if(reset && !worker->env.cfg->stat_cumulative) { + lock_rw_wrlock(&sne->biglock); + } else { + lock_rw_rdlock(&sne->biglock); + } + svr->num_query_subnet = (long long)(sne->num_msg_nocache + sne->num_msg_cache); + svr->num_query_subnet_cache = (long long)sne->num_msg_cache; + if(reset && !worker->env.cfg->stat_cumulative) { + sne->num_msg_cache = 0; + sne->num_msg_nocache = 0; + } + lock_rw_unlock(&sne->biglock); +} +#endif /* CLIENT_SUBNET */ + +/** Set the neg cache stats. */ +static void +set_neg_cache_stats(struct worker* worker, struct ub_server_stats* svr, + int reset) +{ + int m = modstack_find(&worker->env.mesh->mods, "validator"); + struct val_env* ve; + struct val_neg_cache* neg; + if(m == -1) + return; + ve = (struct val_env*)worker->env.modinfo[m]; + if(!ve->neg_cache) + return; + neg = ve->neg_cache; + lock_basic_lock(&neg->lock); + svr->num_neg_cache_noerror = (long long)neg->num_neg_cache_noerror; + svr->num_neg_cache_nxdomain = (long long)neg->num_neg_cache_nxdomain; + if(reset && !worker->env.cfg->stat_cumulative) { + neg->num_neg_cache_noerror = 0; + neg->num_neg_cache_nxdomain = 0; + } + lock_basic_unlock(&neg->lock); +} + /** get rrsets bogus number from validator */ static size_t -get_rrset_bogus(struct worker* worker) +get_rrset_bogus(struct worker* worker, int reset) { int m = modstack_find(&worker->env.mesh->mods, "validator"); struct val_env* ve; @@ -131,56 +193,164 @@ ve = (struct val_env*)worker->env.modinfo[m]; lock_basic_lock(&ve->bogus_lock); r = ve->num_rrset_bogus; - if(!worker->env.cfg->stat_cumulative) + if(reset && !worker->env.cfg->stat_cumulative) ve->num_rrset_bogus = 0; lock_basic_unlock(&ve->bogus_lock); return r; } +/** get number of ratelimited queries from iterator */ +static size_t +get_queries_ratelimit(struct worker* worker, int reset) +{ + int m = modstack_find(&worker->env.mesh->mods, "iterator"); + struct iter_env* ie; + size_t r; + if(m == -1) + return 0; + ie = (struct iter_env*)worker->env.modinfo[m]; + lock_basic_lock(&ie->queries_ratelimit_lock); + r = ie->num_queries_ratelimited; + if(reset && !worker->env.cfg->stat_cumulative) + ie->num_queries_ratelimited = 0; + lock_basic_unlock(&ie->queries_ratelimit_lock); + return r; +} + +#ifdef USE_DNSCRYPT +/** get the number of shared secret cache miss */ +static size_t +get_dnscrypt_cache_miss(struct worker* worker, int reset) +{ + size_t r; + struct dnsc_env* de = worker->daemon->dnscenv; + if(!de) return 0; + + lock_basic_lock(&de->shared_secrets_cache_lock); + r = de->num_query_dnscrypt_secret_missed_cache; + if(reset && !worker->env.cfg->stat_cumulative) + de->num_query_dnscrypt_secret_missed_cache = 0; + lock_basic_unlock(&de->shared_secrets_cache_lock); + return r; +} + +/** get the number of replayed queries */ +static size_t +get_dnscrypt_replay(struct worker* worker, int reset) +{ + size_t r; + struct dnsc_env* de = worker->daemon->dnscenv; + + lock_basic_lock(&de->nonces_cache_lock); + r = de->num_query_dnscrypt_replay; + if(reset && !worker->env.cfg->stat_cumulative) + de->num_query_dnscrypt_replay = 0; + lock_basic_unlock(&de->nonces_cache_lock); + return r; +} +#endif /* USE_DNSCRYPT */ + void -server_stats_compile(struct worker* worker, struct stats_info* s, int reset) +server_stats_compile(struct worker* worker, struct ub_stats_info* s, int reset) { int i; struct listen_list* lp; s->svr = worker->stats; - s->mesh_num_states = worker->env.mesh->all.count; - s->mesh_num_reply_states = worker->env.mesh->num_reply_states; - s->mesh_jostled = worker->env.mesh->stats_jostled; - s->mesh_dropped = worker->env.mesh->stats_dropped; - s->mesh_replies_sent = worker->env.mesh->replies_sent; - s->mesh_replies_sum_wait = worker->env.mesh->replies_sum_wait; + s->mesh_num_states = (long long)worker->env.mesh->all.count; + s->mesh_num_reply_states = (long long)worker->env.mesh->num_reply_states; + s->mesh_jostled = (long long)worker->env.mesh->stats_jostled; + s->mesh_dropped = (long long)worker->env.mesh->stats_dropped; + s->mesh_replies_sent = (long long)worker->env.mesh->replies_sent; + s->mesh_replies_sum_wait_sec = (long long)worker->env.mesh->replies_sum_wait.tv_sec; + s->mesh_replies_sum_wait_usec = (long long)worker->env.mesh->replies_sum_wait.tv_usec; s->mesh_time_median = timehist_quartile(worker->env.mesh->histogram, 0.50); /* add in the values from the mesh */ - s->svr.ans_secure += worker->env.mesh->ans_secure; - s->svr.ans_bogus += worker->env.mesh->ans_bogus; - s->svr.ans_rcode_nodata += worker->env.mesh->ans_nodata; - for(i=0; i<16; i++) - s->svr.ans_rcode[i] += worker->env.mesh->ans_rcode[i]; + s->svr.ans_secure += (long long)worker->env.mesh->ans_secure; + s->svr.ans_bogus += (long long)worker->env.mesh->ans_bogus; + s->svr.ans_rcode_nodata += (long long)worker->env.mesh->ans_nodata; + for(i=0; isvr.ans_rcode[i] += (long long)worker->env.mesh->ans_rcode[i]; + for(i=0; isvr.rpz_action[i] += (long long)worker->env.mesh->rpz_action[i]; timehist_export(worker->env.mesh->histogram, s->svr.hist, NUM_BUCKETS_HIST); /* values from outside network */ - s->svr.unwanted_replies = worker->back->unwanted_replies; - s->svr.qtcp_outgoing = worker->back->num_tcp_outgoing; + s->svr.unwanted_replies = (long long)worker->back->unwanted_replies; + s->svr.qtcp_outgoing = (long long)worker->back->num_tcp_outgoing; /* get and reset validator rrset bogus number */ - s->svr.rrset_bogus = get_rrset_bogus(worker); + s->svr.rrset_bogus = (long long)get_rrset_bogus(worker, reset); + /* get and reset iterator query ratelimit number */ + s->svr.queries_ratelimited = (long long)get_queries_ratelimit(worker, reset); + /* get cache sizes */ - s->svr.msg_cache_count = count_slabhash_entries(worker->env.msg_cache); - s->svr.rrset_cache_count = count_slabhash_entries(&worker->env.rrset_cache->table); - s->svr.infra_cache_count = count_slabhash_entries(worker->env.infra_cache->hosts); + s->svr.msg_cache_count = (long long)count_slabhash_entries(worker->env.msg_cache); + s->svr.rrset_cache_count = (long long)count_slabhash_entries(&worker->env.rrset_cache->table); + s->svr.infra_cache_count = (long long)count_slabhash_entries(worker->env.infra_cache->hosts); if(worker->env.key_cache) - s->svr.key_cache_count = count_slabhash_entries(worker->env.key_cache->slab); + s->svr.key_cache_count = (long long)count_slabhash_entries(worker->env.key_cache->slab); else s->svr.key_cache_count = 0; +#ifdef USE_DNSCRYPT + if(worker->daemon->dnscenv) { + s->svr.num_query_dnscrypt_secret_missed_cache = + (long long)get_dnscrypt_cache_miss(worker, reset); + s->svr.shared_secret_cache_count = (long long)count_slabhash_entries( + worker->daemon->dnscenv->shared_secrets_cache); + s->svr.nonce_cache_count = (long long)count_slabhash_entries( + worker->daemon->dnscenv->nonces_cache); + s->svr.num_query_dnscrypt_replay = + (long long)get_dnscrypt_replay(worker, reset); + } else { + s->svr.num_query_dnscrypt_secret_missed_cache = 0; + s->svr.shared_secret_cache_count = 0; + s->svr.nonce_cache_count = 0; + s->svr.num_query_dnscrypt_replay = 0; + } +#else + s->svr.num_query_dnscrypt_secret_missed_cache = 0; + s->svr.shared_secret_cache_count = 0; + s->svr.nonce_cache_count = 0; + s->svr.num_query_dnscrypt_replay = 0; +#endif /* USE_DNSCRYPT */ + if(worker->env.auth_zones) { + if(reset && !worker->env.cfg->stat_cumulative) { + lock_rw_wrlock(&worker->env.auth_zones->lock); + } else { + lock_rw_rdlock(&worker->env.auth_zones->lock); + } + s->svr.num_query_authzone_up = (long long)worker->env. + auth_zones->num_query_up; + s->svr.num_query_authzone_down = (long long)worker->env. + auth_zones->num_query_down; + if(reset && !worker->env.cfg->stat_cumulative) { + worker->env.auth_zones->num_query_up = 0; + worker->env.auth_zones->num_query_down = 0; + } + lock_rw_unlock(&worker->env.auth_zones->lock); + } + s->svr.mem_stream_wait = + (long long)tcp_req_info_get_stream_buffer_size(); + + /* Set neg cache usage numbers */ + set_neg_cache_stats(worker, &s->svr, reset); +#ifdef CLIENT_SUBNET + /* EDNS Subnet usage numbers */ + set_subnet_stats(worker, &s->svr, reset); +#else + s->svr.num_query_subnet = 0; + s->svr.num_query_subnet_cache = 0; +#endif + /* get tcp accept usage */ s->svr.tcp_accept_usage = 0; for(lp = worker->front->cps; lp; lp = lp->next) { if(lp->com->type == comm_tcp_accept) - s->svr.tcp_accept_usage += lp->com->cur_tcp_count; + s->svr.tcp_accept_usage += (long long)lp->com->cur_tcp_count; } if(reset && !worker->env.cfg->stat_cumulative) { @@ -189,7 +359,7 @@ } void server_stats_obtain(struct worker* worker, struct worker* who, - struct stats_info* s, int reset) + struct ub_stats_info* s, int reset) { uint8_t *reply = NULL; uint32_t len = 0; @@ -215,7 +385,7 @@ void server_stats_reply(struct worker* worker, int reset) { - struct stats_info s; + struct ub_stats_info s; server_stats_compile(worker, &s, reset); verbose(VERB_ALGO, "write stats replymsg"); if(!tube_write_msg(worker->daemon->workers[0]->cmd, @@ -223,12 +393,22 @@ fatal_exit("could not write stat values over cmd channel"); } -void server_stats_add(struct stats_info* total, struct stats_info* a) +void server_stats_add(struct ub_stats_info* total, struct ub_stats_info* a) { total->svr.num_queries += a->svr.num_queries; + total->svr.num_queries_ip_ratelimited += a->svr.num_queries_ip_ratelimited; total->svr.num_queries_missed_cache += a->svr.num_queries_missed_cache; total->svr.num_queries_prefetch += a->svr.num_queries_prefetch; total->svr.sum_query_list_size += a->svr.sum_query_list_size; + total->svr.ans_expired += a->svr.ans_expired; +#ifdef USE_DNSCRYPT + total->svr.num_query_dnscrypt_crypted += a->svr.num_query_dnscrypt_crypted; + total->svr.num_query_dnscrypt_cert += a->svr.num_query_dnscrypt_cert; + total->svr.num_query_dnscrypt_cleartext += \ + a->svr.num_query_dnscrypt_cleartext; + total->svr.num_query_dnscrypt_crypted_malformed += \ + a->svr.num_query_dnscrypt_crypted_malformed; +#endif /* USE_DNSCRYPT */ /* the max size reached is upped to higher of both */ if(a->svr.max_query_list_size > total->svr.max_query_list_size) total->svr.max_query_list_size = a->svr.max_query_list_size; @@ -239,6 +419,8 @@ total->svr.qclass_big += a->svr.qclass_big; total->svr.qtcp += a->svr.qtcp; total->svr.qtcp_outgoing += a->svr.qtcp_outgoing; + total->svr.qtls += a->svr.qtls; + total->svr.qtls_resume += a->svr.qtls_resume; total->svr.qipv6 += a->svr.qipv6; total->svr.qbit_QR += a->svr.qbit_QR; total->svr.qbit_AA += a->svr.qbit_AA; @@ -253,20 +435,21 @@ total->svr.ans_rcode_nodata += a->svr.ans_rcode_nodata; total->svr.ans_secure += a->svr.ans_secure; total->svr.ans_bogus += a->svr.ans_bogus; - total->svr.rrset_bogus += a->svr.rrset_bogus; total->svr.unwanted_replies += a->svr.unwanted_replies; total->svr.unwanted_queries += a->svr.unwanted_queries; total->svr.tcp_accept_usage += a->svr.tcp_accept_usage; - for(i=0; isvr.qtype[i] += a->svr.qtype[i]; - for(i=0; isvr.qclass[i] += a->svr.qclass[i]; - for(i=0; isvr.qopcode[i] += a->svr.qopcode[i]; - for(i=0; isvr.ans_rcode[i] += a->svr.ans_rcode[i]; for(i=0; isvr.hist[i] += a->svr.hist[i]; + for(i=0; isvr.rpz_action[i] += a->svr.rpz_action[i]; } total->mesh_num_states += a->mesh_num_states; @@ -274,7 +457,7 @@ total->mesh_jostled += a->mesh_jostled; total->mesh_dropped += a->mesh_dropped; total->mesh_replies_sent += a->mesh_replies_sent; - timeval_add(&total->mesh_replies_sum_wait, &a->mesh_replies_sum_wait); + stats_timeval_add(&total->mesh_replies_sum_wait_sec, &total->mesh_replies_sum_wait_usec, a->mesh_replies_sum_wait_sec, a->mesh_replies_sum_wait_usec); /* the medians are averaged together, this is not as accurate as * taking the median over all of the data, but is good and fast * added up here, division later*/ @@ -281,20 +464,28 @@ total->mesh_time_median += a->mesh_time_median; } -void server_stats_insquery(struct server_stats* stats, struct comm_point* c, +void server_stats_insquery(struct ub_server_stats* stats, struct comm_point* c, uint16_t qtype, uint16_t qclass, struct edns_data* edns, struct comm_reply* repinfo) { uint16_t flags = sldns_buffer_read_u16_at(c->buffer, 2); - if(qtype < STATS_QTYPE_NUM) + if(qtype < UB_STATS_QTYPE_NUM) stats->qtype[qtype]++; else stats->qtype_big++; - if(qclass < STATS_QCLASS_NUM) + if(qclass < UB_STATS_QCLASS_NUM) stats->qclass[qclass]++; else stats->qclass_big++; stats->qopcode[ LDNS_OPCODE_WIRE(sldns_buffer_begin(c->buffer)) ]++; - if(c->type != comm_udp) + if(c->type != comm_udp) { stats->qtcp++; + if(c->ssl != NULL) { + stats->qtls++; +#ifdef HAVE_SSL + if(SSL_session_reused(c->ssl)) + stats->qtls_resume++; +#endif + } + } if(repinfo && addr_is_ip6(&repinfo->addr, repinfo->addrlen)) stats->qipv6++; if( (flags&BIT_QR) ) @@ -320,7 +511,7 @@ } } -void server_stats_insrcode(struct server_stats* stats, sldns_buffer* buf) +void server_stats_insrcode(struct ub_server_stats* stats, sldns_buffer* buf) { if(stats->extended && sldns_buffer_limit(buf) != 0) { int r = (int)LDNS_RCODE_WIRE( sldns_buffer_begin(buf) ); --- contrib/unbound/daemon/stats.h.orig +++ contrib/unbound/daemon/stats.h @@ -50,143 +50,24 @@ struct edns_data; struct sldns_buffer; -/** number of qtype that is stored for in array */ -#define STATS_QTYPE_NUM 256 -/** number of qclass that is stored for in array */ -#define STATS_QCLASS_NUM 256 -/** number of rcodes in stats */ -#define STATS_RCODE_NUM 16 -/** number of opcodes in stats */ -#define STATS_OPCODE_NUM 16 +/* stats struct */ +#include "libunbound/unbound.h" -/** per worker statistics */ -struct server_stats { - /** number of queries from clients received. */ - size_t num_queries; - /** number of queries that had a cache-miss. */ - size_t num_queries_missed_cache; - /** number of prefetch queries - cachehits with prefetch */ - size_t num_queries_prefetch; - - /** - * Sum of the querylistsize of the worker for - * every query that missed cache. To calculate average. - */ - size_t sum_query_list_size; - /** max value of query list size reached. */ - size_t max_query_list_size; - - /** Extended stats below (bool) */ - int extended; - - /** qtype stats */ - size_t qtype[STATS_QTYPE_NUM]; - /** bigger qtype values not in array */ - size_t qtype_big; - /** qclass stats */ - size_t qclass[STATS_QCLASS_NUM]; - /** bigger qclass values not in array */ - size_t qclass_big; - /** query opcodes */ - size_t qopcode[STATS_OPCODE_NUM]; - /** number of queries over TCP */ - size_t qtcp; - /** number of outgoing queries over TCP */ - size_t qtcp_outgoing; - /** number of queries over IPv6 */ - size_t qipv6; - /** number of queries with QR bit */ - size_t qbit_QR; - /** number of queries with AA bit */ - size_t qbit_AA; - /** number of queries with TC bit */ - size_t qbit_TC; - /** number of queries with RD bit */ - size_t qbit_RD; - /** number of queries with RA bit */ - size_t qbit_RA; - /** number of queries with Z bit */ - size_t qbit_Z; - /** number of queries with AD bit */ - size_t qbit_AD; - /** number of queries with CD bit */ - size_t qbit_CD; - /** number of queries with EDNS OPT record */ - size_t qEDNS; - /** number of queries with EDNS with DO flag */ - size_t qEDNS_DO; - /** answer rcodes */ - size_t ans_rcode[STATS_RCODE_NUM]; - /** answers with pseudo rcode 'nodata' */ - size_t ans_rcode_nodata; - /** answers that were secure (AD) */ - size_t ans_secure; - /** answers that were bogus (withheld as SERVFAIL) */ - size_t ans_bogus; - /** rrsets marked bogus by validator */ - size_t rrset_bogus; - /** unwanted traffic received on server-facing ports */ - size_t unwanted_replies; - /** unwanted traffic received on client-facing ports */ - size_t unwanted_queries; - /** usage of tcp accept list */ - size_t tcp_accept_usage; - - /** histogram data exported to array - * if the array is the same size, no data is lost, and - * if all histograms are same size (is so by default) then - * adding up works well. */ - size_t hist[NUM_BUCKETS_HIST]; - - /** number of message cache entries */ - size_t msg_cache_count; - /** number of rrset cache entries */ - size_t rrset_cache_count; - /** number of infra cache entries */ - size_t infra_cache_count; - /** number of key cache entries */ - size_t key_cache_count; -}; - /** - * Statistics to send over the control pipe when asked - * This struct is made to be memcpied, sent in binary. - */ -struct stats_info { - /** the thread stats */ - struct server_stats svr; - - /** mesh stats: current number of states */ - size_t mesh_num_states; - /** mesh stats: current number of reply (user) states */ - size_t mesh_num_reply_states; - /** mesh stats: number of reply states overwritten with a new one */ - size_t mesh_jostled; - /** mesh stats: number of incoming queries dropped */ - size_t mesh_dropped; - /** mesh stats: replies sent */ - size_t mesh_replies_sent; - /** mesh stats: sum of waiting times for the replies */ - struct timeval mesh_replies_sum_wait; - /** mesh stats: median of waiting times for replies (in sec) */ - double mesh_time_median; -}; - -/** * Initialize server stats to 0. * @param stats: what to init (this is alloced by the caller). * @param cfg: with extended statistics option. */ -void server_stats_init(struct server_stats* stats, struct config_file* cfg); +void server_stats_init(struct ub_server_stats* stats, struct config_file* cfg); /** add query if it missed the cache */ -void server_stats_querymiss(struct server_stats* stats, struct worker* worker); +void server_stats_querymiss(struct ub_server_stats* stats, struct worker* worker); /** add query if was cached and also resulted in a prefetch */ -void server_stats_prefetch(struct server_stats* stats, struct worker* worker); +void server_stats_prefetch(struct ub_server_stats* stats, struct worker* worker); /** display the stats to the log */ -void server_stats_log(struct server_stats* stats, struct worker* worker, +void server_stats_log(struct ub_server_stats* stats, struct worker* worker, int threadnum); /** @@ -197,7 +78,7 @@ * @param reset: if stats can be reset. */ void server_stats_obtain(struct worker* worker, struct worker* who, - struct stats_info* s, int reset); + struct ub_stats_info* s, int reset); /** * Compile stats into structure for this thread worker. @@ -207,7 +88,7 @@ * @param reset: if true, depending on config stats are reset. * if false, statistics are not reset. */ -void server_stats_compile(struct worker* worker, struct stats_info* s, +void server_stats_compile(struct worker* worker, struct ub_stats_info* s, int reset); /** @@ -223,7 +104,7 @@ * @param total: sum of the two entries. * @param a: to add to it. */ -void server_stats_add(struct stats_info* total, struct stats_info* a); +void server_stats_add(struct ub_stats_info* total, struct ub_stats_info* a); /** * Add stats for this query @@ -234,7 +115,7 @@ * @param edns: edns record * @param repinfo: reply info with remote address */ -void server_stats_insquery(struct server_stats* stats, struct comm_point* c, +void server_stats_insquery(struct ub_server_stats* stats, struct comm_point* c, uint16_t qtype, uint16_t qclass, struct edns_data* edns, struct comm_reply* repinfo); @@ -243,6 +124,6 @@ * @param stats: the stats * @param buf: buffer with rcode. If buffer is length0: not counted. */ -void server_stats_insrcode(struct server_stats* stats, struct sldns_buffer* buf); +void server_stats_insrcode(struct ub_server_stats* stats, struct sldns_buffer* buf); #endif /* DAEMON_STATS_H */ --- contrib/unbound/daemon/unbound.c.orig +++ contrib/unbound/daemon/unbound.c @@ -67,6 +67,7 @@ #ifdef HAVE_GRP_H #include #endif +#include #ifndef S_SPLINT_S /* splint chokes on this system header file */ @@ -87,13 +88,9 @@ # include "nss.h" #endif -#ifdef HAVE_SBRK -/** global debug value to keep track of heap memory allocation */ -void* unbound_start_brk = 0; -#endif - -/** print usage. */ -static void usage(void) +/** print build options. */ +static void +print_build_options(void) { const char** m; const char *evnm="event", *evsys="", *evmethod=""; @@ -100,22 +97,11 @@ time_t t; struct timeval now; struct ub_event_base* base; - printf("usage: unbound [options]\n"); - printf(" start unbound daemon DNS resolver.\n"); - printf("-h this help\n"); - printf("-c file config file to read instead of %s\n", CONFIGFILE); - printf(" file format is described in unbound.conf(5).\n"); - printf("-d do not fork into the background.\n"); - printf("-v verbose (more times to increase verbosity)\n"); -#ifdef UB_ON_WINDOWS - printf("-w opt windows option: \n"); - printf(" install, remove - manage the services entry\n"); - printf(" service - used to start from services control panel\n"); -#endif - printf("Version %s\n", PACKAGE_VERSION); + printf("Version %s\n\n", PACKAGE_VERSION); + printf("Configure line: %s\n", CONFCMDLINE); base = ub_default_event_base(0,&t,&now); ub_get_event_sys(base, &evnm, &evsys, &evmethod); - printf("linked libs: %s %s (it uses %s), %s\n", + printf("Linked libs: %s %s (it uses %s), %s\n", evnm, evsys, evmethod, #ifdef HAVE_SSL # ifdef SSLEAY_VERSION @@ -129,13 +115,42 @@ "nettle" #endif ); - printf("linked modules:"); + printf("Linked modules:"); for(m = module_list_avail(); *m; m++) printf(" %s", *m); printf("\n"); +#ifdef USE_DNSCRYPT + printf("DNSCrypt feature available\n"); +#endif +#ifdef USE_TCP_FASTOPEN + printf("TCP Fastopen feature available\n"); +#endif + ub_event_base_free(base); + printf("\nBSD licensed, see LICENSE in source package for details.\n"); + printf("Report bugs to %s\n", PACKAGE_BUGREPORT); +} + +/** print usage. */ +static void +usage(void) +{ + printf("usage: unbound [options]\n"); + printf(" start unbound daemon DNS resolver.\n"); + printf("-h this help.\n"); + printf("-c file config file to read instead of %s\n", CONFIGFILE); + printf(" file format is described in unbound.conf(5).\n"); + printf("-d do not fork into the background.\n"); + printf("-p do not create a pidfile.\n"); + printf("-v verbose (more times to increase verbosity).\n"); + printf("-V show version number and build options.\n"); +#ifdef UB_ON_WINDOWS + printf("-w opt windows option: \n"); + printf(" install, remove - manage the services entry\n"); + printf(" service - used to start from services control panel\n"); +#endif + printf("\nVersion %s\n", PACKAGE_VERSION); printf("BSD licensed, see LICENSE in source package for details.\n"); printf("Report bugs to %s\n", PACKAGE_BUGREPORT); - ub_event_base_free(base); } #ifndef unbound_testbound @@ -246,7 +261,7 @@ /** set verbosity, check rlimits, cache settings */ static void -apply_settings(struct daemon* daemon, struct config_file* cfg, +apply_settings(struct daemon* daemon, struct config_file* cfg, int cmdline_verbose, int debug_mode) { /* apply if they have changed */ @@ -253,10 +268,17 @@ verbosity = cmdline_verbose + cfg->verbosity; if (debug_mode > 1) { cfg->use_syslog = 0; + free(cfg->logfile); cfg->logfile = NULL; } daemon_apply_cfg(daemon, cfg); checkrlimits(cfg); + + if (cfg->use_systemd && cfg->do_daemonize) { + log_warn("use-systemd and do-daemonize should not be enabled at the same time"); + } + + log_ident_set_or_default(cfg->log_identity); } #ifdef HAVE_KILL @@ -384,10 +406,10 @@ #endif /* HAVE_DAEMON */ } -/** daemonize, drop user priviliges and chroot if needed */ +/** daemonize, drop user privileges and chroot if needed */ static void perform_setup(struct daemon* daemon, struct config_file* cfg, int debug_mode, - const char** cfgfile) + const char** cfgfile, int need_pidfile) { #ifdef HAVE_KILL int pidinchroot; @@ -405,17 +427,6 @@ w_config_adjust_directory(cfg); #endif - /* init syslog (as root) if needed, before daemonize, otherwise - * a fork error could not be printed since daemonize closed stderr.*/ - if(cfg->use_syslog) { - log_init(cfg->logfile, cfg->use_syslog, cfg->chrootdir); - } - /* if using a logfile, we cannot open it because the logfile would - * be created with the wrong permissions, we cannot chown it because - * we cannot chown system logfiles, so we do not open at all. - * So, using a logfile, the user does not see errors unless -d is - * given to unbound on the commandline. */ - /* read ssl keys while superuser and outside chroot */ #ifdef HAVE_SSL if(!(daemon->rc = daemon_remote_create(cfg))) @@ -424,20 +435,50 @@ if(!(daemon->listen_sslctx = listen_sslctx_create( cfg->ssl_service_key, cfg->ssl_service_pem, NULL))) fatal_exit("could not set up listen SSL_CTX"); + if(cfg->tls_ciphers && cfg->tls_ciphers[0]) { + if (!SSL_CTX_set_cipher_list(daemon->listen_sslctx, cfg->tls_ciphers)) { + fatal_exit("failed to set tls-cipher %s", cfg->tls_ciphers); + } + } +#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES + if(cfg->tls_ciphersuites && cfg->tls_ciphersuites[0]) { + if (!SSL_CTX_set_ciphersuites(daemon->listen_sslctx, cfg->tls_ciphersuites)) { + fatal_exit("failed to set tls-ciphersuites %s", cfg->tls_ciphersuites); + } + } +#endif + if(cfg->tls_session_ticket_keys.first && + cfg->tls_session_ticket_keys.first->str[0] != 0) { + if(!listen_sslctx_setup_ticket_keys(daemon->listen_sslctx, cfg->tls_session_ticket_keys.first)) { + fatal_exit("could not set session ticket SSL_CTX"); + } + } } - if(!(daemon->connect_sslctx = connect_sslctx_create(NULL, NULL, NULL))) + if(!(daemon->connect_sslctx = connect_sslctx_create(NULL, NULL, + cfg->tls_cert_bundle, cfg->tls_win_cert))) fatal_exit("could not set up connect SSL_CTX"); #endif + /* init syslog (as root) if needed, before daemonize, otherwise + * a fork error could not be printed since daemonize closed stderr.*/ + if(cfg->use_syslog) { + log_init(cfg->logfile, cfg->use_syslog, cfg->chrootdir); + } + /* if using a logfile, we cannot open it because the logfile would + * be created with the wrong permissions, we cannot chown it because + * we cannot chown system logfiles, so we do not open at all. + * So, using a logfile, the user does not see errors unless -d is + * given to unbound on the commandline. */ + #ifdef HAVE_KILL /* true if pidfile is inside chrootdir, or nochroot */ - pidinchroot = !(cfg->chrootdir && cfg->chrootdir[0]) || + pidinchroot = need_pidfile && (!(cfg->chrootdir && cfg->chrootdir[0]) || (cfg->chrootdir && cfg->chrootdir[0] && strncmp(cfg->pidfile, cfg->chrootdir, - strlen(cfg->chrootdir))==0); + strlen(cfg->chrootdir))==0)); /* check old pid file before forking */ - if(cfg->pidfile && cfg->pidfile[0]) { + if(cfg->pidfile && cfg->pidfile[0] && need_pidfile) { /* calculate position of pidfile */ if(cfg->pidfile[0] == '/') daemon->pidfile = strdup(cfg->pidfile); @@ -456,7 +497,7 @@ /* write new pidfile (while still root, so can be outside chroot) */ #ifdef HAVE_KILL - if(cfg->pidfile && cfg->pidfile[0]) { + if(cfg->pidfile && cfg->pidfile[0] && need_pidfile) { writepid(daemon->pidfile, getpid()); if(cfg->username && cfg->username[0] && cfg_uid != (uid_t)-1 && pidinchroot) { @@ -471,6 +512,7 @@ } #else (void)daemon; + (void)need_pidfile; #endif /* HAVE_KILL */ /* Set user context */ @@ -586,9 +628,10 @@ * @param cmdline_verbose: verbosity resulting from commandline -v. * These increase verbosity as specified in the config file. * @param debug_mode: if set, do not daemonize. + * @param need_pidfile: if false, no pidfile is checked or created. */ static void -run_daemon(const char* cfgfile, int cmdline_verbose, int debug_mode) +run_daemon(const char* cfgfile, int cmdline_verbose, int debug_mode, int need_pidfile) { struct config_file* cfg = NULL; struct daemon* daemon = NULL; @@ -606,8 +649,10 @@ fatal_exit("Could not alloc config defaults"); if(!config_read(cfg, cfgfile, daemon->chroot)) { if(errno != ENOENT) - fatal_exit("Could not read config file: %s", - cfgfile); + fatal_exit("Could not read config file: %s." + " Maybe try unbound -dd, it stays on " + "the commandline to see more errors, " + "or unbound-checkconf", cfgfile); log_warn("Continuing with default config settings"); } apply_settings(daemon, cfg, cmdline_verbose, debug_mode); @@ -618,7 +663,7 @@ if(!daemon_open_shared_ports(daemon)) fatal_exit("could not open ports"); if(!done_setup) { - perform_setup(daemon, cfg, debug_mode, &cfgfile); + perform_setup(daemon, cfg, debug_mode, &cfgfile, need_pidfile); done_setup = 1; } else { /* reopen log after HUP to facilitate log rotation */ @@ -665,21 +710,21 @@ int c; const char* cfgfile = CONFIGFILE; const char* winopt = NULL; + const char* log_ident_default; int cmdline_verbose = 0; int debug_mode = 0; + int need_pidfile = 1; + #ifdef UB_ON_WINDOWS int cmdline_cfg = 0; #endif -#ifdef HAVE_SBRK - /* take debug snapshot of heap */ - unbound_start_brk = sbrk(0); -#endif - log_init(NULL, 0, NULL); - log_ident_set(strrchr(argv[0],'/')?strrchr(argv[0],'/')+1:argv[0]); + log_ident_default = strrchr(argv[0],'/')?strrchr(argv[0],'/')+1:argv[0]; + log_ident_set_default(log_ident_default); + log_ident_set(log_ident_default); /* parse the options */ - while( (c=getopt(argc, argv, "c:dhvw:")) != -1) { + while( (c=getopt(argc, argv, "c:dhpvw:V")) != -1) { switch(c) { case 'c': cfgfile = optarg; @@ -691,6 +736,9 @@ cmdline_verbose++; verbosity++; break; + case 'p': + need_pidfile = 0; + break; case 'd': debug_mode++; break; @@ -697,6 +745,9 @@ case 'w': winopt = optarg; break; + case 'V': + print_build_options(); + return 0; case '?': case 'h': default: @@ -705,7 +756,7 @@ } } argc -= optind; - argv += optind; + /* argv += optind; not using further arguments */ if(winopt) { #ifdef UB_ON_WINDOWS @@ -721,7 +772,12 @@ return 1; } - run_daemon(cfgfile, cmdline_verbose, debug_mode); + run_daemon(cfgfile, cmdline_verbose, debug_mode, need_pidfile); log_init(NULL, 0, NULL); /* close logfile */ +#ifndef unbound_testbound + if(log_get_lock()) { + lock_basic_destroy((lock_basic_type*)log_get_lock()); + } +#endif return 0; } --- contrib/unbound/daemon/worker.c.orig +++ contrib/unbound/daemon/worker.c @@ -58,20 +58,27 @@ #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/tube.h" +#include "util/edns.h" #include "iterator/iter_fwd.h" #include "iterator/iter_hints.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" #ifdef HAVE_SYS_TYPES_H # include @@ -101,51 +108,6 @@ */ #define PREFETCH_EXPIRY_ADD 60 -#ifdef UNBOUND_ALLOC_STATS -/** measure memory leakage */ -static void -debug_memleak(size_t accounted, size_t heap, - size_t total_alloc, size_t total_free) -{ - static int init = 0; - static size_t base_heap, base_accounted, base_alloc, base_free; - size_t base_af, cur_af, grow_af, grow_acc; - if(!init) { - init = 1; - base_heap = heap; - base_accounted = accounted; - base_alloc = total_alloc; - base_free = total_free; - } - base_af = base_alloc - base_free; - cur_af = total_alloc - total_free; - grow_af = cur_af - base_af; - grow_acc = accounted - base_accounted; - log_info("Leakage: %d leaked. growth: %u use, %u acc, %u heap", - (int)(grow_af - grow_acc), (unsigned)grow_af, - (unsigned)grow_acc, (unsigned)(heap - base_heap)); -} - -/** give debug heap size indication */ -static void -debug_total_mem(size_t calctotal) -{ -#ifdef HAVE_SBRK - extern void* unbound_start_brk; - extern size_t unbound_mem_alloc, unbound_mem_freed; - void* cur = sbrk(0); - int total = cur-unbound_start_brk; - log_info("Total heap memory estimate: %u total-alloc: %u " - "total-free: %u", (unsigned)total, - (unsigned)unbound_mem_alloc, (unsigned)unbound_mem_freed); - debug_memleak(calctotal, (size_t)total, - unbound_mem_alloc, unbound_mem_freed); -#else - (void)calctotal; -#endif /* HAVE_SBRK */ -} -#endif /* UNBOUND_ALLOC_STATS */ - /** Report on memory usage by this thread and global */ static void worker_mem_report(struct worker* ATTR_UNUSED(worker), @@ -152,10 +114,15 @@ 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); @@ -175,6 +142,12 @@ 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, + "subnet")==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); } @@ -192,7 +165,18 @@ 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, @@ -199,9 +183,15 @@ (unsigned)mesh, (unsigned)msg, (unsigned)rrset, (unsigned)infra, (unsigned)iter, (unsigned)val, (unsigned)anch, (unsigned)ac, (unsigned)superac, (unsigned)me); - debug_total_mem(total); +#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; @@ -211,12 +201,27 @@ 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, + "subnet")==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 */ } @@ -339,7 +344,8 @@ verbose(VERB_QUERY, "request bad, has TC bit on"); return worker_err_ratelimit(worker, LDNS_RCODE_FORMERR); } - if(LDNS_OPCODE_WIRE(sldns_buffer_begin(pkt)) != LDNS_PACKET_QUERY) { + 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))); return worker_err_ratelimit(worker, LDNS_RCODE_NOTIMPL); @@ -349,7 +355,9 @@ LDNS_QDCOUNT(sldns_buffer_begin(pkt))); return worker_err_ratelimit(worker, LDNS_RCODE_FORMERR); } - if(LDNS_ANCOUNT(sldns_buffer_begin(pkt)) != 0) { + if(LDNS_ANCOUNT(sldns_buffer_begin(pkt)) != 0 && + (LDNS_ANCOUNT(sldns_buffer_begin(pkt)) != 1 || + LDNS_OPCODE_WIRE(sldns_buffer_begin(pkt)) != LDNS_PACKET_NOTIFY)) { verbose(VERB_QUERY, "request wrong nr an=%d", LDNS_ANCOUNT(sldns_buffer_begin(pkt))); return worker_err_ratelimit(worker, LDNS_RCODE_FORMERR); @@ -471,6 +479,7 @@ * Then check if it needs validation, if so, this routine fails, * so that iterator can prime and validator can verify rrsets. */ + struct edns_data edns_bak; uint16_t udpsize = edns->udp_size; int secure = 0; time_t timenow = *worker->env.now; @@ -485,6 +494,10 @@ 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: @@ -492,13 +505,15 @@ * 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(!edns_opt_inplace_reply(edns, worker->scratchpad)) - return 0; + if(!inplace_cb_reply_servfail_call(&worker->env, qinfo, NULL, + msg->rep, LDNS_RCODE_SERVFAIL, edns, repinfo, worker->scratchpad)) + return 0; error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL, &msg->qinfo, id, flags, edns); if(worker->stats.extended) { @@ -522,16 +537,23 @@ } } /* return this delegation from the cache */ + edns_bak = *edns; edns->edns_version = EDNS_ADVERTISED_VERSION; edns->udp_size = EDNS_ADVERTISED_SIZE; edns->ext_rcode = 0; edns->bits &= EDNS_DO; - if(!edns_opt_inplace_reply(edns, worker->scratchpad)) - return 0; + if(!inplace_cb_reply_cache_call(&worker->env, qinfo, NULL, msg->rep, + (int)(flags&LDNS_RCODE_MASK), edns, repinfo, worker->scratchpad)) + return 0; msg->rep->flags |= BIT_QR|BIT_RA; - if(!reply_info_answer_encode(&msg->qinfo, msg->rep, id, flags, + if(!apply_edns_options(edns, &edns_bak, worker->env.cfg, + repinfo->c, worker->scratchpad) || + !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)) + edns->opt_list = NULL; error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL, &msg->qinfo, id, flags, edns); } @@ -542,53 +564,130 @@ return 1; } -/** answer query from the cache */ +/** 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 comm_reply* repinfo, struct ub_packed_rrset_key** alias_rrset, + struct reply_info** encode_repp, struct auth_zones* az) +{ + struct respip_action_info actinfo = {0}; + 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)) + 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; + + /* 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, + repinfo); + + 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 reply_info* rep, uint16_t id, uint16_t flags, + 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) { + struct edns_data edns_bak; time_t timenow = *worker->env.now; uint16_t udpsize = edns->udp_size; - int secure; + struct reply_info* encode_rep = rep; + struct reply_info* partial_rep = *partial_repp; int must_validate = (!(flags&BIT_CD) || worker->env.cfg->ignore_cd) && worker->env.need_to_validate; - /* see if it is possible */ + *partial_repp = NULL; /* avoid accidental further pass */ + + /* Check TTL */ if(rep->ttl < timenow) { - /* the rrsets may have been updated in the meantime. - * we will refetch the message format from the - * authoritative server - */ - return 0; + /* 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; + 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; } - 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 == + 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"); - bail_out: - rrset_array_unlock_touch(worker->env.rrset_cache, - worker->scratchpad, rep->ref, rep->rrset_count); - return 0; + goto bail_out; } } /* check security status of the cached answer */ - if( rep->security == sec_status_bogus && must_validate) { + 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(!edns_opt_inplace_reply(edns, worker->scratchpad)) - return 0; - error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL, + if(!inplace_cb_reply_servfail_call(&worker->env, qinfo, NULL, rep, + LDNS_RCODE_SERVFAIL, edns, repinfo, worker->scratchpad)) + goto bail_out; + error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL, qinfo, id, flags, edns); - rrset_array_unlock_touch(worker->env.rrset_cache, + rrset_array_unlock_touch(worker->env.rrset_cache, worker->scratchpad, rep->ref, rep->rrset_count); if(worker->stats.extended) { worker->stats.ans_bogus ++; @@ -595,32 +694,67 @@ worker->stats.ans_rcode[LDNS_RCODE_SERVFAIL] ++; } return 1; - } else if( rep->security == sec_status_unchecked && must_validate) { + } 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)) - secure = 1; - else { + 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 */ } - secure = 0; + *is_secure_answer = 0; } - } else secure = 0; + } else *is_secure_answer = 0; + edns_bak = *edns; edns->edns_version = EDNS_ADVERTISED_VERSION; edns->udp_size = EDNS_ADVERTISED_SIZE; edns->ext_rcode = 0; edns->bits &= EDNS_DO; - if(!edns_opt_inplace_reply(edns, worker->scratchpad)) - return 0; - if(!reply_info_answer_encode(qinfo, rep, id, flags, + if(!inplace_cb_reply_cache_call(&worker->env, qinfo, NULL, rep, + (int)(flags&LDNS_RCODE_MASK), edns, repinfo, worker->scratchpad)) + goto bail_out; + *alias_rrset = NULL; /* avoid confusion if caller set it to non-NULL */ + if((worker->daemon->use_response_ip || worker->daemon->use_rpz) && + !partial_rep && !apply_respip_action(worker, qinfo, cinfo, rep, + repinfo, 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(!apply_edns_options(edns, &edns_bak, worker->env.cfg, + repinfo->c, worker->scratchpad) || + !reply_info_answer_encode(qinfo, encode_rep, id, flags, repinfo->c->buffer, timenow, 1, worker->scratchpad, - udpsize, edns, (int)(edns->bits & EDNS_DO), secure)) { + 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)) + edns->opt_list = NULL; error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL, qinfo, id, flags, edns); } @@ -628,22 +762,30 @@ * is bad while holding locks. */ rrset_array_unlock_touch(worker->env.rrset_cache, worker->scratchpad, rep->ref, rep->rrset_count); - if(worker->stats.extended) { - if(secure) worker->stats.ans_secure++; - server_stats_insrcode(&worker->stats, repinfo->c->buffer); - } /* 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 */ +/** 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) + uint16_t flags, struct comm_reply* repinfo, time_t leeway, int noreply) { /* first send answer to client to keep its latency * as small as a cachereply */ - comm_point_send_reply(repinfo); + 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); /* create the prefetch in the mesh as a normal lookup without @@ -658,17 +800,19 @@ * 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, const char* str, struct edns_data* edns, - struct worker* worker) +chaos_replystr(sldns_buffer* pkt, char** str, int num, struct edns_data* edns, + struct worker* worker, struct comm_reply* repinfo) { - size_t len = strlen(str); + int i; unsigned int rd = LDNS_RD_WIRE(sldns_buffer_begin(pkt)); unsigned int cd = LDNS_CD_WIRE(sldns_buffer_begin(pkt)); - if(len>255) len=255; /* cap size of TXT record */ 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)); @@ -675,39 +819,113 @@ 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, 1); /* ancount */ + 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 */ - 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, len); + for(i=0; i255) len=255; /* cap size of TXT record */ + 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); edns->edns_version = EDNS_ADVERTISED_VERSION; edns->udp_size = EDNS_ADVERTISED_SIZE; edns->bits &= EDNS_DO; - if(!edns_opt_inplace_reply(edns, worker->scratchpad)) - edns->opt_list = NULL; - attach_edns_record(pkt, edns); + if(!inplace_cb_reply_local_call(&worker->env, NULL, NULL, NULL, + LDNS_RCODE_NOERROR, edns, repinfo, worker->scratchpad)) + edns->opt_list = NULL; + 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; ilock); + } + 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, sldns_buffer* pkt) +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) @@ -723,13 +941,13 @@ char buf[MAXHOSTNAMELEN+1]; if (gethostname(buf, MAXHOSTNAMELEN) == 0) { buf[MAXHOSTNAMELEN] = 0; - chaos_replystr(pkt, buf, edns, w); + chaos_replyonestr(pkt, buf, edns, w, repinfo); } else { log_err("gethostname: %s", strerror(errno)); - chaos_replystr(pkt, "no hostname", edns, w); + chaos_replyonestr(pkt, "no hostname", edns, w, repinfo); } } - else chaos_replystr(pkt, cfg->identity, edns, w); + else chaos_replyonestr(pkt, cfg->identity, edns, w, repinfo); return 1; } if(query_dname_compare(qinfo->qname, @@ -740,13 +958,82 @@ if(cfg->hide_version) return 0; if(cfg->version==NULL || cfg->version[0]==0) - chaos_replystr(pkt, PACKAGE_STRING, edns, w); - else chaos_replystr(pkt, cfg->version, edns, w); + 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 repinfo: reply info with source address. + * @param pkt: packet buffer. + */ +static void +answer_notify(struct worker* w, struct query_info* qinfo, + struct edns_data* edns, sldns_buffer* pkt, struct comm_reply* repinfo) +{ + 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, &repinfo->addr, + repinfo->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, &repinfo->addr, repinfo->addrlen); + } + edns->edns_version = EDNS_ADVERTISED_VERSION; + edns->udp_size = EDNS_ADVERTISED_SIZE; + edns->ext_rcode = 0; + edns->bits &= EDNS_DO; + edns->opt_list = NULL; + 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, @@ -801,7 +1088,7 @@ { struct worker* worker = (struct worker*)arg; int ret; - hashvalue_t h; + hashvalue_type h; struct lruhash_entry* e; struct query_info qinfo; struct edns_data edns; @@ -808,12 +1095,68 @@ enum acl_access acl; struct acl_addr* acladdr; int rc = 0; + int need_drop = 0; + int is_expired_answer = 0; + int is_secure_answer = 0; + /* 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; + memset(&qinfo, 0, sizeof(qinfo)); - if(error != NETEVENT_NOERROR) { + if(error != NETEVENT_NOERROR || !repinfo) { /* some bad tcp query DNS formats give these error calls */ verbose(VERB_ALGO, "handle request called with err=%d", error); 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 */ + if(worker_check_request(c->buffer, worker) != 0) { + verbose(VERB_ALGO, + "dnscrypt: worker check request: bad query."); + log_addr(VERB_CLIENT,"from",&repinfo->addr, + repinfo->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->addr, + repinfo->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 if(worker->dtenv.log_client_query_messages) dt_msg_send_client_query(&worker->dtenv, &repinfo->addr, c->type, @@ -839,11 +1182,34 @@ comm_point_drop_reply(repinfo); return 0; } + worker->stats.num_queries++; + + /* check if this query should be dropped based on source ip rate limiting */ + if(!infra_ip_ratelimit_inc(worker->env.infra_cache, repinfo, + *worker->env.now, c->buffer)) { + /* See if we are passed through with slip factor */ + if(worker->env.cfg->ip_ratelimit_factor != 0 && + ub_random_max(worker->env.rnd, + worker->env.cfg->ip_ratelimit_factor) == 0) { + + char addrbuf[128]; + addr_to_str(&repinfo->addr, repinfo->addrlen, + addrbuf, sizeof(addrbuf)); + verbose(VERB_QUERY, "ip_ratelimit allowed through for ip address %s because of slip in ip_ratelimit_factor", + addrbuf); + } else { + worker->stats.num_queries_ip_ratelimited++; + comm_point_drop_reply(repinfo); + return 0; + } + } + /* see if query is in the cache */ if(!query_info_parse(&qinfo, c->buffer)) { verbose(VERB_ALGO, "worker parse request: formerror."); log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->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; @@ -858,7 +1224,7 @@ if(worker->env.cfg->log_queries) { char ip[128]; addr_to_str(&repinfo->addr, repinfo->addrlen, ip, sizeof(ip)); - log_nametypeclass(0, ip, qinfo.qname, qinfo.qtype, qinfo.qclass); + log_query_in(ip, qinfo.qname, qinfo.qtype, qinfo.qclass); } if(qinfo.qtype == LDNS_RR_TYPE_AXFR || qinfo.qtype == LDNS_RR_TYPE_IXFR) { @@ -874,6 +1240,28 @@ } 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->addr, repinfo->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]++; + server_stats_insrcode(&worker->stats, c->buffer); + } + goto send_reply; + } if((ret=parse_edns_from_pkt(c->buffer, &edns, worker->scratchpad)) != 0) { struct edns_data reply_edns; verbose(VERB_ALGO, "worker parse edns: formerror."); @@ -889,28 +1277,53 @@ server_stats_insrcode(&worker->stats, c->buffer); goto send_reply; } - if(edns.edns_present && edns.edns_version != 0) { - edns.ext_rcode = (uint8_t)(EDNS_RCODE_BADVERS>>4); - edns.edns_version = EDNS_ADVERTISED_VERSION; - edns.udp_size = EDNS_ADVERTISED_SIZE; - edns.bits &= EDNS_DO; - edns.opt_list = NULL; - verbose(VERB_ALGO, "query with bad edns version."); - log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen); - error_encode(c->buffer, EDNS_RCODE_BADVERS&0xf, &qinfo, - *(uint16_t*)(void *)sldns_buffer_begin(c->buffer), - sldns_buffer_read_u16_at(c->buffer, 2), NULL); - attach_edns_record(c->buffer, &edns); - regional_free_all(worker->scratchpad); - goto send_reply; + if(edns.edns_present) { + struct edns_option* edns_opt; + if(edns.edns_version != 0) { + edns.ext_rcode = (uint8_t)(EDNS_RCODE_BADVERS>>4); + edns.edns_version = EDNS_ADVERTISED_VERSION; + edns.udp_size = EDNS_ADVERTISED_SIZE; + edns.bits &= EDNS_DO; + edns.opt_list = NULL; + verbose(VERB_ALGO, "query with bad edns version."); + log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen); + error_encode(c->buffer, EDNS_RCODE_BADVERS&0xf, &qinfo, + *(uint16_t*)(void *)sldns_buffer_begin(c->buffer), + sldns_buffer_read_u16_at(c->buffer, 2), NULL); + if(sldns_buffer_capacity(c->buffer) >= + sldns_buffer_limit(c->buffer)+calc_edns_field_size(&edns)) + attach_edns_record(c->buffer, &edns); + 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->addr, repinfo->addrlen); + edns.udp_size = NORMAL_UDP_SIZE; + } + if(c->type != comm_udp) { + edns_opt = edns_opt_list_find(edns.opt_list, LDNS_EDNS_KEEPALIVE); + if(edns_opt && edns_opt->opt_len > 0) { + edns.ext_rcode = 0; + edns.edns_version = EDNS_ADVERTISED_VERSION; + edns.udp_size = EDNS_ADVERTISED_SIZE; + edns.bits &= EDNS_DO; + edns.opt_list = NULL; + verbose(VERB_ALGO, "query with bad edns keepalive."); + log_addr(VERB_CLIENT,"from",&repinfo->addr, repinfo->addrlen); + error_encode(c->buffer, LDNS_RCODE_FORMERR, &qinfo, + *(uint16_t*)(void *)sldns_buffer_begin(c->buffer), + sldns_buffer_read_u16_at(c->buffer, 2), NULL); + if(sldns_buffer_capacity(c->buffer) >= + sldns_buffer_limit(c->buffer)+calc_edns_field_size(&edns)) + attach_edns_record(c->buffer, &edns); + regional_free_all(worker->scratchpad); + goto send_reply; + } + } } - if(edns.edns_present && 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->addr, repinfo->addrlen); - edns.udp_size = NORMAL_UDP_SIZE; - } if(edns.udp_size > worker->daemon->cfg->max_udp_size && c->type == comm_udp) { verbose(VERB_QUERY, @@ -939,17 +1352,23 @@ 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, c->buffer)) { + &edns, repinfo, c->buffer)) { server_stats_insrcode(&worker->stats, c->buffer); regional_free_all(worker->scratchpad); goto send_reply; } - if(local_zones_answer(worker->daemon->local_zones, &qinfo, &edns, - c->buffer, worker->scratchpad, repinfo, - acladdr->taglist, acladdr->taglen, acladdr->tag_actions, + if(LDNS_OPCODE_WIRE(sldns_buffer_begin(c->buffer)) == + LDNS_PACKET_NOTIFY) { + answer_notify(worker, &qinfo, &edns, c->buffer, repinfo); + 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)) { + worker->daemon->cfg->num_tags, acladdr->view)) { regional_free_all(worker->scratchpad); if(sldns_buffer_limit(c->buffer) == 0) { comm_point_drop_reply(repinfo); @@ -958,6 +1377,34 @@ server_stats_insrcode(&worker->stats, c->buffer); goto send_reply; } + if(worker->env.auth_zones && + rpz_apply_qname_trigger(worker->env.auth_zones, + &worker->env, &qinfo, &edns, c->buffer, worker->scratchpad, + repinfo, acladdr->taglist, acladdr->taglen, &worker->stats)) { + regional_free_all(worker->scratchpad); + if(sldns_buffer_limit(c->buffer) == 0) { + comm_point_drop_reply(repinfo); + return 0; + } + server_stats_insrcode(&worker->stats, c->buffer); + 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)); + server_stats_insrcode(&worker->stats, 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. */ @@ -970,16 +1417,19 @@ } /* 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 ) { - sldns_buffer_set_limit(c->buffer, LDNS_HEADER_SIZE); - sldns_buffer_write_at(c->buffer, 4, - (uint8_t*)"\0\0\0\0\0\0\0\0", 8); - LDNS_QR_SET(sldns_buffer_begin(c->buffer)); - LDNS_RCODE_SET(sldns_buffer_begin(c->buffer), - LDNS_RCODE_REFUSED); - sldns_buffer_flip(c->buffer); + error_encode(c->buffer, LDNS_RCODE_REFUSED, &qinfo, + *(uint16_t*)(void *)sldns_buffer_begin(c->buffer), + sldns_buffer_read_u16_at(c->buffer, 2), NULL); regional_free_all(worker->scratchpad); server_stats_insrcode(&worker->stats, c->buffer); log_addr(VERB_ALGO, "refused nonrec (cache snoop) query from", @@ -986,44 +1436,127 @@ &repinfo->addr, repinfo->addrlen); goto send_reply; } - h = query_info_hash(&qinfo, sldns_buffer_read_u16_at(c->buffer, 2)); - if((e=slabhash_lookup(worker->env.msg_cache, h, &qinfo, 0))) { - /* answer from cache - we have acquired a readlock on it */ - if(answer_from_cache(worker, &qinfo, - (struct reply_info*)e->data, - *(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 */ - if(worker->env.cfg->prefetch && *worker->env.now >= - ((struct reply_info*)e->data)->prefetch_ttl) { - time_t leeway = ((struct reply_info*)e-> - data)->ttl - *worker->env.now; - lock_rw_unlock(&e->lock); - reply_and_prefetch(worker, &qinfo, - sldns_buffer_read_u16_at(c->buffer, 2), - repinfo, leeway); - rc = 0; - regional_free_all(worker->scratchpad); - goto send_reply_rc; - } - lock_rw_unlock(&e->lock); + + /* 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); - goto send_reply; + return 0; /* drop it */ } - verbose(VERB_ALGO, "answer from the cache failed"); - lock_rw_unlock(&e->lock); + qinfo.qname = d->rr_data[0] + 2; + qinfo.qname_len = d->rr_len[0] - 2; } - 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; + + /* 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; + } + +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, &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))) { + /* answer from cache - we have acquired a readlock on it */ + if(answer_from_cache(worker, &qinfo, + cinfo, &need_drop, &is_expired_answer, &is_secure_answer, + &alias_rrset, &partial_rep, (struct reply_info*)e->data, + *(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 >= + ((struct reply_info*)e->data)->prefetch_ttl) || + (worker->env.cfg->serve_expired && + *worker->env.now >= ((struct reply_info*)e->data)->ttl)) { + + time_t leeway = ((struct reply_info*)e-> + data)->ttl - *worker->env.now; + if(((struct reply_info*)e->data)->ttl + < *worker->env.now) + 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)); + 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); } - verbose(VERB_ALGO, "answer norec from cache -- " - "need to validate or not primed"); + 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); @@ -1037,7 +1570,7 @@ } /* grab a work request structure for this new request */ - mesh_new_client(worker->env.mesh, &qinfo, + 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)); regional_free_all(worker->scratchpad); @@ -1047,11 +1580,43 @@ 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++; + } + if(worker->stats.extended) { + if(is_secure_answer) worker->stats.ans_secure++; + server_stats_insrcode(&worker->stats, repinfo->c->buffer); + } #ifdef USE_DNSTAP if(worker->dtenv.log_client_response_messages) dt_msg_send_client_response(&worker->dtenv, &repinfo->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->addr, repinfo->addrlen, + tv, 1, c->buffer); + } else { + log_reply_info(NO_VERBOSE, &qinfo, &repinfo->addr, repinfo->addrlen, + tv, 1, c->buffer); + } + } +#ifdef USE_DNSCRYPT + if(!dnsc_handle_uncurved_request(repinfo)) { + return 0; + } +#endif return rc; } @@ -1107,6 +1672,10 @@ 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); } @@ -1148,11 +1717,7 @@ return NULL; } /* create random state here to avoid locking trouble in RAND_bytes */ - seed = (unsigned int)time(NULL) ^ (unsigned int)getpid() ^ - (((unsigned int)worker->thread_num)<<17); - /* shift thread_num so it does not match out pid bits */ - if(!(worker->rndstate = ub_initstate(seed, daemon->rand))) { - seed = 0; + if(!(worker->rndstate = ub_initstate(daemon->rand))) { log_err("could not init random numbers."); tube_delete(worker->cmd); free(worker->ports); @@ -1159,7 +1724,7 @@ free(worker); return NULL; } - seed = 0; + explicit_bzero(&seed, sizeof(seed)); #ifdef USE_DNSTAP if(daemon->cfg->dnstap) { log_assert(daemon->dtenv != NULL); @@ -1227,9 +1792,13 @@ worker->comsig = NULL; } worker->front = listen_create(worker->base, ports, - cfg->msg_buffer_size, (int)cfg->incoming_num_tcp, - worker->daemon->listen_sslctx, dtenv, worker_handle_request, - worker); + cfg->msg_buffer_size, (int)cfg->incoming_num_tcp, + cfg->do_tcp_keepalive + ? cfg->tcp_keepalive_timeout + : cfg->tcp_idle_timeout, + 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); @@ -1243,7 +1812,8 @@ cfg->use_caps_bits_for_id, worker->ports, worker->numports, cfg->unwanted_threshold, cfg->outgoing_tcp_mss, &worker_alloc_cleanup, worker, - cfg->do_udp, worker->daemon->connect_sslctx, cfg->delay_close, + cfg->do_udp || cfg->udp_upstream_without_downstream, + worker->daemon->connect_sslctx, cfg->delay_close, dtenv); if(!worker->back) { log_err("could not create outgoing sockets"); @@ -1278,16 +1848,31 @@ 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); - if(worker->thread_num == 0) - log_set_time(worker->env.now); worker->env.worker = worker; + worker->env.worker_base = worker->base; worker->env.send_query = &worker_send_query; worker->env.alloc = &worker->alloc; + worker->env.outnet = worker->back; worker->env.rnd = worker->rndstate; - worker->env.scratch = worker->scratchpad; + /* 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); + /* 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); @@ -1321,6 +1906,14 @@ 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); + } if(!worker->env.mesh || !worker->env.scratch_buffer) { worker_delete(worker); return 0; @@ -1364,7 +1957,6 @@ comm_timer_delete(worker->env.probe_timer); free(worker->ports); if(worker->thread_num == 0) { - log_set_time(NULL); #ifdef UB_ON_WINDOWS wsvc_desetup_worker(worker); #endif /* UB_ON_WINDOWS */ @@ -1372,16 +1964,16 @@ comm_base_delete(worker->base); ub_randfree(worker->rndstate); alloc_clear(&worker->alloc); + regional_destroy(worker->env.scratch); regional_destroy(worker->scratchpad); free(worker); } struct outbound_entry* -worker_send_query(uint8_t* qname, size_t qnamelen, uint16_t qtype, - uint16_t qclass, uint16_t flags, int dnssec, int want_dnssec, - int nocaps, struct edns_option* opt_list, - struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone, - size_t zonelen, struct module_qstate* q) +worker_send_query(struct query_info* qinfo, uint16_t flags, int dnssec, + int want_dnssec, int nocaps, struct sockaddr_storage* addr, + socklen_t addrlen, uint8_t* zone, size_t zonelen, int ssl_upstream, + char* tls_auth_name, struct module_qstate* q) { struct worker* worker = q->env->worker; struct outbound_entry* e = (struct outbound_entry*)regional_alloc( @@ -1389,11 +1981,10 @@ if(!e) return NULL; e->qstate = q; - e->qsent = outnet_serviced_query(worker->back, qname, - qnamelen, qtype, qclass, flags, dnssec, want_dnssec, nocaps, - q->env->cfg->tcp_upstream, q->env->cfg->ssl_upstream, opt_list, - addr, addrlen, zone, zonelen, worker_handle_service_reply, e, - worker->back->udp_buff); + e->qsent = outnet_serviced_query(worker->back, qinfo, flags, dnssec, + want_dnssec, nocaps, q->env->cfg->tcp_upstream, + ssl_upstream, tls_auth_name, addr, addrlen, zone, zonelen, q, + worker_handle_service_reply, e, worker->back->udp_buff, q->env); if(!e->qsent) { return NULL; } @@ -1433,14 +2024,14 @@ } /* --- fake callbacks for fptr_wlist to work --- */ -struct outbound_entry* libworker_send_query(uint8_t* ATTR_UNUSED(qname), - size_t ATTR_UNUSED(qnamelen), uint16_t ATTR_UNUSED(qtype), - uint16_t ATTR_UNUSED(qclass), uint16_t ATTR_UNUSED(flags), - int ATTR_UNUSED(dnssec), int ATTR_UNUSED(want_dnssec), - int ATTR_UNUSED(nocaps), struct edns_option* ATTR_UNUSED(opt_list), - struct sockaddr_storage* ATTR_UNUSED(addr), - socklen_t ATTR_UNUSED(addrlen), uint8_t* ATTR_UNUSED(zone), - size_t ATTR_UNUSED(zonelen), struct module_qstate* ATTR_UNUSED(q)) +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), + struct sockaddr_storage* ATTR_UNUSED(addr), socklen_t ATTR_UNUSED(addrlen), + uint8_t* ATTR_UNUSED(zone), size_t ATTR_UNUSED(zonelen), + int ATTR_UNUSED(ssl_upstream), char* ATTR_UNUSED(tls_auth_name), + struct module_qstate* ATTR_UNUSED(q)) { log_assert(0); return 0; @@ -1470,22 +2061,22 @@ } 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)) + 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)) + 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)) + sldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(s), + char* ATTR_UNUSED(why_bogus), int ATTR_UNUSED(was_ratelimited)) { log_assert(0); } @@ -1498,13 +2089,13 @@ int order_lock_cmp(const void* ATTR_UNUSED(e1), const void* ATTR_UNUSED(e2)) { - log_assert(0); - return 0; + log_assert(0); + return 0; } int codeline_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b)) { - log_assert(0); - return 0; + log_assert(0); + return 0; } --- contrib/unbound/daemon/worker.h.orig +++ contrib/unbound/daemon/worker.h @@ -61,6 +61,7 @@ struct regional; struct tube; struct daemon_remote; +struct query_info; /** worker commands */ enum worker_commands { @@ -84,7 +85,7 @@ /** global shared daemon structure */ struct daemon* daemon; /** thread id */ - ub_thread_t thr_id; + ub_thread_type thr_id; /** pipe, for commands for this worker */ struct tube* cmd; /** the event base this worker works with */ @@ -115,7 +116,7 @@ /** allocation cache for this thread */ struct alloc_cache alloc; /** per thread statistics */ - struct server_stats stats; + struct ub_server_stats stats; /** thread scratch regional */ struct regional* scratchpad; --- contrib/unbound/dns64/dns64.c.orig +++ contrib/unbound/dns64/dns64.c @@ -48,6 +48,9 @@ #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" /****************************************************************************** * * @@ -67,12 +70,9 @@ #define MAX_PTR_QNAME_IPV4 30 /** - * Per-query module-specific state. This is usually a dynamically-allocated - * structure, but in our case we only need to store one variable describing the - * state the query is in. So we repurpose the minfo pointer by storing an - * integer in there. + * State of DNS64 processing for a query. */ -enum dns64_qstate { +enum dns64_state { DNS64_INTERNAL_QUERY, /**< Internally-generated query, no DNS64 processing. */ DNS64_NEW_QUERY, /**< Query for which we're the first module in @@ -81,6 +81,19 @@ 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; +}; /****************************************************************************** * * @@ -111,6 +124,11 @@ * 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; }; @@ -173,16 +191,19 @@ * * \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[16], const int offset) +extract_ipv4(const uint8_t ipv6[], size_t ipv6_len, const int offset) { - uint32_t ipv4 = (uint32_t)ipv6[offset/8+0] << (24 + (offset%8)) - | (uint32_t)ipv6[offset/8+1] << (16 + (offset%8)) - | (uint32_t)ipv6[offset/8+2] << ( 8 + (offset%8)) - | (uint32_t)ipv6[offset/8+3] << ( 0 + (offset%8)); + uint32_t ipv4; + log_assert(ipv6_len == 16); (void)ipv6_len; + ipv4 = (uint32_t)ipv6[offset/8+0] << (24 + (offset%8)) + | (uint32_t)ipv6[offset/8+1] << (16 + (offset%8)) + | (uint32_t)ipv6[offset/8+2] << ( 8 + (offset%8)) + | (uint32_t)ipv6[offset/8+3] << ( 0 + (offset%8)); if (offset/8+4 < 16) ipv4 |= (uint32_t)ipv6[offset/8+4] >> (8 - offset%8); return ipv4; @@ -196,22 +217,26 @@ * \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[MAX_PTR_QNAME_IPV4]) +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); 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; @@ -223,13 +248,15 @@ * * \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[16]) +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; @@ -257,14 +284,20 @@ * 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[16], int prefix_net, - const uint8_t a[4], uint8_t aaaa[16]) +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) { + log_assert(prefix_addr_len == 16 && a_len == 4 && aaaa_len == 16); + (void)prefix_addr_len; (void)a_len; (void)aaaa_len; memcpy(aaaa, prefix_addr, 16); aaaa[prefix_net/8+0] |= a[0] >> (0+prefix_net%8); aaaa[prefix_net/8+1] |= a[0] << (8-prefix_net%8); @@ -285,6 +318,40 @@ ******************************************************************************/ /** + * 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. @@ -295,6 +362,7 @@ 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, @@ -311,6 +379,11 @@ 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; } @@ -329,7 +402,8 @@ log_err("malloc failure"); return 0; } - env->modinfo[id] = (void*)dns64_env; + 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; @@ -337,6 +411,16 @@ 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. * @@ -346,8 +430,14 @@ 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; } @@ -372,7 +462,8 @@ /* 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)) + 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. */ /* @@ -395,7 +486,8 @@ 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, - dns64_env->prefix_net), (char*)qinfo.qname); + 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)); @@ -405,37 +497,12 @@ if (subq) { subq->curmod = id; subq->ext_state[id] = module_state_initial; - subq->minfo[id] = NULL; + subq->minfo[id] = NULL; } return module_wait_subquery; } -/** allocate (special) rrset keys, return 0 on error */ -static int -repinfo_alloc_rrset_keys(struct reply_info* rep, - struct regional* region) -{ - size_t i; - for(i=0; irrset_count; i++) { - if(region) { - rep->rrsets[i] = (struct ub_packed_rrset_key*) - regional_alloc(region, - sizeof(struct ub_packed_rrset_key)); - if(rep->rrsets[i]) { - memset(rep->rrsets[i], 0, - sizeof(struct ub_packed_rrset_key)); - rep->rrsets[i]->entry.key = rep->rrsets[i]; - } - } - else return 0;/* rep->rrsets[i] = alloc_special_obtain(alloc);*/ - if(!rep->rrsets[i]) - return 0; - rep->rrsets[i]->entry.data = NULL; - } - return 1; -} - static enum module_ext_state generate_type_A_query(struct module_qstate* qstate, int id) { @@ -466,6 +533,25 @@ } /** + * 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 @@ -481,7 +567,8 @@ static enum module_ext_state handle_event_pass(struct module_qstate* qstate, int id) { - if ((uintptr_t)qstate->minfo[id] == DNS64_NEW_QUERY + 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")) @@ -489,12 +576,20 @@ return handle_ipv6_ptr(qstate, id); if (qstate->env->cfg->dns64_synthall && - (uintptr_t)qstate->minfo[id] == DNS64_NEW_QUERY + iq && iq->state == DNS64_NEW_QUERY && qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA) return generate_type_A_query(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"); + return generate_type_A_query(qstate, id); + } + /* We are finished when our sub-query is finished. */ - if ((uintptr_t)qstate->minfo[id] == DNS64_SUBQUERY_FINISHED) + if (iq && iq->state == DNS64_SUBQUERY_FINISHED) return module_finished; /* Otherwise, pass request to next module. */ @@ -515,6 +610,7 @@ 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: * @@ -526,17 +622,36 @@ * synthesize in (sec 5.1.2 of RFC6147). * - A successful AAAA query with an answer. */ - if ( (enum dns64_qstate)qstate->minfo[id] == DNS64_INTERNAL_QUERY - || qstate->qinfo.qtype != LDNS_RR_TYPE_AAAA - || (qstate->query_flags & BIT_CD) - || (qstate->return_msg && + 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))) - return module_finished; + /* 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); - /* 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"); + 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)) + log_err("out of memory"); + + /* do nothing */ + return module_finished; } /** @@ -555,6 +670,7 @@ 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]), @@ -564,7 +680,13 @@ switch(event) { case module_event_new: /* Tag this query as being new and fall through. */ - qstate->minfo[id] = (void*)DNS64_NEW_QUERY; + iq = (struct dns64_qstate*)regional_alloc( + qstate->region, sizeof(*iq)); + 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; @@ -575,6 +697,11 @@ 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 @@ -630,8 +757,10 @@ 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], - &dd->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]; } @@ -701,13 +830,14 @@ * Build the actual reply. */ cp = construct_reply_info_base(super->region, rep->flags, rep->qdcount, - rep->ttl, rep->prefetch_ttl, rep->an_numrrsets, rep->ns_numrrsets, - rep->ar_numrrsets, rep->rrset_count, rep->security); + rep->ttl, rep->prefetch_ttl, rep->serve_expired_ttl, + rep->an_numrrsets, rep->ns_numrrsets, rep->ar_numrrsets, + rep->rrset_count, rep->security); if(!cp) return; /* allocate ub_key structures special or not */ - if(!repinfo_alloc_rrset_keys(cp, super->region)) { + if(!reply_info_alloc_rrset_keys(cp, NULL, super->region)) { return; } @@ -729,6 +859,12 @@ 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, @@ -780,9 +916,10 @@ * initial query's domain name. */ answer = reply_find_answer_rrset(&qstate->qinfo, super->return_msg->rep); - log_assert(answer); - answer->rk.dname = super->qinfo.qname; - answer->rk.dname_len = super->qinfo.qname_len; + if(answer) { + answer->rk.dname = super->qinfo.qname; + answer->rk.dname_len = super->qinfo.qname_len; + } } /** @@ -798,6 +935,7 @@ 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); @@ -806,16 +944,32 @@ * Signal that the sub-query is finished, no matter whether we are * successful or not. This lets the state machine terminate. */ - super->minfo[id] = (void*)DNS64_SUBQUERY_FINISHED; + 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 - || !reply_find_answer_rrset(&qstate->qinfo, - qstate->return_msg->rep)) + || !qstate->return_msg->rep) { return; + } + /* Use return code from A query in response to client. */ + if (super->return_rcode != LDNS_RCODE_NOERROR) + super->return_rcode = qstate->return_rcode; + /* Generate a response suitable for the original query. */ if (qstate->qinfo.qtype == LDNS_RR_TYPE_A) { dns64_adjust_a(id, super, qstate); @@ -825,8 +979,9 @@ } /* Store the generated response in cache. */ - if (!dns_cache_store(super->env, &super->qinfo, super->return_msg->rep, - 0, 0, 0, NULL, super->query_flags)) + 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)) log_err("out of memory"); } --- contrib/unbound/dnscrypt/cert.h.orig +++ contrib/unbound/dnscrypt/cert.h @@ -0,0 +1,32 @@ +#ifndef UNBOUND_DNSCRYPT_CERT_H +#define UNBOUND_DNSCRYPT_CERT_H + +/** + * \file + * certificate type for dnscrypt for use in other header files + */ + +#include +#define CERT_MAGIC_CERT "DNSC" +#define CERT_MAJOR_VERSION 1 +#define CERT_MINOR_VERSION 0 +#define CERT_OLD_MAGIC_HEADER "7PYqwfzt" + +#define CERT_FILE_EXPIRE_DAYS 365 + +struct SignedCert { + uint8_t magic_cert[4]; + uint8_t version_major[2]; + uint8_t version_minor[2]; + + // Signed Content + uint8_t signed_content[64]; + uint8_t server_publickey[crypto_box_PUBLICKEYBYTES]; + uint8_t magic_query[8]; + uint8_t serial[4]; + uint8_t ts_begin[4]; + uint8_t ts_end[4]; +}; + + +#endif --- contrib/unbound/dnscrypt/dnscrypt.c.orig +++ contrib/unbound/dnscrypt/dnscrypt.c @@ -0,0 +1,1108 @@ + +#include "config.h" +#include +#include +#ifdef HAVE_TIME_H +#include +#endif +#include +#include +#include +#include "sldns/sbuffer.h" +#include "util/config_file.h" +#include "util/net_help.h" +#include "util/netevent.h" +#include "util/log.h" +#include "util/storage/slabhash.h" +#include "util/storage/lookup3.h" + +#include "dnscrypt/cert.h" +#include "dnscrypt/dnscrypt.h" +#include "dnscrypt/dnscrypt_config.h" + +#include + + +/** + * \file + * dnscrypt functions for encrypting DNS packets. + */ + +#define DNSCRYPT_QUERY_BOX_OFFSET \ + (DNSCRYPT_MAGIC_HEADER_LEN + crypto_box_PUBLICKEYBYTES + \ + crypto_box_HALF_NONCEBYTES) + +// 8 bytes: magic header (CERT_MAGIC_HEADER) +// 12 bytes: the client's nonce +// 12 bytes: server nonce extension +// 16 bytes: Poly1305 MAC (crypto_box_ZEROBYTES - crypto_box_BOXZEROBYTES) + +#define DNSCRYPT_REPLY_BOX_OFFSET \ + (DNSCRYPT_MAGIC_HEADER_LEN + crypto_box_HALF_NONCEBYTES + \ + crypto_box_HALF_NONCEBYTES) + + +/** + * Shared secret cache key length. + * secret key. + * 1 byte: ES_VERSION[1] + * 32 bytes: client crypto_box_PUBLICKEYBYTES + * 32 bytes: server crypto_box_SECRETKEYBYTES + */ +#define DNSCRYPT_SHARED_SECRET_KEY_LENGTH \ + (1 + crypto_box_PUBLICKEYBYTES + crypto_box_SECRETKEYBYTES) + + +struct shared_secret_cache_key { + /** the hash table key */ + uint8_t key[DNSCRYPT_SHARED_SECRET_KEY_LENGTH]; + /** the hash table entry, data is uint8_t pointer of size crypto_box_BEFORENMBYTES which contains the shared secret. */ + struct lruhash_entry entry; +}; + + +struct nonce_cache_key { + /** the nonce used by the client */ + uint8_t nonce[crypto_box_HALF_NONCEBYTES]; + /** the client_magic used by the client, this is associated to 1 cert only */ + uint8_t magic_query[DNSCRYPT_MAGIC_HEADER_LEN]; + /** the client public key */ + uint8_t client_publickey[crypto_box_PUBLICKEYBYTES]; + /** the hash table entry, data is uint8_t */ + struct lruhash_entry entry; +}; + +/** + * Generate a key suitable to find shared secret in slabhash. + * \param[in] key: a uint8_t pointer of size DNSCRYPT_SHARED_SECRET_KEY_LENGTH + * \param[in] esversion: The es version least significant byte. + * \param[in] pk: The public key of the client. uint8_t pointer of size + * crypto_box_PUBLICKEYBYTES. + * \param[in] sk: The secret key of the server matching the magic query number. + * uint8_t pointer of size crypto_box_SECRETKEYBYTES. + * \return the hash of the key. + */ +static uint32_t +dnsc_shared_secrets_cache_key(uint8_t* key, + uint8_t esversion, + uint8_t* pk, + uint8_t* sk) +{ + key[0] = esversion; + memcpy(key + 1, pk, crypto_box_PUBLICKEYBYTES); + memcpy(key + 1 + crypto_box_PUBLICKEYBYTES, sk, crypto_box_SECRETKEYBYTES); + return hashlittle(key, DNSCRYPT_SHARED_SECRET_KEY_LENGTH, 0); +} + +/** + * Inserts a shared secret into the shared_secrets_cache slabhash. + * The shared secret is copied so the caller can use it freely without caring + * about the cache entry being evicted or not. + * \param[in] cache: the slabhash in which to look for the key. + * \param[in] key: a uint8_t pointer of size DNSCRYPT_SHARED_SECRET_KEY_LENGTH + * which contains the key of the shared secret. + * \param[in] hash: the hash of the key. + * \param[in] nmkey: a uint8_t pointer of size crypto_box_BEFORENMBYTES which + * contains the shared secret. + */ +static void +dnsc_shared_secret_cache_insert(struct slabhash *cache, + uint8_t key[DNSCRYPT_SHARED_SECRET_KEY_LENGTH], + uint32_t hash, + uint8_t nmkey[crypto_box_BEFORENMBYTES]) +{ + struct shared_secret_cache_key* k = + (struct shared_secret_cache_key*)calloc(1, sizeof(*k)); + uint8_t* d = malloc(crypto_box_BEFORENMBYTES); + if(!k || !d) { + free(k); + free(d); + return; + } + memcpy(d, nmkey, crypto_box_BEFORENMBYTES); + lock_rw_init(&k->entry.lock); + memcpy(k->key, key, DNSCRYPT_SHARED_SECRET_KEY_LENGTH); + k->entry.hash = hash; + k->entry.key = k; + k->entry.data = d; + slabhash_insert(cache, + hash, &k->entry, + d, + NULL); +} + +/** + * Lookup a record in shared_secrets_cache. + * \param[in] cache: a pointer to shared_secrets_cache slabhash. + * \param[in] key: a uint8_t pointer of size DNSCRYPT_SHARED_SECRET_KEY_LENGTH + * containing the key to look for. + * \param[in] hash: a hash of the key. + * \return a pointer to the locked cache entry or NULL on failure. + */ +static struct lruhash_entry* +dnsc_shared_secrets_lookup(struct slabhash* cache, + uint8_t key[DNSCRYPT_SHARED_SECRET_KEY_LENGTH], + uint32_t hash) +{ + return slabhash_lookup(cache, hash, key, 0); +} + +/** + * Generate a key hash suitable to find a nonce in slabhash. + * \param[in] nonce: a uint8_t pointer of size crypto_box_HALF_NONCEBYTES + * \param[in] magic_query: a uint8_t pointer of size DNSCRYPT_MAGIC_HEADER_LEN + * \param[in] pk: The public key of the client. uint8_t pointer of size + * crypto_box_PUBLICKEYBYTES. + * \return the hash of the key. + */ +static uint32_t +dnsc_nonce_cache_key_hash(const uint8_t nonce[crypto_box_HALF_NONCEBYTES], + const uint8_t magic_query[DNSCRYPT_MAGIC_HEADER_LEN], + const uint8_t pk[crypto_box_PUBLICKEYBYTES]) +{ + uint32_t h = 0; + h = hashlittle(nonce, crypto_box_HALF_NONCEBYTES, h); + h = hashlittle(magic_query, DNSCRYPT_MAGIC_HEADER_LEN, h); + return hashlittle(pk, crypto_box_PUBLICKEYBYTES, h); +} + +/** + * Inserts a nonce, magic_query, pk tuple into the nonces_cache slabhash. + * \param[in] cache: the slabhash in which to look for the key. + * \param[in] nonce: a uint8_t pointer of size crypto_box_HALF_NONCEBYTES + * \param[in] magic_query: a uint8_t pointer of size DNSCRYPT_MAGIC_HEADER_LEN + * \param[in] pk: The public key of the client. uint8_t pointer of size + * crypto_box_PUBLICKEYBYTES. + * \param[in] hash: the hash of the key. + */ +static void +dnsc_nonce_cache_insert(struct slabhash *cache, + const uint8_t nonce[crypto_box_HALF_NONCEBYTES], + const uint8_t magic_query[DNSCRYPT_MAGIC_HEADER_LEN], + const uint8_t pk[crypto_box_PUBLICKEYBYTES], + uint32_t hash) +{ + struct nonce_cache_key* k = + (struct nonce_cache_key*)calloc(1, sizeof(*k)); + if(!k) { + free(k); + return; + } + lock_rw_init(&k->entry.lock); + memcpy(k->nonce, nonce, crypto_box_HALF_NONCEBYTES); + memcpy(k->magic_query, magic_query, DNSCRYPT_MAGIC_HEADER_LEN); + memcpy(k->client_publickey, pk, crypto_box_PUBLICKEYBYTES); + k->entry.hash = hash; + k->entry.key = k; + k->entry.data = NULL; + slabhash_insert(cache, + hash, &k->entry, + NULL, + NULL); +} + +/** + * Lookup a record in nonces_cache. + * \param[in] cache: the slabhash in which to look for the key. + * \param[in] nonce: a uint8_t pointer of size crypto_box_HALF_NONCEBYTES + * \param[in] magic_query: a uint8_t pointer of size DNSCRYPT_MAGIC_HEADER_LEN + * \param[in] pk: The public key of the client. uint8_t pointer of size + * crypto_box_PUBLICKEYBYTES. + * \param[in] hash: the hash of the key. + * \return a pointer to the locked cache entry or NULL on failure. + */ +static struct lruhash_entry* +dnsc_nonces_lookup(struct slabhash* cache, + const uint8_t nonce[crypto_box_HALF_NONCEBYTES], + const uint8_t magic_query[DNSCRYPT_MAGIC_HEADER_LEN], + const uint8_t pk[crypto_box_PUBLICKEYBYTES], + uint32_t hash) +{ + struct nonce_cache_key k; + memset(&k, 0, sizeof(k)); + k.entry.hash = hash; + memcpy(k.nonce, nonce, crypto_box_HALF_NONCEBYTES); + memcpy(k.magic_query, magic_query, DNSCRYPT_MAGIC_HEADER_LEN); + memcpy(k.client_publickey, pk, crypto_box_PUBLICKEYBYTES); + + return slabhash_lookup(cache, hash, &k, 0); +} + +/** + * Decrypt a query using the dnsccert that was found using dnsc_find_cert. + * The client nonce will be extracted from the encrypted query and stored in + * client_nonce, a shared secret will be computed and stored in nmkey and the + * buffer will be decrypted inplace. + * \param[in] env the dnscrypt environment. + * \param[in] cert the cert that matches this encrypted query. + * \param[in] client_nonce where the client nonce will be stored. + * \param[in] nmkey where the shared secret key will be written. + * \param[in] buffer the encrypted buffer. + * \return 0 on success. + */ +static int +dnscrypt_server_uncurve(struct dnsc_env* env, + const dnsccert *cert, + uint8_t client_nonce[crypto_box_HALF_NONCEBYTES], + uint8_t nmkey[crypto_box_BEFORENMBYTES], + struct sldns_buffer* buffer) +{ + size_t len = sldns_buffer_limit(buffer); + uint8_t *const buf = sldns_buffer_begin(buffer); + uint8_t nonce[crypto_box_NONCEBYTES]; + struct dnscrypt_query_header *query_header; + // shared secret cache + uint8_t key[DNSCRYPT_SHARED_SECRET_KEY_LENGTH]; + struct lruhash_entry* entry; + uint32_t hash; + + uint32_t nonce_hash; + + if (len <= DNSCRYPT_QUERY_HEADER_SIZE) { + return -1; + } + + query_header = (struct dnscrypt_query_header *)buf; + + /* Detect replay attacks */ + nonce_hash = dnsc_nonce_cache_key_hash( + query_header->nonce, + cert->magic_query, + query_header->publickey); + + lock_basic_lock(&env->nonces_cache_lock); + entry = dnsc_nonces_lookup( + env->nonces_cache, + query_header->nonce, + cert->magic_query, + query_header->publickey, + nonce_hash); + + if(entry) { + lock_rw_unlock(&entry->lock); + env->num_query_dnscrypt_replay++; + lock_basic_unlock(&env->nonces_cache_lock); + return -1; + } + + dnsc_nonce_cache_insert( + env->nonces_cache, + query_header->nonce, + cert->magic_query, + query_header->publickey, + nonce_hash); + lock_basic_unlock(&env->nonces_cache_lock); + + /* Find existing shared secret */ + hash = dnsc_shared_secrets_cache_key(key, + cert->es_version[1], + query_header->publickey, + cert->keypair->crypt_secretkey); + entry = dnsc_shared_secrets_lookup(env->shared_secrets_cache, + key, + hash); + + if(!entry) { + lock_basic_lock(&env->shared_secrets_cache_lock); + env->num_query_dnscrypt_secret_missed_cache++; + lock_basic_unlock(&env->shared_secrets_cache_lock); + if(cert->es_version[1] == 2) { +#ifdef USE_DNSCRYPT_XCHACHA20 + if (crypto_box_curve25519xchacha20poly1305_beforenm( + nmkey, query_header->publickey, + cert->keypair->crypt_secretkey) != 0) { + return -1; + } +#else + return -1; +#endif + } else { + if (crypto_box_beforenm(nmkey, + query_header->publickey, + cert->keypair->crypt_secretkey) != 0) { + return -1; + } + } + // Cache the shared secret we just computed. + dnsc_shared_secret_cache_insert(env->shared_secrets_cache, + key, + hash, + nmkey); + } else { + /* copy shared secret and unlock entry */ + memcpy(nmkey, entry->data, crypto_box_BEFORENMBYTES); + lock_rw_unlock(&entry->lock); + } + + memcpy(nonce, query_header->nonce, crypto_box_HALF_NONCEBYTES); + memset(nonce + crypto_box_HALF_NONCEBYTES, 0, crypto_box_HALF_NONCEBYTES); + + if(cert->es_version[1] == 2) { +#ifdef USE_DNSCRYPT_XCHACHA20 + if (crypto_box_curve25519xchacha20poly1305_open_easy_afternm + (buf, + buf + DNSCRYPT_QUERY_BOX_OFFSET, + len - DNSCRYPT_QUERY_BOX_OFFSET, nonce, + nmkey) != 0) { + return -1; + } +#else + return -1; +#endif + } else { + if (crypto_box_open_easy_afternm + (buf, + buf + DNSCRYPT_QUERY_BOX_OFFSET, + len - DNSCRYPT_QUERY_BOX_OFFSET, nonce, + nmkey) != 0) { + return -1; + } + } + + len -= DNSCRYPT_QUERY_HEADER_SIZE; + + while (*sldns_buffer_at(buffer, --len) == 0) + ; + + if (*sldns_buffer_at(buffer, len) != 0x80) { + return -1; + } + + memcpy(client_nonce, nonce, crypto_box_HALF_NONCEBYTES); + + sldns_buffer_set_position(buffer, 0); + sldns_buffer_set_limit(buffer, len); + + return 0; +} + + +/** + * Add random padding to a buffer, according to a client nonce. + * The length has to depend on the query in order to avoid reply attacks. + * + * @param buf a buffer + * @param len the initial size of the buffer + * @param max_len the maximum size + * @param nonce a nonce, made of the client nonce repeated twice + * @param secretkey + * @return the new size, after padding + */ +size_t +dnscrypt_pad(uint8_t *buf, const size_t len, const size_t max_len, + const uint8_t *nonce, const uint8_t *secretkey) +{ + uint8_t *buf_padding_area = buf + len; + size_t padded_len; + uint32_t rnd; + + // no padding + if (max_len < len + DNSCRYPT_MIN_PAD_LEN) + return len; + + assert(nonce[crypto_box_HALF_NONCEBYTES] == nonce[0]); + + crypto_stream((unsigned char *)&rnd, (unsigned long long)sizeof(rnd), nonce, + secretkey); + padded_len = + len + DNSCRYPT_MIN_PAD_LEN + rnd % (max_len - len - + DNSCRYPT_MIN_PAD_LEN + 1); + padded_len += DNSCRYPT_BLOCK_SIZE - padded_len % DNSCRYPT_BLOCK_SIZE; + if (padded_len > max_len) + padded_len = max_len; + + memset(buf_padding_area, 0, padded_len - len); + *buf_padding_area = 0x80; + + return padded_len; +} + +uint64_t +dnscrypt_hrtime(void) +{ + struct timeval tv; + uint64_t ts = (uint64_t)0U; + int ret; + + ret = gettimeofday(&tv, NULL); + if (ret == 0) { + ts = (uint64_t)tv.tv_sec * 1000000U + (uint64_t)tv.tv_usec; + } else { + log_err("gettimeofday: %s", strerror(errno)); + } + return ts; +} + +/** + * Add the server nonce part to once. + * The nonce is made half of client nonce and the seconf half of the server + * nonce, both of them of size crypto_box_HALF_NONCEBYTES. + * \param[in] nonce: a uint8_t* of size crypto_box_NONCEBYTES + */ +static void +add_server_nonce(uint8_t *nonce) +{ + randombytes_buf(nonce + crypto_box_HALF_NONCEBYTES, 8/*tsn*/+4/*suffix*/); +} + +/** + * Encrypt a reply using the dnsccert that was used with the query. + * The client nonce will be extracted from the encrypted query and stored in + * The buffer will be encrypted inplace. + * \param[in] cert the dnsccert that matches this encrypted query. + * \param[in] client_nonce client nonce used during the query + * \param[in] nmkey shared secret key used during the query. + * \param[in] buffer the buffer where to encrypt the reply. + * \param[in] udp if whether or not it is a UDP query. + * \param[in] max_udp_size configured max udp size. + * \return 0 on success. + */ +static int +dnscrypt_server_curve(const dnsccert *cert, + uint8_t client_nonce[crypto_box_HALF_NONCEBYTES], + uint8_t nmkey[crypto_box_BEFORENMBYTES], + struct sldns_buffer* buffer, + uint8_t udp, + size_t max_udp_size) +{ + size_t dns_reply_len = sldns_buffer_limit(buffer); + size_t max_len = dns_reply_len + DNSCRYPT_MAX_PADDING \ + + DNSCRYPT_REPLY_HEADER_SIZE; + size_t max_reply_size = max_udp_size - 20U - 8U; + uint8_t nonce[crypto_box_NONCEBYTES]; + uint8_t *boxed; + uint8_t *const buf = sldns_buffer_begin(buffer); + size_t len = sldns_buffer_limit(buffer); + + if(udp){ + if (max_len > max_reply_size) + max_len = max_reply_size; + } + + + memcpy(nonce, client_nonce, crypto_box_HALF_NONCEBYTES); + memcpy(nonce + crypto_box_HALF_NONCEBYTES, client_nonce, + crypto_box_HALF_NONCEBYTES); + + boxed = buf + DNSCRYPT_REPLY_BOX_OFFSET; + memmove(boxed + crypto_box_MACBYTES, buf, len); + len = dnscrypt_pad(boxed + crypto_box_MACBYTES, len, + max_len - DNSCRYPT_REPLY_HEADER_SIZE, nonce, + cert->keypair->crypt_secretkey); + sldns_buffer_set_at(buffer, + DNSCRYPT_REPLY_BOX_OFFSET - crypto_box_BOXZEROBYTES, + 0, crypto_box_ZEROBYTES); + + // add server nonce extension + add_server_nonce(nonce); + + if(cert->es_version[1] == 2) { +#ifdef USE_DNSCRYPT_XCHACHA20 + if (crypto_box_curve25519xchacha20poly1305_easy_afternm + (boxed, boxed + crypto_box_MACBYTES, len, nonce, nmkey) != 0) { + return -1; + } +#else + return -1; +#endif + } else { + if (crypto_box_easy_afternm + (boxed, boxed + crypto_box_MACBYTES, len, nonce, nmkey) != 0) { + return -1; + } + } + + sldns_buffer_write_at(buffer, + 0, + DNSCRYPT_MAGIC_RESPONSE, + DNSCRYPT_MAGIC_HEADER_LEN); + sldns_buffer_write_at(buffer, + DNSCRYPT_MAGIC_HEADER_LEN, + nonce, + crypto_box_NONCEBYTES); + sldns_buffer_set_limit(buffer, len + DNSCRYPT_REPLY_HEADER_SIZE); + return 0; +} + +/** + * Read the content of fname into buf. + * \param[in] fname name of the file to read. + * \param[in] buf the buffer in which to read the content of the file. + * \param[in] count number of bytes to read. + * \return 0 on success. + */ +static int +dnsc_read_from_file(char *fname, char *buf, size_t count) +{ + int fd; + fd = open(fname, O_RDONLY); + if (fd == -1) { + return -1; + } + if (read(fd, buf, count) != (ssize_t)count) { + close(fd); + return -2; + } + close(fd); + return 0; +} + +/** + * Given an absolute path on the original root, returns the absolute path + * within the chroot. If chroot is disabled, the path is not modified. + * No char * is malloced so there is no need to free this. + * \param[in] cfg the configuration. + * \param[in] path the path from the original root. + * \return the path from inside the chroot. + */ +static char * +dnsc_chroot_path(struct config_file *cfg, char *path) +{ + char *nm; + nm = path; + if(cfg->chrootdir && cfg->chrootdir[0] && strncmp(nm, + cfg->chrootdir, strlen(cfg->chrootdir)) == 0) + nm += strlen(cfg->chrootdir); + return nm; +} + +/** + * Parse certificates files provided by the configuration and load them into + * dnsc_env. + * \param[in] env the dnsc_env structure to load the certs into. + * \param[in] cfg the configuration. + * \return the number of certificates loaded. + */ +static int +dnsc_parse_certs(struct dnsc_env *env, struct config_file *cfg) +{ + struct config_strlist *head, *head2; + size_t signed_cert_id; + size_t rotated_cert_id; + char *nm; + + env->signed_certs_count = 0U; + env->rotated_certs_count = 0U; + for (head = cfg->dnscrypt_provider_cert; head; head = head->next) { + env->signed_certs_count++; + } + for (head = cfg->dnscrypt_provider_cert_rotated; head; head = head->next) { + env->rotated_certs_count++; + } + env->signed_certs = sodium_allocarray(env->signed_certs_count, + sizeof *env->signed_certs); + + env->rotated_certs = sodium_allocarray(env->rotated_certs_count, + sizeof env->signed_certs); + signed_cert_id = 0U; + rotated_cert_id = 0U; + for(head = cfg->dnscrypt_provider_cert; head; head = head->next, signed_cert_id++) { + nm = dnsc_chroot_path(cfg, head->str); + if(dnsc_read_from_file( + nm, + (char *)(env->signed_certs + signed_cert_id), + sizeof(struct SignedCert)) != 0) { + fatal_exit("dnsc_parse_certs: failed to load %s: %s", head->str, strerror(errno)); + } + for(head2 = cfg->dnscrypt_provider_cert_rotated; head2; head2 = head2->next) { + if(strcmp(head->str, head2->str) == 0) { + *(env->rotated_certs + rotated_cert_id) = env->signed_certs + signed_cert_id; + rotated_cert_id++; + verbose(VERB_OPS, "Cert %s is rotated and will not be distributed via DNS", head->str); + break; + } + } + verbose(VERB_OPS, "Loaded cert %s", head->str); + } + return signed_cert_id; +} + +/** + * Helper function to convert a binary key into a printable fingerprint. + * \param[in] fingerprint the buffer in which to write the printable key. + * \param[in] key the key to convert. + */ +void +dnsc_key_to_fingerprint(char fingerprint[80U], const uint8_t * const key) +{ + const size_t fingerprint_size = 80U; + size_t fingerprint_pos = (size_t) 0U; + size_t key_pos = (size_t) 0U; + + for (;;) { + assert(fingerprint_size > fingerprint_pos); + snprintf(&fingerprint[fingerprint_pos], + fingerprint_size - fingerprint_pos, "%02X%02X", + key[key_pos], key[key_pos + 1U]); + key_pos += 2U; + if (key_pos >= crypto_box_PUBLICKEYBYTES) { + break; + } + fingerprint[fingerprint_pos + 4U] = ':'; + fingerprint_pos += 5U; + } +} + +/** + * Find the cert matching a DNSCrypt query. + * \param[in] dnscenv The DNSCrypt environment, which contains the list of certs + * supported by the server. + * \param[in] buffer The encrypted DNS query. + * \return a dnsccert * if we found a cert matching the magic_number of the + * query, NULL otherwise. + */ +static const dnsccert * +dnsc_find_cert(struct dnsc_env* dnscenv, struct sldns_buffer* buffer) +{ + const dnsccert *certs = dnscenv->certs; + struct dnscrypt_query_header *dnscrypt_header; + size_t i; + + if (sldns_buffer_limit(buffer) < DNSCRYPT_QUERY_HEADER_SIZE) { + return NULL; + } + dnscrypt_header = (struct dnscrypt_query_header *)sldns_buffer_begin(buffer); + for (i = 0U; i < dnscenv->signed_certs_count; i++) { + if (memcmp(certs[i].magic_query, dnscrypt_header->magic_query, + DNSCRYPT_MAGIC_HEADER_LEN) == 0) { + return &certs[i]; + } + } + return NULL; +} + +/** + * Insert local-zone and local-data into configuration. + * In order to be able to serve certs over TXT, we can reuse the local-zone and + * local-data config option. The zone and qname are infered from the + * provider_name and the content of the TXT record from the certificate content. + * returns the number of certificate TXT record that were loaded. + * < 0 in case of error. + */ +static int +dnsc_load_local_data(struct dnsc_env* dnscenv, struct config_file *cfg) +{ + size_t i, j; + // Insert 'local-zone: "2.dnscrypt-cert.example.com" deny' + if(!cfg_str2list_insert(&cfg->local_zones, + strdup(dnscenv->provider_name), + strdup("deny"))) { + log_err("Could not load dnscrypt local-zone: %s deny", + dnscenv->provider_name); + return -1; + } + + // Add local data entry of type: + // 2.dnscrypt-cert.example.com 86400 IN TXT "DNSC......" + for(i=0; isigned_certs_count; i++) { + const char *ttl_class_type = " 86400 IN TXT \""; + int rotated_cert = 0; + uint32_t serial; + uint16_t rrlen; + char* rr; + struct SignedCert *cert = dnscenv->signed_certs + i; + // Check if the certificate is being rotated and should not be published + for(j=0; jrotated_certs_count; j++){ + if(cert == dnscenv->rotated_certs[j]) { + rotated_cert = 1; + break; + } + } + memcpy(&serial, cert->serial, sizeof serial); + serial = htonl(serial); + if(rotated_cert) { + verbose(VERB_OPS, + "DNSCrypt: not adding cert with serial #%" + PRIu32 + " to local-data as it is rotated", + serial + ); + continue; + } + if((unsigned)strlen(dnscenv->provider_name) >= (unsigned)0xffff0000) { + /* guard against integer overflow in rrlen calculation */ + verbose(VERB_OPS, "cert #%" PRIu32 " is too long", serial); + continue; + } + rrlen = strlen(dnscenv->provider_name) + + strlen(ttl_class_type) + + 4 * sizeof(struct SignedCert) + // worst case scenario + 1 + // trailing double quote + 1; + rr = malloc(rrlen); + if(!rr) { + log_err("Could not allocate memory"); + return -2; + } + snprintf(rr, rrlen - 1, "%s 86400 IN TXT \"", dnscenv->provider_name); + for(j=0; jlocal_data, strdup(rr)); + free(rr); + } + return dnscenv->signed_certs_count; +} + +static const char * +key_get_es_version(uint8_t version[2]) +{ + struct es_version { + uint8_t es_version[2]; + const char *name; + }; + + const int num_versions = 2; + struct es_version es_versions[] = { + {{0x00, 0x01}, "X25519-XSalsa20Poly1305"}, + {{0x00, 0x02}, "X25519-XChacha20Poly1305"}, + }; + int i; + for(i=0; i < num_versions; i++){ + if(es_versions[i].es_version[0] == version[0] && + es_versions[i].es_version[1] == version[1]){ + return es_versions[i].name; + } + } + return NULL; +} + + +/** + * Parse the secret key files from `dnscrypt-secret-key` config and populates + * a list of dnsccert with es_version, magic number and secret/public keys + * supported by dnscrypt listener. + * \param[in] env The dnsc_env structure which will hold the keypairs. + * \param[in] cfg The config with the secret key file paths. + */ +static int +dnsc_parse_keys(struct dnsc_env *env, struct config_file *cfg) +{ + struct config_strlist *head; + size_t cert_id, keypair_id; + size_t c; + char *nm; + + env->keypairs_count = 0U; + for (head = cfg->dnscrypt_secret_key; head; head = head->next) { + env->keypairs_count++; + } + + env->keypairs = sodium_allocarray(env->keypairs_count, + sizeof *env->keypairs); + env->certs = sodium_allocarray(env->signed_certs_count, + sizeof *env->certs); + + cert_id = 0U; + keypair_id = 0U; + for(head = cfg->dnscrypt_secret_key; head; head = head->next, keypair_id++) { + char fingerprint[80]; + int found_cert = 0; + KeyPair *current_keypair = &env->keypairs[keypair_id]; + nm = dnsc_chroot_path(cfg, head->str); + if(dnsc_read_from_file( + nm, + (char *)(current_keypair->crypt_secretkey), + crypto_box_SECRETKEYBYTES) != 0) { + fatal_exit("dnsc_parse_keys: failed to load %s: %s", head->str, strerror(errno)); + } + verbose(VERB_OPS, "Loaded key %s", head->str); + if (crypto_scalarmult_base(current_keypair->crypt_publickey, + current_keypair->crypt_secretkey) != 0) { + fatal_exit("dnsc_parse_keys: could not generate public key from %s", head->str); + } + dnsc_key_to_fingerprint(fingerprint, current_keypair->crypt_publickey); + verbose(VERB_OPS, "Crypt public key fingerprint for %s: %s", head->str, fingerprint); + // find the cert matching this key + for(c = 0; c < env->signed_certs_count; c++) { + if(memcmp(current_keypair->crypt_publickey, + env->signed_certs[c].server_publickey, + crypto_box_PUBLICKEYBYTES) == 0) { + dnsccert *current_cert = &env->certs[cert_id++]; + found_cert = 1; + current_cert->keypair = current_keypair; + memcpy(current_cert->magic_query, + env->signed_certs[c].magic_query, + sizeof env->signed_certs[c].magic_query); + memcpy(current_cert->es_version, + env->signed_certs[c].version_major, + sizeof env->signed_certs[c].version_major + ); + dnsc_key_to_fingerprint(fingerprint, + current_cert->keypair->crypt_publickey); + verbose(VERB_OPS, "Crypt public key fingerprint for %s: %s", + head->str, fingerprint); + verbose(VERB_OPS, "Using %s", + key_get_es_version(current_cert->es_version)); +#ifndef USE_DNSCRYPT_XCHACHA20 + if (current_cert->es_version[1] == 0x02) { + fatal_exit("Certificate for XChacha20 but libsodium does not support it."); + } +#endif + + } + } + if (!found_cert) { + fatal_exit("dnsc_parse_keys: could not match certificate for key " + "%s. Unable to determine ES version.", + head->str); + } + } + return cert_id; +} + +static void +sodium_misuse_handler(void) +{ + fatal_exit( + "dnscrypt: libsodium could not be initialized, this typically" + " happens when no good source of entropy is found. If you run" + " unbound in a chroot, make sure /dev/urandom is available. See" + " https://www.unbound.net/documentation/unbound.conf.html"); +} + + +/** + * ######################################################### + * ############# Publicly accessible functions ############# + * ######################################################### + */ + +int +dnsc_handle_curved_request(struct dnsc_env* dnscenv, + struct comm_reply* repinfo) +{ + struct comm_point* c = repinfo->c; + + repinfo->is_dnscrypted = 0; + if( !c->dnscrypt ) { + return 1; + } + // Attempt to decrypt the query. If it is not crypted, we may still need + // to serve the certificate. + verbose(VERB_ALGO, "handle request called on DNSCrypt socket"); + if ((repinfo->dnsc_cert = dnsc_find_cert(dnscenv, c->buffer)) != NULL) { + if(dnscrypt_server_uncurve(dnscenv, + repinfo->dnsc_cert, + repinfo->client_nonce, + repinfo->nmkey, + c->buffer) != 0){ + verbose(VERB_ALGO, "dnscrypt: Failed to uncurve"); + comm_point_drop_reply(repinfo); + return 0; + } + repinfo->is_dnscrypted = 1; + sldns_buffer_rewind(c->buffer); + } + return 1; +} + +int +dnsc_handle_uncurved_request(struct comm_reply *repinfo) +{ + if(!repinfo->c->dnscrypt) { + return 1; + } + sldns_buffer_copy(repinfo->c->dnscrypt_buffer, repinfo->c->buffer); + if(!repinfo->is_dnscrypted) { + return 1; + } + if(dnscrypt_server_curve(repinfo->dnsc_cert, + repinfo->client_nonce, + repinfo->nmkey, + repinfo->c->dnscrypt_buffer, + repinfo->c->type == comm_udp, + repinfo->max_udp_size) != 0){ + verbose(VERB_ALGO, "dnscrypt: Failed to curve cached missed answer"); + comm_point_drop_reply(repinfo); + return 0; + } + return 1; +} + +struct dnsc_env * +dnsc_create(void) +{ + struct dnsc_env *env; +#ifdef SODIUM_MISUSE_HANDLER + sodium_set_misuse_handler(sodium_misuse_handler); +#endif + if (sodium_init() == -1) { + fatal_exit("dnsc_create: could not initialize libsodium."); + } + env = (struct dnsc_env *) calloc(1, sizeof(struct dnsc_env)); + lock_basic_init(&env->shared_secrets_cache_lock); + lock_protect(&env->shared_secrets_cache_lock, + &env->num_query_dnscrypt_secret_missed_cache, + sizeof(env->num_query_dnscrypt_secret_missed_cache)); + lock_basic_init(&env->nonces_cache_lock); + lock_protect(&env->nonces_cache_lock, + &env->nonces_cache, + sizeof(env->nonces_cache)); + lock_protect(&env->nonces_cache_lock, + &env->num_query_dnscrypt_replay, + sizeof(env->num_query_dnscrypt_replay)); + + return env; +} + +int +dnsc_apply_cfg(struct dnsc_env *env, struct config_file *cfg) +{ + if(dnsc_parse_certs(env, cfg) <= 0) { + fatal_exit("dnsc_apply_cfg: no cert file loaded"); + } + if(dnsc_parse_keys(env, cfg) <= 0) { + fatal_exit("dnsc_apply_cfg: no key file loaded"); + } + randombytes_buf(env->hash_key, sizeof env->hash_key); + env->provider_name = cfg->dnscrypt_provider; + + if(dnsc_load_local_data(env, cfg) <= 0) { + fatal_exit("dnsc_apply_cfg: could not load local data"); + } + lock_basic_lock(&env->shared_secrets_cache_lock); + env->shared_secrets_cache = slabhash_create( + cfg->dnscrypt_shared_secret_cache_slabs, + HASH_DEFAULT_STARTARRAY, + cfg->dnscrypt_shared_secret_cache_size, + dnsc_shared_secrets_sizefunc, + dnsc_shared_secrets_compfunc, + dnsc_shared_secrets_delkeyfunc, + dnsc_shared_secrets_deldatafunc, + NULL + ); + lock_basic_unlock(&env->shared_secrets_cache_lock); + if(!env->shared_secrets_cache){ + fatal_exit("dnsc_apply_cfg: could not create shared secrets cache."); + } + lock_basic_lock(&env->nonces_cache_lock); + env->nonces_cache = slabhash_create( + cfg->dnscrypt_nonce_cache_slabs, + HASH_DEFAULT_STARTARRAY, + cfg->dnscrypt_nonce_cache_size, + dnsc_nonces_sizefunc, + dnsc_nonces_compfunc, + dnsc_nonces_delkeyfunc, + dnsc_nonces_deldatafunc, + NULL + ); + lock_basic_unlock(&env->nonces_cache_lock); + return 0; +} + +void +dnsc_delete(struct dnsc_env *env) +{ + if(!env) { + return; + } + verbose(VERB_OPS, "DNSCrypt: Freeing environment."); + sodium_free(env->signed_certs); + sodium_free(env->rotated_certs); + sodium_free(env->certs); + sodium_free(env->keypairs); + lock_basic_destroy(&env->shared_secrets_cache_lock); + lock_basic_destroy(&env->nonces_cache_lock); + slabhash_delete(env->shared_secrets_cache); + slabhash_delete(env->nonces_cache); + free(env); +} + +/** + * ######################################################### + * ############# Shared secrets cache functions ############ + * ######################################################### + */ + +size_t +dnsc_shared_secrets_sizefunc(void *k, void* ATTR_UNUSED(d)) +{ + struct shared_secret_cache_key* ssk = (struct shared_secret_cache_key*)k; + size_t key_size = sizeof(struct shared_secret_cache_key) + + lock_get_mem(&ssk->entry.lock); + size_t data_size = crypto_box_BEFORENMBYTES; + (void)ssk; /* otherwise ssk is unused if no threading, or fixed locksize */ + return key_size + data_size; +} + +int +dnsc_shared_secrets_compfunc(void *m1, void *m2) +{ + return sodium_memcmp(m1, m2, DNSCRYPT_SHARED_SECRET_KEY_LENGTH); +} + +void +dnsc_shared_secrets_delkeyfunc(void *k, void* ATTR_UNUSED(arg)) +{ + struct shared_secret_cache_key* ssk = (struct shared_secret_cache_key*)k; + lock_rw_destroy(&ssk->entry.lock); + free(ssk); +} + +void +dnsc_shared_secrets_deldatafunc(void* d, void* ATTR_UNUSED(arg)) +{ + uint8_t* data = (uint8_t*)d; + free(data); +} + +/** + * ######################################################### + * ############### Nonces cache functions ################## + * ######################################################### + */ + +size_t +dnsc_nonces_sizefunc(void *k, void* ATTR_UNUSED(d)) +{ + struct nonce_cache_key* nk = (struct nonce_cache_key*)k; + size_t key_size = sizeof(struct nonce_cache_key) + + lock_get_mem(&nk->entry.lock); + (void)nk; /* otherwise ssk is unused if no threading, or fixed locksize */ + return key_size; +} + +int +dnsc_nonces_compfunc(void *m1, void *m2) +{ + struct nonce_cache_key *k1 = m1, *k2 = m2; + return + sodium_memcmp( + k1->nonce, + k2->nonce, + crypto_box_HALF_NONCEBYTES) != 0 || + sodium_memcmp( + k1->magic_query, + k2->magic_query, + DNSCRYPT_MAGIC_HEADER_LEN) != 0 || + sodium_memcmp( + k1->client_publickey, k2->client_publickey, + crypto_box_PUBLICKEYBYTES) != 0; +} + +void +dnsc_nonces_delkeyfunc(void *k, void* ATTR_UNUSED(arg)) +{ + struct nonce_cache_key* nk = (struct nonce_cache_key*)k; + lock_rw_destroy(&nk->entry.lock); + free(nk); +} + +void +dnsc_nonces_deldatafunc(void* ATTR_UNUSED(d), void* ATTR_UNUSED(arg)) +{ + return; +} --- contrib/unbound/dnscrypt/dnscrypt.h.orig +++ contrib/unbound/dnscrypt/dnscrypt.h @@ -0,0 +1,175 @@ +#ifndef UNBOUND_DNSCRYPT_H +#define UNBOUND_DNSCRYPT_H + +/** + * \file + * dnscrypt functions for encrypting DNS packets. + */ + +#include "dnscrypt/dnscrypt_config.h" +#ifdef USE_DNSCRYPT + +#define DNSCRYPT_MAGIC_HEADER_LEN 8U +#define DNSCRYPT_MAGIC_RESPONSE "r6fnvWj8" + +#ifndef DNSCRYPT_MAX_PADDING +# define DNSCRYPT_MAX_PADDING 256U +#endif +#ifndef DNSCRYPT_BLOCK_SIZE +# define DNSCRYPT_BLOCK_SIZE 64U +#endif +#ifndef DNSCRYPT_MIN_PAD_LEN +# define DNSCRYPT_MIN_PAD_LEN 8U +#endif + +#define crypto_box_HALF_NONCEBYTES (crypto_box_NONCEBYTES / 2U) + +#include "config.h" +#include "dnscrypt/cert.h" +#include "util/locks.h" + +#define DNSCRYPT_QUERY_HEADER_SIZE \ + (DNSCRYPT_MAGIC_HEADER_LEN + crypto_box_PUBLICKEYBYTES + crypto_box_HALF_NONCEBYTES + crypto_box_MACBYTES) +#define DNSCRYPT_RESPONSE_HEADER_SIZE \ + (DNSCRYPT_MAGIC_HEADER_LEN + crypto_box_NONCEBYTES + crypto_box_MACBYTES) + +#define DNSCRYPT_REPLY_HEADER_SIZE \ + (DNSCRYPT_MAGIC_HEADER_LEN + crypto_box_HALF_NONCEBYTES * 2 + crypto_box_MACBYTES) + +struct sldns_buffer; +struct config_file; +struct comm_reply; +struct slabhash; + +typedef struct KeyPair_ { + uint8_t crypt_publickey[crypto_box_PUBLICKEYBYTES]; + uint8_t crypt_secretkey[crypto_box_SECRETKEYBYTES]; +} KeyPair; + +typedef struct cert_ { + uint8_t magic_query[DNSCRYPT_MAGIC_HEADER_LEN]; + uint8_t es_version[2]; + KeyPair *keypair; +} dnsccert; + +struct dnsc_env { + struct SignedCert *signed_certs; + struct SignedCert **rotated_certs; + dnsccert *certs; + size_t signed_certs_count; + size_t rotated_certs_count; + uint8_t provider_publickey[crypto_sign_ed25519_PUBLICKEYBYTES]; + uint8_t provider_secretkey[crypto_sign_ed25519_SECRETKEYBYTES]; + KeyPair *keypairs; + size_t keypairs_count; + uint64_t nonce_ts_last; + unsigned char hash_key[crypto_shorthash_KEYBYTES]; + char * provider_name; + + /** Caches */ + struct slabhash *shared_secrets_cache; + /** lock on shared secret cache counters */ + lock_basic_type shared_secrets_cache_lock; + /** number of misses from shared_secrets_cache */ + size_t num_query_dnscrypt_secret_missed_cache; + + /** slabhash keeping track of nonce/cient pk/server sk pairs. */ + struct slabhash *nonces_cache; + /** lock on nonces_cache, used to avoid race condition in updating the hash */ + lock_basic_type nonces_cache_lock; + /** number of replayed queries */ + size_t num_query_dnscrypt_replay; +}; + +struct dnscrypt_query_header { + uint8_t magic_query[DNSCRYPT_MAGIC_HEADER_LEN]; + uint8_t publickey[crypto_box_PUBLICKEYBYTES]; + uint8_t nonce[crypto_box_HALF_NONCEBYTES]; + uint8_t mac[crypto_box_MACBYTES]; +}; + +/** + * Initialize DNSCrypt environment. + * Initialize sodium library and allocate the dnsc_env structure. + * \return an uninitialized struct dnsc_env. + */ +struct dnsc_env * dnsc_create(void); + +/** + * Apply configuration. + * Read certificates and secret keys from configuration. Initialize hashkey and + * provider name as well as loading cert TXT records. + * In case of issue applying configuration, this function fatals. + * \param[in] env the struct dnsc_env to populate. + * \param[in] cfg the config_file struct with dnscrypt options. + * \return 0 on success. + */ +int dnsc_apply_cfg(struct dnsc_env *env, struct config_file *cfg); + +/** + * Delete DNSCrypt environment + * + */ +void dnsc_delete(struct dnsc_env *env); + +/** + * handle a crypted dnscrypt request. + * Determine wether or not a query is coming over the dnscrypt listener and + * attempt to uncurve it or detect if it is a certificate query. + * return 0 in case of failure. + */ +int dnsc_handle_curved_request(struct dnsc_env* dnscenv, + struct comm_reply* repinfo); +/** + * handle an unencrypted dnscrypt request. + * Determine wether or not a query is going over the dnscrypt channel and + * attempt to curve it unless it was not crypted like when it is a + * certificate query. + * \return 0 in case of failure. + */ + +int dnsc_handle_uncurved_request(struct comm_reply *repinfo); + +/** + * Computes the size of the shared secret cache entry. + */ +size_t dnsc_shared_secrets_sizefunc(void *k, void *d); + +/** + * Compares two shared secret cache keys. + */ +int dnsc_shared_secrets_compfunc(void *m1, void *m2); + +/** + * Function to delete a shared secret cache key. + */ +void dnsc_shared_secrets_delkeyfunc(void *k, void* arg); + +/** + * Function to delete a share secret cache value. + */ +void dnsc_shared_secrets_deldatafunc(void* d, void* arg); + +/** + * Computes the size of the nonce cache entry. + */ +size_t dnsc_nonces_sizefunc(void *k, void *d); + +/** + * Compares two nonce cache keys. + */ +int dnsc_nonces_compfunc(void *m1, void *m2); + +/** + * Function to delete a nonce cache key. + */ +void dnsc_nonces_delkeyfunc(void *k, void* arg); + +/** + * Function to delete a nonce cache value. + */ +void dnsc_nonces_deldatafunc(void* d, void* arg); + + +#endif /* USE_DNSCRYPT */ +#endif --- contrib/unbound/dnscrypt/dnscrypt.m4.orig +++ contrib/unbound/dnscrypt/dnscrypt.m4 @@ -0,0 +1,44 @@ +# dnscrypt.m4 + +# dnsc_DNSCRYPT([action-if-true], [action-if-false]) +# -------------------------------------------------------------------------- +# Check for required dnscrypt libraries and add dnscrypt configure args. +AC_DEFUN([dnsc_DNSCRYPT], +[ + AC_ARG_ENABLE([dnscrypt], + AS_HELP_STRING([--enable-dnscrypt], + [Enable dnscrypt support (requires libsodium)]), + [opt_dnscrypt=$enableval], [opt_dnscrypt=no]) + + if test "x$opt_dnscrypt" != "xno"; then + AC_ARG_WITH([libsodium], AC_HELP_STRING([--with-libsodium=path], + [Path where libsodium is installed, for dnscrypt]), [ + CFLAGS="$CFLAGS -I$withval/include" + LDFLAGS="$LDFLAGS -L$withval/lib" + ]) + AC_SEARCH_LIBS([sodium_init], [sodium], [], + AC_MSG_ERROR([The sodium library was not found. Please install sodium!])) + AC_SEARCH_LIBS([crypto_box_curve25519xchacha20poly1305_beforenm], [sodium], + [ + AC_SUBST([ENABLE_DNSCRYPT_XCHACHA20], [1]) + AC_DEFINE( + [USE_DNSCRYPT_XCHACHA20], [1], + [Define to 1 to enable dnscrypt with xchacha20 support]) + ], + [ + AC_SUBST([ENABLE_DNSCRYPT_XCHACHA20], [0]) + ]) + AC_SEARCH_LIBS([sodium_set_misuse_handler], [sodium], + [ + AC_DEFINE( + [SODIUM_MISUSE_HANDLER], [1], + [Define to 1 if libsodium supports sodium_set_misuse_handler]) + ], + [ + ]) + $1 + else + AC_SUBST([ENABLE_DNSCRYPT_XCHACHA20], [0]) + $2 + fi +]) --- contrib/unbound/dnscrypt/dnscrypt_config.h.orig +++ contrib/unbound/dnscrypt/dnscrypt_config.h @@ -0,0 +1,17 @@ +#ifndef UNBOUND_DNSCRYPT_CONFIG_H +#define UNBOUND_DNSCRYPT_CONFIG_H + +/* + * Process this file (dnscrypt_config.h.in) with AC_CONFIG_FILES to generate + * dnscrypt_config.h. + * + * This file exists so that USE_DNSCRYPT can be used without including config.h. + */ + +#if 0 /* ENABLE_DNSCRYPT */ +# ifndef USE_DNSCRYPT +# define USE_DNSCRYPT 1 +# endif +#endif + +#endif /* UNBOUND_DNSCRYPT_CONFIG_H */ --- contrib/unbound/dnscrypt/dnscrypt_config.h.in.orig +++ contrib/unbound/dnscrypt/dnscrypt_config.h.in @@ -0,0 +1,17 @@ +#ifndef UNBOUND_DNSCRYPT_CONFIG_H +#define UNBOUND_DNSCRYPT_CONFIG_H + +/* + * Process this file (dnscrypt_config.h.in) with AC_CONFIG_FILES to generate + * dnscrypt_config.h. + * + * This file exists so that USE_DNSCRYPT can be used without including config.h. + */ + +#if @ENABLE_DNSCRYPT@ /* ENABLE_DNSCRYPT */ +# ifndef USE_DNSCRYPT +# define USE_DNSCRYPT 1 +# endif +#endif + +#endif /* UNBOUND_DNSCRYPT_CONFIG_H */ --- contrib/unbound/dnstap/dnstap.c.orig +++ contrib/unbound/dnstap/dnstap.c @@ -39,6 +39,10 @@ #include "config.h" #include #include +#ifdef HAVE_SYS_STAT_H +#include +#endif +#include #include "sldns/sbuffer.h" #include "util/config_file.h" #include "util/net_help.h" @@ -118,10 +122,24 @@ } } +/* check that the socket file can be opened and exists, print error if not */ +static void +check_socket_file(const char* socket_path) +{ + struct stat statbuf; + memset(&statbuf, 0, sizeof(statbuf)); + if(stat(socket_path, &statbuf) < 0) { + log_warn("could not open dnstap-socket-path: %s, %s", + socket_path, strerror(errno)); + } +} + struct dt_env * dt_create(const char *socket_path, unsigned num_workers) { +#ifdef UNBOUND_DEBUG fstrm_res res; +#endif struct dt_env *env; struct fstrm_iothr_options *fopt; struct fstrm_unix_writer_options *fuwopt; @@ -132,6 +150,7 @@ socket_path); log_assert(socket_path != NULL); log_assert(num_workers > 0); + check_socket_file(socket_path); env = (struct dt_env *) calloc(1, sizeof(struct dt_env)); if (!env) @@ -138,7 +157,12 @@ return NULL; fwopt = fstrm_writer_options_init(); - res = fstrm_writer_options_add_content_type(fwopt, +#ifdef UNBOUND_DEBUG + res = +#else + (void) +#endif + fstrm_writer_options_add_content_type(fwopt, DNSTAP_CONTENT_TYPE, sizeof(DNSTAP_CONTENT_TYPE) - 1); log_assert(res == fstrm_res_success); --- contrib/unbound/dnstap/dnstap.proto.orig +++ contrib/unbound/dnstap/dnstap.proto @@ -13,6 +13,7 @@ // with this file. If not, see: // // . +syntax = "proto2"; package dnstap; --- contrib/unbound/doc/Changelog.orig +++ contrib/unbound/doc/Changelog @@ -1,3 +1,2695 @@ +20 February 2020: Wouter + - Updated contrib/unbound_smf23.tar.gz with Solaris SMF service for + Unbound from Yuri Voinov. + +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: # 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 + - 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" + +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. @@ -273,7 +2965,7 @@ compatibility with cisco dns guard. This lowers false positives. 18 April 2016: Wouter - - Fix some malformed reponses to edns queries get fallback to nonedns. + - Fix some malformed responses to edns queries get fallback to nonedns. 15 April 2016: Wouter - cachedb module event handling design. --- contrib/unbound/doc/README.orig +++ contrib/unbound/doc/README @@ -1,4 +1,4 @@ -README for Unbound 1.5.10 +README for Unbound 1.10.1 Copyright 2007 NLnet Labs http://unbound.net @@ -76,6 +76,8 @@ 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. @@ -97,7 +99,7 @@ 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/random from . + enabled, may be solved with a symbolic link to /dev/urandom from . 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...]. --- contrib/unbound/doc/README.ipset.md.orig +++ contrib/unbound/doc/README.ipset.md @@ -0,0 +1,65 @@ +## Created a module to support the ipset that could add the domain's ip to a list easily. + +### Purposes: +* In my case, I can't access the facebook, twitter, youtube and thousands web site for some reason. VPN is a solution. But the internet too slow whether all traffics pass through the vpn. +So, I set up a transparent proxy to proxy the traffic which has been blocked only. +At the final step, I need to install a dns service which would work with ipset well to launch the system. +I did some research for this. Unfortunately, Unbound, My favorite dns service doesn't support ipset yet. So, I decided to implement it by my self and contribute the patch. It's good for me and the community. +``` +# unbound.conf +server: + ... + local-zone: "facebook.com" ipset + local-zone: "twitter.com" ipset + local-zone: "instagram.com" ipset + more social website + +ipset: + name-v4: "gfwlist" +``` +``` +# iptables +iptables -A PREROUTING -p tcp -m set --match-set gfwlist dst -j REDIRECT --to-ports 10800 +iptables -A OUTPUT -p tcp -m set --match-set gfwlist dst -j REDIRECT --to-ports 10800 +``` + +* This patch could work with iptables rules to batch block the IPs. +``` +# unbound.conf +server: + ... + local-zone: "facebook.com" ipset + local-zone: "twitter.com" ipset + local-zone: "instagram.com" ipset + more social website + +ipset: + name-v4: "blacklist" + name-v6: "blacklist6" +``` +``` +# iptables +iptables -A INPUT -m set --set blacklist src -j DROP +ip6tables -A INPUT -m set --set blacklist6 src -j DROP +``` + +### Notes: +* To enable this module the root privileges is required. +* Please create a set with ipset command first. eg. **ipset -N blacklist iphash** + +### How to use: +``` +./configure --enable-ipset +make && make install +``` + +### Configuration: +``` +# unbound.conf +server: + ... + local-zone: "example.com" ipset + +ipset: + name-v4: "blacklist" +``` --- contrib/unbound/doc/TODO.orig +++ contrib/unbound/doc/TODO @@ -29,7 +29,7 @@ o add local-file: config with authority features. o (option) to make local-data answers be secure for libunbound (default=no) o (option) to make chroot: copy all needed files into jail (or make jail) - perhaps also print reminder to link /dev/random and sysloghack. + perhaps also print reminder to link /dev/urandom and sysloghack. o overhaul outside-network servicedquery to merge with udpwait and tcpwait, to make timers in servicedquery independent of udpwait queues. o check into rebinding ports for efficiency, configure time test. --- contrib/unbound/doc/example.conf.orig +++ contrib/unbound/doc/example.conf @@ -1,7 +1,7 @@ # # Example configuration file. # -# See unbound.conf(5) man page, version 1.5.10. +# See unbound.conf(5) man page, version 1.9.2. # # this is a comment. @@ -19,6 +19,14 @@ # 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 @@ -52,7 +60,7 @@ # 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 @@ -95,7 +103,8 @@ # so-sndbuf: 0 # use SO_REUSEPORT to distribute queries over threads. - # so-reuseport: no + # 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 @@ -108,7 +117,7 @@ # ip-freebind: no # EDNS reassembly buffer to advertise to UDP peers (the actual buffer - # is set with msg-buffer-size). 1480 can solve fragmentation (timeouts). + # is set with msg-buffer-size). 1472 can solve fragmentation (timeouts) # edns-buffer-size: 4096 # Maximum UDP response size (not applied to TCP response). @@ -115,6 +124,9 @@ # Suggested values are 512 to 4096. Default is 4096. 65536 disables it. # max-udp-size: 4096 + # 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 @@ -137,6 +149,10 @@ # msec to wait before close of port on timeout UDP. 0 disables. # delay-close: 0 + # 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 @@ -171,7 +187,7 @@ # 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. # repeat the define-tag statement to add additional tags. # define-tag: "tag1 tag2 tag3" @@ -192,6 +208,10 @@ # 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 @@ -200,7 +220,20 @@ # 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 + + # 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 @@ -207,7 +240,8 @@ # 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_snoop (recursive and nonrecursive ok) + # 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: 0.0.0.0/0 refuse @@ -230,6 +264,9 @@ # 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 + # 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. @@ -272,9 +309,13 @@ # logfile: "" # Log to syslog(3) if yes. The log facility LOG_DAEMON is used to - # log to, with identity "unbound". If yes, it overrides the logfile. + # 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 @@ -281,6 +322,21 @@ # 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: "/var/unbound/unbound.pid" @@ -294,6 +350,9 @@ # enable to not answer version.server and version.bind queries. # hide-version: no + # enable to not answer trustanchor.unbound queries. + # hide-trustanchor: no + # the identity to report. Leave "" or default to return hostname. # identity: "" @@ -326,9 +385,9 @@ # harden-dnssec-stripped: yes # Harden against queries that fall under dnssec-signed nxdomain names. - # harden-below-nxdomain: no + # harden-below-nxdomain: yes - # Harden the referral path by performing additional queries for + # 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. @@ -341,9 +400,19 @@ # Sent minimum amount of information to upstream servers to enhance # privacy. Only sent minimum required labels of the QNAME and set QTYPE - # to NS when possible. - # qname-minimisation: no + # 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: no + # 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 @@ -392,12 +461,15 @@ # 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: no # if yes, Unbound doesn't insert authority/additional sections # into response messages when those sections are not required. - # minimal-responses: no + # minimal-responses: yes # true to disable DNSSEC lameness check in iterator. # disable-dnssec-lame-check: no @@ -404,6 +476,9 @@ # 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, @@ -416,6 +491,12 @@ # and under the terms of our LICENSE (see that file in the source). # auto-trust-anchor-file: "/var/unbound/root.key" + # 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 DLV trusted keys. Same format as trust-anchor-file. # There can be only one DLV configured, it is trusted from root down. # DLV is going to be decommissioned. Please do not use it any more. @@ -477,6 +558,20 @@ # that set CD but cannot validate themselves. # ignore-cd-flag: no + # Serve expired responses from cache, with TTL 0 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 + # 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 @@ -524,6 +619,8 @@ # 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: "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 @@ -578,10 +675,12 @@ # 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 resolves normally, but logs client IP address + # 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, resolve in - # that way but ignore local data for that name. + # that way but ignore local data for that name + # 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 @@ -614,21 +713,46 @@ # 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 SSL (on the TCP sockets), with plain DNS inside - # the SSL stream. Give the certificate to use and private key. + # service clients over TLS (on the TCP sockets), with plain DNS inside + # the TLS stream. Give the certificate to use and private key. # default is "" (disabled). requires restart to take effect. - # ssl-service-key: "path/to/privatekeyfile.key" - # ssl-service-pem: "path/to/publiccertfile.pem" - # ssl-port: 853 + # tls-service-key: "path/to/privatekeyfile.key" + # tls-service-pem: "path/to/publiccertfile.pem" + # tls-port: 853 - # request upstream over SSL (with plain DNS inside the SSL stream). + # 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" + + # 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. - # ssl-upstream: no + # 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 + + # Also serve tls on these port numbers (eg. 443, ...), by listing + # tls-additional-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. @@ -649,9 +773,62 @@ # 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 + + # 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 + + # 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 whitelisted. + # ipsecmod-whitelist: "example.com" + # ipsecmod-whitelist: "nlnetlabs.nl" + + # 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 @@ -663,12 +840,10 @@ # set up the keys and certificates with unbound-control-setup. # control-enable: no - # Set to no and use an absolute path as control-interface to use - # a unix local named pipe for unbound-control. - # control-use-cert: yes - # 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 @@ -675,6 +850,10 @@ # 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: "/var/unbound/unbound_server.key" @@ -700,6 +879,8 @@ # stub-addr: 192.0.2.68 # stub-prime: no # stub-first: no +# stub-tls-upstream: no +# stub-no-cache: no # stub-zone: # name: "example.org" # stub-host: ns.example.com. @@ -715,6 +896,99 @@ # forward-addr: 192.0.2.68 # forward-addr: 192.0.2.73@5355 # forward to port 5355. # forward-first: 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), master: fetches with AXFR and IXFR, or url to zonefile. +# With allow-notify: you can give additional (apart from masters) sources of +# notifies. +# auth-zone: +# name: "." +# master: 199.9.14.201 # b.root-servers.net +# master: 192.33.4.12 # c.root-servers.net +# master: 199.7.91.13 # d.root-servers.net +# master: 192.5.5.241 # f.root-servers.net +# master: 192.112.36.4 # g.root-servers.net +# master: 193.0.14.129 # k.root-servers.net +# master: 192.0.47.132 # xfr.cjr.dns.icann.org +# master: 192.0.32.132 # xfr.lax.dns.icann.org +# master: 2001:500:200::b # b.root-servers.net +# master: 2001:500:2::c # c.root-servers.net +# master: 2001:500:2d::d # d.root-servers.net +# master: 2001:500:2f::f # f.root-servers.net +# master: 2001:500:12::d0d # g.root-servers.net +# master: 2001:7fd::1 # k.root-servers.net +# master: 2620:0:2830:202::132 # xfr.cjr.dns.icann.org +# master: 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 +# 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 +# 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 +# Enable external backend DB as auxiliary cache. 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" +# +# # For "redis" backend: +# # redis server's IP address or host name +# redis-server-host: 127.0.0.1 +# # redis server's TCP port +# redis-server-port: 6379 +# # timeout (in ms) for communication with the redis server +# redis-timeout: 100 --- contrib/unbound/doc/example.conf.in.orig +++ contrib/unbound/doc/example.conf.in @@ -1,7 +1,7 @@ # # Example configuration file. # -# See unbound.conf(5) man page, version 1.5.10. +# See unbound.conf(5) man page, version 1.10.1. # # this is a comment. @@ -19,6 +19,14 @@ # 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 @@ -52,7 +60,7 @@ # 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 @@ -95,7 +103,8 @@ # so-sndbuf: 0 # use SO_REUSEPORT to distribute queries over threads. - # so-reuseport: no + # 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 @@ -108,7 +117,7 @@ # ip-freebind: no # EDNS reassembly buffer to advertise to UDP peers (the actual buffer - # is set with msg-buffer-size). 1480 can solve fragmentation (timeouts). + # is set with msg-buffer-size). 1472 can solve fragmentation (timeouts) # edns-buffer-size: 4096 # Maximum UDP response size (not applied to TCP response). @@ -115,6 +124,9 @@ # Suggested values are 512 to 4096. Default is 4096. 65536 disables it. # max-udp-size: 4096 + # 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 @@ -137,6 +149,10 @@ # msec to wait before close of port on timeout UDP. 0 disables. # delay-close: 0 + # 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 @@ -171,7 +187,7 @@ # 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. # repeat the define-tag statement to add additional tags. # define-tag: "tag1 tag2 tag3" @@ -192,6 +208,10 @@ # 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 @@ -200,7 +220,20 @@ # 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 + + # 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 @@ -207,7 +240,8 @@ # 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_snoop (recursive and nonrecursive ok) + # 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: 0.0.0.0/0 refuse @@ -230,6 +264,9 @@ # 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 + # 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. @@ -249,7 +286,7 @@ # 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/random (for entropy). + # 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 /. @@ -272,9 +309,13 @@ # logfile: "" # Log to syslog(3) if yes. The log facility LOG_DAEMON is used to - # log to, with identity "unbound". If yes, it overrides the logfile. + # 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 @@ -281,6 +322,21 @@ # 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@" @@ -294,6 +350,9 @@ # enable to not answer version.server and version.bind queries. # hide-version: no + # enable to not answer trustanchor.unbound queries. + # hide-trustanchor: no + # the identity to report. Leave "" or default to return hostname. # identity: "" @@ -326,9 +385,9 @@ # harden-dnssec-stripped: yes # Harden against queries that fall under dnssec-signed nxdomain names. - # harden-below-nxdomain: no + # harden-below-nxdomain: yes - # Harden the referral path by performing additional queries for + # 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. @@ -341,9 +400,19 @@ # Sent minimum amount of information to upstream servers to enhance # privacy. Only sent minimum required labels of the QNAME and set QTYPE - # to NS when possible. - # qname-minimisation: no + # 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: no + # 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 @@ -392,12 +461,15 @@ # 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: no # if yes, Unbound doesn't insert authority/additional sections # into response messages when those sections are not required. - # minimal-responses: no + # minimal-responses: yes # true to disable DNSSEC lameness check in iterator. # disable-dnssec-lame-check: no @@ -404,6 +476,9 @@ # 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, @@ -416,6 +491,12 @@ # 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 DLV trusted keys. Same format as trust-anchor-file. # There can be only one DLV configured, it is trusted from root down. # DLV is going to be decommissioned. Please do not use it any more. @@ -477,6 +558,30 @@ # that set CD but cannot validate themselves. # ignore-cd-flag: 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 + # draft-ietf-dnsop-serve-stale-10 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 + # 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 @@ -524,6 +629,8 @@ # 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: "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 @@ -557,6 +664,9 @@ # 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 @@ -578,10 +688,12 @@ # 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 resolves normally, but logs client IP address + # 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, resolve in - # that way but ignore local data for that name. + # that way but ignore local data for that name + # 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 @@ -614,21 +726,46 @@ # 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 SSL (on the TCP sockets), with plain DNS inside - # the SSL stream. Give the certificate to use and private key. + # service clients over TLS (on the TCP sockets), with plain DNS inside + # the TLS stream. Give the certificate to use and private key. # default is "" (disabled). requires restart to take effect. - # ssl-service-key: "path/to/privatekeyfile.key" - # ssl-service-pem: "path/to/publiccertfile.pem" - # ssl-port: 853 + # tls-service-key: "path/to/privatekeyfile.key" + # tls-service-pem: "path/to/publiccertfile.pem" + # tls-port: 853 - # request upstream over SSL (with plain DNS inside the SSL stream). + # 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" + + # 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. - # ssl-upstream: no + # 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 + + # Also serve tls on these port numbers (eg. 443, ...), by listing + # tls-additional-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. @@ -649,9 +786,62 @@ # 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 + + # 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 + + # 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 whitelisted. + # ipsecmod-whitelist: "example.com" + # ipsecmod-whitelist: "nlnetlabs.nl" + + # 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 @@ -663,12 +853,10 @@ # set up the keys and certificates with unbound-control-setup. # control-enable: no - # Set to no and use an absolute path as control-interface to use - # a unix local named pipe for unbound-control. - # control-use-cert: yes - # 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 @@ -675,6 +863,10 @@ # 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" @@ -700,6 +892,8 @@ # stub-addr: 192.0.2.68 # stub-prime: no # stub-first: no +# stub-tls-upstream: no +# stub-no-cache: no # stub-zone: # name: "example.org" # stub-host: ns.example.com. @@ -715,6 +909,127 @@ # forward-addr: 192.0.2.68 # forward-addr: 192.0.2.73@5355 # forward to port 5355. # forward-first: 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), master: fetches with AXFR and IXFR, or url to zonefile. +# With allow-notify: you can give additional (apart from masters) sources of +# notifies. +# auth-zone: +# name: "." +# master: 199.9.14.201 # b.root-servers.net +# master: 192.33.4.12 # c.root-servers.net +# master: 199.7.91.13 # d.root-servers.net +# master: 192.5.5.241 # f.root-servers.net +# master: 192.112.36.4 # g.root-servers.net +# master: 193.0.14.129 # k.root-servers.net +# master: 192.0.47.132 # xfr.cjr.dns.icann.org +# master: 192.0.32.132 # xfr.lax.dns.icann.org +# master: 2001:500:200::b # b.root-servers.net +# master: 2001:500:2::c # c.root-servers.net +# master: 2001:500:2d::d # d.root-servers.net +# master: 2001:500:2f::f # f.root-servers.net +# master: 2001:500:12::d0d # g.root-servers.net +# master: 2001:7fd::1 # k.root-servers.net +# master: 2620:0:2830:202::132 # xfr.cjr.dns.icann.org +# master: 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 +# 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 +# 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 +# Enable external backend DB as auxiliary cache. 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" +# +# # For "redis" backend: +# # redis server's IP address or host name +# redis-server-host: 127.0.0.1 +# # redis server's TCP port +# redis-server-port: 6379 +# # timeout (in ms) for communication with the redis server +# redis-timeout: 100 + +# IPSet +# Add specify domain into set via ipset. +# Note: To enable ipset needs run unbound as root user. +# ipset: +# # set name for ip v4 addresses +# name-v4: "list-v4" +# # set name for ip v6 addresses +# name-v6: "list-v6" +# + +# Response Policy Zones +# RPZ policies. Applied in order of configuration. QNAME and Response IP +# Address trigger are the only supported triggers. Supported actions are: +# NXDOMAIN, NODATA, PASSTHRU, DROP and Local Data. Policies can be loaded from +# file, 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" +# master: 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" +# tags: "example" --- contrib/unbound/doc/libunbound.3.orig +++ contrib/unbound/doc/libunbound.3 @@ -1,4 +1,4 @@ -.TH "libunbound" "3" "Sep 27, 2016" "NLnet Labs" "unbound 1.5.10" +.TH "libunbound" "3" "Jun 17, 2019" "NLnet Labs" "unbound 1.9.2" .\" .\" libunbound.3 -- unbound library functions manual .\" @@ -12,7 +12,7 @@ .B unbound.h, .B ub_ctx, .B ub_result, -.B ub_callback_t, +.B ub_callback_type, .B ub_ctx_create, .B ub_ctx_delete, .B ub_ctx_set_option, @@ -20,6 +20,7 @@ .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, @@ -43,7 +44,7 @@ .B ub_ctx_zone_remove, .B ub_ctx_data_add, .B ub_ctx_data_remove -\- Unbound DNS validating resolver 1.5.10 functions. +\- Unbound DNS validating resolver 1.9.2 functions. .SH "SYNOPSIS" .B #include .LP @@ -72,6 +73,9 @@ \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 @@ -120,7 +124,7 @@ .br \fIint\fR rrtype, \fIint\fR rrclass, \fIvoid*\fR mydata, .br - \fIub_callback_t\fR callback, \fIint*\fR async_id); + \fIub_callback_type\fR callback, \fIint*\fR async_id); .LP \fIint\fR \fBub_cancel\fR(\fIstruct ub_ctx*\fR ctx, \fIint\fR async_id); @@ -150,7 +154,8 @@ is an implementation of a DNS resolver, that does caching and DNSSEC validation. This is the library API, for using the \-lunbound library. The server daemon is described in \fIunbound\fR(8). -The library can be used to convert hostnames to ip addresses, and back, +The library works independent from a running unbound server, and +can be used to convert hostnames to ip addresses, and back, and obtain other information from the DNS. The library performs public\-key validation of results with DNSSEC. .P @@ -162,7 +167,7 @@ It can be created and deleted at any time. Creating it anew removes any previous configuration (such as trusted keys) and clears any cached results. .P -The functions are thread\-safe, and a context an be used in a threaded (as +The functions are thread\-safe, and a context can be used in a threaded (as well as in a non\-threaded) environment. Also resolution (and validation) can be performed blocking and non\-blocking (also called asynchronous). The async method returns from the call immediately, so that processing @@ -180,7 +185,7 @@ .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 asyncronous operation if you use +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 @@ -203,7 +208,10 @@ A power\-user interface that lets you specify an unbound config file, see \fIunbound.conf\fR(5), which is read for configuration. Not all options are relevant. For some specific options, such as adding trust anchors, special -routines exist. +routines exist. This function is thread\-safe only if a single instance of +ub_ctx* exists in the application. If several instances exist the +application has to ensure that ub_ctx_config is not called in parallel by +the different instances. .TP .B ub_ctx_set_fwd Set machine to forward DNS queries to, the caching resolver to use. @@ -223,6 +231,12 @@ 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 @@ -407,6 +421,10 @@ 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). --- contrib/unbound/doc/libunbound.3.in.orig +++ contrib/unbound/doc/libunbound.3.in @@ -1,4 +1,4 @@ -.TH "libunbound" "3" "Sep 27, 2016" "NLnet Labs" "unbound 1.5.10" +.TH "libunbound" "3" "May 19, 2020" "NLnet Labs" "unbound 1.10.1" .\" .\" libunbound.3 -- unbound library functions manual .\" @@ -12,7 +12,7 @@ .B unbound.h, .B ub_ctx, .B ub_result, -.B ub_callback_t, +.B ub_callback_type, .B ub_ctx_create, .B ub_ctx_delete, .B ub_ctx_set_option, @@ -20,6 +20,7 @@ .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, @@ -43,7 +44,7 @@ .B ub_ctx_zone_remove, .B ub_ctx_data_add, .B ub_ctx_data_remove -\- Unbound DNS validating resolver 1.5.10 functions. +\- Unbound DNS validating resolver 1.10.1 functions. .SH "SYNOPSIS" .B #include .LP @@ -72,6 +73,9 @@ \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 @@ -120,7 +124,7 @@ .br \fIint\fR rrtype, \fIint\fR rrclass, \fIvoid*\fR mydata, .br - \fIub_callback_t\fR callback, \fIint*\fR async_id); + \fIub_callback_type\fR callback, \fIint*\fR async_id); .LP \fIint\fR \fBub_cancel\fR(\fIstruct ub_ctx*\fR ctx, \fIint\fR async_id); @@ -150,7 +154,8 @@ is an implementation of a DNS resolver, that does caching and DNSSEC validation. This is the library API, for using the \-lunbound library. The server daemon is described in \fIunbound\fR(8). -The library can be used to convert hostnames to ip addresses, and back, +The library works independent from a running unbound server, and +can be used to convert hostnames to ip addresses, and back, and obtain other information from the DNS. The library performs public\-key validation of results with DNSSEC. .P @@ -162,7 +167,7 @@ It can be created and deleted at any time. Creating it anew removes any previous configuration (such as trusted keys) and clears any cached results. .P -The functions are thread\-safe, and a context an be used in a threaded (as +The functions are thread\-safe, and a context can be used in a threaded (as well as in a non\-threaded) environment. Also resolution (and validation) can be performed blocking and non\-blocking (also called asynchronous). The async method returns from the call immediately, so that processing @@ -180,7 +185,7 @@ .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 asyncronous operation if you use +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 @@ -203,7 +208,10 @@ A power\-user interface that lets you specify an unbound config file, see \fIunbound.conf\fR(5), which is read for configuration. Not all options are relevant. For some specific options, such as adding trust anchors, special -routines exist. +routines exist. This function is thread\-safe only if a single instance of +ub_ctx* exists in the application. If several instances exist the +application has to ensure that ub_ctx_config is not called in parallel by +the different instances. .TP .B ub_ctx_set_fwd Set machine to forward DNS queries to, the caching resolver to use. @@ -223,6 +231,12 @@ 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 @@ -382,12 +396,13 @@ 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 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 */ + 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 @@ -407,6 +422,10 @@ 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). --- contrib/unbound/doc/requirements.txt.orig +++ contrib/unbound/doc/requirements.txt @@ -81,7 +81,7 @@ 5. Choices ---------- -o rfc2181 decourages duplicates RRs in RRsets. unbound does not create +o rfc2181 discourages duplicates RRs in RRsets. unbound does not create duplicates, but when presented with duplicates on the wire from the authoritative servers, does not perform duplicate removal. It does do some rrsig duplicate removal, in the msgparser, for dnssec qtype --- contrib/unbound/doc/unbound-anchor.8.orig +++ contrib/unbound/doc/unbound-anchor.8 @@ -1,4 +1,4 @@ -.TH "unbound-anchor" "8" "Sep 27, 2016" "NLnet Labs" "unbound 1.5.10" +.TH "unbound-anchor" "8" "Jun 17, 2019" "NLnet Labs" "unbound 1.9.2" .\" .\" unbound-anchor.8 -- unbound anchor maintenance utility manual .\" @@ -67,7 +67,7 @@ .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 addres or IPv6 address (no brackets) if you want. +You can pass an IPv4 address or IPv6 address (no brackets) if you want. .TP .B \-x \fIpath The pathname to the root\-anchors.xml file on the server. (forms URL with \-u). @@ -109,6 +109,11 @@ 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 --- contrib/unbound/doc/unbound-anchor.8.in.orig +++ contrib/unbound/doc/unbound-anchor.8.in @@ -1,4 +1,4 @@ -.TH "unbound-anchor" "8" "Sep 27, 2016" "NLnet Labs" "unbound 1.5.10" +.TH "unbound-anchor" "8" "May 19, 2020" "NLnet Labs" "unbound 1.10.1" .\" .\" unbound-anchor.8 -- unbound anchor maintenance utility manual .\" @@ -67,8 +67,12 @@ .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 addres or IPv6 address (no brackets) if you want. +You can pass an IPv4 address or IPv6 address (no brackets) if you want. .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. @@ -109,6 +113,11 @@ 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 --- contrib/unbound/doc/unbound-checkconf.8.orig +++ contrib/unbound/doc/unbound-checkconf.8 @@ -1,4 +1,4 @@ -.TH "unbound-checkconf" "8" "Sep 27, 2016" "NLnet Labs" "unbound 1.5.10" +.TH "unbound-checkconf" "8" "Jun 17, 2019" "NLnet Labs" "unbound 1.9.2" .\" .\" unbound-checkconf.8 -- unbound configuration checker manual .\" @@ -8,7 +8,7 @@ .\" .\" .SH "NAME" -unbound\-checkconf +.B unbound\-checkconf \- Check unbound configuration file for errors. .SH "SYNOPSIS" .B unbound\-checkconf --- contrib/unbound/doc/unbound-checkconf.8.in.orig +++ contrib/unbound/doc/unbound-checkconf.8.in @@ -1,4 +1,4 @@ -.TH "unbound-checkconf" "8" "Sep 27, 2016" "NLnet Labs" "unbound 1.5.10" +.TH "unbound-checkconf" "8" "May 19, 2020" "NLnet Labs" "unbound 1.10.1" .\" .\" unbound-checkconf.8 -- unbound configuration checker manual .\" @@ -8,7 +8,7 @@ .\" .\" .SH "NAME" -unbound\-checkconf +.B unbound\-checkconf \- Check unbound configuration file for errors. .SH "SYNOPSIS" .B unbound\-checkconf --- contrib/unbound/doc/unbound-control.8.orig +++ contrib/unbound/doc/unbound-control.8 @@ -1,4 +1,4 @@ -.TH "unbound-control" "8" "Sep 27, 2016" "NLnet Labs" "unbound 1.5.10" +.TH "unbound-control" "8" "Jun 17, 2019" "NLnet Labs" "unbound 1.9.2" .\" .\" unbound-control.8 -- unbound remote control manual .\" @@ -99,6 +99,22 @@ 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. @@ -128,6 +144,9 @@ 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. @@ -178,7 +197,7 @@ 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, -cache\-max\-ttl, cache\-min\-ttl, cache\-max\-negative\-ttl. +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 ':'. @@ -263,6 +282,49 @@ 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. +.TP +.B auth_zone_reload \fIzone\fR +Reload the auth zone from zonefile. The zonefile is read in overwriting +the current contents of the zone in memory. This changes the auth zone +contents itself, not the cache contents. Such cache contents exists if +you set unbound to validate with for-upstream yes and that can be cleared +with \fBflush_zone\fR \fIzone\fR. +.TP +.B auth_zone_transfer \fIzone\fR +Transfer the auth zone from master. The auth zone probe sequence is started, +where the masters are probed to see if they have an updated zone (with the SOA +serial check). And then the zone is transferred for a newer zone version. +.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 \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" @@ -288,6 +350,9 @@ .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.cachehits number of queries that were successfully answered using a cache lookup .TP @@ -294,6 +359,19 @@ .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, @@ -301,6 +379,9 @@ Not part of the recursivereplies (or the histogram thereof) or cachemiss, as a cache response was sent. .TP +.I threadX.num.zero_ttl +number of replies with ttl zero, because they served an expired cache entry. +.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 @@ -347,9 +428,24 @@ .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.zero_ttl +summed over threads. +.TP .I total.num.recursivereplies summed over threads. .TP @@ -384,9 +480,6 @@ time since last statistics printout, in seconds. .SH EXTENDED STATISTICS .TP -.I mem.total.sbrk -If sbrk(2) is available, an estimate of the heap size of the program in number of bytes. Close to the total memory used by the program, as reported by top and ps. Could be wrong if the OS allocates memory non\-contiguously. -.TP .I mem.cache.rrset Memory in bytes in use by the RRset cache. .TP @@ -393,6 +486,12 @@ .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 @@ -400,6 +499,10 @@ 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 histogram...to.. Shows a histogram, summed over all threads. Every element counts the recursive queries whose reply time fit between the lower and upper bound. @@ -431,6 +534,14 @@ Number of queries that the unbound server made using TCP 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.ipv6 Number of queries that were made using IPv6 towards the unbound server. .TP @@ -447,6 +558,18 @@ 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. +The can be use to compute the shared secret hitrate. +.TP +.I num.query.dnscrypt.replay +The number of dnscrypt queries that found a nonce hit in the nonce cache and +hence are considered a query replay. +.TP .I num.answer.rcode.NXDOMAIN The number of answers to queries, from cache or from recursion, that had the return code NXDOMAIN. Also printed for the other return codes. @@ -496,6 +619,47 @@ .I key.cache.count The number of items in the key cache. These are DNSSEC keys, one item per delegation point, and their validation status. +.TP +.I dnscrypt_shared_secret.cache.count +The number of items in the shared secret cache. These are precomputed shared +secrets for a given client public key/server secret key pair. Shared secrets +are CPU intensive and this cache allows unbound to avoid recomputing the +shared secret when multiple dnscrypt queries are sent from the same client. +.TP +.I dnscrypt_nonce.cache.count +The number of items in the client nonce cache. This cache is used to prevent +dnscrypt queries replay. The client nonce must be unique for each client public +key/server secret key pair. This cache should be able to host QPS * `replay +window` interval keys to prevent replay of a query during `replay window` +seconds. +.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. .SH "FILES" .TP .I /var/unbound/unbound.conf --- contrib/unbound/doc/unbound-control.8.in.orig +++ contrib/unbound/doc/unbound-control.8.in @@ -1,4 +1,4 @@ -.TH "unbound-control" "8" "Sep 27, 2016" "NLnet Labs" "unbound 1.5.10" +.TH "unbound-control" "8" "May 19, 2020" "NLnet Labs" "unbound 1.10.1" .\" .\" unbound-control.8 -- unbound remote control manual .\" @@ -99,6 +99,22 @@ 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. @@ -128,6 +144,9 @@ 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. @@ -178,7 +197,7 @@ 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, -cache\-max\-ttl, cache\-min\-ttl, cache\-max\-negative\-ttl. +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 ':'. @@ -263,6 +282,52 @@ 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. +.TP +.B auth_zone_reload \fIzone\fR +Reload the auth zone from zonefile. The zonefile is read in overwriting +the current contents of the zone in memory. This changes the auth zone +contents itself, not the cache contents. Such cache contents exists if +you set unbound to validate with for-upstream yes and that can be cleared +with \fBflush_zone\fR \fIzone\fR. +.TP +.B auth_zone_transfer \fIzone\fR +Transfer the auth zone from master. The auth zone probe sequence is started, +where the masters are probed to see if they have an updated zone (with the SOA +serial check). And then the zone is transferred for a newer zone version. +.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" @@ -288,6 +353,9 @@ .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.cachehits number of queries that were successfully answered using a cache lookup .TP @@ -294,6 +362,19 @@ .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, @@ -301,6 +382,9 @@ 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.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 @@ -347,9 +431,24 @@ .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.recursivereplies summed over threads. .TP @@ -384,9 +483,6 @@ time since last statistics printout, in seconds. .SH EXTENDED STATISTICS .TP -.I mem.total.sbrk -If sbrk(2) is available, an estimate of the heap size of the program in number of bytes. Close to the total memory used by the program, as reported by top and ps. Could be wrong if the OS allocates memory non\-contiguously. -.TP .I mem.cache.rrset Memory in bytes in use by the RRset cache. .TP @@ -393,6 +489,12 @@ .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 @@ -400,6 +502,10 @@ 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 histogram...to.. Shows a histogram, summed over all threads. Every element counts the recursive queries whose reply time fit between the lower and upper bound. @@ -431,6 +537,14 @@ Number of queries that the unbound server made using TCP 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.ipv6 Number of queries that were made using IPv6 towards the unbound server. .TP @@ -447,6 +561,18 @@ 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. +The can be use to compute the shared secret hitrate. +.TP +.I num.query.dnscrypt.replay +The number of dnscrypt queries that found a nonce hit in the nonce cache and +hence are considered a query replay. +.TP .I num.answer.rcode.NXDOMAIN The number of answers to queries, from cache or from recursion, that had the return code NXDOMAIN. Also printed for the other return codes. @@ -496,6 +622,52 @@ .I key.cache.count The number of items in the key cache. These are DNSSEC keys, one item per delegation point, and their validation status. +.TP +.I dnscrypt_shared_secret.cache.count +The number of items in the shared secret cache. These are precomputed shared +secrets for a given client public key/server secret key pair. Shared secrets +are CPU intensive and this cache allows unbound to avoid recomputing the +shared secret when multiple dnscrypt queries are sent from the same client. +.TP +.I dnscrypt_nonce.cache.count +The number of items in the client nonce cache. This cache is used to prevent +dnscrypt queries replay. The client nonce must be unique for each client public +key/server secret key pair. This cache should be able to host QPS * `replay +window` interval keys to prevent replay of a query during `replay window` +seconds. +.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.rpz.action. +Number of queries answered using configured RPZ policy, per RPZ action type. +Possible actions are: nxdomain, nodata, passthru, drop, local_data, disabled, +and cname_override. .SH "FILES" .TP .I @ub_conf_file@ --- contrib/unbound/doc/unbound-host.1.orig +++ contrib/unbound/doc/unbound-host.1 @@ -1,4 +1,4 @@ -.TH "unbound\-host" "1" "Sep 27, 2016" "NLnet Labs" "unbound 1.5.10" +.TH "unbound\-host" "1" "Jun 17, 2019" "NLnet Labs" "unbound 1.9.2" .\" .\" unbound-host.1 -- unbound DNS lookup utility .\" @@ -12,12 +12,13 @@ \- unbound DNS lookup utility .SH "SYNOPSIS" .B unbound\-host +.RB [ \-C +.IR configfile ] .RB [ \-vdhr46D ] .RB [ \-c .IR class ] .RB [ \-t .IR type ] -.I hostname .RB [ \-y .IR key ] .RB [ \-f @@ -24,8 +25,7 @@ .IR keyfile ] .RB [ \-F .IR namedkeyfile ] -.RB [ \-C -.IR configfile ] +.I hostname .SH "DESCRIPTION" .B Unbound\-host uses the unbound validating resolver to query for the hostname and display @@ -86,6 +86,8 @@ .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 --- contrib/unbound/doc/unbound-host.1.in.orig +++ contrib/unbound/doc/unbound-host.1.in @@ -1,4 +1,4 @@ -.TH "unbound\-host" "1" "Sep 27, 2016" "NLnet Labs" "unbound 1.5.10" +.TH "unbound\-host" "1" "May 19, 2020" "NLnet Labs" "unbound 1.10.1" .\" .\" unbound-host.1 -- unbound DNS lookup utility .\" @@ -12,12 +12,13 @@ \- unbound DNS lookup utility .SH "SYNOPSIS" .B unbound\-host +.RB [ \-C +.IR configfile ] .RB [ \-vdhr46D ] .RB [ \-c .IR class ] .RB [ \-t .IR type ] -.I hostname .RB [ \-y .IR key ] .RB [ \-f @@ -24,8 +25,7 @@ .IR keyfile ] .RB [ \-F .IR namedkeyfile ] -.RB [ \-C -.IR configfile ] +.I hostname .SH "DESCRIPTION" .B Unbound\-host uses the unbound validating resolver to query for the hostname and display @@ -86,6 +86,8 @@ .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 --- contrib/unbound/doc/unbound.8.orig +++ contrib/unbound/doc/unbound.8 @@ -1,4 +1,4 @@ -.TH "unbound" "8" "Sep 27, 2016" "NLnet Labs" "unbound 1.5.10" +.TH "unbound" "8" "Jun 17, 2019" "NLnet Labs" "unbound 1.9.2" .\" .\" unbound.8 -- unbound manual .\" @@ -9,11 +9,12 @@ .\" .SH "NAME" .B unbound -\- Unbound DNS validating resolver 1.5.10. +\- Unbound DNS validating resolver 1.9.2. .SH "SYNOPSIS" .B unbound .RB [ \-h ] .RB [ \-d ] +.RB [ \-p ] .RB [ \-v ] .RB [ \-c .IR cfgfile ] @@ -67,6 +68,11 @@ 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 in addition to the verbosity (if any) from the config file. --- contrib/unbound/doc/unbound.8.in.orig +++ contrib/unbound/doc/unbound.8.in @@ -1,4 +1,4 @@ -.TH "unbound" "8" "Sep 27, 2016" "NLnet Labs" "unbound 1.5.10" +.TH "unbound" "8" "May 19, 2020" "NLnet Labs" "unbound 1.10.1" .\" .\" unbound.8 -- unbound manual .\" @@ -9,11 +9,12 @@ .\" .SH "NAME" .B unbound -\- Unbound DNS validating resolver 1.5.10. +\- Unbound DNS validating resolver 1.10.1. .SH "SYNOPSIS" .B unbound .RB [ \-h ] .RB [ \-d ] +.RB [ \-p ] .RB [ \-v ] .RB [ \-c .IR cfgfile ] @@ -53,7 +54,7 @@ The available options are: .TP .B \-h -Show the version and commandline option help. +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 @@ -67,9 +68,17 @@ 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 in addition 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), --- contrib/unbound/doc/unbound.conf.5.orig +++ contrib/unbound/doc/unbound.conf.5 @@ -1,4 +1,4 @@ -.TH "unbound.conf" "5" "Sep 27, 2016" "NLnet Labs" "unbound 1.5.10" +.TH "unbound.conf" "5" "Jun 17, 2019" "NLnet Labs" "unbound 1.9.2" .\" .\" unbound.conf.5 -- unbound.conf manual .\" @@ -16,13 +16,14 @@ .B unbound.conf is used to configure \fIunbound\fR(8). -The file format has attributes and values. Some attributes have attributes inside them. +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 +The utility \fIunbound\-checkconf\fR(8) can be used to check unbound.conf prior to usage. .SH "EXAMPLE" @@ -30,7 +31,7 @@ and start the server with: .P .nf - $ unbound \-c /etc/unbound/unbound.conf + $ unbound \-c /etc/unbound/unbound.conf .fi .P Most settings are the defaults. Stop the server with: @@ -62,8 +63,8 @@ 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 its containing attributes, or a value. +There must be whitespace between keywords. Attribute keywords end with a colon ':'. +An attribute is followed by its containing attributes, or a value. .P Files can be included using the .B include: @@ -71,7 +72,7 @@ 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 +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). .SS "Server Options" @@ -80,17 +81,17 @@ clause. .TP .B verbosity: \fI -The verbosity number, level 0 means no verbosity, only errors. Level 1 +The verbosity number, level 0 means no verbosity, only errors. Level 1 gives operational information. Level 2 gives detailed operational -information. 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. +information. 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 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, +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 @@ -99,7 +100,7 @@ the statistics counters after logging the statistics. Default is no. .TP .B extended\-statistics: \fI -If enabled, extended statistics are printed from \fIunbound\-control\fR(8). +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 @@ -112,7 +113,7 @@ .B interface: \fI 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 +Can be given multiple times to work on several interfaces. If none are given the default is to listen to localhost. 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 @@ -120,22 +121,22 @@ \fBport\fR) is used. .TP .B ip\-address: \fI -Same as interface: (for easy of compatibility with nsd.conf). +Same as interface: (for ease of compatibility with nsd.conf). .TP .B interface\-automatic: \fI -Detect source interface on UDP queries and copy them to replies. This +Detect source interface on UDP queries and copy them to replies. This feature is experimental, and needs support in your OS for particular socket options. Default value is no. .TP .B outgoing\-interface: \fI 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 +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 +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, @@ -151,12 +152,12 @@ 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 +ip \-6 addr add mynetblock/64 dev lo && +ip \-6 route add local mynetblock/64 dev lo .TP .B outgoing\-range: \fI -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 +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 @@ -163,18 +164,18 @@ .B outgoing\-port\-permit: \fI 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. +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 +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 -Do not permit unbound to open this port or range of ports for use to send +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. @@ -196,7 +197,7 @@ buffer size is determined by msg\-buffer\-size (both for TCP and UDP). Do not set higher than that value. Default is 4096 which is RFC recommended. If you have fragmentation reassembly problems, usually seen as timeouts, -then a value of 1480 can fix it. Setting to 512 bypasses even the most +then a value of 1472 can fix it. 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). @@ -204,13 +205,23 @@ .B max\-udp\-size: \fI Maximum UDP response size (not applied to TCP response). 65536 disables the udp response size maximum, and uses the choice from the client, always. -Suggested values are 512 to 4096. Default is 4096. +Suggested values are 512 to 4096. Default is 4096. .TP +.B stream\-wait\-size: \fI +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 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 +for DNS data, such as for huge resource records, will result in a SERVFAIL reply to the client. .TP .B msg\-cache\-size: \fI @@ -220,7 +231,7 @@ .TP .B msg\-cache\-slabs: \fI 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 +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 @@ -232,12 +243,12 @@ .TP .B jostle\-timeout: \fI 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 +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 +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 +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 @@ -252,6 +263,12 @@ the ID and remote IP of packets, and unwanted packets are added to the unwanted packet counter. .TP +.B unknown\-server\-time\-limit: \fI +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 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 @@ -277,22 +294,25 @@ .B so\-reuseport: \fI 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 no. 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), +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 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\-existant IP addresses that +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. +permissions on some systems. The option uses IP_BINDANY on FreeBSD systems +and SO_BINDANY on OpenBSD systems. .TP .B ip\-freebind: \fI If yes, then use IP_FREEBIND socket option on sockets where unbound @@ -308,15 +328,13 @@ .TP .B rrset\-cache\-slabs: \fI Number of slabs in the RRset cache. Slabs reduce lock contention by threads. -Must be set to a power of 2. +Must be set to a power of 2. .TP .B cache\-max\-ttl: \fI -Time to live maximum for RRsets and messages in the cache. Default is -86400 seconds (1 day). If the maximum kicks in, responses to clients -still get decrementing TTLs based on the original (larger) values. -When the internal TTL expires, the cache item has expired. +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. +trust (very large) TTL values. Downstream clients also see the lower TTL. .TP .B cache\-min\-ttl: \fI Time to live minimum for RRsets and messages in the cache. Default is 0. @@ -323,20 +341,21 @@ 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 +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 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 -Time to live for entries in the host cache. The host cache contains +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 of slabs in the infrastructure cache. Slabs reduce lock contention -by threads. Must be set to a power of 2. +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 of hosts for which information is cached. Default is 10000. @@ -372,7 +391,7 @@ .TP .B tcp\-mss: \fI Maximum segment size (MSS) of TCP socket on which the server responds -to queries. Value lower than common MSS on Ethernet +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 @@ -386,69 +405,186 @@ Default is system default MSS determined by interface MTU and negotiation between Unbound and other servers. .TP +.B tcp-idle-timeout: \fI\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 edns-tcp-keepalive: \fI\fR +Enable or disable EDNS TCP Keepalive. Default is no. +.TP +.B edns-tcp-keepalive-timeout: \fI\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 tcp\-upstream: \fI Enable or disable whether the upstream queries use TCP only for transport. Default is no. Useful in tunneling scenarios. .TP +.B udp\-upstream\-without\-downstream: \fI +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 +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 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 -Enabled or disable whether the upstream queries use SSL only for transport. -Default is no. Useful in tunneling scenarios. The SSL contains plain DNS in -TCP wireformat. The other server must support this (see \fBssl\-service\-key\fR). +Alternate syntax for \fBtls\-upstream\fR. If both are present in the config +file the last is used. .TP -.B ssl\-service-key: \fI -If enabled, the server provider SSL service on its TCP sockets. The clients -have to use ssl\-upstream: yes. The file is the private key for the TLS -session. The public certificate is in the ssl\-service\-pem file. Default -is "", turned off. Requires a restart (a reload is not enough) if changed, -because the private key is read while root permissions are held and before -chroot (if any). Normal DNS TCP service is not provided and gives errors, -this service is best run with a different \fBport:\fR config or \fI@port\fR -suffixes in the \fBinterface\fR config. +.B tls\-service\-key: \fI +If enabled, the server provides TLS service on the TCP ports marked +implicitly or explicitly for TLS service with tls\-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 do not provide +normal DNS TCP service. .TP -.B ssl\-service\-pem: \fI -The public key certificate pem file for the ssl service. Default is "", +.B ssl\-service\-key: \fI +Alternate syntax for \fBtls\-service\-key\fR. +.TP +.B tls\-service\-pem: \fI +The public key certificate pem file for the tls service. Default is "", turned off. .TP +.B ssl\-service\-pem: \fI +Alternate syntax for \fBtls\-service\-pem\fR. +.TP +.B tls\-port: \fI +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 -The port number on which to provide TCP SSL service, default 853, only -interfaces configured with that port number as @number get the SSL service. +Alternate syntax for \fBtls\-port\fR. .TP +.B tls\-cert\-bundle: \fI +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. +.TP +.B ssl\-cert\-bundle: \fI +Alternate syntax for \fBtls\-cert\-bundle\fR. +.TP +.B tls\-win\-cert: \fI +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. +.TP +.B tls\-additional\-port: \fI +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 +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 +Set the list of ciphers to allow when serving TLS. Use "" for defaults, +and that is the default. +.TP +.B tls\-ciphersuites: \fI +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 use\-systemd: \fI +Enable or disable systemd socket activation. +Default is no. +.TP .B do\-daemonize: \fI Enable or disable whether the unbound server forks into the background as -a daemon. Default is yes. +a daemon. Set the value to \fIno\fR when unbound runs as systemd service. +Default is yes. .TP +.B tcp\-connection\-limit: \fI +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 -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_snoop\fR, \fIdeny_non_local\fR or \fIrefuse_non_local\fR. +The netblock is given as an IP4 or IP6 address with /size appended for a +classless network block. The action can be \fIdeny\fR, \fIrefuse\fR, +\fIallow\fR, \fIallow_setrd\fR, \fIallow_snoop\fR, \fIdeny_non_local\fR or +\fIrefuse_non_local\fR. The most specific netblock match is used, if none match \fIdeny\fR is used. +The order of the access\-control statements therefore does not matter. .IP The action \fIdeny\fR stops queries from hosts from that netblock. .IP -The action \fIrefuse\fR stops queries too, but sends a DNS rcode REFUSED +The action \fIrefuse\fR stops queries too, but sends a DNS rcode REFUSED error message back. .IP -The action \fIallow\fR gives access to clients from that netblock. -It gives only access for recursion clients (which is +The action \fIallow\fR 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 +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 +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 action \fIallow_snoop\fR gives nonrecursive access too. This give -both recursive and non recursive access. The name \fIallow_snoop\fR refers +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 action \fIallow_snoop\fR 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 +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 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 +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 @@ -474,11 +610,14 @@ .B access\-control\-tag\-data: \fI <"resource record string"> Set redirect data for particular tag for given access control element. .TP +.B access\-control\-view: \fI +Set view for given access control element. +.TP .B chroot: \fI 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. +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: @@ -489,22 +628,23 @@ .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 +to chroot and dropping permissions. This allows the pidfile to be /var/run/unbound.pid and the chroot to be /var/unbound, for example. .IP Additionally, unbound may need to access /dev/random (for entropy) from inside the chroot. .IP -If given a chroot is done to the given directory. The default is -"/var/unbound". If you give "" no chroot is performed. +If given a chroot is done to the given directory. By default chroot is +enabled and the default is "/var/unbound". If you give "" no +chroot is performed. .TP .B username: \fI If given, after binding the port the user privileges are dropped. Default is -"unbound". If you give username: "" no user change is performed. +"unbound". 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 +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 @@ -516,21 +656,28 @@ .TP .B logfile: \fI If "" is given, logging goes to stderr, or nowhere once daemonized. -The logfile is appended to, in the following format: +The logfile is appended to, in the following format: .nf -[seconds since 1970] unbound[pid:tid]: type: message. +[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 +The logfile is reopened (for append) when the config file is reread, on SIGHUP. .TP .B use\-syslog: \fI -Sets unbound to send log messages to the syslogd, using -\fIsyslog\fR(3). +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 +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 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 @@ -542,21 +689,43 @@ lines which makes the server (significantly) slower. Odd (nonprintable) characters in names are printed as '?'. .TP +.B log\-replies: \fI +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 +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 +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 +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 -The process id is written to the file. Default is "/var/unbound/unbound.pid". +The process id is written to the file. Default is "/var/unbound/unbound.pid". So, .nf -kill \-HUP `cat /var/unbound/unbound.pid` +kill \-HUP `cat /var/unbound/unbound.pid` .fi triggers a reload, .nf -kill \-TERM `cat /var/unbound/unbound.pid` +kill \-TERM `cat /var/unbound/unbound.pid` .fi gracefully terminates. .TP .B root\-hints: \fI 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 +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 @@ -574,25 +743,28 @@ Set the version to report. If set to "", the default, then the package version is returned. .TP +.B hide\-trustanchor: \fI +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. +dependency depth. .IP The number of values determines the maximum dependency depth -that unbound will pursue in answering a query. +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. +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 +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 Very small EDNS buffer sizes from queries are ignored. Default is off, since -it is legal protocol wise to send these, and unbound tries to give very +it is legal protocol wise to send these, and unbound tries to give very small answers to these queries, where possible. .TP .B harden\-large\-queries: \fI @@ -606,30 +778,31 @@ .B harden\-dnssec\-stripped: \fI 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 +(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 on. .TP .B harden\-below\-nxdomain: \fI -From draft\-vixie\-dnsext\-resimprove, returns nxdomain to queries for a name +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 off. -Currently, draft\-ietf\-dnsop\-nxdomain\-cut promotes this technique. +have DNSSEC. Default is on. +The nxdomain must be secure, this means nsec3 with optout is insufficient. .TP .B harden\-referral\-path: \fI 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 +NS sets and the nameserver addresses that are encountered on the referral path to the answer. -Default off, because it burdens the authority servers, and it is +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 @@ -644,9 +817,9 @@ .TP .B use\-caps\-for\-id: \fI 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 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\-whitelist: \fI @@ -657,10 +830,23 @@ .TP .B qname\-minimisation: \fI Send minimum amount of information to upstream servers to enhance privacy. -Only sent minimum required labels of the QNAME and set QTYPE to NS when -possible. Best effort approach, full QNAME and original QTYPE will be sent when -upstream replies with a RCODE other than NOERROR. Default is off. +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 +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 off. +.TP +.B aggressive\-nsec: \fI +Aggressive NSEC uses the DNSSEC NSEC chain to synthesize NXDOMAIN +and other denials, using information from previous NXDOMAINs answers. +Default is no. It helps to reduce the query rate towards targets that get +a very high nonexistent name lookup rate. +.TP .B private\-address: \fI Give IPv4 of IPv6 addresses or classless subnets. These are addresses on your private network, and are not allowed to be returned for @@ -682,7 +868,7 @@ .TP .B private\-domain: \fI Allow this domain, and all its subdomains to contain private addresses. -Give multiple times to allow multiple domain names to contain private +Give multiple times to allow multiple domain names to contain private addresses. Default is none. .TP .B unwanted\-reply\-threshold: \fI @@ -693,7 +879,7 @@ is suggested. Default is 0 (turned off). .TP .B do\-not\-query\-address: \fI -Do not query the given IP address. Can be IP4 or IP6. Append /num to +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 @@ -708,12 +894,18 @@ 10 percent more traffic and load on the machine, but popular items do not expire from the cache. .TP -.B prefetch-key: \fI +.B prefetch\-key: \fI 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 rrset-roundrobin: \fI +.B deny\-any: \fI +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 If yes, Unbound rotates RRSet order in response (the random number is taken from the query ID, for speed and thread safety). Default is no. .TP @@ -721,9 +913,11 @@ If yes, Unbound doesn't 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 no, because the DNS +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. +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 If true, disables the DNSSEC lameness check in the iterator. This check @@ -739,6 +933,12 @@ Setting this to "validator iterator" will turn on DNSSEC validation. The ordering of the modules is important. You must also set trust\-anchors for validation to be useful. +The default is "validator iterator". When the server is built with +EDNS client subnet support the default is "subnetcache validator iterator". +Most modules that need to be listed here have to be listed at the beginning +of the line. The cachedb 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. .TP .B trust\-anchor\-file: \fI File with trusted keys for validation. Both DS and DNSKEY entries can appear @@ -752,7 +952,8 @@ \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). +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 @@ -759,17 +960,23 @@ 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. +"" 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 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, +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 +Send RFC8145 key tag query after trust anchor priming. Default is on. +.TP +.B root\-key\-sentinel: \fI +Root key trust anchor sentinel. Default is on. +.TP .B dlv\-anchor\-file: \fI This option was used during early days DNSSEC deployment when no parent-side DS record registrations were easily available. Nowadays, it is best to have @@ -777,9 +984,9 @@ File with trusted keys for DLV (DNSSEC Lookaside Validation). Both DS and DNSKEY entries can be used in the file, in the same format as for \fItrust\-anchor\-file:\fR statements. Only one DLV can be configured, more -would be slow. The DLV configured is used as a root trusted DLV, this -means that it is a lookaside for the root. Default is "", or no dlv anchor file. -DLV is going to be decommissioned. Please do not use it any more. +would be slow. The DLV configured is used as a root trusted DLV, this +means that it is a lookaside for the root. Default is "", or no dlv anchor +file. DLV is going to be decommissioned. Please do not use it any more. .TP .B dlv\-anchor: \fI<"Resource Record"> Much like trust\-anchor, this is a DLV anchor with the DS or DNSKEY inline. @@ -791,17 +998,17 @@ domain secure with a DS record, such a DS record is then ignored. Also keys from DLV are ignored for the domain. 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 +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 +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 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 +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 @@ -831,7 +1038,7 @@ 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 +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 @@ -846,10 +1053,10 @@ .B val\-permissive\-mode: \fI 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 +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". +The default value is "no". .TP .B ignore\-cd\-flag: \fI Instruct unbound to ignore the CD flag from clients and refuse to @@ -859,12 +1066,28 @@ the clients, and then unbound provides them with DNSSEC protection. The default value is "no". .TP +.B serve\-expired: \fI +If enabled, unbound attempts to serve old responses from cache with a +TTL of 0 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 +Limit serving of expired responses to configured seconds after expiration. 0 +disables the limit. This option only applies when \fBserve\-expired\fR is +enabled. The default is 0. +.TP +.B serve\-expired\-ttl\-reset: \fI +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 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 500 4096 2500". 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 +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 @@ -899,7 +1122,7 @@ .TP .B key\-cache\-slabs: \fI 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 +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 @@ -907,7 +1130,7 @@ 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 +.B unblock\-lan\-zones: \fI 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, @@ -918,7 +1141,7 @@ 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 +.B insecure\-lan\-zones: \fI 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. @@ -927,7 +1150,7 @@ 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, -always_transparent, always_refuse, always_nxdomain, +inform_redirect, always_transparent, always_refuse, always_nxdomain, 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. @@ -950,7 +1173,7 @@ 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 +\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 @@ -958,7 +1181,7 @@ If no local\-zone is given local\-data causes a transparent zone to be created by default. .TP 10 -\h'5'\fItypetransparent\fR +\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 @@ -965,115 +1188,144 @@ 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 +\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 +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. 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. +\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 +\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'\fIalways_transparent\fR +\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'\fIalways_refuse\fR +\h'5'\fIalways_refuse\fR Like refuse, but ignores local data and refuses the query. .TP 10 -\h'5'\fIalways_nxdomain\fR +\h'5'\fIalways_nxdomain\fR Like static, but ignores local data and returns nxdomain for the query. .TP 10 -\h'5'\fInodefault\fR +\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 +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 onion 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. +The default zones are localhost, reverse 127.0.0.1 and ::1, 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 +\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." static +local\-zone: "localhost." redirect local\-data: "localhost. 10800 IN NS localhost." -local\-data: "localhost. 10800 IN +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 +\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 +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 +local\-data: "1.0.0.127.in\-addr.arpa. 10800 IN PTR localhost." .fi .TP 10 -\h'5'\fIreverse IPv6 loopback\fR +\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 + 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 + 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 + 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'\fIonion (RFC 7686)\fR +\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 +local\-data: "onion. 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 +\h'5'\fItest (RFC 2606)\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 2606)\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 +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, +\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 +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. @@ -1098,11 +1350,11 @@ .TP 5 .B local\-data: \fI"" 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 +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 +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, @@ -1118,10 +1370,11 @@ 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. +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 -Override the localzone type for queries from addresses matching netblock. +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. @@ -1158,12 +1411,13 @@ and enter the cache, whilst also mitigating the traffic flow by the factor given. .TP 5 -.B ratelimit\-for\-domain: \fI +.B ratelimit\-for\-domain: \fI 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 +.B ratelimit\-below\-domain: \fI 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 @@ -1170,6 +1424,50 @@ 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 +Enable global ratelimiting of queries accepted per ip address. +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 +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. +.TP 5 +.B ip\-ratelimit\-size: \fI +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 +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 +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 fast\-server\-permil: \fI +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 +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. .SS "Remote Control Options" In the .B remote\-control: @@ -1176,7 +1474,7 @@ 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 SSLv3 / TLSv1 security for the connection. The +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. @@ -1192,6 +1490,14 @@ 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 local socket is used. The local 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 The port number to listen on for IPv4 or IPv6 control interfaces, @@ -1200,11 +1506,9 @@ the server for the change to take effect. .TP 5 .B control\-use\-cert: \fI -Whether to require certificate authentication of control connections. -The default is "yes". -This should not be changed unless there are other mechanisms in place -to prevent untrusted users from accessing the remote control -interface. +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 Path to the server private key, by default unbound_server.key. @@ -1237,21 +1541,21 @@ .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 +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: -. -The unbound resolver can then access the data, without referring to the -public internet for it. +. +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 +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. +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 @@ -1270,8 +1574,8 @@ To use a nondefault port for DNS communication append '@' with the port number. .TP .B stub\-prime: \fI -This option is by default off. If enabled it performs NS set priming, -which is similar to root hints, where it starts using the list of nameservers +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 @@ -1280,6 +1584,17 @@ 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 +Enabled or disable whether the queries to this stub use TLS for transport. +Default is no. +.TP +.B stub\-ssl\-upstream: \fI +Alternate syntax for \fBstub\-tls\-upstream\fR. +.TP +.B stub\-no\-cache: \fI +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 @@ -1291,6 +1606,9 @@ 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). @@ -1304,11 +1622,143 @@ .B forward\-addr: \fI 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. +.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 -If enabled, a query is attempted without the forward 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. +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 +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 +Alternate syntax for \fBforward\-tls\-upstream\fR. +.TP +.B forward\-no\-cache: \fI +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 are processed after \fBlocal\-zones\fR and before +cache (\fBfor\-downstream:\fR \fIyes\fR), and when used in this manner +make unbound respond like an authority server. Authority zones are also +processed after cache, just before going to the network to fetch +information for recursion (\fBfor\-upstream:\fR \fIyes\fR), and when used +in this manner provide a local copy of an authority server that speeds up +lookups of that data. +.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 master (even if older), and if +fallback is enabled, the fallback activates to fetch from the upstream instead +of the SERVFAIL. +.TP +.B name: \fI +Name of the authority zone. +.TP +.B master: \fI +Where to download a copy of the zone from, with AXFR and IXFR. Multiple +masters can be specified. They are all tried if one fails. +With the "ip#name" notation a AXFR over TLS can be used. +.TP +.B url: \fI +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 +masters are listed, the masters 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 masters 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 +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 master, it first attempts that master. Otherwise +other masters are attempted. If there are no masters, but only urls, the +file is downloaded when notified. The masters from master: statements are +allowed notify by default. +.TP +.B fallback\-enabled: \fI +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 +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 +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 zonefile: \fI +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 master 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 +Name of the view. Must be unique. This name is used in access\-control\-view +elements. +.TP +.B local\-zone: \fI +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"" +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 +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 @@ -1319,9 +1769,15 @@ 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). +.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\fR -The script file to load. +The script file to load. .SS "DNS64 Module Options" .LP The dns64 module must be configured in the \fBmodule\-config:\fR "dns64 @@ -1335,6 +1791,291 @@ .B dns64\-synthall: \fI\fR Debug option, default no. If enabled, synthesize all AAAA records despite the presence of actual AAAA records. +.TP +.B dns64\-ignore\-aaaa: \fI\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 "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\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 +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\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\fR +Path to the time limited secret key file. This option may be specified multiple +times. +.TP +.B dnscrypt\-provider\-cert: \fI\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\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 +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 +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 +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 +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 whitelisted with 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 to the authority if present in the whitelist, or +\fBclient\-subnet\-always\-forward\fR is set to yes. In this case the lookup in +the regular cache is skipped. +.LP +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. +.TP +.B send\-client\-subnet: \fI\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\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\fR +Specify whether the ECS whitelist 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 whitelist check is skipped when the client +query contains an ECS record. Default is no. +.TP +.B max\-client\-subnet\-ipv6: \fI\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\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\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\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\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\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 the daemon 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\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\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\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\fR +Time to live maximum for A/AAAA cached records after calling the external hook. +Defaults to 3600. +.TP +.B ipsecmod-ignore-bogus: \fI\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\-whitelist: \fI\fR +Whitelist 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 whitelisted (default). +.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 +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. +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\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". +.P +The following +.B cachedb +otions are specific to the redis backend. +.TP +.B redis-server-host: \fI\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\fR +The TCP port number of the Redis server. +This option defaults to 6379. +.TP +.B redis-timeout: \fI\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. .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 @@ -1342,7 +2083,7 @@ 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. +which on BSD\-32bit tops out at 30\-40 Mb after heavy usage. .P .nf # example settings that reduce memory usage @@ -1383,12 +2124,12 @@ 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). +unbound log file. default is to log to +\fIsyslog\fR(3). .SH "SEE ALSO" -\fIunbound\fR(8), +\fIunbound\fR(8), \fIunbound\-checkconf\fR(8). .SH "AUTHORS" -.B Unbound +.B Unbound was written by NLnet Labs. Please see CREDITS file in the distribution for further details. --- contrib/unbound/doc/unbound.conf.5.in.orig +++ contrib/unbound/doc/unbound.conf.5.in @@ -1,4 +1,4 @@ -.TH "unbound.conf" "5" "Sep 27, 2016" "NLnet Labs" "unbound 1.5.10" +.TH "unbound.conf" "5" "May 19, 2020" "NLnet Labs" "unbound 1.10.1" .\" .\" unbound.conf.5 -- unbound.conf manual .\" @@ -16,13 +16,14 @@ .B unbound.conf is used to configure \fIunbound\fR(8). -The file format has attributes and values. Some attributes have attributes inside them. +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 +The utility \fIunbound\-checkconf\fR(8) can be used to check unbound.conf prior to usage. .SH "EXAMPLE" @@ -30,7 +31,7 @@ and start the server with: .P .nf - $ unbound \-c /etc/unbound/unbound.conf + $ unbound \-c /etc/unbound/unbound.conf .fi .P Most settings are the defaults. Stop the server with: @@ -49,7 +50,7 @@ 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/random /etc/unbound/dev/random + # 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. @@ -62,8 +63,10 @@ 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 its containing attributes, or a value. +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: @@ -71,7 +74,7 @@ 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 +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). .SS "Server Options" @@ -80,17 +83,17 @@ clause. .TP .B verbosity: \fI -The verbosity number, level 0 means no verbosity, only errors. Level 1 +The verbosity number, level 0 means no verbosity, only errors. Level 1 gives operational information. Level 2 gives detailed operational -information. 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. +information. 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 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, +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 @@ -99,7 +102,7 @@ the statistics counters after logging the statistics. Default is no. .TP .B extended\-statistics: \fI -If enabled, extended statistics are printed from \fIunbound\-control\fR(8). +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 @@ -112,7 +115,7 @@ .B interface: \fI 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 +Can be given multiple times to work on several interfaces. If none are given the default is to listen to localhost. 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 @@ -120,22 +123,22 @@ \fBport\fR) is used. .TP .B ip\-address: \fI -Same as interface: (for easy of compatibility with nsd.conf). +Same as interface: (for ease of compatibility with nsd.conf). .TP .B interface\-automatic: \fI -Detect source interface on UDP queries and copy them to replies. This +Detect source interface on UDP queries and copy them to replies. This feature is experimental, and needs support in your OS for particular socket options. Default value is no. .TP .B outgoing\-interface: \fI 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 +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 +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, @@ -151,12 +154,12 @@ 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 +ip \-6 addr add mynetblock/64 dev lo && +ip \-6 route add local mynetblock/64 dev lo .TP .B outgoing\-range: \fI -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 +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 @@ -163,18 +166,18 @@ .B outgoing\-port\-permit: \fI 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. +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 +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 -Do not permit unbound to open this port or range of ports for use to send +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. @@ -196,7 +199,7 @@ buffer size is determined by msg\-buffer\-size (both for TCP and UDP). Do not set higher than that value. Default is 4096 which is RFC recommended. If you have fragmentation reassembly problems, usually seen as timeouts, -then a value of 1480 can fix it. Setting to 512 bypasses even the most +then a value of 1472 can fix it. 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). @@ -204,13 +207,23 @@ .B max\-udp\-size: \fI Maximum UDP response size (not applied to TCP response). 65536 disables the udp response size maximum, and uses the choice from the client, always. -Suggested values are 512 to 4096. Default is 4096. +Suggested values are 512 to 4096. Default is 4096. .TP +.B stream\-wait\-size: \fI +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 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 +for DNS data, such as for huge resource records, will result in a SERVFAIL reply to the client. .TP .B msg\-cache\-size: \fI @@ -220,7 +233,7 @@ .TP .B msg\-cache\-slabs: \fI 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 +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 @@ -232,12 +245,12 @@ .TP .B jostle\-timeout: \fI 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 +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 +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 +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 @@ -252,6 +265,12 @@ the ID and remote IP of packets, and unwanted packets are added to the unwanted packet counter. .TP +.B unknown\-server\-time\-limit: \fI +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 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 @@ -277,22 +296,25 @@ .B so\-reuseport: \fI 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 no. 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), +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 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\-existant IP addresses that +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. +permissions on some systems. The option uses IP_BINDANY on FreeBSD systems +and SO_BINDANY on OpenBSD systems. .TP .B ip\-freebind: \fI If yes, then use IP_FREEBIND socket option on sockets where unbound @@ -308,15 +330,13 @@ .TP .B rrset\-cache\-slabs: \fI Number of slabs in the RRset cache. Slabs reduce lock contention by threads. -Must be set to a power of 2. +Must be set to a power of 2. .TP .B cache\-max\-ttl: \fI -Time to live maximum for RRsets and messages in the cache. Default is -86400 seconds (1 day). If the maximum kicks in, responses to clients -still get decrementing TTLs based on the original (larger) values. -When the internal TTL expires, the cache item has expired. +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. +trust (very large) TTL values. Downstream clients also see the lower TTL. .TP .B cache\-min\-ttl: \fI Time to live minimum for RRsets and messages in the cache. Default is 0. @@ -323,20 +343,21 @@ 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 +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 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 -Time to live for entries in the host cache. The host cache contains +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 of slabs in the infrastructure cache. Slabs reduce lock contention -by threads. Must be set to a power of 2. +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 of hosts for which information is cached. Default is 10000. @@ -372,7 +393,7 @@ .TP .B tcp\-mss: \fI Maximum segment size (MSS) of TCP socket on which the server responds -to queries. Value lower than common MSS on Ethernet +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 @@ -386,69 +407,186 @@ Default is system default MSS determined by interface MTU and negotiation between Unbound and other servers. .TP +.B tcp-idle-timeout: \fI\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 edns-tcp-keepalive: \fI\fR +Enable or disable EDNS TCP Keepalive. Default is no. +.TP +.B edns-tcp-keepalive-timeout: \fI\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 tcp\-upstream: \fI Enable or disable whether the upstream queries use TCP only for transport. Default is no. Useful in tunneling scenarios. .TP +.B udp\-upstream\-without\-downstream: \fI +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 +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 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 -Enabled or disable whether the upstream queries use SSL only for transport. -Default is no. Useful in tunneling scenarios. The SSL contains plain DNS in -TCP wireformat. The other server must support this (see \fBssl\-service\-key\fR). +Alternate syntax for \fBtls\-upstream\fR. If both are present in the config +file the last is used. .TP -.B ssl\-service-key: \fI -If enabled, the server provider SSL service on its TCP sockets. The clients -have to use ssl\-upstream: yes. The file is the private key for the TLS -session. The public certificate is in the ssl\-service\-pem file. Default -is "", turned off. Requires a restart (a reload is not enough) if changed, -because the private key is read while root permissions are held and before -chroot (if any). Normal DNS TCP service is not provided and gives errors, -this service is best run with a different \fBport:\fR config or \fI@port\fR -suffixes in the \fBinterface\fR config. +.B tls\-service\-key: \fI +If enabled, the server provides TLS service on the TCP ports marked +implicitly or explicitly for TLS service with tls\-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 do not provide +normal DNS TCP service. .TP -.B ssl\-service\-pem: \fI -The public key certificate pem file for the ssl service. Default is "", +.B ssl\-service\-key: \fI +Alternate syntax for \fBtls\-service\-key\fR. +.TP +.B tls\-service\-pem: \fI +The public key certificate pem file for the tls service. Default is "", turned off. .TP +.B ssl\-service\-pem: \fI +Alternate syntax for \fBtls\-service\-pem\fR. +.TP +.B tls\-port: \fI +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 -The port number on which to provide TCP SSL service, default 853, only -interfaces configured with that port number as @number get the SSL service. +Alternate syntax for \fBtls\-port\fR. .TP +.B tls\-cert\-bundle: \fI +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. +.TP +.B ssl\-cert\-bundle: \fI +Alternate syntax for \fBtls\-cert\-bundle\fR. +.TP +.B tls\-win\-cert: \fI +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. +.TP +.B tls\-additional\-port: \fI +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 +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 +Set the list of ciphers to allow when serving TLS. Use "" for defaults, +and that is the default. +.TP +.B tls\-ciphersuites: \fI +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 use\-systemd: \fI +Enable or disable systemd socket activation. +Default is no. +.TP .B do\-daemonize: \fI Enable or disable whether the unbound server forks into the background as -a daemon. Default is yes. +a daemon. Set the value to \fIno\fR when unbound runs as systemd service. +Default is yes. .TP +.B tcp\-connection\-limit: \fI +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 -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_snoop\fR, \fIdeny_non_local\fR or \fIrefuse_non_local\fR. +The netblock is given as an IP4 or IP6 address with /size appended for a +classless network block. The action can be \fIdeny\fR, \fIrefuse\fR, +\fIallow\fR, \fIallow_setrd\fR, \fIallow_snoop\fR, \fIdeny_non_local\fR or +\fIrefuse_non_local\fR. The most specific netblock match is used, if none match \fIdeny\fR is used. +The order of the access\-control statements therefore does not matter. .IP The action \fIdeny\fR stops queries from hosts from that netblock. .IP -The action \fIrefuse\fR stops queries too, but sends a DNS rcode REFUSED +The action \fIrefuse\fR stops queries too, but sends a DNS rcode REFUSED error message back. .IP -The action \fIallow\fR gives access to clients from that netblock. -It gives only access for recursion clients (which is +The action \fIallow\fR 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 +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 +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 action \fIallow_snoop\fR gives nonrecursive access too. This give -both recursive and non recursive access. The name \fIallow_snoop\fR refers +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 action \fIallow_snoop\fR 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 +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 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 +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 @@ -474,11 +612,14 @@ .B access\-control\-tag\-data: \fI <"resource record string"> Set redirect data for particular tag for given access control element. .TP +.B access\-control\-view: \fI +Set view for given access control element. +.TP .B chroot: \fI 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. +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: @@ -489,22 +630,25 @@ .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. +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/random (for entropy) +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. The default is -"@UNBOUND_CHROOT_DIR@". If you give "" no chroot is performed. +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 If given, after binding the port the user privileges are dropped. Default is -"@UNBOUND_USERNAME@". If you give username: "" no user change is performed. +"@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 +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 @@ -516,21 +660,28 @@ .TP .B logfile: \fI If "" is given, logging goes to stderr, or nowhere once daemonized. -The logfile is appended to, in the following format: +The logfile is appended to, in the following format: .nf -[seconds since 1970] unbound[pid:tid]: type: message. +[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 +The logfile is reopened (for append) when the config file is reread, on SIGHUP. .TP .B use\-syslog: \fI -Sets unbound to send log messages to the syslogd, using -\fIsyslog\fR(3). +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 +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 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 @@ -542,21 +693,43 @@ lines which makes the server (significantly) slower. Odd (nonprintable) characters in names are printed as '?'. .TP +.B log\-replies: \fI +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 +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 +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 +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 -The process id is written to the file. Default is "@UNBOUND_PIDFILE@". +The process id is written to the file. Default is "@UNBOUND_PIDFILE@". So, .nf -kill \-HUP `cat @UNBOUND_PIDFILE@` +kill \-HUP `cat @UNBOUND_PIDFILE@` .fi triggers a reload, .nf -kill \-TERM `cat @UNBOUND_PIDFILE@` +kill \-TERM `cat @UNBOUND_PIDFILE@` .fi gracefully terminates. .TP .B root\-hints: \fI 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 +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 @@ -574,25 +747,28 @@ Set the version to report. If set to "", the default, then the package version is returned. .TP +.B hide\-trustanchor: \fI +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. +dependency depth. .IP The number of values determines the maximum dependency depth -that unbound will pursue in answering a query. +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. +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 +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 Very small EDNS buffer sizes from queries are ignored. Default is off, since -it is legal protocol wise to send these, and unbound tries to give very +it is legal protocol wise to send these, and unbound tries to give very small answers to these queries, where possible. .TP .B harden\-large\-queries: \fI @@ -601,35 +777,36 @@ payload is very large. .TP .B harden\-glue: \fI -Will trust glue only if it is within the servers authority. Default is on. +Will trust glue only if it is within the servers authority. Default is yes. .TP .B harden\-dnssec\-stripped: \fI 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 on. +(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 -From draft\-vixie\-dnsext\-resimprove, returns nxdomain to queries for a name +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 off. -Currently, draft\-ietf\-dnsop\-nxdomain\-cut promotes this technique. +have DNSSEC. Default is yes. +The nxdomain must be secure, this means nsec3 with optout is insufficient. .TP .B harden\-referral\-path: \fI 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 +NS sets and the nameserver addresses that are encountered on the referral path to the answer. -Default off, because it burdens the authority servers, and it is +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 @@ -644,9 +821,9 @@ .TP .B use\-caps\-for\-id: \fI 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 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\-whitelist: \fI @@ -657,10 +834,23 @@ .TP .B qname\-minimisation: \fI Send minimum amount of information to upstream servers to enhance privacy. -Only sent minimum required labels of the QNAME and set QTYPE to NS when -possible. Best effort approach, full QNAME and original QTYPE will be sent when -upstream replies with a RCODE other than NOERROR. Default is off. +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 +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 off. +.TP +.B aggressive\-nsec: \fI +Aggressive NSEC uses the DNSSEC NSEC chain to synthesize NXDOMAIN +and other denials, using information from previous NXDOMAINs answers. +Default is no. It helps to reduce the query rate towards targets that get +a very high nonexistent name lookup rate. +.TP .B private\-address: \fI Give IPv4 of IPv6 addresses or classless subnets. These are addresses on your private network, and are not allowed to be returned for @@ -682,7 +872,7 @@ .TP .B private\-domain: \fI Allow this domain, and all its subdomains to contain private addresses. -Give multiple times to allow multiple domain names to contain private +Give multiple times to allow multiple domain names to contain private addresses. Default is none. .TP .B unwanted\-reply\-threshold: \fI @@ -693,7 +883,7 @@ is suggested. Default is 0 (turned off). .TP .B do\-not\-query\-address: \fI -Do not query the given IP address. Can be IP4 or IP6. Append /num to +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 @@ -708,12 +898,18 @@ 10 percent more traffic and load on the machine, but popular items do not expire from the cache. .TP -.B prefetch-key: \fI +.B prefetch\-key: \fI 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 rrset-roundrobin: \fI +.B deny\-any: \fI +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 If yes, Unbound rotates RRSet order in response (the random number is taken from the query ID, for speed and thread safety). Default is no. .TP @@ -721,9 +917,11 @@ If yes, Unbound doesn't 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 no, because the DNS +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. +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 If true, disables the DNSSEC lameness check in the iterator. This check @@ -739,6 +937,12 @@ Setting this to "validator iterator" will turn on DNSSEC validation. The ordering of the modules is important. You must also set trust\-anchors for validation to be useful. +The default is "validator iterator". When the server is built with +EDNS client subnet support the default is "subnetcache validator iterator". +Most modules that need to be listed here have to be listed at the beginning +of the line. The cachedb 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. .TP .B trust\-anchor\-file: \fI File with trusted keys for validation. Both DS and DNSKEY entries can appear @@ -747,12 +951,13 @@ .TP .B auto\-trust\-anchor\-file: \fI File with trust anchor for one zone, which is tracked with RFC5011 probes. -The probes are several times per month, thus the machine must be online +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). +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 @@ -759,17 +964,23 @@ 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. +"" 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 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, +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 +Send RFC8145 key tag query after trust anchor priming. Default is yes. +.TP +.B root\-key\-sentinel: \fI +Root key trust anchor sentinel. Default is yes. +.TP .B dlv\-anchor\-file: \fI This option was used during early days DNSSEC deployment when no parent-side DS record registrations were easily available. Nowadays, it is best to have @@ -777,9 +988,9 @@ File with trusted keys for DLV (DNSSEC Lookaside Validation). Both DS and DNSKEY entries can be used in the file, in the same format as for \fItrust\-anchor\-file:\fR statements. Only one DLV can be configured, more -would be slow. The DLV configured is used as a root trusted DLV, this -means that it is a lookaside for the root. Default is "", or no dlv anchor file. -DLV is going to be decommissioned. Please do not use it any more. +would be slow. The DLV configured is used as a root trusted DLV, this +means that it is a lookaside for the root. Default is "", or no dlv anchor +file. DLV is going to be decommissioned. Please do not use it any more. .TP .B dlv\-anchor: \fI<"Resource Record"> Much like trust\-anchor, this is a DLV anchor with the DS or DNSKEY inline. @@ -791,17 +1002,17 @@ domain secure with a DS record, such a DS record is then ignored. Also keys from DLV are ignored for the domain. 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 +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 +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 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 +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 @@ -831,7 +1042,7 @@ 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 +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 @@ -846,10 +1057,10 @@ .B val\-permissive\-mode: \fI 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 +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". +The default value is "no". .TP .B ignore\-cd\-flag: \fI Instruct unbound to ignore the CD flag from clients and refuse to @@ -859,12 +1070,43 @@ the clients, and then unbound provides them with DNSSEC protection. The default value is "no". .TP +.B serve\-expired: \fI +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 +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 draft-ietf-dnsop-serve-stale-10 is between +86400 (1 day) and 259200 (3 days). The default is 0. +.TP +.B serve\-expired\-ttl\-reset: \fI +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 +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 (draft-ietf-dnsop-serve-stale-10). The default is 30. +.TP +.B serve\-expired\-client\-timeout: \fI +Time in milliseconds before replying to the client with expired data. This +essentially enables the serve-stale behavior as specified in +draft-ietf-dnsop-serve-stale-10 that first tries to resolve before immediately +responding with expired data. A recommended value per +draft-ietf-dnsop-serve-stale-10 is 1800. Setting this to 0 will disable this +behavior. Default is 0. +.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 500 4096 2500". 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 +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 @@ -899,7 +1141,7 @@ .TP .B key\-cache\-slabs: \fI 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 +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 @@ -907,7 +1149,7 @@ 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 +.B unblock\-lan\-zones: \fI 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, @@ -918,7 +1160,7 @@ 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 +.B insecure\-lan\-zones: \fI 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. @@ -927,7 +1169,7 @@ 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, -always_transparent, always_refuse, always_nxdomain, +inform_redirect, always_transparent, always_refuse, always_nxdomain, 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. @@ -950,7 +1192,7 @@ 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 +\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 @@ -958,7 +1200,7 @@ If no local\-zone is given local\-data causes a transparent zone to be created by default. .TP 10 -\h'5'\fItypetransparent\fR +\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 @@ -965,115 +1207,144 @@ 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 +\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 +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. 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. +\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 +\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'\fIalways_transparent\fR +\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'\fIalways_refuse\fR +\h'5'\fIalways_refuse\fR Like refuse, but ignores local data and refuses the query. .TP 10 -\h'5'\fIalways_nxdomain\fR +\h'5'\fIalways_nxdomain\fR Like static, but ignores local data and returns nxdomain for the query. .TP 10 -\h'5'\fInodefault\fR +\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 +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 onion 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. +The default zones are localhost, reverse 127.0.0.1 and ::1, 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 +\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." static +local\-zone: "localhost." redirect local\-data: "localhost. 10800 IN NS localhost." -local\-data: "localhost. 10800 IN +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 +\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 +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 +local\-data: "1.0.0.127.in\-addr.arpa. 10800 IN PTR localhost." .fi .TP 10 -\h'5'\fIreverse IPv6 loopback\fR +\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 + 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 + 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 + 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'\fIonion (RFC 7686)\fR +\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 +local\-data: "onion. 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 +\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 +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, +\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 +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. @@ -1098,11 +1369,11 @@ .TP 5 .B local\-data: \fI"" 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 +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 +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, @@ -1118,10 +1389,11 @@ 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. +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 -Override the localzone type for queries from addresses matching netblock. +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. @@ -1158,12 +1430,13 @@ and enter the cache, whilst also mitigating the traffic flow by the factor given. .TP 5 -.B ratelimit\-for\-domain: \fI +.B ratelimit\-for\-domain: \fI 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 +.B ratelimit\-below\-domain: \fI 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 @@ -1170,6 +1443,50 @@ 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 +Enable global ratelimiting of queries accepted per ip address. +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 +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. +.TP 5 +.B ip\-ratelimit\-size: \fI +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 +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 +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 fast\-server\-permil: \fI +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 +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. .SS "Remote Control Options" In the .B remote\-control: @@ -1176,7 +1493,7 @@ 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 SSLv3 / TLSv1 security for the connection. The +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. @@ -1192,6 +1509,14 @@ 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 local socket is used. The local 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 The port number to listen on for IPv4 or IPv6 control interfaces, @@ -1200,11 +1525,9 @@ the server for the change to take effect. .TP 5 .B control\-use\-cert: \fI -Whether to require certificate authentication of control connections. -The default is "yes". -This should not be changed unless there are other mechanisms in place -to prevent untrusted users from accessing the remote control -interface. +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 Path to the server private key, by default unbound_server.key. @@ -1237,21 +1560,21 @@ .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 +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: -. -The unbound resolver can then access the data, without referring to the -public internet for it. +. +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 +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. +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 @@ -1270,8 +1593,8 @@ To use a nondefault port for DNS communication append '@' with the port number. .TP .B stub\-prime: \fI -This option is by default off. If enabled it performs NS set priming, -which is similar to root hints, where it starts using the list of nameservers +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 @@ -1280,6 +1603,17 @@ 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 +Enabled or disable whether the queries to this stub use TLS for transport. +Default is no. +.TP +.B stub\-ssl\-upstream: \fI +Alternate syntax for \fBstub\-tls\-upstream\fR. +.TP +.B stub\-no\-cache: \fI +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 @@ -1291,6 +1625,9 @@ 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). @@ -1304,11 +1641,153 @@ .B forward\-addr: \fI 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. +.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 -If enabled, a query is attempted without the forward 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. +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 +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 +Alternate syntax for \fBforward\-tls\-upstream\fR. +.TP +.B forward\-no\-cache: \fI +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 are processed after \fBlocal\-zones\fR and before +cache (\fBfor\-downstream:\fR \fIyes\fR), and when used in this manner +make unbound respond like an authority server. Authority zones are also +processed after cache, just before going to the network to fetch +information for recursion (\fBfor\-upstream:\fR \fIyes\fR), and when used +in this manner provide a local copy of an authority server that speeds up +lookups of that data. +.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 master (even if older), and if +fallback is enabled, the fallback activates to fetch from the upstream instead +of the SERVFAIL. +.TP +.B name: \fI +Name of the authority zone. +.TP +.B master: \fI +Where to download a copy of the zone from, with AXFR and IXFR. Multiple +masters can be specified. They are all tried if one fails. +With the "ip#name" notation a AXFR over TLS can be used. +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 url: \fI +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 +masters are listed, the masters 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 masters 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-master/example.com.zone", with an explicit IP address. +.TP +.B allow\-notify: \fI +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 master, it first attempts that master. Otherwise +other masters are attempted. If there are no masters, but only urls, the +file is downloaded when notified. The masters from master: statements are +allowed notify by default. +.TP +.B fallback\-enabled: \fI +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 +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 +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 zonefile: \fI +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 master 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 +Name of the view. Must be unique. This name is used in access\-control\-view +elements. +.TP +.B local\-zone: \fI +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"" +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 +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 @@ -1318,10 +1797,18 @@ 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). +(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\fR -The script file to load. +The script file to load. Repeat this option for every python 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 @@ -1335,6 +1822,362 @@ .B dns64\-synthall: \fI\fR Debug option, default no. If enabled, synthesize all AAAA records despite the presence of actual AAAA records. +.TP +.B dns64\-ignore\-aaaa: \fI\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 "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\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 +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\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\fR +Path to the time limited secret key file. This option may be specified multiple +times. +.TP +.B dnscrypt\-provider\-cert: \fI\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\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 +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 +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 +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 +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 whitelisted with 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 to the authority if present in the whitelist, or +\fBclient\-subnet\-always\-forward\fR is set to yes. In this case the lookup in +the regular cache is skipped. +.LP +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. +.TP +.B send\-client\-subnet: \fI\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\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\fR +Specify whether the ECS whitelist 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 whitelist check is skipped when the client +query contains an ECS record. Default is no. +.TP +.B max\-client\-subnet\-ipv6: \fI\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\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\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\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\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\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 the daemon 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\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\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\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\fR +Time to live maximum for A/AAAA cached records after calling the external hook. +Defaults to 3600. +.TP +.B ipsecmod-ignore-bogus: \fI\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\-whitelist: \fI\fR +Whitelist 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 whitelisted (default). +.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. +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\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". +.P +The following +.B cachedb +otions are specific to the redis backend. +.TP +.B redis-server-host: \fI\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\fR +The TCP port number of the Redis server. +This option defaults to 6379. +.TP +.B redis-timeout: \fI\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. +.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 +Only the QNAME and Response IP Address triggers are supported. The supported RPZ +actions are: NXDOMAIN, NODATA, PASSTHRU, DROP and Local Data. RPZ QNAME triggers +are applied after +\fBlocal-zones\fR and before \fBauth-zones\fR. +.TP +.B name: \fI +Name of the authority zone. +.TP +.B master: \fI +Where to download a copy of the zone from, with AXFR and IXFR. Multiple +masters can be specified. They are all tried if one fails. +.TP +.B url: \fI +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 +masters are listed, the masters 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 masters 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 +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 master, it first attempts that master. Otherwise +other masters are attempted. If there are no masters, but only urls, the +file is downloaded when notified. The masters from master: statements are +allowed notify by default. +.TP +.B zonefile: \fI +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 master servers). +.TP +.B rpz\-action\-override: \fI +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 +The CNAME target domain to use if the cname action is configured for +\fBrpz\-action\-override\fR. +.TP +.B rpz\-log: \fI +Log all applied RPZ actions for this RPZ zone. Default is no. +.TP +.B rpz\-log\-name: \fI +Specify a string to be part of the log line, for easy referencing. +.TP +.B tags: \fI +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 @@ -1342,7 +2185,7 @@ 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. +which on BSD\-32bit tops out at 30\-40 Mb after heavy usage. .P .nf # example settings that reduce memory usage @@ -1383,12 +2226,12 @@ 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). +unbound log file. default is to log to +\fIsyslog\fR(3). .SH "SEE ALSO" -\fIunbound\fR(8), +\fIunbound\fR(8), \fIunbound\-checkconf\fR(8). .SH "AUTHORS" -.B Unbound +.B Unbound was written by NLnet Labs. Please see CREDITS file in the distribution for further details. --- contrib/unbound/doc/unbound.doxygen.orig +++ contrib/unbound/doc/unbound.doxygen @@ -612,18 +612,24 @@ EXCLUDE = ./build \ ./compat \ + ./contrib \ util/configparser.c \ util/configparser.h \ util/configlexer.c \ util/locks.h \ + pythonmod/doc \ + pythonmod/examples \ pythonmod/unboundmodule.py \ pythonmod/interface.h \ - pythonmod/examples/resgen.py \ - pythonmod/examples/resmod.py \ - pythonmod/examples/resip.py \ + pythonmod/ubmodule-msg.py \ + pythonmod/ubmodule-tst.py \ + unboundmodule.py \ libunbound/python/unbound.py \ libunbound/python/libunbound_wrap.c \ + libunbound/python/doc \ + libunbound/python/examples \ ./ldns-src \ + README.md \ doc/control_proto_spec.txt \ doc/requirements.txt --- contrib/unbound/edns-subnet/addrtree.c.orig +++ contrib/unbound/edns-subnet/addrtree.c @@ -0,0 +1,532 @@ +/* + * edns-subnet/addrtree.c -- radix tree for edns subnet cache. + * + * 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 + * addrtree -- radix tree for edns subnet cache. + */ + +#include "config.h" +#include "util/log.h" +#include "util/data/msgreply.h" +#include "util/module.h" +#include "addrtree.h" + +/** + * Create a new edge + * @param node: Child node this edge will connect to. + * @param addr: full key to this edge. + * @param addrlen: length of relevant part of key for this node + * @param parent_node: Parent node for node + * @param parent_index: Index of child node at parent node + * @return new addredge or NULL on failure + */ +static struct addredge * +edge_create(struct addrnode *node, const addrkey_t *addr, + addrlen_t addrlen, struct addrnode *parent_node, int parent_index) +{ + size_t n; + struct addredge *edge = (struct addredge *)malloc( sizeof (*edge) ); + if (!edge) + return NULL; + edge->node = node; + edge->len = addrlen; + edge->parent_index = parent_index; + edge->parent_node = parent_node; + /* ceil() */ + n = (size_t)((addrlen / KEYWIDTH) + ((addrlen % KEYWIDTH != 0)?1:0)); + edge->str = (addrkey_t *)calloc(n, sizeof (addrkey_t)); + if (!edge->str) { + free(edge); + return NULL; + } + memcpy(edge->str, addr, n * sizeof (addrkey_t)); + /* Only manipulate other objects after successful alloc */ + node->parent_edge = edge; + log_assert(parent_node->edge[parent_index] == NULL); + parent_node->edge[parent_index] = edge; + return edge; +} + +/** + * Create a new node + * @param tree: Tree the node lives in. + * @param elem: Element to store at this node + * @param scope: Scopemask from server reply + * @param ttl: Element is valid up to this time. Absolute, seconds + * @return new addrnode or NULL on failure + */ +static struct addrnode * +node_create(struct addrtree *tree, void *elem, addrlen_t scope, + time_t ttl) +{ + struct addrnode* node = (struct addrnode *)malloc( sizeof (*node) ); + if (!node) + return NULL; + node->elem = elem; + tree->node_count++; + node->scope = scope; + node->ttl = ttl; + node->edge[0] = NULL; + node->edge[1] = NULL; + node->parent_edge = NULL; + node->next = NULL; + node->prev = NULL; + return node; +} + +/** Size in bytes of node and parent edge + * @param tree: tree the node lives in + * @param n: node which size must be calculated + * @return size in bytes. + **/ +static inline size_t +node_size(const struct addrtree *tree, const struct addrnode *n) +{ + return sizeof *n + sizeof *n->parent_edge + n->parent_edge->len + + (n->elem?tree->sizefunc(n->elem):0); +} + +struct addrtree * +addrtree_create(addrlen_t max_depth, void (*delfunc)(void *, void *), + size_t (*sizefunc)(void *), void *env, uint32_t max_node_count) +{ + struct addrtree *tree; + log_assert(delfunc != NULL); + log_assert(sizefunc != NULL); + tree = (struct addrtree *)calloc(1, sizeof(*tree)); + if (!tree) + return NULL; + tree->root = node_create(tree, NULL, 0, 0); + if (!tree->root) { + free(tree); + return NULL; + } + tree->size_bytes = sizeof *tree + sizeof *tree->root; + tree->first = NULL; + tree->last = NULL; + tree->max_depth = max_depth; + tree->delfunc = delfunc; + tree->sizefunc = sizefunc; + tree->env = env; + tree->node_count = 0; + tree->max_node_count = max_node_count; + return tree; +} + +/** + * Scrub a node clean of elem + * @param tree: tree the node lives in. + * @param node: node to be cleaned. + */ +static void +clean_node(struct addrtree *tree, struct addrnode *node) +{ + if (!node->elem) return; + tree->size_bytes -= tree->sizefunc(node->elem); + tree->delfunc(tree->env, node->elem); + node->elem = NULL; +} + +/** Remove specified node from LRU list */ +static void +lru_pop(struct addrtree *tree, struct addrnode *node) +{ + if (node == tree->first) { + if (!node->next) { /* it is the last as well */ + tree->first = NULL; + tree->last = NULL; + } else { + tree->first = node->next; + tree->first->prev = NULL; + } + } else if (node == tree->last) { /* but not the first */ + tree->last = node->prev; + tree->last->next = NULL; + } else { + node->prev->next = node->next; + node->next->prev = node->prev; + } +} + +/** Add node to LRU list as most recently used. */ +static void +lru_push(struct addrtree *tree, struct addrnode *node) +{ + if (!tree->first) { + tree->first = node; + node->prev = NULL; + } else { + tree->last->next = node; + node->prev = tree->last; + } + tree->last = node; + node->next = NULL; +} + +/** Move node to the end of LRU list */ +static void +lru_update(struct addrtree *tree, struct addrnode *node) +{ + if (tree->root == node) return; + lru_pop(tree, node); + lru_push(tree, node); +} + +/** + * Purge a node from the tree. Node and parentedge are cleaned and + * free'd. + * @param tree: Tree the node lives in. + * @param node: Node to be freed + */ +static void +purge_node(struct addrtree *tree, struct addrnode *node) +{ + struct addredge *parent_edge, *child_edge = NULL; + int index; + int keep = node->edge[0] && node->edge[1]; + + clean_node(tree, node); + parent_edge = node->parent_edge; + if (keep || !parent_edge) return; + tree->node_count--; + index = parent_edge->parent_index; + child_edge = node->edge[!node->edge[0]]; + if (child_edge) { + child_edge->parent_node = parent_edge->parent_node; + child_edge->parent_index = index; + } + parent_edge->parent_node->edge[index] = child_edge; + tree->size_bytes -= node_size(tree, node); + free(parent_edge->str); + free(parent_edge); + lru_pop(tree, node); + free(node); +} + +/** + * If a limit is set remove old nodes while above that limit. + * @param tree: Tree to be cleaned up. + */ +static void +lru_cleanup(struct addrtree *tree) +{ + struct addrnode *n, *p; + int children; + if (tree->max_node_count == 0) return; + while (tree->node_count > tree->max_node_count) { + n = tree->first; + if (!n) break; + children = (n->edge[0] != NULL) + (n->edge[1] != NULL); + /** Don't remove this node, it is either the root or we can't + * do without it because it has 2 children */ + if (children == 2 || !n->parent_edge) { + lru_update(tree, n); + continue; + } + p = n->parent_edge->parent_node; + purge_node(tree, n); + /** Since we removed n, n's parent p is eligible for deletion + * if it is not the root node, caries no data and has only 1 + * child */ + children = (p->edge[0] != NULL) + (p->edge[1] != NULL); + if (!p->elem && children == 1 && p->parent_edge) { + purge_node(tree, p); + } + } +} + +inline size_t +addrtree_size(const struct addrtree *tree) +{ + return tree?tree->size_bytes:0; +} + +void addrtree_delete(struct addrtree *tree) +{ + struct addrnode *n; + if (!tree) return; + clean_node(tree, tree->root); + free(tree->root); + tree->size_bytes -= sizeof(struct addrnode); + while ((n = tree->first)) { + tree->first = n->next; + clean_node(tree, n); + tree->size_bytes -= node_size(tree, n); + free(n->parent_edge->str); + free(n->parent_edge); + free(n); + } + log_assert(sizeof *tree == addrtree_size(tree)); + free(tree); +} + +/** + * Get N'th bit from address + * @param addr: address to inspect + * @param addrlen: length of addr in bits + * @param n: index of bit to test. Must be in range [0, addrlen) + * @return 0 or 1 + */ +static int +getbit(const addrkey_t *addr, addrlen_t addrlen, addrlen_t n) +{ + log_assert(addrlen > n); + (void)addrlen; + return (int)(addr[n/KEYWIDTH]>>((KEYWIDTH-1)-(n%KEYWIDTH))) & 1; +} + +/** + * Test for equality on N'th bit. + * @return 0 for equal, 1 otherwise + */ +static inline int +cmpbit(const addrkey_t *key1, const addrkey_t *key2, addrlen_t n) +{ + addrkey_t c = key1[n/KEYWIDTH] ^ key2[n/KEYWIDTH]; + return (int)(c >> ((KEYWIDTH-1)-(n%KEYWIDTH))) & 1; +} + +/** + * Common number of bits in prefix. + * @param s1: first prefix. + * @param l1: length of s1 in bits. + * @param s2: second prefix. + * @param l2: length of s2 in bits. + * @param skip: nr of bits already checked. + * @return common number of bits. + */ +static addrlen_t +bits_common(const addrkey_t *s1, addrlen_t l1, + const addrkey_t *s2, addrlen_t l2, addrlen_t skip) +{ + addrlen_t len, i; + len = (l1 > l2) ? l2 : l1; + log_assert(skip < len); + for (i = skip; i < len; i++) { + if (cmpbit(s1, s2, i)) return i; + } + return len; +} + +/** + * Tests if s1 is a substring of s2 + * @param s1: first prefix. + * @param l1: length of s1 in bits. + * @param s2: second prefix. + * @param l2: length of s2 in bits. + * @param skip: nr of bits already checked. + * @return 1 for substring, 0 otherwise + */ +static int +issub(const addrkey_t *s1, addrlen_t l1, + const addrkey_t *s2, addrlen_t l2, addrlen_t skip) +{ + return bits_common(s1, l1, s2, l2, skip) == l1; +} + +void +addrtree_insert(struct addrtree *tree, const addrkey_t *addr, + addrlen_t sourcemask, addrlen_t scope, void *elem, time_t ttl, + time_t now) +{ + struct addrnode *newnode, *node; + struct addredge *edge; + int index; + addrlen_t common, depth; + + node = tree->root; + log_assert(node != NULL); + + /* Protect our cache against too much fine-grained data */ + if (tree->max_depth < scope) scope = tree->max_depth; + /* Server answer was less specific than question */ + if (scope < sourcemask) sourcemask = scope; + + depth = 0; + while (1) { + log_assert(depth <= sourcemask); + /* Case 1: update existing node */ + if (depth == sourcemask) { + /* update this node's scope and data */ + clean_node(tree, node); + node->ttl = ttl; + node->elem = elem; + node->scope = scope; + tree->size_bytes += tree->sizefunc(elem); + return; + } + index = getbit(addr, sourcemask, depth); + /* Get an edge to an unexpired node */ + edge = node->edge[index]; + while (edge) { + /* Purge all expired nodes on path */ + if (!edge->node->elem || edge->node->ttl >= now) + break; + purge_node(tree, edge->node); + edge = node->edge[index]; + } + /* Case 2: New leafnode */ + if (!edge) { + newnode = node_create(tree, elem, scope, ttl); + if (!newnode) return; + if (!edge_create(newnode, addr, sourcemask, node, + index)) { + clean_node(tree, newnode); + tree->node_count--; + free(newnode); + return; + } + tree->size_bytes += node_size(tree, newnode); + lru_push(tree, newnode); + lru_cleanup(tree); + return; + } + /* Case 3: Traverse edge */ + common = bits_common(edge->str, edge->len, addr, sourcemask, + depth); + if (common == edge->len) { + /* We update the scope of intermediate nodes. Apparently + * the * authority changed its mind. If we would not do + * this we might not be able to reach our new node. */ + node->scope = scope; + depth = edge->len; + node = edge->node; + continue; + } + /* Case 4: split. */ + if (!(newnode = node_create(tree, NULL, 0, 0))) + return; + node->edge[index] = NULL; + if (!edge_create(newnode, addr, common, node, index)) { + node->edge[index] = edge; + clean_node(tree, newnode); + tree->node_count--; + free(newnode); + return; + } + lru_push(tree, newnode); + /* connect existing child to our new node */ + index = getbit(edge->str, edge->len, common); + newnode->edge[index] = edge; + edge->parent_node = newnode; + edge->parent_index = (int)index; + + if (common == sourcemask) { + /* Data is stored in the node */ + newnode->elem = elem; + newnode->scope = scope; + newnode->ttl = ttl; + } + + tree->size_bytes += node_size(tree, newnode); + + if (common != sourcemask) { + /* Data is stored in other leafnode */ + node = newnode; + newnode = node_create(tree, elem, scope, ttl); + if (!edge_create(newnode, addr, sourcemask, node, + index^1)) { + clean_node(tree, newnode); + tree->node_count--; + free(newnode); + return; + } + tree->size_bytes += node_size(tree, newnode); + lru_push(tree, newnode); + } + lru_cleanup(tree); + return; + } +} + +struct addrnode * +addrtree_find(struct addrtree *tree, const addrkey_t *addr, + addrlen_t sourcemask, time_t now) +{ + struct addrnode *node = tree->root; + struct addredge *edge = NULL; + addrlen_t depth = 0; + + log_assert(node != NULL); + while (1) { + /* Current node more specific then question. */ + log_assert(depth <= sourcemask); + /* does this node have data? if yes, see if we have a match */ + if (node->elem && node->ttl >= now) { + /* saved at wrong depth */; + log_assert(node->scope >= depth); + if (depth == node->scope || + (node->scope > sourcemask && + depth == sourcemask)) { + /* Authority indicates it does not have a more + * precise answer or we cannot ask a more + * specific question. */ + lru_update(tree, node); + return node; + } + } + /* This is our final depth, but we haven't found an answer. */ + if (depth == sourcemask) + return NULL; + /* Find an edge to traverse */ + edge = node->edge[getbit(addr, sourcemask, depth)]; + if (!edge || !edge->node) + return NULL; + if (edge->len > sourcemask ) + return NULL; + if (!issub(edge->str, edge->len, addr, sourcemask, depth)) + return NULL; + log_assert(depth < edge->len); + depth = edge->len; + node = edge->node; + } +} + +/** Wrappers for static functions to unit test */ +int unittest_wrapper_addrtree_cmpbit(const addrkey_t *key1, + const addrkey_t *key2, addrlen_t n) { + return cmpbit(key1, key2, n); +} +addrlen_t unittest_wrapper_addrtree_bits_common(const addrkey_t *s1, + addrlen_t l1, const addrkey_t *s2, addrlen_t l2, addrlen_t skip) { + return bits_common(s1, l1, s2, l2, skip); +} +int unittest_wrapper_addrtree_getbit(const addrkey_t *addr, + addrlen_t addrlen, addrlen_t n) { + return getbit(addr, addrlen, n); +} +int unittest_wrapper_addrtree_issub(const addrkey_t *s1, addrlen_t l1, + const addrkey_t *s2, addrlen_t l2, addrlen_t skip) { + return issub(s1, l1, s2, l2, skip); +} --- contrib/unbound/edns-subnet/addrtree.h.orig +++ contrib/unbound/edns-subnet/addrtree.h @@ -0,0 +1,187 @@ +/* + * edns-subnet/addrtree.h -- radix tree for edns subnet cache. + * + * 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 + * The addrtree is a radix tree designed for edns subnet. Most notable + * is the addition of 'scope' to a node. Scope is only relevant for + * nodes with elem set, it indicates the number of bits the authority + * desires. + * + * For retrieving data one needs an address and address length + * (sourcemask). While traversing the tree the first matching node is + * returned. A node matches when + * node.scope<=sourcemask && node.elem!=NULL + * (This is the most specific answer the authority has.) + * or + * node.sourcemask==sourcemask && node.elem!=NULL + * (This is the most specific question the client can ask.) + * + * Insertion needs an address, sourcemask and scope. The length of the + * address is capped by min(sourcemask, scope). While traversing the + * tree the scope of all visited nodes is updated. This ensures we are + * always able to find the most specific answer available. + */ + +#ifndef ADDRTREE_H +#define ADDRTREE_H + +typedef uint8_t addrlen_t; +typedef uint8_t addrkey_t; +#define KEYWIDTH 8 + +struct addrtree { + struct addrnode *root; + /** Number of elements in the tree (not always equal to number of + * nodes) */ + uint32_t node_count; + /** Maximum number of allowed nodes, will be enforced by LRU list. + * Excluding the root node, 0 for unlimited */ + uint32_t max_node_count; + /** Size of tree in bytes */ + size_t size_bytes; + /** Maximum prefix length we are willing to cache. */ + addrlen_t max_depth; + /** External function to delete elem. Called as + * delfunc(addrnode->elem, addrtree->env) */ + void (*delfunc)(void *, void *); + /** Environment for delfunc */ + void *env; + /** External function returning size of elem. Called as + * sizefunc(addrnode->elem) */ + size_t (*sizefunc)(void *); + /** first node in LRU list, first candidate to go */ + struct addrnode* first; + /** last node in LRU list, last candidate to go */ + struct addrnode *last; +}; + +struct addrnode { + /** Payload of node, may be NULL */ + void *elem; + /** Abs time in seconds in which elem is meaningful */ + time_t ttl; + /** Number of significant bits in address. */ + addrlen_t scope; + /** A node can have 0-2 edges, set to NULL for unused */ + struct addredge *edge[2]; + /** edge between this node and parent */ + struct addredge *parent_edge; + /** previous node in LRU list */ + struct addrnode *prev; + /** next node in LRU list */ + struct addrnode *next; +}; + +struct addredge { + /** address of connected node */ + addrkey_t *str; + /** length in bits of str */ + addrlen_t len; + /** child node this edge is connected to */ + struct addrnode *node; + /** Parent node this ege is connected to */ + struct addrnode *parent_node; + /** Index of this edge in parent_node */ + int parent_index; +}; + +/** + * Size of tree in bytes. + * @param tree: Tree. + * @return size of tree in bytes. + */ +size_t addrtree_size(const struct addrtree *tree); + +/** + * Create a new tree. + * @param max_depth: Tree will cap keys to this length. + * @param delfunc: f(element, env) delete element. + * @param sizefunc: f(element) returning the size of element. + * @param env: Module environment for alloc information. + * @param max_node_count: Maximum size of this data structure in nodes. + * 0 for unlimited. + * @return new addrtree or NULL on failure. + */ +struct addrtree * +addrtree_create(addrlen_t max_depth, void (*delfunc)(void *, void *), + size_t (*sizefunc)(void *), void *env, uint32_t max_node_count); + +/** + * Free tree and all nodes below. + * @param tree: Tree to be freed. + */ +void addrtree_delete(struct addrtree *tree); + +/** + * Insert an element in the tree. Failures are silent. Sourcemask and + * scope might be changed according to local policy. Caller should no + * longer access elem, it could be free'd now or later during future + * inserts. + * + * @param tree: Tree insert elem in. + * @param addr: key for element lookup. + * @param sourcemask: Length of addr in bits. + * @param scope: Number of significant bits in addr. + * @param elem: data to store in the tree. + * @param ttl: elem is valid up to this time, seconds. + * @param now: Current time in seconds. + */ +void addrtree_insert(struct addrtree *tree, const addrkey_t *addr, + addrlen_t sourcemask, addrlen_t scope, void *elem, time_t ttl, + time_t now); + +/** + * Find a node containing an element in the tree. + * + * @param tree: Tree to search. + * @param addr: key for element lookup. + * @param sourcemask: Length of addr in bits. + * @param now: Current time in seconds. + * @return addrnode or NULL on miss. + */ +struct addrnode * addrtree_find(struct addrtree *tree, + const addrkey_t *addr, addrlen_t sourcemask, time_t now); + +/** Wrappers for static functions to unit test */ +int unittest_wrapper_addrtree_cmpbit(const addrkey_t *key1, + const addrkey_t *key2, addrlen_t n); +addrlen_t unittest_wrapper_addrtree_bits_common(const addrkey_t *s1, + addrlen_t l1, const addrkey_t *s2, addrlen_t l2, addrlen_t skip); +int unittest_wrapper_addrtree_getbit(const addrkey_t *addr, + addrlen_t addrlen, addrlen_t n); +int unittest_wrapper_addrtree_issub(const addrkey_t *s1, addrlen_t l1, + const addrkey_t *s2, addrlen_t l2, addrlen_t skip); +#endif /* ADDRTREE_H */ --- contrib/unbound/edns-subnet/edns-subnet.c.orig +++ contrib/unbound/edns-subnet/edns-subnet.c @@ -0,0 +1,65 @@ +/* + * edns-subnet/edns-subnet.c - Subnet option related constants + * + * 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 option related constants. + */ + +#include "config.h" + +#ifdef CLIENT_SUBNET /* keeps splint happy */ +#include "edns-subnet/edns-subnet.h" +#include + +int +copy_clear(uint8_t* dst, size_t dstlen, uint8_t* src, size_t srclen, size_t n) +{ + size_t intpart = n / 8; /* bytes */ + size_t fracpart = n % 8; /* bits */ + size_t written = intpart; + if (intpart > dstlen || intpart > srclen) + return 1; + if (fracpart && (intpart+1 > dstlen || intpart+1 > srclen)) + return 1; + memcpy(dst, src, intpart); + if (fracpart) { + dst[intpart] = src[intpart] & ~(0xFF >> fracpart); + written++; + } + memset(dst + written, 0, dstlen - written); + return 0; +} + +#endif /* CLIENT_SUBNET */ --- contrib/unbound/edns-subnet/edns-subnet.h.orig +++ contrib/unbound/edns-subnet/edns-subnet.h @@ -0,0 +1,67 @@ +/* + * edns-subnet/edns-subnet.h - Subnet option related constants + * + * 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 option related constants. + */ + +#include "util/net_help.h" + +#ifndef EDNSSUBNET_EDNSSUBNET_H +#define EDNSSUBNET_EDNSSUBNET_H + +/** In use by the edns subnet option code, as assigned by IANA */ +#define EDNSSUBNET_ADDRFAM_IP4 1 +#define EDNSSUBNET_ADDRFAM_IP6 2 + +/** + * ECS option + */ +struct ecs_data { + uint16_t subnet_addr_fam; + uint8_t subnet_source_mask; + uint8_t subnet_scope_mask; + uint8_t subnet_addr[INET6_SIZE]; + int subnet_validdata; +}; + +/** + * copy the first n BITS from src to dst iff both src and dst + * are large enough, return 0 on succes + */ +int +copy_clear(uint8_t* dst, size_t dstlen, uint8_t* src, size_t srclen, size_t n); + +#endif /* EDNSSUBNET_EDNSSUBNET_H */ --- contrib/unbound/edns-subnet/subnet-whitelist.c.orig +++ contrib/unbound/edns-subnet/subnet-whitelist.c @@ -0,0 +1,207 @@ +/* + * edns-subnet/subnet-whitelist.c - Hosts we actively try to send subnet option + * to. + * + * 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 + * + * Keep track of the white listed servers for subnet option. Based + * on acl_list.c|h + */ + +#include "config.h" + +#ifdef CLIENT_SUBNET /* keeps splint happy */ +#include "edns-subnet/edns-subnet.h" +#include "edns-subnet/subnet-whitelist.h" +#include "util/regional.h" +#include "util/log.h" +#include "util/config_file.h" +#include "util/net_help.h" +#include "util/storage/dnstree.h" +#include "sldns/str2wire.h" +#include "util/data/dname.h" + +struct ecs_whitelist* +ecs_whitelist_create(void) +{ + struct ecs_whitelist* whitelist = + (struct ecs_whitelist*)calloc(1, + sizeof(struct ecs_whitelist)); + if(!whitelist) + return NULL; + whitelist->region = regional_create(); + if(!whitelist->region) { + ecs_whitelist_delete(whitelist); + return NULL; + } + return whitelist; +} + +void +ecs_whitelist_delete(struct ecs_whitelist* whitelist) +{ + if(!whitelist) + return; + regional_destroy(whitelist->region); + free(whitelist); +} + +/** insert new address into whitelist structure */ +static int +upstream_insert(struct ecs_whitelist* whitelist, + struct sockaddr_storage* addr, socklen_t addrlen, int net) +{ + struct addr_tree_node* node = (struct addr_tree_node*)regional_alloc( + whitelist->region, sizeof(*node)); + if(!node) + return 0; + if(!addr_tree_insert(&whitelist->upstream, node, addr, addrlen, net)) { + verbose(VERB_QUERY, + "duplicate send-client-subnet address ignored."); + } + return 1; +} + +/** apply edns-subnet string */ +static int +upstream_str_cfg(struct ecs_whitelist* whitelist, const char* str) +{ + struct sockaddr_storage addr; + int net; + socklen_t addrlen; + verbose(VERB_ALGO, "send-client-subnet: %s", str); + if(!netblockstrtoaddr(str, UNBOUND_DNS_PORT, &addr, &addrlen, &net)) { + log_err("cannot parse send-client-subnet netblock: %s", str); + return 0; + } + if(!upstream_insert(whitelist, &addr, addrlen, net)) { + log_err("out of memory"); + return 0; + } + return 1; +} + +/** read client_subnet config */ +static int +read_upstream(struct ecs_whitelist* whitelist, struct config_file* cfg) +{ + struct config_strlist* p; + for(p = cfg->client_subnet; p; p = p->next) { + log_assert(p->str); + if(!upstream_str_cfg(whitelist, p->str)) + return 0; + } + return 1; +} + +/** read client_subnet_zone config */ +static int +read_names(struct ecs_whitelist* whitelist, 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->client_subnet_zone; p; p = p->next) { + log_assert(p->str); + nm = sldns_str2wire_dname(p->str, &nm_len); + if(!nm) { + log_err("cannot parse client-subnet-zone: %s", p->str); + return 0; + } + nm_labs = dname_count_size_labels(nm, &nm_len); + nmr = (uint8_t*)regional_alloc_init(whitelist->region, nm, + nm_len); + free(nm); + if(!nmr) { + log_err("out of memory"); + return 0; + } + n = (struct name_tree_node*)regional_alloc(whitelist->region, + sizeof(*n)); + if(!n) { + log_err("out of memory"); + return 0; + } + if(!name_tree_insert(&whitelist->dname, n, nmr, nm_len, nm_labs, + LDNS_RR_CLASS_IN)) { + verbose(VERB_QUERY, "ignoring duplicate " + "client-subnet-zone: %s", p->str); + } + } + return 1; +} + +int +ecs_whitelist_apply_cfg(struct ecs_whitelist* whitelist, + struct config_file* cfg) +{ + regional_free_all(whitelist->region); + addr_tree_init(&whitelist->upstream); + name_tree_init(&whitelist->dname); + if(!read_upstream(whitelist, cfg)) + return 0; + if(!read_names(whitelist, cfg)) + return 0; + addr_tree_init_parents(&whitelist->upstream); + name_tree_init_parents(&whitelist->dname); + return 1; +} + +int +ecs_is_whitelisted(struct ecs_whitelist* whitelist, + struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* qname, + size_t qname_len, uint16_t qclass) +{ + int labs; + if(addr_tree_lookup(&whitelist->upstream, addr, addrlen)) + return 1; + /* Not in upstream whitelist, check dname whitelist. */ + labs = dname_count_labels(qname); + return name_tree_lookup(&whitelist->dname, qname, qname_len, labs, + qclass) != NULL; +} + +size_t +ecs_whitelist_get_mem(struct ecs_whitelist* whitelist) +{ + if(!whitelist) return 0; + return sizeof(*whitelist) + regional_get_mem(whitelist->region); +} + +#endif /* CLIENT_SUBNET */ --- contrib/unbound/edns-subnet/subnet-whitelist.h.orig +++ contrib/unbound/edns-subnet/subnet-whitelist.h @@ -0,0 +1,111 @@ +/* + * edns-subnet/subnet-whitelist.h - Hosts we actively try to send subnet option + * to. + * + * 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 + * + * Keep track of the white listed servers and domain names for subnet option. + * Based on acl_list.c|h + */ + +#ifndef EDNSSUBNET_WHITELIST_H +#define EDNSSUBNET_WHITELIST_H +#include "util/storage/dnstree.h" + +struct config_file; +struct regional; + +/** + * ecs_whitelist structure + */ +struct ecs_whitelist { + /** regional for allocation */ + struct regional* region; + /** + * Tree of the address spans that are whitelisted. + * contents of type addr_tree_node. Each node is an address span + * Unbound will append subnet option for. + */ + rbtree_type upstream; + /** + * Tree of domain names for which Unbound will append an ECS option. + * rbtree of struct name_tree_node. + */ + rbtree_type dname; +}; + +/** + * Create ecs_whitelist structure + * @return new structure or NULL on error. + */ +struct ecs_whitelist* ecs_whitelist_create(void); + +/** + * Delete ecs_whitelist structure. + * @param whitelist: to delete. + */ +void ecs_whitelist_delete(struct ecs_whitelist* whitelist); + +/** + * Process ecs_whitelist config. + * @param whitelist: where to store. + * @param cfg: config options. + * @return 0 on error. + */ +int ecs_whitelist_apply_cfg(struct ecs_whitelist* whitelist, + struct config_file* cfg); + +/** + * See if an address or domain is whitelisted. + * @param whitelist: structure for address storage. + * @param addr: address to check + * @param addrlen: length of addr. + * @param qname: dname in query + * @param qname_len: length of dname + * @param qclass: class in query + * @return: true if the address is whitelisted for subnet option. + */ +int ecs_is_whitelisted(struct ecs_whitelist* whitelist, + struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* qname, + size_t qname_len, uint16_t qclass); + +/** + * Get memory used by ecs_whitelist structure. + * @param whitelist: structure for address storage. + * @return bytes in use. + */ +size_t ecs_whitelist_get_mem(struct ecs_whitelist* whitelist); + +#endif /* EDNSSUBNET_WHITELIST_H */ --- contrib/unbound/edns-subnet/subnetmod.c.orig +++ contrib/unbound/edns-subnet/subnetmod.c @@ -0,0 +1,866 @@ +/* + * 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 "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; + return 1; +} + +/** Add ecs struct to edns list, after parsing it to wire format. */ +static void +ecs_opt_list_append(struct ecs_data* ecs, struct edns_option** list, + struct module_qstate *qstate) +{ + 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), qstate->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* ATTR_UNUSED(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)) { + qstate->no_cache_store = 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(!(sq->subnet_sent)) { + ecs_opt_list_append(&sq->ecs_server_out, + &qstate->edns_opts_back_out, qstate); + sq->subnet_sent = 1; + } + } + else if(sq->subnet_sent) { + /* Outgoing ECS option is set, but we don't want to sent it to + * this address, remove option. */ + edns_opt_list_remove(&qstate->edns_opts_back_out, + qstate->env->cfg->client_subnet_opcode); + 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; + /* 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("subnet: 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("subnet: could not create ECS whitelist"); + slabhash_delete(sn_env->subnet_msg_cache); + free(sn_env); + env->modinfo[id] = NULL; + return 0; + } + + verbose(VERB_QUERY, "subnet: 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 */, + 0 /* no aggregation */, env)) { + log_err("subnet: 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; + + /* We already calculated hash upon lookup */ + hashvalue_type h = qstate->minfo[id] ? + ((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("Subnet 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("Subnet cache insertion failed"); + return; + } + + /* store RRsets */ + for(i=0; irrset_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 */ + addrtree_insert(tree, (addrkey_t*)edns->subnet_addr, + edns->subnet_source_mask, + sq->ecs_server_in.subnet_scope_mask, rep, + rep->ttl, *qstate->env->now); + + 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) +{ + 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 */ + 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; + } + 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 (s_in->subnet_validdata) + verbose(VERB_QUERY, "subnet: 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) { + /* The authority indicated no support for edns subnet. As a + * consequence the answer ended up in the regular cache. It + * is still usefull to put it in the edns subnet cache for + * when a client explicitly asks for subnet specific answer. */ + verbose(VERB_QUERY, "subnet: 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; + } + + /* 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, "subnet: 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; + 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 = s_in->subnet_scope_mask; + /* 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; +} + +static 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; + memset(&sq->ecs_server_out, 0, sizeof(sq->ecs_server_out)); + } + return 1; +} + +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))) { + if(parse_subnet_option(ecs_opt, &sq->ecs_server_in) && + sq->subnet_sent && + sq->ecs_server_in.subnet_validdata) + /* Only skip global cache store if we sent an ECS option + * and received one back. Answers from non-whitelisted + * servers will end up in global cache. Answers for + * queries with 0 source will not (unless nameserver + * does not support ECS). */ + 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, "subnet[module %d] operate: extstate:%s " + "event:%s", id, strextstate(qstate->ext_state[id]), + strmodulevent(event)); + log_query_info(VERB_QUERY, "subnet 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; + } + sq->subnet_downstream = 1; + } + else if(qstate->mesh_info->reply_list) { + subnet_option_from_ss( + &qstate->mesh_info->reply_list->query_reply.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, "subnet: 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; + } + + lock_rw_wrlock(&sne->biglock); + if (lookup_and_reply(qstate, id, sq)) { + sne->num_msg_cache++; + lock_rw_unlock(&sne->biglock); + verbose(VERB_QUERY, "subnet: answered from cache"); + qstate->ext_state[id] = module_finished; + + ecs_opt_list_append(&sq->ecs_client_out, + &qstate->edns_opts_front_out, qstate); + 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. 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, + "subnet: 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) { + ecs_opt_list_append(&sq->ecs_client_out, + &qstate->edns_opts_front_out, qstate); + } + qstate->no_cache_store = sq->started_no_cache_store; + 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, "subnet: 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("subnet: 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 = { + "subnet", &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 */ --- contrib/unbound/edns-subnet/subnetmod.h.orig +++ contrib/unbound/edns-subnet/subnetmod.h @@ -0,0 +1,139 @@ +/* + * 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 "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; + /** 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; + /** has the subnet module been started with no_cache_store? */ + int started_no_cache_store; +}; + +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); + +#endif /* SUBNETMOD_H */ --- contrib/unbound/freebsd-configure.sh.orig +++ contrib/unbound/freebsd-configure.sh @@ -13,6 +13,15 @@ unbound=$(dirname $(realpath $0)) cd $unbound +# Run autotools before we drop LOCALBASE out of PATH +(cd $unbound && libtoolize --copy && autoheader && autoconf) + +# Ensure we use the correct toolchain and clean our environment +export CC=$(echo ".include " | make -f /dev/stdin -VCC) +export CPP=$(echo ".include " | make -f /dev/stdin -VCPP) +unset CFLAGS CPPFLAGS LDFLAGS LD_LIBRARY_PATH LIBS +export PATH=/bin:/sbin:/usr/bin:/usr/sbin + ldnssrc=$(realpath $unbound/../ldns) [ -f $ldnssrc/ldns/ldns.h ] || error "can't find LDNS sources" export CFLAGS="-I$ldnssrc" @@ -24,17 +33,9 @@ [ -f $ldnsobj/libprivateldns.a ] || error "can't find LDNS object directory" export LDFLAGS="-L$ldnsobj" -export CC=$(echo ".include " | make -f /dev/stdin -VCC) -export CPP=$(echo ".include " | make -f /dev/stdin -VCPP) - -autoconf -autoheader +cd $unbound ./configure \ --prefix= --exec-prefix=/usr \ --with-conf-file=/var/unbound/unbound.conf \ --with-run-dir=/var/unbound \ --with-username=unbound - -# Don't try to provide bogus memory usage statistics based on sbrk(2). -sed -n -i.orig -e '/HAVE_SBRK/!p' config.status -./config.status config.h --- contrib/unbound/install-sh.orig +++ contrib/unbound/install-sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/sh # install - install a program, script, or datafile scriptversion=2013-12-25.23; # UTC --- contrib/unbound/ipsecmod/ipsecmod-whitelist.c.orig +++ contrib/unbound/ipsecmod/ipsecmod-whitelist.c @@ -0,0 +1,158 @@ +/* + * ipsecmod/ipsecmod-whitelist.h - White listed domains for the ipsecmod to + * operate on. + * + * 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 + * + * Keep track of the white listed domains for ipsecmod. + */ + +#include "config.h" + +#ifdef USE_IPSECMOD +#include "ipsecmod/ipsecmod.h" +#include "ipsecmod/ipsecmod-whitelist.h" +#include "util/regional.h" +#include "util/log.h" +#include "util/config_file.h" +#include "util/rbtree.h" +#include "util/data/dname.h" +#include "util/storage/dnstree.h" +#include "sldns/str2wire.h" + +/** Apply ipsecmod-whitelist string. */ +static int +whitelist_str_cfg(rbtree_type* whitelist, const char* name) +{ + struct name_tree_node* n; + size_t len; + uint8_t* nm = sldns_str2wire_dname(name, &len); + if(!nm) { + log_err("ipsecmod: could not parse %s for whitelist.", name); + return 0; + } + n = (struct name_tree_node*)calloc(1, sizeof(*n)); + if(!n) { + log_err("ipsecmod: out of memory while creating whitelist."); + free(nm); + return 0; + } + n->node.key = n; + n->name = nm; + n->len = len; + n->labs = dname_count_labels(nm); + n->dclass = LDNS_RR_CLASS_IN; + if(!name_tree_insert(whitelist, n, nm, len, n->labs, n->dclass)) { + /* duplicate element ignored, idempotent */ + free(n->name); + free(n); + } + return 1; +} + +/** Read ipsecmod-whitelist config. */ +static int +read_whitelist(rbtree_type* whitelist, struct config_file* cfg) +{ + struct config_strlist* p; + for(p = cfg->ipsecmod_whitelist; p; p = p->next) { + log_assert(p->str); + if(!whitelist_str_cfg(whitelist, p->str)) + return 0; + } + return 1; +} + +int +ipsecmod_whitelist_apply_cfg(struct ipsecmod_env* ie, + struct config_file* cfg) +{ + ie->whitelist = rbtree_create(name_tree_compare); + if(!read_whitelist(ie->whitelist, cfg)) + return 0; + name_tree_init_parents(ie->whitelist); + return 1; +} + +/** Delete ipsecmod_env->whitelist element. */ +static void +whitelist_free(struct rbnode_type* n, void* ATTR_UNUSED(d)) +{ + if(n) { + free(((struct name_tree_node*)n)->name); + free(n); + } +} + +/** Get memory usage of ipsecmod_env->whitelist element. */ +static void +whitelist_get_mem(struct rbnode_type* n, void* arg) +{ + struct name_tree_node* node = (struct name_tree_node*)n; + size_t* size = (size_t*) arg; + if(node) { + *size += sizeof(node) + node->len; + } +} + +void +ipsecmod_whitelist_delete(rbtree_type* whitelist) +{ + if(whitelist) { + traverse_postorder(whitelist, whitelist_free, NULL); + free(whitelist); + } +} + +int +ipsecmod_domain_is_whitelisted(struct ipsecmod_env* ie, uint8_t* dname, + size_t dname_len, uint16_t qclass) +{ + if(!ie->whitelist) return 1; /* No whitelist, treat as whitelisted. */ + return name_tree_lookup(ie->whitelist, dname, dname_len, + dname_count_labels(dname), qclass) != NULL; +} + +size_t +ipsecmod_whitelist_get_mem(rbtree_type* whitelist) +{ + size_t size = 0; + if(whitelist) { + traverse_postorder(whitelist, whitelist_get_mem, &size); + } + return size; +} + +#endif /* USE_IPSECMOD */ --- contrib/unbound/ipsecmod/ipsecmod-whitelist.h.orig +++ contrib/unbound/ipsecmod/ipsecmod-whitelist.h @@ -0,0 +1,82 @@ +/* + * ipsecmod/ipsecmod-whitelist.h - White listed domains for the ipsecmod to + * operate on. + * + * 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 + * + * Keep track of the white listed domains for ipsecmod. + */ + +#ifndef IPSECMOD_WHITELIST_H +#define IPSECMOD_WHITELIST_H +#include "util/storage/dnstree.h" + +struct config_file; +struct regional; + +/** + * Process ipsecmod_whitelist config. + * @param ie: ipsecmod environment. + * @param cfg: config options. + * @return 0 on error. + */ +int ipsecmod_whitelist_apply_cfg(struct ipsecmod_env* ie, + struct config_file* cfg); + +/** + * Delete the ipsecmod whitelist. + * @param whitelist: ipsecmod whitelist. + */ +void ipsecmod_whitelist_delete(rbtree_type* whitelist); + +/** + * See if a domain is whitelisted. + * @param ie: ipsecmod environment. + * @param dname: domain name to check. + * @param dname_len: length of domain name. + * @param qclass: query CLASS. + * @return: true if the domain is whitelisted for the ipsecmod. + */ +int ipsecmod_domain_is_whitelisted(struct ipsecmod_env* ie, uint8_t* dname, + size_t dname_len, uint16_t qclass); + +/** + * Get memory used by ipsecmod whitelist. + * @param whitelist: structure for domain storage. + * @return bytes in use. + */ +size_t ipsecmod_whitelist_get_mem(rbtree_type* whitelist); + +#endif /* IPSECMOD_WHITELIST_H */ --- contrib/unbound/ipsecmod/ipsecmod.c.orig +++ contrib/unbound/ipsecmod/ipsecmod.c @@ -0,0 +1,610 @@ +/* + * ipsecmod/ipsecmod.c - facilitate opportunistic IPsec module + * + * 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 a module that facilitates opportunistic IPsec. It does so + * by also quering for the IPSECKEY for A/AAAA queries and calling a + * configurable hook (eg. signaling an IKE daemon) before replying. + */ + +#include "config.h" +#ifdef USE_IPSECMOD +#include "ipsecmod/ipsecmod.h" +#include "ipsecmod/ipsecmod-whitelist.h" +#include "util/fptr_wlist.h" +#include "util/regional.h" +#include "util/net_help.h" +#include "util/config_file.h" +#include "services/cache/dns.h" +#include "sldns/wire2str.h" + +/** Apply configuration to ipsecmod module 'global' state. */ +static int +ipsecmod_apply_cfg(struct ipsecmod_env* ipsecmod_env, struct config_file* cfg) +{ + if(!cfg->ipsecmod_hook || (cfg->ipsecmod_hook && !cfg->ipsecmod_hook[0])) { + log_err("ipsecmod: missing ipsecmod-hook."); + return 0; + } + if(cfg->ipsecmod_whitelist && + !ipsecmod_whitelist_apply_cfg(ipsecmod_env, cfg)) + return 0; + return 1; +} + +int +ipsecmod_init(struct module_env* env, int id) +{ + struct ipsecmod_env* ipsecmod_env = (struct ipsecmod_env*)calloc(1, + sizeof(struct ipsecmod_env)); + if(!ipsecmod_env) { + log_err("malloc failure"); + return 0; + } + env->modinfo[id] = (void*)ipsecmod_env; + ipsecmod_env->whitelist = NULL; + if(!ipsecmod_apply_cfg(ipsecmod_env, env->cfg)) { + log_err("ipsecmod: could not apply configuration settings."); + return 0; + } + return 1; +} + +void +ipsecmod_deinit(struct module_env* env, int id) +{ + struct ipsecmod_env* ipsecmod_env; + if(!env || !env->modinfo[id]) + return; + ipsecmod_env = (struct ipsecmod_env*)env->modinfo[id]; + /* Free contents. */ + ipsecmod_whitelist_delete(ipsecmod_env->whitelist); + free(ipsecmod_env); + env->modinfo[id] = NULL; +} + +/** New query for ipsecmod. */ +static int +ipsecmod_new(struct module_qstate* qstate, int id) +{ + struct ipsecmod_qstate* iq = (struct ipsecmod_qstate*)regional_alloc( + qstate->region, sizeof(struct ipsecmod_qstate)); + qstate->minfo[id] = iq; + if(!iq) + return 0; + /* Initialise it. */ + memset(iq, 0, sizeof(*iq)); + iq->enabled = qstate->env->cfg->ipsecmod_enabled; + iq->is_whitelisted = ipsecmod_domain_is_whitelisted( + (struct ipsecmod_env*)qstate->env->modinfo[id], qstate->qinfo.qname, + qstate->qinfo.qname_len, qstate->qinfo.qclass); + return 1; +} + +/** + * Exit module with an error status. + * @param qstate: query state + * @param id: module id. + */ +static void +ipsecmod_error(struct module_qstate* qstate, int id) +{ + qstate->ext_state[id] = module_error; + qstate->return_rcode = LDNS_RCODE_SERVFAIL; +} + +/** + * Generate a request for the IPSECKEY. + * + * @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. + * @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; + struct query_info ask; + ask.qname = name; + ask.qname_len = namelen; + ask.qtype = qtype; + ask.qclass = qclass; + ask.local_alias = NULL; + log_query_info(VERB_ALGO, "ipsecmod: generate request", &ask); + fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub)); + if(!(*qstate->env->attach_sub)(qstate, &ask, + (uint16_t)(BIT_RD|flags), 0, 0, &newq)){ + log_err("Could not generate request: out of memory"); + return 0; + } + qstate->ext_state[id] = module_wait_subquery; + return 1; +} + +/** + * Check if the string passed is a valid domain name with safe characters to + * pass to a shell. + * This will only allow: + * - digits + * - alphas + * - hyphen (not at the start) + * - dot (not at the start, or the only character) + * - underscore + * @param s: pointer to the string. + * @param slen: string's length. + * @return true if s only contains safe characters; false otherwise. + */ +static int +domainname_has_safe_characters(char* s, size_t slen) { + size_t i; + for(i = 0; i < slen; i++) { + if(s[i] == '\0') return 1; + if((s[i] == '-' && i != 0) + || (s[i] == '.' && (i != 0 || s[1] == '\0')) + || (s[i] == '_') || (s[i] >= '0' && s[i] <= '9') + || (s[i] >= 'A' && s[i] <= 'Z') + || (s[i] >= 'a' && s[i] <= 'z')) { + continue; + } + return 0; + } + return 1; +} + +/** + * Check if the stringified IPSECKEY RDATA contains safe characters to pass to + * a shell. + * This is only relevant for checking the gateway when the gateway type is 3 + * (domainname). + * @param s: pointer to the string. + * @param slen: string's length. + * @return true if s contains only safe characters; false otherwise. + */ +static int +ipseckey_has_safe_characters(char* s, size_t slen) { + int precedence, gateway_type, algorithm; + char* gateway; + gateway = (char*)calloc(slen, sizeof(char)); + if(!gateway) { + log_err("ipsecmod: out of memory when calling the hook"); + return 0; + } + if(sscanf(s, "%d %d %d %s ", + &precedence, &gateway_type, &algorithm, gateway) != 4) { + free(gateway); + return 0; + } + if(gateway_type != 3) { + free(gateway); + return 1; + } + if(domainname_has_safe_characters(gateway, slen)) { + free(gateway); + return 1; + } + free(gateway); + return 0; +} + +/** + * Prepare the data and call the hook. + * + * @param qstate: query state. + * @param iq: ipsecmod qstate. + * @param ie: ipsecmod environment. + * @return true on success, false otherwise. + */ +static int +call_hook(struct module_qstate* qstate, struct ipsecmod_qstate* iq, + struct ipsecmod_env* ATTR_UNUSED(ie)) +{ + size_t slen, tempdata_len, tempstring_len, i; + char str[65535], *s, *tempstring; + int w = 0, w_temp, qtype; + struct ub_packed_rrset_key* rrset_key; + struct packed_rrset_data* rrset_data; + uint8_t *tempdata; + + /* Check if a shell is available */ + if(system(NULL) == 0) { + log_err("ipsecmod: no shell available for ipsecmod-hook"); + return 0; + } + + /* Zero the buffer. */ + s = str; + slen = sizeof(str); + memset(s, 0, slen); + + /* Copy the hook into the buffer. */ + w += sldns_str_print(&s, &slen, "%s", qstate->env->cfg->ipsecmod_hook); + /* Put space into the buffer. */ + w += sldns_str_print(&s, &slen, " "); + /* Copy the qname into the buffer. */ + tempstring = sldns_wire2str_dname(qstate->qinfo.qname, + qstate->qinfo.qname_len); + if(!tempstring) { + log_err("ipsecmod: out of memory when calling the hook"); + return 0; + } + if(!domainname_has_safe_characters(tempstring, strlen(tempstring))) { + log_err("ipsecmod: qname has unsafe characters"); + free(tempstring); + return 0; + } + w += sldns_str_print(&s, &slen, "\"%s\"", tempstring); + free(tempstring); + /* Put space into the buffer. */ + w += sldns_str_print(&s, &slen, " "); + /* Copy the IPSECKEY TTL into the buffer. */ + rrset_data = (struct packed_rrset_data*)iq->ipseckey_rrset->entry.data; + w += sldns_str_print(&s, &slen, "\"%ld\"", (long)rrset_data->ttl); + /* Put space into the buffer. */ + w += sldns_str_print(&s, &slen, " "); + rrset_key = reply_find_answer_rrset(&qstate->return_msg->qinfo, + qstate->return_msg->rep); + /* Double check that the records are indeed A/AAAA. + * This should never happen as this function is only executed for A/AAAA + * queries but make sure we don't pass anything other than A/AAAA to the + * shell. */ + qtype = ntohs(rrset_key->rk.type); + if(qtype != LDNS_RR_TYPE_AAAA && qtype != LDNS_RR_TYPE_A) { + log_err("ipsecmod: Answer is not of A or AAAA type"); + return 0; + } + rrset_data = (struct packed_rrset_data*)rrset_key->entry.data; + /* Copy the A/AAAA record(s) into the buffer. Start and end this section + * with a double quote. */ + w += sldns_str_print(&s, &slen, "\""); + for(i=0; icount; i++) { + if(i > 0) { + /* Put space into the buffer. */ + w += sldns_str_print(&s, &slen, " "); + } + /* Ignore the first two bytes, they are the rr_data len. */ + w_temp = sldns_wire2str_rdata_buf(rrset_data->rr_data[i] + 2, + rrset_data->rr_len[i] - 2, s, slen, qstate->qinfo.qtype); + if(w_temp < 0) { + /* Error in printout. */ + log_err("ipsecmod: Error in printing IP address"); + return 0; + } else if((size_t)w_temp >= slen) { + s = NULL; /* We do not want str to point outside of buffer. */ + slen = 0; + log_err("ipsecmod: shell command too long"); + return 0; + } else { + s += w_temp; + slen -= w_temp; + w += w_temp; + } + } + w += sldns_str_print(&s, &slen, "\""); + /* Put space into the buffer. */ + w += sldns_str_print(&s, &slen, " "); + /* Copy the IPSECKEY record(s) into the buffer. Start and end this section + * with a double quote. */ + w += sldns_str_print(&s, &slen, "\""); + rrset_data = (struct packed_rrset_data*)iq->ipseckey_rrset->entry.data; + for(i=0; icount; i++) { + if(i > 0) { + /* Put space into the buffer. */ + w += sldns_str_print(&s, &slen, " "); + } + /* Ignore the first two bytes, they are the rr_data len. */ + tempdata = rrset_data->rr_data[i] + 2; + tempdata_len = rrset_data->rr_len[i] - 2; + /* Save the buffer pointers. */ + tempstring = s; tempstring_len = slen; + w_temp = sldns_wire2str_ipseckey_scan(&tempdata, &tempdata_len, &s, + &slen, NULL, 0, NULL); + /* There was an error when parsing the IPSECKEY; reset the buffer + * pointers to their previous values. */ + if(w_temp == -1) { + s = tempstring; slen = tempstring_len; + } else if(w_temp > 0) { + if(!ipseckey_has_safe_characters( + tempstring, tempstring_len - slen)) { + log_err("ipsecmod: ipseckey has unsafe characters"); + return 0; + } + w += w_temp; + } + } + w += sldns_str_print(&s, &slen, "\""); + if(w >= (int)sizeof(str)) { + log_err("ipsecmod: shell command too long"); + return 0; + } + verbose(VERB_ALGO, "ipsecmod: shell command: '%s'", str); + /* ipsecmod-hook should return 0 on success. */ + if(system(str) != 0) + return 0; + return 1; +} + +/** + * Handle an ipsecmod 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 +ipsecmod_handle_query(struct module_qstate* qstate, + struct ipsecmod_qstate* iq, struct ipsecmod_env* ie, int id) +{ + struct ub_packed_rrset_key* rrset_key; + struct packed_rrset_data* rrset_data; + size_t i; + /* Pass to next module if we are not enabled and whitelisted. */ + if(!(iq->enabled && iq->is_whitelisted)) { + qstate->ext_state[id] = module_wait_module; + return; + } + /* New query, check if the query is for an A/AAAA record and disable + * caching for other modules. */ + if(!iq->ipseckey_done) { + if(qstate->qinfo.qtype == LDNS_RR_TYPE_A || + qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA) { + char type[16]; + sldns_wire2str_type_buf(qstate->qinfo.qtype, type, + sizeof(type)); + verbose(VERB_ALGO, "ipsecmod: query for %s; engaging", + type); + qstate->no_cache_store = 1; + } + /* Pass request to next module. */ + qstate->ext_state[id] = module_wait_module; + return; + } + /* IPSECKEY subquery is finished. */ + /* We have an IPSECKEY answer. */ + if(iq->ipseckey_rrset) { + rrset_data = (struct packed_rrset_data*)iq->ipseckey_rrset->entry.data; + if(rrset_data) { + /* If bogus return SERVFAIL. */ + if(!qstate->env->cfg->ipsecmod_ignore_bogus && + rrset_data->security == sec_status_bogus) { + log_err("ipsecmod: bogus IPSECKEY"); + ipsecmod_error(qstate, id); + return; + } + /* We have a valid IPSECKEY reply, call hook. */ + if(!call_hook(qstate, iq, ie) && + qstate->env->cfg->ipsecmod_strict) { + log_err("ipsecmod: ipsecmod-hook failed"); + ipsecmod_error(qstate, id); + return; + } + /* Make sure the A/AAAA's TTL is equal/less than the + * ipsecmod_max_ttl. */ + rrset_key = reply_find_answer_rrset(&qstate->return_msg->qinfo, + qstate->return_msg->rep); + rrset_data = (struct packed_rrset_data*)rrset_key->entry.data; + if(rrset_data->ttl > (time_t)qstate->env->cfg->ipsecmod_max_ttl) { + /* Update TTL for rrset to fixed value. */ + rrset_data->ttl = qstate->env->cfg->ipsecmod_max_ttl; + for(i=0; icount+rrset_data->rrsig_count; i++) + rrset_data->rr_ttl[i] = qstate->env->cfg->ipsecmod_max_ttl; + /* Also update reply_info's TTL */ + if(qstate->return_msg->rep->ttl > (time_t)qstate->env->cfg->ipsecmod_max_ttl) { + qstate->return_msg->rep->ttl = + qstate->env->cfg->ipsecmod_max_ttl; + qstate->return_msg->rep->prefetch_ttl = PREFETCH_TTL_CALC( + qstate->return_msg->rep->ttl); + qstate->return_msg->rep->serve_expired_ttl = qstate->return_msg->rep->ttl + + qstate->env->cfg->serve_expired_ttl; + } + } + } + } + /* Store A/AAAA in cache. */ + if(!dns_cache_store(qstate->env, &qstate->qinfo, + qstate->return_msg->rep, 0, qstate->prefetch_leeway, + 0, qstate->region, qstate->query_flags)) { + log_err("ipsecmod: out of memory caching record"); + } + qstate->ext_state[id] = module_finished; +} + +/** + * Handle an ipsecmod 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 +ipsecmod_handle_response(struct module_qstate* qstate, + struct ipsecmod_qstate* ATTR_UNUSED(iq), + struct ipsecmod_env* ATTR_UNUSED(ie), int id) +{ + /* Pass to previous module if we are not enabled and whitelisted. */ + if(!(iq->enabled && iq->is_whitelisted)) { + qstate->ext_state[id] = module_finished; + return; + } + /* check if the response is for an A/AAAA query. */ + if((qstate->qinfo.qtype == LDNS_RR_TYPE_A || + qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA) && + /* check that we had an answer for the A/AAAA query. */ + qstate->return_msg && + reply_find_answer_rrset(&qstate->return_msg->qinfo, + qstate->return_msg->rep) && + /* check that another module didn't SERVFAIL. */ + qstate->return_rcode == LDNS_RCODE_NOERROR) { + char type[16]; + sldns_wire2str_type_buf(qstate->qinfo.qtype, type, + sizeof(type)); + verbose(VERB_ALGO, "ipsecmod: response for %s; generating IPSECKEY " + "subquery", type); + /* generate an IPSECKEY query. */ + if(!generate_request(qstate, id, qstate->qinfo.qname, + qstate->qinfo.qname_len, LDNS_RR_TYPE_IPSECKEY, + qstate->qinfo.qclass, 0)) { + log_err("ipsecmod: could not generate subquery."); + ipsecmod_error(qstate, id); + } + return; + } + /* we are done with the query. */ + qstate->ext_state[id] = module_finished; +} + +void +ipsecmod_operate(struct module_qstate* qstate, enum module_ev event, int id, + struct outbound_entry* outbound) +{ + struct ipsecmod_env* ie = (struct ipsecmod_env*)qstate->env->modinfo[id]; + struct ipsecmod_qstate* iq = (struct ipsecmod_qstate*)qstate->minfo[id]; + verbose(VERB_QUERY, "ipsecmod[module %d] operate: extstate:%s event:%s", + id, strextstate(qstate->ext_state[id]), strmodulevent(event)); + if(iq) log_query_info(VERB_QUERY, "ipsecmod operate: query", + &qstate->qinfo); + + /* create ipsecmod_qstate. */ + if((event == module_event_new || event == module_event_pass) && + iq == NULL) { + if(!ipsecmod_new(qstate, id)) { + ipsecmod_error(qstate, id); + return; + } + iq = (struct ipsecmod_qstate*)qstate->minfo[id]; + } + if(iq && (event == module_event_pass || event == module_event_new)) { + ipsecmod_handle_query(qstate, iq, ie, id); + return; + } + if(iq && (event == module_event_moddone)) { + ipsecmod_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"); + ipsecmod_error(qstate, id); + return; + } + if(!iq && (event == module_event_moddone)) { + /* during priming, module done but we never started. */ + qstate->ext_state[id] = module_finished; + return; + } + + log_err("ipsecmod: bad event %s", strmodulevent(event)); + ipsecmod_error(qstate, id); + return; +} + +void +ipsecmod_inform_super(struct module_qstate* qstate, int id, + struct module_qstate* super) +{ + struct ipsecmod_qstate* siq; + log_query_info(VERB_ALGO, "ipsecmod: inform_super, sub is", + &qstate->qinfo); + log_query_info(VERB_ALGO, "super is", &super->qinfo); + siq = (struct ipsecmod_qstate*)super->minfo[id]; + if(!siq) { + verbose(VERB_ALGO, "super has no ipsecmod state"); + return; + } + + if(qstate->return_msg) { + struct ub_packed_rrset_key* rrset_key = reply_find_answer_rrset( + &qstate->return_msg->qinfo, qstate->return_msg->rep); + if(rrset_key) { + /* We have an answer. */ + /* Copy to super's region. */ + rrset_key = packed_rrset_copy_region(rrset_key, super->region, 0); + siq->ipseckey_rrset = rrset_key; + if(!rrset_key) { + log_err("ipsecmod: out of memory."); + } + } + } + /* Notify super to proceed. */ + siq->ipseckey_done = 1; +} + +void +ipsecmod_clear(struct module_qstate* qstate, int id) +{ + if(!qstate) + return; + qstate->minfo[id] = NULL; +} + +size_t +ipsecmod_get_mem(struct module_env* env, int id) +{ + struct ipsecmod_env* ie = (struct ipsecmod_env*)env->modinfo[id]; + if(!ie) + return 0; + return sizeof(*ie) + ipsecmod_whitelist_get_mem(ie->whitelist); +} + +/** + * The ipsecmod function block + */ +static struct module_func_block ipsecmod_block = { + "ipsecmod", + &ipsecmod_init, &ipsecmod_deinit, &ipsecmod_operate, + &ipsecmod_inform_super, &ipsecmod_clear, &ipsecmod_get_mem +}; + +struct module_func_block* +ipsecmod_get_funcblock(void) +{ + return &ipsecmod_block; +} +#endif /* USE_IPSECMOD */ --- contrib/unbound/ipsecmod/ipsecmod.h.orig +++ contrib/unbound/ipsecmod/ipsecmod.h @@ -0,0 +1,97 @@ +/* + * ipsecmod/ipsecmod.h - facilitate opportunistic IPsec module + * + * 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 a module that facilitates opportunistic IPsec. It does so + * by also quering for the IPSECKEY for A/AAAA queries and calling a + * configurable hook (eg. signaling an IKE daemon) before replying. + */ + +#ifndef IPSECMOD_H +#define IPSECMOD_H +#include "util/module.h" +#include "util/rbtree.h" + +/** + * The global variable environment contents for the ipsecmod + * Shared between threads, this represents long term information. + */ +struct ipsecmod_env { + /** White listed domains for ipsecmod. */ + rbtree_type* whitelist; +}; + +/** + * Per query state for the ipsecmod module. + */ +struct ipsecmod_qstate { + /** State of the IPsec module. */ + /** NOTE: This value is copied here from the configuration so that a change + * with unbound-control would not complicate an already running mesh. */ + int enabled; + /** If the qname is whitelisted or not. */ + /** NOTE: No whitelist means all qnames are whitelisted. */ + int is_whitelisted; + /** Pointer to IPSECKEY rrset allocated in the qstate region. NULL if there + * was no IPSECKEY reply from the subquery. */ + struct ub_packed_rrset_key* ipseckey_rrset; + /** If the IPSECKEY subquery has finished. */ + int ipseckey_done; +}; + +/** Init the ipsecmod module */ +int ipsecmod_init(struct module_env* env, int id); +/** Deinit the ipsecmod module */ +void ipsecmod_deinit(struct module_env* env, int id); +/** Operate on an event on a query (in qstate). */ +void ipsecmod_operate(struct module_qstate* qstate, enum module_ev event, + int id, struct outbound_entry* outbound); +/** Subordinate query done, inform this super request of its conclusion */ +void ipsecmod_inform_super(struct module_qstate* qstate, int id, + struct module_qstate* super); +/** clear the ipsecmod query-specific contents out of qstate */ +void ipsecmod_clear(struct module_qstate* qstate, int id); +/** return memory estimate for the ipsecmod module */ +size_t ipsecmod_get_mem(struct module_env* env, int id); + +/** + * Get the function block with pointers to the ipsecmod functions + * @return the function block for "ipsecmod". + */ +struct module_func_block* ipsecmod_get_funcblock(void); + +#endif /* IPSECMOD_H */ --- contrib/unbound/ipset/ipset.c.orig +++ contrib/unbound/ipset/ipset.c @@ -0,0 +1,383 @@ +/** + * \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 +#include +#include + +#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 *setname, int af) +{ + static char dname[BUFF_LEN]; + const char *s; + 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; + } + if (dname[dlen - 1] == '.') { + dlen--; + } + + for (p = env->cfg->local_zones_ipset; p; p = p->next) { + plen = strlen(p->str); + + if (dlen >= plen) { + s = dname + (dlen - plen); + + if (strncasecmp(p->str, s, 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 ipset_env *ie) { + struct mnl_socket *mnl; + + size_t i; + + const char *setname; + + struct ub_packed_rrset_key *rrset; + + int af; + + + mnl = (struct mnl_socket *)ie->mnl; + if (!mnl) { + // retry to create mnl socket + mnl = open_mnl_socket(); + if (!mnl) { + return -1; + } + + ie->mnl = mnl; + } + + for (i = 0; i < return_msg->rep->rrset_count; ++i) { + setname = NULL; + + rrset = return_msg->rep->rrsets[i]; + + if (rrset->rk.type == htons(LDNS_RR_TYPE_A)) { + af = AF_INET; + if ((ie->v4_enabled == 1)) { + setname = ie->name_v4; + } + } else { + af = AF_INET6; + if ((ie->v6_enabled == 1)) { + setname = ie->name_v6; + } + } + + if (setname) { + if(ipset_check_zones_for_rrset(env, ie, mnl, rrset, + 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, 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; +} + --- contrib/unbound/ipset/ipset.h.orig +++ contrib/unbound/ipset/ipset.h @@ -0,0 +1,79 @@ +/** + * ipset.h + * + * Author: Kevin Chou + * Email: k9982874@gmail.com + */ +#ifndef IPSET_H +#define IPSET_H +/** \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. + * + * To use the IPset module, install the libmnl-dev (or libmnl-devel) package + * and configure with --enable-ipset. And compile. Then enable the ipset + * module in unbound.conf with module-config: "ipset validator iterator" + * then create it with ipset -N blacklist iphash and then add + * local-zone: "example.com." ipset + * statements for the zones where you want the addresses of the names + * looked up added to the set. + * + * Set the name of the set with + * ipset: + * name-v4: "blacklist" + * name-v6: "blacklist6" + * in unbound.conf. The set can be used in this way: + * iptables -A INPUT -m set --set blacklist src -j DROP + * ip6tables -A INPUT -m set --set blacklist6 src -j DROP + */ + +#include "util/module.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct ipset_env { + void* mnl; + + int v4_enabled; + int v6_enabled; + + const char *name_v4; + const char *name_v6; +}; + +struct ipset_qstate { + int dummy; +}; + +/** Init the ipset module */ +int ipset_init(struct module_env* env, int id); +/** Deinit the ipset module */ +void ipset_deinit(struct module_env* env, int id); +/** Operate on an event on a query (in qstate). */ +void ipset_operate(struct module_qstate* qstate, enum module_ev event, + int id, struct outbound_entry* outbound); +/** Subordinate query done, inform this super request of its conclusion */ +void ipset_inform_super(struct module_qstate* qstate, int id, + struct module_qstate* super); +/** clear the ipset query-specific contents out of qstate */ +void ipset_clear(struct module_qstate* qstate, int id); +/** return memory estimate for ipset module */ +size_t ipset_get_mem(struct module_env* env, int id); + +/** + * Get the function block with pointers to the ipset functions + * @return the function block for "ipset". + */ +struct module_func_block* ipset_get_funcblock(void); + +#ifdef __cplusplus +} +#endif + +#endif /* IPSET_H */ + --- contrib/unbound/iterator/iter_delegpt.c.orig +++ contrib/unbound/iterator/iter_delegpt.c @@ -72,6 +72,7 @@ return NULL; copy->bogus = dp->bogus; copy->has_parent_side_NS = dp->has_parent_side_NS; + copy->ssl_upstream = dp->ssl_upstream; for(ns = dp->nslist; ns; ns = ns->next) { if(!delegpt_add_ns(copy, region, ns->name, ns->lame)) return NULL; @@ -83,7 +84,7 @@ } for(a = dp->target_list; a; a = a->next_target) { if(!delegpt_add_addr(copy, region, &a->addr, a->addrlen, - a->bogus, a->lame)) + a->bogus, a->lame, a->tls_auth_name, NULL)) return NULL; } return copy; @@ -160,7 +161,7 @@ int delegpt_add_target(struct delegpt* dp, struct regional* region, uint8_t* name, size_t namelen, struct sockaddr_storage* addr, - socklen_t addrlen, uint8_t bogus, uint8_t lame) + socklen_t addrlen, uint8_t bogus, uint8_t lame, int* additions) { struct delegpt_ns* ns = delegpt_find_ns(dp, name, namelen); log_assert(!dp->dp_type_mlc); @@ -175,13 +176,14 @@ if(ns->got4 && ns->got6) ns->resolved = 1; } - return delegpt_add_addr(dp, region, addr, addrlen, bogus, lame); + return delegpt_add_addr(dp, region, addr, addrlen, bogus, lame, NULL, + additions); } int delegpt_add_addr(struct delegpt* dp, struct regional* region, struct sockaddr_storage* addr, socklen_t addrlen, uint8_t bogus, - uint8_t lame) + uint8_t lame, char* tls_auth_name, int* additions) { struct delegpt_addr* a; log_assert(!dp->dp_type_mlc); @@ -193,6 +195,8 @@ a->lame = 0; return 1; } + if(additions) + *additions = 1; a = (struct delegpt_addr*)regional_alloc(region, sizeof(struct delegpt_addr)); @@ -209,6 +213,13 @@ a->bogus = bogus; a->lame = lame; a->dnsseclame = 0; + if(tls_auth_name) { + a->tls_auth_name = regional_strdup(region, tls_auth_name); + if(!a->tls_auth_name) + return 0; + } else { + a->tls_auth_name = NULL; + } return 1; } @@ -275,11 +286,16 @@ (ns->done_pside6?" PSIDE_AAAA":"")); } for(a = dp->target_list; a; a = a->next_target) { + char s[128]; const char* str = " "; if(a->bogus && a->lame) str = " BOGUS ADDR_LAME "; else if(a->bogus) str = " BOGUS "; else if(a->lame) str = " ADDR_LAME "; - log_addr(VERB_ALGO, str, &a->addr, a->addrlen); + if(a->tls_auth_name) + snprintf(s, sizeof(s), "%s[%s]", str, + a->tls_auth_name); + else snprintf(s, sizeof(s), "%s", str); + log_addr(VERB_ALGO, s, &a->addr, a->addrlen); } } } @@ -369,10 +385,10 @@ continue; if(ntohs(s->rk.type) == LDNS_RR_TYPE_A) { - if(!delegpt_add_rrset_A(dp, region, s, 0)) + if(!delegpt_add_rrset_A(dp, region, s, 0, NULL)) return NULL; } else if(ntohs(s->rk.type) == LDNS_RR_TYPE_AAAA) { - if(!delegpt_add_rrset_AAAA(dp, region, s, 0)) + if(!delegpt_add_rrset_AAAA(dp, region, s, 0, NULL)) return NULL; } } @@ -403,7 +419,7 @@ int delegpt_add_rrset_A(struct delegpt* dp, struct regional* region, - struct ub_packed_rrset_key* ak, uint8_t lame) + struct ub_packed_rrset_key* ak, uint8_t lame, int* additions) { struct packed_rrset_data* d=(struct packed_rrset_data*)ak->entry.data; size_t i; @@ -419,7 +435,7 @@ memmove(&sa.sin_addr, d->rr_data[i]+2, INET_SIZE); if(!delegpt_add_target(dp, region, ak->rk.dname, ak->rk.dname_len, (struct sockaddr_storage*)&sa, - len, (d->security==sec_status_bogus), lame)) + len, (d->security==sec_status_bogus), lame, additions)) return 0; } return 1; @@ -427,7 +443,7 @@ int delegpt_add_rrset_AAAA(struct delegpt* dp, struct regional* region, - struct ub_packed_rrset_key* ak, uint8_t lame) + struct ub_packed_rrset_key* ak, uint8_t lame, int* additions) { struct packed_rrset_data* d=(struct packed_rrset_data*)ak->entry.data; size_t i; @@ -443,7 +459,7 @@ memmove(&sa.sin6_addr, d->rr_data[i]+2, INET6_SIZE); if(!delegpt_add_target(dp, region, ak->rk.dname, ak->rk.dname_len, (struct sockaddr_storage*)&sa, - len, (d->security==sec_status_bogus), lame)) + len, (d->security==sec_status_bogus), lame, additions)) return 0; } return 1; @@ -451,7 +467,7 @@ int delegpt_add_rrset(struct delegpt* dp, struct regional* region, - struct ub_packed_rrset_key* rrset, uint8_t lame) + struct ub_packed_rrset_key* rrset, uint8_t lame, int* additions) { if(!rrset) return 1; @@ -458,13 +474,26 @@ if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_NS) return delegpt_rrset_add_ns(dp, region, rrset, lame); else if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_A) - return delegpt_add_rrset_A(dp, region, rrset, lame); + return delegpt_add_rrset_A(dp, region, rrset, lame, additions); else if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_AAAA) - return delegpt_add_rrset_AAAA(dp, region, rrset, lame); + return delegpt_add_rrset_AAAA(dp, region, rrset, lame, + additions); log_warn("Unknown rrset type added to delegpt"); return 1; } +void delegpt_mark_neg(struct delegpt_ns* ns, uint16_t qtype) +{ + if(ns) { + if(qtype == LDNS_RR_TYPE_A) + ns->got4 = 2; + else if(qtype == LDNS_RR_TYPE_AAAA) + ns->got6 = 2; + if(ns->got4 && ns->got6) + ns->resolved = 1; + } +} + void delegpt_add_neg_msg(struct delegpt* dp, struct msgreply_entry* msg) { struct reply_info* rep = (struct reply_info*)msg->entry.data; @@ -474,14 +503,7 @@ if(FLAGS_GET_RCODE(rep->flags) != 0 || rep->an_numrrsets == 0) { struct delegpt_ns* ns = delegpt_find_ns(dp, msg->key.qname, msg->key.qname_len); - if(ns) { - if(msg->key.qtype == LDNS_RR_TYPE_A) - ns->got4 = 1; - else if(msg->key.qtype == LDNS_RR_TYPE_AAAA) - ns->got6 = 1; - if(ns->got4 && ns->got6) - ns->resolved = 1; - } + delegpt_mark_neg(ns, msg->key.qtype); } } @@ -538,6 +560,7 @@ a = dp->target_list; while(a) { na = a->next_target; + free(a->tls_auth_name); free(a); a = na; } @@ -584,7 +607,7 @@ } int delegpt_add_addr_mlc(struct delegpt* dp, struct sockaddr_storage* addr, - socklen_t addrlen, uint8_t bogus, uint8_t lame) + socklen_t addrlen, uint8_t bogus, uint8_t lame, char* tls_auth_name) { struct delegpt_addr* a; log_assert(dp->dp_type_mlc); @@ -611,6 +634,15 @@ a->bogus = bogus; a->lame = lame; a->dnsseclame = 0; + if(tls_auth_name) { + a->tls_auth_name = strdup(tls_auth_name); + if(!a->tls_auth_name) { + free(a); + return 0; + } + } else { + a->tls_auth_name = NULL; + } return 1; } @@ -631,7 +663,7 @@ if(ns->got4 && ns->got6) ns->resolved = 1; } - return delegpt_add_addr_mlc(dp, addr, addrlen, bogus, lame); + return delegpt_add_addr_mlc(dp, addr, addrlen, bogus, lame, NULL); } size_t delegpt_get_mem(struct delegpt* dp) --- contrib/unbound/iterator/iter_delegpt.h.orig +++ contrib/unbound/iterator/iter_delegpt.h @@ -81,6 +81,12 @@ uint8_t has_parent_side_NS; /** for assertions on type of delegpt */ uint8_t dp_type_mlc; + /** use SSL for upstream query */ + uint8_t ssl_upstream; + /** delegpt from authoritative zone that is locally hosted */ + uint8_t auth_dp; + /*** no cache */ + int no_cache; }; /** @@ -100,9 +106,10 @@ * and marked true if got4 and got6 are both true. */ int resolved; - /** if the ipv4 address is in the delegpt */ + /** if the ipv4 address is in the delegpt, 0=not, 1=yes 2=negative, + * negative means it was done, but no content. */ uint8_t got4; - /** if the ipv6 address is in the delegpt */ + /** if the ipv6 address is in the delegpt, 0=not, 1=yes 2=negative */ uint8_t got6; /** * If the name is parent-side only and thus dispreferred. @@ -147,6 +154,8 @@ * option is useful to mark the address dnsseclame. * This value is not copied in addr-copy and dp-copy. */ uint8_t dnsseclame; + /** the TLS authentication name, (if not NULL) to use. */ + char* tls_auth_name; }; /** @@ -207,11 +216,12 @@ * @param addrlen: the length of addr. * @param bogus: security status for the address, pass true if bogus. * @param lame: address is lame. + * @param additions: will be set to 1 if a new address is added * @return false on error. */ int delegpt_add_target(struct delegpt* dp, struct regional* regional, uint8_t* name, size_t namelen, struct sockaddr_storage* addr, - socklen_t addrlen, uint8_t bogus, uint8_t lame); + socklen_t addrlen, uint8_t bogus, uint8_t lame, int* additions); /** * Add A RRset to delegpt. @@ -219,10 +229,11 @@ * @param regional: where to allocate the info. * @param rrset: RRset A to add. * @param lame: rrset is lame, disprefer it. + * @param additions: will be set to 1 if a new address is added * @return 0 on alloc error. */ int delegpt_add_rrset_A(struct delegpt* dp, struct regional* regional, - struct ub_packed_rrset_key* rrset, uint8_t lame); + struct ub_packed_rrset_key* rrset, uint8_t lame, int* additions); /** * Add AAAA RRset to delegpt. @@ -230,10 +241,11 @@ * @param regional: where to allocate the info. * @param rrset: RRset AAAA to add. * @param lame: rrset is lame, disprefer it. + * @param additions: will be set to 1 if a new address is added * @return 0 on alloc error. */ int delegpt_add_rrset_AAAA(struct delegpt* dp, struct regional* regional, - struct ub_packed_rrset_key* rrset, uint8_t lame); + struct ub_packed_rrset_key* rrset, uint8_t lame, int* additions); /** * Add any RRset to delegpt. @@ -242,10 +254,11 @@ * @param regional: where to allocate the info. * @param rrset: RRset to add, NS, A, AAAA. * @param lame: rrset is lame, disprefer it. + * @param additions: will be set to 1 if a new address is added * @return 0 on alloc error. */ int delegpt_add_rrset(struct delegpt* dp, struct regional* regional, - struct ub_packed_rrset_key* rrset, uint8_t lame); + struct ub_packed_rrset_key* rrset, uint8_t lame, int* additions); /** * Add address to the delegation point. No servername is associated or checked. @@ -255,11 +268,13 @@ * @param addrlen: the length of addr. * @param bogus: if address is bogus. * @param lame: if address is lame. + * @param tls_auth_name: TLS authentication name (or NULL). + * @param additions: will be set to 1 if a new address is added * @return false on error. */ int delegpt_add_addr(struct delegpt* dp, struct regional* regional, struct sockaddr_storage* addr, socklen_t addrlen, - uint8_t bogus, uint8_t lame); + uint8_t bogus, uint8_t lame, char* tls_auth_name, int* additions); /** * Find NS record in name list of delegation point. @@ -333,6 +348,14 @@ struct regional* regional); /** + * Mark negative return in delegation point for specific nameserver. + * sets the got4 or got6 to negative, updates the ns->resolved. + * @param ns: the nameserver in the delegpt. + * @param qtype: A or AAAA (host order). + */ +void delegpt_mark_neg(struct delegpt_ns* ns, uint16_t qtype); + +/** * Add negative message to delegation point. * @param dp: delegation point. * @param msg: the message added, marks off A or AAAA from an NS entry. @@ -355,7 +378,7 @@ /** * create malloced delegation point, with the given name - * @param name: uncompressed wireformat of degegpt name. + * @param name: uncompressed wireformat of delegpt name. * @return NULL on alloc failure */ struct delegpt* delegpt_create_mlc(uint8_t* name); @@ -390,10 +413,11 @@ * @param addrlen: the length of addr. * @param bogus: if address is bogus. * @param lame: if address is lame. + * @param tls_auth_name: TLS authentication name (or NULL). * @return false on error. */ int delegpt_add_addr_mlc(struct delegpt* dp, struct sockaddr_storage* addr, - socklen_t addrlen, uint8_t bogus, uint8_t lame); + socklen_t addrlen, uint8_t bogus, uint8_t lame, char* tls_auth_name); /** * Add target address to the delegation point. --- contrib/unbound/iterator/iter_donotq.h.orig +++ contrib/unbound/iterator/iter_donotq.h @@ -58,7 +58,7 @@ * contents of type addr_tree_node. Each node is an address span * that must not be used to send queries to. */ - rbtree_t tree; + rbtree_type tree; }; /** --- contrib/unbound/iterator/iter_fwd.c.orig +++ contrib/unbound/iterator/iter_fwd.c @@ -82,7 +82,7 @@ free(n); } -static void delfwdnode(rbnode_t* n, void* ATTR_UNUSED(arg)) +static void delfwdnode(rbnode_type* n, void* ATTR_UNUSED(arg)) { struct iter_forward_zone* node = (struct iter_forward_zone*)n; fwd_zone_free(node); @@ -231,14 +231,21 @@ struct config_strlist* p; struct sockaddr_storage addr; socklen_t addrlen; + char* tls_auth_name; for(p = s->addrs; p; p = p->next) { log_assert(p->str); - if(!extstrtoaddr(p->str, &addr, &addrlen)) { + if(!authextstrtoaddr(p->str, &addr, &addrlen, &tls_auth_name)) { log_err("cannot parse forward %s ip address: '%s'", s->name, p->str); return 0; } - if(!delegpt_add_addr_mlc(dp, &addr, addrlen, 0, 0)) { +#if ! defined(HAVE_SSL_SET1_HOST) && ! defined(HAVE_X509_VERIFY_PARAM_SET1_HOST) + if(tls_auth_name) + log_err("no name verification functionality in " + "ssl library, ignored name for %s", p->str); +#endif + if(!delegpt_add_addr_mlc(dp, &addr, addrlen, 0, 0, + tls_auth_name)) { log_err("out of memory"); return 0; } @@ -265,6 +272,10 @@ * last resort will ask for parent-side NS record and thus * fallback to the internet name servers on a failure */ dp->has_parent_side_NS = (uint8_t)!s->isfirst; + /* Do not cache if set. */ + dp->no_cache = s->no_cache; + /* use SSL for queries to this forwarder */ + dp->ssl_upstream = (uint8_t)s->ssl_upstream; verbose(VERB_QUERY, "Forward zone server list:"); delegpt_log(VERB_QUERY, dp); if(!forwards_insert(fwd, LDNS_RR_CLASS_IN, dp)) @@ -330,7 +341,7 @@ struct delegpt* forwards_find(struct iter_forwards* fwd, uint8_t* qname, uint16_t qclass) { - rbnode_t* res = NULL; + rbnode_type* res = NULL; struct iter_forward_zone key; key.node.key = &key; key.dclass = qclass; @@ -345,7 +356,7 @@ forwards_lookup(struct iter_forwards* fwd, uint8_t* qname, uint16_t qclass) { /* lookup the forward zone in the tree */ - rbnode_t* res = NULL; + rbnode_type* res = NULL; struct iter_forward_zone *result; struct iter_forward_zone key; key.node.key = &key; @@ -386,7 +397,7 @@ forwards_next_root(struct iter_forwards* fwd, uint16_t* dclass) { struct iter_forward_zone key; - rbnode_t* n; + rbnode_type* n; struct iter_forward_zone* p; if(*dclass == 0) { /* first root item is first item in tree */ --- contrib/unbound/iterator/iter_fwd.h.orig +++ contrib/unbound/iterator/iter_fwd.h @@ -57,7 +57,7 @@ * match which gives the ancestor needed. * contents of type iter_forward_zone. */ - rbtree_t* tree; + rbtree_type* tree; }; /** @@ -65,7 +65,7 @@ */ struct iter_forward_zone { /** redblacktree node, key is this structure: class and name */ - rbnode_t node; + rbnode_type node; /** name */ uint8_t* name; /** length of name */ --- contrib/unbound/iterator/iter_hints.c.orig +++ contrib/unbound/iterator/iter_hints.c @@ -67,7 +67,7 @@ free(s); } -static void delhintnode(rbnode_t* n, void* ATTR_UNUSED(arg)) +static void delhintnode(rbnode_type* n, void* ATTR_UNUSED(arg)) { struct iter_hints_stub* node = (struct iter_hints_stub*)n; hints_stub_free(node); @@ -129,7 +129,7 @@ dp->has_parent_side_NS = 1; if(do_ip4) { if(!ah(dp, "A.ROOT-SERVERS.NET.", "198.41.0.4")) goto failed; - if(!ah(dp, "B.ROOT-SERVERS.NET.", "192.228.79.201")) goto failed; + if(!ah(dp, "B.ROOT-SERVERS.NET.", "199.9.14.201")) goto failed; if(!ah(dp, "C.ROOT-SERVERS.NET.", "192.33.4.12")) goto failed; if(!ah(dp, "D.ROOT-SERVERS.NET.", "199.7.91.13")) goto failed; if(!ah(dp, "E.ROOT-SERVERS.NET.", "192.203.230.10")) goto failed; @@ -144,11 +144,12 @@ } if(do_ip6) { if(!ah(dp, "A.ROOT-SERVERS.NET.", "2001:503:ba3e::2:30")) goto failed; - if(!ah(dp, "B.ROOT-SERVERS.NET.", "2001:500:84::b")) goto failed; + if(!ah(dp, "B.ROOT-SERVERS.NET.", "2001:500:200::b")) goto failed; if(!ah(dp, "C.ROOT-SERVERS.NET.", "2001:500:2::c")) goto failed; if(!ah(dp, "D.ROOT-SERVERS.NET.", "2001:500:2d::d")) goto failed; if(!ah(dp, "E.ROOT-SERVERS.NET.", "2001:500:a8::e")) goto failed; if(!ah(dp, "F.ROOT-SERVERS.NET.", "2001:500:2f::f")) goto failed; + if(!ah(dp, "G.ROOT-SERVERS.NET.", "2001:500:12::d0d")) goto failed; if(!ah(dp, "H.ROOT-SERVERS.NET.", "2001:500:1::53")) goto failed; if(!ah(dp, "I.ROOT-SERVERS.NET.", "2001:7fe::53")) goto failed; if(!ah(dp, "J.ROOT-SERVERS.NET.", "2001:503:c27::2:30")) goto failed; @@ -243,14 +244,21 @@ struct config_strlist* p; struct sockaddr_storage addr; socklen_t addrlen; + char* auth_name; for(p = s->addrs; p; p = p->next) { log_assert(p->str); - if(!extstrtoaddr(p->str, &addr, &addrlen)) { + if(!authextstrtoaddr(p->str, &addr, &addrlen, &auth_name)) { log_err("cannot parse stub %s ip address: '%s'", s->name, p->str); return 0; } - if(!delegpt_add_addr_mlc(dp, &addr, addrlen, 0, 0)) { +#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", p->str); +#endif + if(!delegpt_add_addr_mlc(dp, &addr, addrlen, 0, 0, + auth_name)) { log_err("out of memory"); return 0; } @@ -275,6 +283,10 @@ * last resort will ask for parent-side NS record and thus * fallback to the internet name servers on a failure */ dp->has_parent_side_NS = (uint8_t)!s->isfirst; + /* Do not cache if set. */ + dp->no_cache = s->no_cache; + /* ssl_upstream */ + dp->ssl_upstream = (uint8_t)s->ssl_upstream; delegpt_log(VERB_QUERY, dp); if(!hints_insert(hints, LDNS_RR_CLASS_IN, dp, !s->isprime)) return 0; --- contrib/unbound/iterator/iter_hints.h.orig +++ contrib/unbound/iterator/iter_hints.h @@ -59,7 +59,7 @@ * contents of type iter_hints_stub. The class IN root is in here. * uses name_tree_node from dnstree.h. */ - rbtree_t tree; + rbtree_type tree; }; /** --- contrib/unbound/iterator/iter_priv.h.orig +++ contrib/unbound/iterator/iter_priv.h @@ -60,7 +60,7 @@ * contents of type addr_tree_node. * No further data need, only presence or absence. */ - rbtree_t a; + rbtree_type a; /** * Tree of the domains spans that are allowed to contain * the blocked address spans. @@ -67,7 +67,7 @@ * contents of type name_tree_node. * No further data need, only presence or absence. */ - rbtree_t n; + rbtree_type n; }; /** --- contrib/unbound/iterator/iter_scrub.c.orig +++ contrib/unbound/iterator/iter_scrub.c @@ -161,8 +161,8 @@ for(rr = rrset->rr_first; rr; rr = rr->next) { if(get_additional_name(rrset, rr, &nm, &nmlen, pkt)) { /* mark A */ - hashvalue_t h = pkt_hash_rrset(pkt, nm, LDNS_RR_TYPE_A, - rrset->rrset_class, 0); + 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); @@ -185,8 +185,9 @@ /** Get target name of a CNAME */ static int parse_get_cname_target(struct rrset_parse* rrset, uint8_t** sname, - size_t* snamelen) + 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 " @@ -204,6 +205,19 @@ *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; } @@ -215,8 +229,12 @@ /* 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)) + 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 */ @@ -316,6 +334,18 @@ 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; +} + /** * This routine normalizes a response. This includes removing "irrelevant" * records from the answer and additional sections and (re)synthesizing @@ -372,7 +402,7 @@ /* check next cname */ uint8_t* t = NULL; size_t tlen = 0; - if(!parse_get_cname_target(nx, &t, &tlen)) + 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 */ @@ -423,7 +453,7 @@ size_t tlen = 0; if(synth_cname(sname, snamelen, nx, alias, &aliaslen, pkt) && - parse_get_cname_target(rrset, &t, &tlen) && + 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 @@ -437,12 +467,14 @@ rrset->rrset_all_next = nx->rrset_all_next; nx->rrset_all_next = rrset; - prev = nx; + /* 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)) + if(!parse_get_cname_target(rrset, &sname, &snamelen, pkt)) return 0; prev = rrset; rrset = rrset->rrset_all_next; @@ -495,6 +527,19 @@ "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 { @@ -503,6 +548,24 @@ 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; @@ -575,18 +638,6 @@ (void)rrset_cache_update(env->rrset_cache, &ref, env->alloc, now); } -/** 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 right hand name in NSEC is within zone * @param rrset: the NSEC rrset --- contrib/unbound/iterator/iter_utils.c.orig +++ contrib/unbound/iterator/iter_utils.c @@ -108,7 +108,7 @@ /** apply config caps whitelist items to name tree */ static int -caps_white_apply_cfg(rbtree_t* ntree, struct config_file* cfg) +caps_white_apply_cfg(rbtree_type* ntree, struct config_file* cfg) { struct config_strlist* p; for(p=cfg->caps_whitelist; p; p=p->next) { @@ -282,10 +282,13 @@ static int iter_fill_rtt(struct iter_env* iter_env, struct module_env* env, uint8_t* name, size_t namelen, uint16_t qtype, time_t now, - struct delegpt* dp, int* best_rtt, struct sock_list* blacklist) + struct delegpt* dp, int* best_rtt, struct sock_list* blacklist, + size_t* num_suitable_results) { int got_it = 0; struct delegpt_addr* a; + *num_suitable_results = 0; + if(dp->bogus) return 0; /* NS bogus, all bogus, nothing found */ for(a=dp->result_list; a; a = a->next_result) { @@ -301,11 +304,58 @@ } else if(a->sel_rtt < *best_rtt) { *best_rtt = a->sel_rtt; } + (*num_suitable_results)++; } } return got_it; } +/** compare two rtts, return -1, 0 or 1 */ +static int +rtt_compare(const void* x, const void* y) +{ + if(*(int*)x == *(int*)y) + return 0; + if(*(int*)x > *(int*)y) + return 1; + return -1; +} + +/** get RTT for the Nth fastest server */ +static int +nth_rtt(struct delegpt_addr* result_list, size_t num_results, size_t n) +{ + int rtt_band; + size_t i; + int* rtt_list, *rtt_index; + + if(num_results < 1 || n >= num_results) { + return -1; + } + + rtt_list = calloc(num_results, sizeof(int)); + if(!rtt_list) { + log_err("malloc failure: allocating rtt_list"); + return -1; + } + rtt_index = rtt_list; + + for(i=0; isel_rtt != -1) { + *rtt_index = result_list->sel_rtt; + rtt_index++; + } + result_list=result_list->next_result; + } + qsort(rtt_list, num_results, sizeof(*rtt_list), rtt_compare); + + log_assert(n > 0); + rtt_band = rtt_list[n-1]; + free(rtt_list); + + return rtt_band; +} + /** filter the address list, putting best targets at front, * returns number of best targets (or 0, no suitable targets) */ static int @@ -312,14 +362,15 @@ iter_filter_order(struct iter_env* iter_env, struct module_env* env, uint8_t* name, size_t namelen, uint16_t qtype, time_t now, struct delegpt* dp, int* selected_rtt, int open_target, - struct sock_list* blacklist) + struct sock_list* blacklist, time_t prefetch) { - int got_num = 0, low_rtt = 0, swap_to_front; + int got_num = 0, low_rtt = 0, swap_to_front, rtt_band = RTT_BAND, nth; + size_t num_results; struct delegpt_addr* a, *n, *prev=NULL; /* fillup sel_rtt and find best rtt in the bunch */ got_num = iter_fill_rtt(iter_env, env, name, namelen, qtype, now, dp, - &low_rtt, blacklist); + &low_rtt, blacklist, &num_results); if(got_num == 0) return 0; if(low_rtt >= USEFUL_SERVER_TOP_TIMEOUT && @@ -329,6 +380,21 @@ return 0 to force the caller to fetch more */ } + if(env->cfg->fast_server_permil != 0 && prefetch == 0 && + num_results > env->cfg->fast_server_num && + ub_random_max(env->rnd, 1000) < env->cfg->fast_server_permil) { + /* the query is not prefetch, but for a downstream client, + * there are more servers available then the fastest N we want + * to choose from. Limit our choice to the fastest servers. */ + nth = nth_rtt(dp->result_list, num_results, + env->cfg->fast_server_num); + if(nth > 0) { + rtt_band = nth - low_rtt; + if(rtt_band > RTT_BAND) + rtt_band = RTT_BAND; + } + } + got_num = 0; a = dp->result_list; while(a) { @@ -340,10 +406,10 @@ } /* classify the server address and determine what to do */ swap_to_front = 0; - if(a->sel_rtt >= low_rtt && a->sel_rtt - low_rtt <= RTT_BAND) { + if(a->sel_rtt >= low_rtt && a->sel_rtt - low_rtt <= rtt_band) { got_num++; swap_to_front = 1; - } else if(a->sel_rttsel_rtt<=RTT_BAND) { + } else if(a->sel_rttsel_rtt<=rtt_band) { got_num++; swap_to_front = 1; } @@ -365,11 +431,34 @@ int got_num6 = 0; int low_rtt6 = 0; int i; + int attempt = -1; /* filter to make sure addresses have + less attempts on them than the first, to force round + robin when all the IPv6 addresses fail */ + int num4ok = 0; /* number ip4 at low attempt count */ + int num4_lowrtt = 0; prev = NULL; a = dp->result_list; for(i = 0; i < got_num; i++) { swap_to_front = 0; + if(a->addr.ss_family != AF_INET6 && attempt == -1) { + /* if we only have ip4 at low attempt count, + * then ip6 is failing, and we need to + * select one of the remaining IPv4 addrs */ + attempt = a->attempts; + num4ok++; + num4_lowrtt = a->sel_rtt; + } else if(a->addr.ss_family != AF_INET6 && attempt == a->attempts) { + num4ok++; + if(num4_lowrtt == 0 || a->sel_rtt < num4_lowrtt) { + num4_lowrtt = a->sel_rtt; + } + } if(a->addr.ss_family == AF_INET6) { + if(attempt == -1) { + attempt = a->attempts; + } else if(a->attempts > attempt) { + break; + } got_num6++; swap_to_front = 1; if(low_rtt6 == 0 || a->sel_rtt < low_rtt6) { @@ -391,6 +480,9 @@ if(got_num6 > 0) { got_num = got_num6; *selected_rtt = low_rtt6; + } else if(num4ok > 0) { + got_num = num4ok; + *selected_rtt = num4_lowrtt; } } return got_num; @@ -400,13 +492,14 @@ iter_server_selection(struct iter_env* iter_env, struct module_env* env, struct delegpt* dp, uint8_t* name, size_t namelen, uint16_t qtype, int* dnssec_lame, - int* chase_to_rd, int open_target, struct sock_list* blacklist) + int* chase_to_rd, int open_target, struct sock_list* blacklist, + time_t prefetch) { int sel; int selrtt; struct delegpt_addr* a, *prev; int num = iter_filter_order(iter_env, env, name, namelen, qtype, - *env->now, dp, &selrtt, open_target, blacklist); + *env->now, dp, &selrtt, open_target, blacklist, prefetch); if(num == 0) return NULL; @@ -532,6 +625,7 @@ qinf.qname_len = namelen; qinf.qtype = t; qinf.qclass = c; + qinf.local_alias = NULL; fptr_ok(fptr_whitelist_modenv_detect_cycle( qstate->env->detect_cycle)); return (*qstate->env->detect_cycle)(qstate, &qinf, @@ -624,7 +718,7 @@ } int -iter_indicates_dnssec_fwd(struct module_env* env, struct query_info *qinfo) +iter_qname_indicates_dnssec(struct module_env* env, struct query_info *qinfo) { struct trust_anchor* a; if(!env || !env->anchors || !qinfo || !qinfo->qname) @@ -655,6 +749,11 @@ /* a trust anchor exists with this name, RRSIGs expected */ if((a=anchor_find(env->anchors, dp->name, dp->namelabs, dp->namelen, dclass))) { + if(a->numDS == 0 && a->numDNSKEY == 0) { + /* insecure trust point */ + lock_basic_unlock(&a->lock); + return 0; + } lock_basic_unlock(&a->lock); return 1; } @@ -783,10 +882,35 @@ return 1; } +/** compare rrsets and sort canonically. Compares rrset name, type, class. + * return 0 if equal, +1 if x > y, and -1 if x < y. + */ +static int +rrset_canonical_sort_cmp(const void* x, const void* y) +{ + struct ub_packed_rrset_key* rrx = *(struct ub_packed_rrset_key**)x; + struct ub_packed_rrset_key* rry = *(struct ub_packed_rrset_key**)y; + int r = dname_canonical_compare(rrx->rk.dname, rry->rk.dname); + if(r != 0) + return r; + if(rrx->rk.type != rry->rk.type) { + if(ntohs(rrx->rk.type) > ntohs(rry->rk.type)) + return 1; + else return -1; + } + if(rrx->rk.rrset_class != rry->rk.rrset_class) { + if(ntohs(rrx->rk.rrset_class) > ntohs(rry->rk.rrset_class)) + return 1; + else return -1; + } + return 0; +} + int reply_equal(struct reply_info* p, struct reply_info* q, struct regional* region) { size_t i; + struct ub_packed_rrset_key** sorted_p, **sorted_q; if(p->flags != q->flags || p->qdcount != q->qdcount || /* do not check TTL, this may differ */ @@ -800,16 +924,43 @@ p->ar_numrrsets != q->ar_numrrsets || p->rrset_count != q->rrset_count) return 0; + /* sort the rrsets in the authority and additional sections before + * compare, the query and answer sections are ordered in the sequence + * they should have (eg. one after the other for aliases). */ + sorted_p = (struct ub_packed_rrset_key**)regional_alloc_init( + region, p->rrsets, sizeof(*sorted_p)*p->rrset_count); + if(!sorted_p) return 0; + log_assert(p->an_numrrsets + p->ns_numrrsets + p->ar_numrrsets <= + p->rrset_count); + qsort(sorted_p + p->an_numrrsets, p->ns_numrrsets, + sizeof(*sorted_p), rrset_canonical_sort_cmp); + qsort(sorted_p + p->an_numrrsets + p->ns_numrrsets, p->ar_numrrsets, + sizeof(*sorted_p), rrset_canonical_sort_cmp); + + sorted_q = (struct ub_packed_rrset_key**)regional_alloc_init( + region, q->rrsets, sizeof(*sorted_q)*q->rrset_count); + if(!sorted_q) { + regional_free_all(region); + return 0; + } + log_assert(q->an_numrrsets + q->ns_numrrsets + q->ar_numrrsets <= + q->rrset_count); + qsort(sorted_q + q->an_numrrsets, q->ns_numrrsets, + sizeof(*sorted_q), rrset_canonical_sort_cmp); + qsort(sorted_q + q->an_numrrsets + q->ns_numrrsets, q->ar_numrrsets, + sizeof(*sorted_q), rrset_canonical_sort_cmp); + + /* compare the rrsets */ for(i=0; irrset_count; i++) { - if(!rrset_equal(p->rrsets[i], q->rrsets[i])) { - if(!rrset_canonical_equal(region, p->rrsets[i], - q->rrsets[i])) { + if(!rrset_equal(sorted_p[i], sorted_q[i])) { + if(!rrset_canonical_equal(region, sorted_p[i], + sorted_q[i])) { regional_free_all(region); return 0; } - regional_free_all(region); } } + regional_free_all(region); return 1; } @@ -991,7 +1142,7 @@ log_rrset_key(VERB_ALGO, "found parent-side", akey); ns->done_pside4 = 1; /* a negative-cache-element has no addresses it adds */ - if(!delegpt_add_rrset_A(dp, region, akey, 1)) + if(!delegpt_add_rrset_A(dp, region, akey, 1, NULL)) log_err("malloc failure in lookup_parent_glue"); lock_rw_unlock(&akey->entry.lock); } @@ -1003,7 +1154,7 @@ log_rrset_key(VERB_ALGO, "found parent-side", akey); ns->done_pside6 = 1; /* a negative-cache-element has no addresses it adds */ - if(!delegpt_add_rrset_AAAA(dp, region, akey, 1)) + if(!delegpt_add_rrset_AAAA(dp, region, akey, 1, NULL)) log_err("malloc failure in lookup_parent_glue"); lock_rw_unlock(&akey->entry.lock); } @@ -1060,6 +1211,19 @@ } } +void +iter_scrub_nxdomain(struct dns_msg* msg) +{ + if(msg->rep->an_numrrsets == 0) + return; + + memmove(msg->rep->rrsets, msg->rep->rrsets+msg->rep->an_numrrsets, + sizeof(struct ub_packed_rrset_key*) * + (msg->rep->rrset_count-msg->rep->an_numrrsets)); + msg->rep->rrset_count -= msg->rep->an_numrrsets; + msg->rep->an_numrrsets = 0; +} + void iter_dec_attempts(struct delegpt* dp, int d) { struct delegpt_addr* a; @@ -1167,3 +1331,50 @@ return 0; return 1; } + +int +iter_stub_fwd_no_cache(struct module_qstate *qstate, struct query_info *qinf) +{ + struct iter_hints_stub *stub; + struct delegpt *dp; + + /* Check for stub. */ + stub = hints_lookup_stub(qstate->env->hints, qinf->qname, + qinf->qclass, NULL); + dp = forwards_lookup(qstate->env->fwds, qinf->qname, qinf->qclass); + + /* see if forward or stub is more pertinent */ + if(stub && stub->dp && dp) { + if(dname_strict_subdomain(dp->name, dp->namelabs, + stub->dp->name, stub->dp->namelabs)) { + stub = NULL; /* ignore stub, forward is lower */ + } else { + dp = NULL; /* ignore forward, stub is lower */ + } + } + + /* check stub */ + if (stub != NULL && stub->dp != NULL) { + if(stub->dp->no_cache) { + char qname[255+1]; + char dpname[255+1]; + dname_str(qinf->qname, qname); + dname_str(stub->dp->name, dpname); + verbose(VERB_ALGO, "stub for %s %s has no_cache", qname, dpname); + } + return (stub->dp->no_cache); + } + + /* Check for forward. */ + if (dp) { + if(dp->no_cache) { + char qname[255+1]; + char dpname[255+1]; + dname_str(qinf->qname, qname); + dname_str(dp->name, dpname); + verbose(VERB_ALGO, "forward for %s %s has no_cache", qname, dpname); + } + return (dp->no_cache); + } + return 0; +} --- contrib/unbound/iterator/iter_utils.h.orig +++ contrib/unbound/iterator/iter_utils.h @@ -87,6 +87,10 @@ * @param open_target: number of currently outstanding target queries. * If we wait for these, perhaps more server addresses become available. * @param blacklist: the IP blacklist to use. + * @param prefetch: if not 0, prefetch is in use for this query. + * This means the query can have different timing, because prefetch is + * not waited upon by the downstream client, and thus a good time to + * perform exploration of other targets. * @return best target or NULL if no target. * if not null, that target is removed from the result list in the dp. */ @@ -93,7 +97,8 @@ struct delegpt_addr* iter_server_selection(struct iter_env* iter_env, struct module_env* env, struct delegpt* dp, uint8_t* name, size_t namelen, uint16_t qtype, int* dnssec_lame, - int* chase_to_rd, int open_target, struct sock_list* blacklist); + int* chase_to_rd, int open_target, struct sock_list* blacklist, + time_t prefetch); /** * Allocate dns_msg from parsed msg, in regional. @@ -174,15 +179,14 @@ struct delegpt* dp); /** - * See if qname has DNSSEC needs in the forwarding case. This is true if - * there is a trust anchor above it. Whether there is an insecure delegation - * to the data is unknown, but CD-retry is needed. + * See if qname has DNSSEC needs. This is true if there is a trust anchor above + * it. Whether there is an insecure delegation to the data is unknown. * @param env: environment with anchors. * @param qinfo: query name and class. * @return true if trust anchor above qname, false if no anchor or insecure * point above qname. */ -int iter_indicates_dnssec_fwd(struct module_env* env, +int iter_qname_indicates_dnssec(struct module_env* env, struct query_info *qinfo); /** @@ -193,7 +197,7 @@ * @param dp: delegation point. * @param msg: delegation message, with DS if a secure referral. * @param dclass: class of query. - * @return 1 if dnssec is expected, 0 if not. + * @return 1 if dnssec is expected, 0 if not or insecure point above qname. */ int iter_indicates_dnssec(struct module_env* env, struct delegpt* dp, struct dns_msg* msg, uint16_t dclass); @@ -252,7 +256,7 @@ int caps_failed_rcode(struct reply_info* rep); /** - * Store parent-side rrset in seperate rrset cache entries for later + * Store parent-side rrset in separate rrset cache entries for later * last-resort * lookups in case the child-side versions of this information * fails. * @param env: environment with cache, time, ... @@ -331,6 +335,13 @@ uint8_t* z); /** + * Prepare an NXDOMAIN message to be used for a subdomain answer by removing all + * RRs from the ANSWER section. + * @param msg: the response to scrub. + */ +void iter_scrub_nxdomain(struct dns_msg* msg); + +/** * Remove query attempts from all available ips. For 0x20. * @param dp: delegpt. * @param d: decrease. @@ -365,4 +376,13 @@ */ int iter_dp_cangodown(struct query_info* qinfo, struct delegpt* dp); +/** + * Lookup if no_cache is set in stub or fwd. + * @param qstate: query state with env with hints and fwds. + * @param qinf: query name to lookup for. + * @return true if no_cache is set in stub or fwd. + */ +int iter_stub_fwd_no_cache(struct module_qstate *qstate, + struct query_info *qinf); + #endif /* ITERATOR_ITER_UTILS_H */ --- contrib/unbound/iterator/iterator.c.orig +++ contrib/unbound/iterator/iterator.c @@ -36,7 +36,7 @@ /** * \file * - * This file contains a module that performs recusive iterative DNS query + * This file contains a module that performs recursive iterative DNS query * processing. */ @@ -53,6 +53,7 @@ #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" @@ -68,6 +69,11 @@ #include "sldns/parseutil.h" #include "sldns/sbuffer.h" +/* in msec */ +int UNKNOWN_SERVER_NICENESS = 376; + +static void target_count_increase_nx(struct iter_qstate* iq, int num); + int iter_init(struct module_env* env, int id) { @@ -78,6 +84,12 @@ 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; @@ -88,7 +100,7 @@ /** delete caps_whitelist element */ static void -caps_free(struct rbnode_t* n, void* ATTR_UNUSED(d)) +caps_free(struct rbnode_type* n, void* ATTR_UNUSED(d)) { if(n) { free(((struct name_tree_node*)n)->name); @@ -103,6 +115,7 @@ 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); @@ -139,6 +152,7 @@ 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; @@ -210,6 +224,7 @@ 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 || @@ -222,20 +237,24 @@ 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); - if(super_iq->dp) - delegpt_log(VERB_ALGO, super_iq->dp); - log_assert(0); 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)) + 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); dpns->resolved = 1; /* mark as failed */ + if((dpns->got4 == 2 || !ie->supports_ipv4) && + (dpns->got6 == 2 || !ie->supports_ipv6)) + target_count_increase_nx(super_iq, 1); } if(qstate->qinfo.qtype == LDNS_RR_TYPE_NS) { /* prime failed to get delegation */ @@ -278,27 +297,81 @@ static int error_response_cache(struct module_qstate* qstate, int id, int rcode) { - /* store in cache */ - struct reply_info err; - if(qstate->prefetch_leeway > NORR_TTL) { - verbose(VERB_ALGO, "error response for prefetch in cache"); - /* attempt to adjust the cache entry prefetch */ - if(dns_cache_prefetch_adjust(qstate->env, &qstate->qinfo, - NORR_TTL, qstate->query_flags)) - return error_response(qstate, id, rcode); - /* if that fails (not in cache), fall through to store err */ + if(!qstate->no_cache_store) { + /* store in cache */ + struct reply_info err; + if(qstate->prefetch_leeway > NORR_TTL) { + verbose(VERB_ALGO, "error response for prefetch in cache"); + /* attempt to adjust the cache entry prefetch */ + if(dns_cache_prefetch_adjust(qstate->env, &qstate->qinfo, + NORR_TTL, qstate->query_flags)) + return error_response(qstate, id, rcode); + /* if that fails (not in cache), fall through to store err */ + } + if(qstate->env->cfg->serve_expired) { + /* if serving expired contents, and such content is + * already available, don't overwrite this servfail */ + struct msgreply_entry* msg; + if((msg=msg_cache_lookup(qstate->env, + qstate->qinfo.qname, qstate->qinfo.qname_len, + qstate->qinfo.qtype, qstate->qinfo.qclass, + qstate->query_flags, 0, + qstate->env->cfg->serve_expired_ttl_reset)) + != NULL) { + if(qstate->env->cfg->serve_expired_ttl_reset) { + struct reply_info* rep = + (struct reply_info*)msg->entry.data; + if(rep && *qstate->env->now + + qstate->env->cfg->serve_expired_ttl > + rep->serve_expired_ttl) { + rep->serve_expired_ttl = + *qstate->env->now + + qstate->env->cfg->serve_expired_ttl; + } + } + lock_rw_unlock(&msg->entry.lock); + return error_response(qstate, id, rcode); + } + /* serving expired contents, but nothing is cached + * at all, so the servfail cache entry is useful + * (stops waste of time on this servfail NORR_TTL) */ + } else { + /* don't overwrite existing (non-expired) data in + * cache with a servfail */ + struct msgreply_entry* msg; + if((msg=msg_cache_lookup(qstate->env, + qstate->qinfo.qname, qstate->qinfo.qname_len, + qstate->qinfo.qtype, qstate->qinfo.qclass, + qstate->query_flags, *qstate->env->now, 0)) + != NULL) { + struct reply_info* rep = (struct reply_info*) + msg->entry.data; + if(FLAGS_GET_RCODE(rep->flags) == + LDNS_RCODE_NOERROR || + FLAGS_GET_RCODE(rep->flags) == + LDNS_RCODE_NXDOMAIN) { + /* we have a good entry, + * don't overwrite */ + lock_rw_unlock(&msg->entry.lock); + return error_response(qstate, id, rcode); + } + lock_rw_unlock(&msg->entry.lock); + } + + } + 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); } - 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); - /* 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); return error_response(qstate, id, rcode); } @@ -344,6 +417,8 @@ 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*)); @@ -356,6 +431,8 @@ 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, @@ -372,6 +449,29 @@ } /** + * 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. @@ -453,7 +553,8 @@ * 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)) { + 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; @@ -460,7 +561,8 @@ } if(ntohs(r->rk.type) == LDNS_RR_TYPE_CNAME && - query_dname_compare(*mname, r->rk.dname) == 0) { + 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; @@ -483,6 +585,35 @@ return 1; } +/** 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) @@ -498,7 +629,7 @@ target_count_create(struct iter_qstate* iq) { if(!iq->target_count) { - iq->target_count = (int*)calloc(2, sizeof(int)); + iq->target_count = (int*)calloc(3, sizeof(int)); /* if calloc fails we simply do not track this number */ if(iq->target_count) iq->target_count[0] = 1; @@ -511,8 +642,17 @@ target_count_create(iq); if(iq->target_count) iq->target_count[1] += 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[2] += num; +} + /** * Generate a subrequest. * Generate a local request event. Local events are tied to this module, and @@ -533,6 +673,7 @@ * @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 @@ -539,7 +680,8 @@ 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) + enum iter_state finalstate, struct module_qstate** subq_ret, int v, + int detached) { struct module_qstate* subq = NULL; struct iter_qstate* subiq = NULL; @@ -551,6 +693,7 @@ 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. */ @@ -565,12 +708,24 @@ valrec = 1; } - /* 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; + 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 */ @@ -592,6 +747,7 @@ subiq->target_count = iq->target_count; if(iq->target_count) iq->target_count[0] ++; /* extra reference */ + subiq->dp_target_count = 0; subiq->num_current_queries = 0; subiq->depth = iq->depth+1; outbound_list_init(&subiq->outlist); @@ -635,7 +791,7 @@ * 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)) { + &subq, 0, 0)) { verbose(VERB_ALGO, "could not prime root"); return 0; } @@ -694,6 +850,11 @@ 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) { @@ -704,6 +865,7 @@ 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 */ } @@ -720,8 +882,9 @@ * 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)) { + 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 */ } @@ -737,6 +900,7 @@ 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 */ } @@ -755,8 +919,100 @@ } /** + * 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 adresses. + * we just got to obtain authoritative information on the addresses. * * @param qstate: the qtstate that triggered the need to prime. * @param iq: iterator query state. @@ -801,7 +1057,7 @@ 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)) { + INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1, 0)) { verbose(VERB_ALGO, "could not generate addr check"); return; } @@ -826,6 +1082,9 @@ 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 && @@ -834,12 +1093,15 @@ 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)) { + INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1, 0)) { verbose(VERB_ALGO, "could not generate ns check"); return; } @@ -896,7 +1158,7 @@ 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)) { + 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; @@ -925,7 +1187,7 @@ struct delegpt* dp; uint8_t* delname = iq->qchase.qname; size_t delnamelen = iq->qchase.qname_len; - if(iq->refetch_glue) { + if(iq->refetch_glue && iq->dp) { delname = iq->dp->name; delnamelen = iq->dp->namelen; } @@ -968,7 +1230,7 @@ { uint8_t* delname; size_t delnamelen; - struct dns_msg* msg; + struct dns_msg* msg = NULL; log_query_info(VERB_DETAIL, "resolving", &qstate->qinfo); /* check effort */ @@ -978,6 +1240,10 @@ if(iq->query_restart_count > MAX_RESTART_COUNT) { 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); } @@ -989,6 +1255,8 @@ 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); } @@ -998,28 +1266,48 @@ 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(qstate->blacklist) { + if (iter_stub_fwd_no_cache(qstate, &iq->qchase)) { + /* 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 { + } 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); - if(!msg && qstate->env->neg_cache) { - /* lookup in negative cache; may result in + qstate->region, qstate->env->scratch, 0); + 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->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 */ @@ -1045,9 +1333,12 @@ verbose(VERB_ALGO, "returning CNAME response from " "cache"); if(!handle_cname_response(qstate, iq, msg, - &sname, &slen)) + &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; /* This *is* a query restart, even if it is a cheap @@ -1056,6 +1347,7 @@ 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; @@ -1065,17 +1357,20 @@ /* 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); } iq->refetch_glue = 0; @@ -1095,6 +1390,7 @@ 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; @@ -1104,7 +1400,8 @@ 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)) { + (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 @@ -1131,8 +1428,15 @@ /* 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 */ - int r = prime_stub(qstate, iq, id, delname, + r = prime_stub(qstate, iq, id, delname, iq->qchase.qclass); if(r == 2) break; /* got noprime-stub-zone, continue */ @@ -1146,6 +1450,7 @@ 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); } @@ -1152,6 +1457,7 @@ 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); } @@ -1179,7 +1485,8 @@ * now will also exceed the rate, keeping cache fresh */ (void)infra_ratelimit_inc(qstate->env->infra_cache, iq->dp->name, iq->dp->namelen, - *qstate->env->now); + *qstate->env->now, &qstate->qinfo, + qstate->reply); /* see if we are passed through with slip factor */ if(qstate->env->cfg->ratelimit_factor != 0 && ub_random_max(qstate->env->rnd, @@ -1189,9 +1496,15 @@ "delegation point", iq->dp->name, LDNS_RR_TYPE_NS, LDNS_RR_CLASS_IN); } else { + lock_basic_lock(&ie->queries_ratelimit_lock); + ie->num_queries_ratelimited++; + lock_basic_unlock(&ie->queries_ratelimit_lock); log_nametypeclass(VERB_ALGO, "ratelimit exceeded with " "delegation point", iq->dp->name, LDNS_RR_TYPE_NS, LDNS_RR_CLASS_IN); + qstate->was_ratelimited = 1; + errinf(qstate, "query was ratelimited"); + errinf_dname(qstate, "for zone", iq->dp->name); return error_response(qstate, id, LDNS_RCODE_SERVFAIL); } } @@ -1210,6 +1523,32 @@ */ if(iter_dp_is_useless(&qstate->qinfo, qstate->query_flags, iq->dp)) { + 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 " @@ -1226,6 +1565,7 @@ 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); } @@ -1256,7 +1596,7 @@ * the same init processing as ones that do not. Request events that reach * this state must have a valid currentDelegationPoint set. * - * This part is primarly handling stub zone priming. Events that reach this + * This part is primarily handling stub zone priming. Events that reach this * state must have a current delegation point. * * @param qstate: query state. @@ -1274,16 +1614,25 @@ 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); } - delname = iq->dp->name; - delnamelen = iq->dp->namelen; - } else { - delname = iq->qchase.qname; - delnamelen = iq->qchase.qname_len; + /* Do not send queries above stub, do not set delname to dp if + * this is above stub without stub-first. */ + stub = hints_lookup_stub( + qstate->env->hints, iq->qchase.qname, iq->qchase.qclass, + iq->dp); + if(!stub || !stub->dp->has_parent_side_NS || + dname_subdomain_c(iq->dp->name, stub->dp->name)) { + delname = iq->dp->name; + delnamelen = iq->dp->namelen; + } } if(iq->qchase.qtype == LDNS_RR_TYPE_DS || iq->refetch_glue) { if(!dname_is_root(delname)) @@ -1290,6 +1639,12 @@ 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 */ @@ -1324,7 +1679,7 @@ /* If the RD flag wasn't set, then we just finish with the * cached referral as the response. */ - if(!(qstate->query_flags & BIT_RD)) { + 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", @@ -1371,7 +1726,7 @@ { struct module_qstate* subq; if(!generate_sub_request(name, namelen, qtype, qclass, qstate, - id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0)) + id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0, 0)) return 0; if(subq) { struct iter_qstate* subiq = @@ -1422,7 +1777,7 @@ { struct module_qstate* subq; if(!generate_sub_request(name, namelen, qtype, qclass, qstate, - id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0)) + id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq, 0, 0)) return 0; log_nametypeclass(VERB_QUERY, "new target", name, qtype, qclass); return 1; @@ -1461,6 +1816,14 @@ "number of glue fetches %d", s, iq->target_count[1]); 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; + } iter_mark_cycle_targets(qstate, iq->dp); missing = (int)delegpt_count_missing_targets(iq->dp); @@ -1531,35 +1894,6 @@ return 1; } -/** see if last resort is possible - does config allow queries to parent */ -static int -can_have_last_resort(struct module_env* env, struct delegpt* dp, - struct iter_qstate* iq) -{ - struct delegpt* fwddp; - struct iter_hints_stub* stub; - /* 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(dp->name) && (stub = (struct iter_hints_stub*) - name_tree_find(&env->hints->tree, dp->name, dp->namelen, - dp->namelabs, iq->qchase.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) { - verbose(VERB_QUERY, "configured stub servers failed -- returning SERVFAIL"); - return 0; - } - if((fwddp = forwards_find(env->fwds, dp->name, iq->qchase.qclass)) && - /* has_parent_side is turned off for forward_first, where - * we are allowed to go to the parent */ - fwddp->has_parent_side_NS) { - verbose(VERB_QUERY, "configured forward servers failed -- returning SERVFAIL"); - return 0; - } - 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 @@ -1581,9 +1915,13 @@ verbose(VERB_ALGO, "No more query targets, attempting last resort"); log_assert(iq->dp); - if(!can_have_last_resort(qstate->env, iq->dp, iq)) { + 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); + 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)) { @@ -1590,7 +1928,6 @@ struct delegpt* p = hints_lookup_root(qstate->env->hints, iq->qchase.qclass); if(p) { - struct delegpt_ns* ns; struct delegpt_addr* a; iq->chase_flags &= ~BIT_RD; /* go to authorities */ for(ns = p->nslist; ns; ns=ns->next) { @@ -1600,7 +1937,7 @@ 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->lame, a->tls_auth_name, NULL); } } iq->dp->has_parent_side_NS = 1; @@ -1617,6 +1954,7 @@ 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); @@ -1642,6 +1980,8 @@ 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; @@ -1653,6 +1993,7 @@ } 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 && @@ -1661,6 +2002,7 @@ dname_str(qstate->qinfo.qname, s); verbose(VERB_QUERY, "request %s has exceeded the maximum " "number of glue fetches %d", s, iq->target_count[1]); + 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 */ @@ -1668,14 +2010,29 @@ /* 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 && !ns->done_pside4)) && + !can_have_last_resort(qstate->env, ns->name, ns->namelen, + iq->qchase.qclass, NULL)) { + log_nametypeclass(VERB_ALGO, "cannot pside lookup ns " + "because it is also a stub/forward,", + ns->name, LDNS_RR_TYPE_NS, iq->qchase.qclass); + if(ie->supports_ipv6) ns->done_pside6 = 1; + if(ie->supports_ipv4) ns->done_pside4 = 1; + 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)) + 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++; } @@ -1683,9 +2040,11 @@ /* Send the A request. */ if(!generate_parentside_target_query(qstate, iq, id, ns->name, ns->namelen, - LDNS_RR_TYPE_A, iq->qchase.qclass)) + 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++; } @@ -1700,11 +2059,14 @@ /* if this was a parent-side glue query itself, then store that * failure in cache. */ - if(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(!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); verbose(VERB_QUERY, "out of query targets -- returning SERVFAIL"); /* fail -- no more targets, no more hope of targets, no hope * of a response. */ @@ -1738,6 +2100,7 @@ } /* 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); } @@ -1757,7 +2120,8 @@ 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)) { + 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); } @@ -1787,8 +2151,9 @@ int tf_policy; struct delegpt_addr* target; struct outbound_entry* outq; - /* EDNS options to set on outgoing packet */ - struct edns_option* opt_list = NULL; + int auth_fallback = 0; + uint8_t* qout_orig = NULL; + size_t qout_orig_len = 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, @@ -1804,18 +2169,28 @@ if(iq->referral_count > MAX_REFERRAL_COUNT) { verbose(VERB_QUERY, "request has exceeded the maximum " "number of referrrals with %d", iq->referral_count); + errinf(qstate, "exceeded the maximum of referrals"); return error_response(qstate, id, LDNS_RCODE_SERVFAIL); } if(iq->sent_count > MAX_SENT_COUNT) { 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); } + if(iq->target_count && iq->target_count[2] > MAX_TARGET_NX) { + verbose(VERB_QUERY, "request has exceeded the maximum " + " number of nxdomain nameserver lookups with %d", + iq->target_count[2]); + errinf(qstate, "exceeded the maximum nameserver nxdomains"); + return error_response(qstate, id, LDNS_RCODE_SERVFAIL); + } /* 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) @@ -1833,11 +2208,199 @@ 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->minimise_timeout_count = 0; + + iter_dec_attempts(iq->dp, 1); + + /* 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); + 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->minimise_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[2]==0) && iq->sent_count < TARGET_FETCH_STOP) { tf_policy = ie->target_fetch_policy[iq->depth]; } @@ -1847,6 +2410,7 @@ 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; @@ -1855,6 +2419,13 @@ /* 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? */ @@ -1875,6 +2446,7 @@ 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; } @@ -1903,7 +2475,8 @@ 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); + iq->num_target_queries, qstate->blacklist, + qstate->prefetch_leeway); /* If no usable target was selected... */ if(!target) { @@ -1926,6 +2499,8 @@ "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); } @@ -1959,6 +2534,7 @@ 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; } @@ -1985,6 +2561,13 @@ iq->num_current_queries); 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; } @@ -1991,111 +2574,19 @@ /* if not forwarding, check ratelimits per delegationpoint name */ if(!(iq->chase_flags & BIT_RD) && !iq->ratelimit_ok) { if(!infra_ratelimit_inc(qstate->env->infra_cache, iq->dp->name, - iq->dp->namelen, *qstate->env->now)) { + iq->dp->namelen, *qstate->env->now, &qstate->qinfo, + qstate->reply)) { + 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); } } - if(iq->minimisation_state == INIT_MINIMISE_STATE) { - /* (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->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); - - iq->qinfo_out.qname = iq->qchase.qname; - iq->qinfo_out.qname_len = iq->qchase.qname_len; - iq->minimise_count++; - iq->minimise_timeout_count = 0; - - iter_dec_attempts(iq->dp, 1); - - /* 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 { - 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); - if(msg && msg->rep->an_numrrsets == 0 - && FLAGS_GET_RCODE(msg->rep->flags) == - LDNS_RCODE_NOERROR) - /* no need to send query if it is already - * cached as NOERROR/NODATA */ - return 1; - } - } - if(iq->minimisation_state == SKIP_MINIMISE_STATE) { - iq->minimise_timeout_count++; - if(iq->minimise_timeout_count < MAX_MINIMISE_TIMEOUT_COUNT) - /* Do not increment qname, continue incrementing next - * iteration */ - iq->minimisation_state = MINIMISE_STATE; - else - /* 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; - /* We have a valid target. */ if(verbosity >= VERB_QUERY) { log_query_info(VERB_QUERY, "sending query:", &iq->qinfo_out); @@ -2106,19 +2597,19 @@ iq->dnssec_lame_query?" but lame_query anyway": ""); } fptr_ok(fptr_whitelist_modenv_send_query(qstate->env->send_query)); - outq = (*qstate->env->send_query)( - iq->qinfo_out.qname, iq->qinfo_out.qname_len, - iq->qinfo_out.qtype, iq->qinfo_out.qclass, + 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->blacklist&&(!iter_indicates_dnssec_fwd(qstate->env, + !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), opt_list, &target->addr, target->addrlen, - iq->dp->name, iq->dp->namelen, qstate); + ie, iq), &target->addr, target->addrlen, + iq->dp->name, iq->dp->namelen, + (iq->dp->ssl_upstream || qstate->env->cfg->ssl_upstream), + target->tls_auth_name, qstate); if(!outq) { log_addr(VERB_DETAIL, "error sending query to auth server", &target->addr, target->addrlen); @@ -2125,6 +2616,8 @@ if(!(iq->chase_flags & BIT_RD) && !iq->ratelimit_ok) infra_ratelimit_dec(qstate->env->infra_cache, iq->dp->name, iq->dp->namelen, *qstate->env->now); + if(qstate->env->cfg->qname_minimisation) + iq->minimisation_state = SKIP_MINIMISE_STATE; return next_state(iq, QUERYTARGETS_STATE); } outbound_list_insert(&iq->outlist, outq); @@ -2168,10 +2661,16 @@ int dnsseclame = 0; 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) + if(qstate->env->cfg->qname_minimisation) { + iq->minimise_timeout_count++; iq->minimisation_state = SKIP_MINIMISE_STATE; + } iq->chase_to_rd = 0; iq->dnssec_lame_query = 0; verbose(VERB_ALGO, "query response was timeout"); @@ -2179,9 +2678,10 @@ } type = response_type_from_server( (int)((iq->chase_flags&BIT_RD) || iq->chase_to_rd), - iq->response, &iq->qchase, iq->dp); + iq->response, &iq->qinfo_out, iq->dp); iq->chase_to_rd = 0; - if(type == RESPONSE_TYPE_REFERRAL && (iq->chase_flags&BIT_RD)) { + 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; @@ -2232,6 +2732,31 @@ } 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 && 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) { @@ -2259,10 +2784,11 @@ iq->num_target_queries = 0; return processDSNSFind(qstate, iq, id); } - iter_dns_store(qstate->env, &iq->response->qinfo, - iq->response->rep, 0, qstate->prefetch_leeway, - iq->dp&&iq->dp->has_parent_side_NS, - qstate->region, qstate->query_flags); + if(!qstate->no_cache_store) + iter_dns_store(qstate->env, &iq->response->qinfo, + iq->response->rep, 0, qstate->prefetch_leeway, + iq->dp&&iq->dp->has_parent_side_NS, + qstate->region, qstate->query_flags); /* close down outstanding requests to be discarded */ outbound_list_clear(&iq->outlist); iq->num_current_queries = 0; @@ -2274,17 +2800,28 @@ sock_list_insert(&qstate->reply_origin, &qstate->reply->addr, qstate->reply->addrlen, qstate->region); - if(iq->minimisation_state != DONOT_MINIMISE_STATE) { - /* Best effort qname-minimisation. - * Stop minimising and send full query when RCODE - * is not NOERROR. */ + if(iq->minimisation_state != DONOT_MINIMISE_STATE + && !(iq->chase_flags & BIT_RD)) { if(FLAGS_GET_RCODE(iq->response->rep->flags) != - LDNS_RCODE_NOERROR) + 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) { /* Stop resolving when NXDOMAIN is DNSSEC - * signed. Based on assumption that namservers + * signed. Based on assumption that nameservers * serving signed zones do not return NXDOMAIN * for empty-non-terminals. */ if(iq->dnssec_expected) @@ -2292,7 +2829,8 @@ /* Make subrequest to validate intermediate * NXDOMAIN if harden-below-nxdomain is * enabled. */ - if(qstate->env->cfg->harden_below_nxdomain) { + 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:", @@ -2304,7 +2842,7 @@ iq->response->qinfo.qclass, qstate, id, iq, INIT_REQUEST_STATE, - FINISHED_STATE, &subq, 1)) + FINISHED_STATE, &subq, 1, 1)) verbose(VERB_ALGO, "could not validate NXDOMAIN " "response"); @@ -2327,7 +2865,8 @@ } /* if hardened, only store referral if we asked for it */ - if(!qstate->env->cfg->harden_referral_path || + 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) @@ -2342,7 +2881,7 @@ 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, @@ -2355,16 +2894,17 @@ iq->response->rep, iq->dp->name); } /* store parent-side-in-zone-glue, if directly queried for */ - if(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); - } + 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 @@ -2373,11 +2913,15 @@ iq->dp = delegpt_from_message(iq->response, qstate->region); if (qstate->env->cfg->qname_minimisation) iq->minimisation_state = INIT_MINIMISE_STATE; - if(!iq->dp) + 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)) + 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); @@ -2385,6 +2929,7 @@ /* 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, @@ -2439,28 +2984,35 @@ } /* Process the CNAME response. */ if(!handle_cname_response(qstate, iq, iq->response, - &sname, &snamelen)) + &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 */ - 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); + 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); /* set the current request's qname to the new value. */ iq->qchase.qname = sname; iq->qchase.qname_len = snamelen; - if (qstate->env->cfg->qname_minimisation) - iq->minimisation_state = INIT_MINIMISE_STATE; /* Clear the query state, since this is a query restart. */ iq->deleg_msg = NULL; iq->dp = NULL; iq->dsns_point = NULL; - /* Note the query restart. */ - iq->query_restart_count++; + 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 @@ -2527,8 +3079,30 @@ /* 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) + 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); } @@ -2607,6 +3181,8 @@ 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; } @@ -2623,7 +3199,7 @@ 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)) { + INIT_REQUEST_STATE, FINISHED_STATE, &subq, 1, 0)) { verbose(VERB_ALGO, "could not generate prime check"); } generate_a_aaaa_check(qstate, iq, id); @@ -2637,7 +3213,7 @@ /** * Do final processing on responses to target queries. Events reach this * state after the iterative resolution algorithm terminates. This state is - * responsible for reactiving the original event, and housekeeping related + * responsible for reactivating the original event, and housekeeping related * to received target responses (caching, updating the current delegation * point, etc). * Callback from walk_supers for every super state that is interested in @@ -2651,6 +3227,7 @@ 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; @@ -2688,7 +3265,7 @@ 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)) + iq->pside_glue, 1, NULL)) log_err("out of memory adding pside glue"); } @@ -2699,6 +3276,7 @@ * 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, @@ -2710,13 +3288,23 @@ } /* if dpns->lame then set the address(es) lame too */ if(!delegpt_add_rrset(foriq->dp, forq->region, rrset, - dpns->lame)) + 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); dpns->resolved = 1; /* fail the target */ + if((dpns->got4 == 2 || !ie->supports_ipv4) && + (dpns->got6 == 2 || !ie->supports_ipv6)) + target_count_increase_nx(foriq, 1); } } @@ -2750,6 +3338,7 @@ 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) */ @@ -2850,6 +3439,8 @@ 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 --; @@ -2887,7 +3478,9 @@ qstate->qinfo.qname_len, qstate->qinfo.qtype, c, qstate, id, iq, INIT_REQUEST_STATE, FINISHED_STATE, &subq, - (int)!(qstate->query_flags&BIT_CD))) { + (int)!(qstate->query_flags&BIT_CD), 0)) { + errinf(qstate, "could not generate class ANY" + " lookup query"); return error_response(qstate, id, LDNS_RCODE_SERVFAIL); } @@ -2927,12 +3520,14 @@ &qstate->qinfo); /* store negative cache element for parent side glue. */ - if(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(!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); } @@ -2966,7 +3561,7 @@ /* 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->query_flags&BIT_RD) { + 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, @@ -2979,7 +3574,7 @@ } /* - * Return priming query results to interestes super querystates. + * Return priming query results to interested super querystates. * * Sets the delegation point and delegation message (not nonRD queries). * This is a callback from walk_supers. @@ -3097,12 +3692,13 @@ if(event == module_event_noreply || event == module_event_error) { if(event == module_event_noreply && iq->sent_count >= 3 && qstate->env->cfg->use_caps_bits_for_id && - !iq->caps_fallback) { + !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 @@ -3117,6 +3713,7 @@ || !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; } @@ -3141,6 +3738,22 @@ if(parse_extract_edns(prs, &edns, qstate->env->scratch) != LDNS_RCODE_NOERROR) goto handle_it; + + /* Copy the edns options we may got from the back end */ + if(edns.opt_list) { + qstate->edns_opts_back_in = edns_opt_copy_region(edns.opt_list, + 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; @@ -3153,6 +3766,7 @@ 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"); @@ -3172,9 +3786,23 @@ 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; @@ -3181,6 +3809,7 @@ 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"); @@ -3212,6 +3841,7 @@ 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; @@ -3250,6 +3880,7 @@ 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; } @@ -3267,11 +3898,13 @@ } 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); } --- contrib/unbound/iterator/iterator.h.orig +++ contrib/unbound/iterator/iterator.h @@ -36,7 +36,7 @@ /** * \file * - * This file contains a module that performs recusive iterative DNS query + * This file contains a module that performs recursive iterative DNS query * processing. */ @@ -51,10 +51,15 @@ struct iter_donotq; struct iter_prep_list; struct iter_priv; -struct rbtree_t; +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 query restarts. Determines max number of CNAME chain. */ #define MAX_RESTART_COUNT 8 /** max number of referrals. Makes sure resolver does not run away */ @@ -62,7 +67,7 @@ /** max number of queries-sent-out. Make sure large NS set does not loop */ #define MAX_SENT_COUNT 32 /** max number of queries for which to perform dnsseclameness detection, - * (rrsigs misssing detection) after that, just pick up that response */ + * (rrsigs missing detection) after that, just pick up that response */ #define DNSSEC_LAME_DETECT_COUNT 4 /** * max number of QNAME minimisation iterations. Limits number of queries for @@ -83,7 +88,7 @@ /** how nice is a server without further information, in msec * Equals rtt initial timeout value. */ -#define UNKNOWN_SERVER_NICENESS 376 +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 @@ -115,7 +120,7 @@ struct iter_priv* priv; /** whitelist for capsforid names */ - struct rbtree_t* caps_white; + struct rbtree_type* caps_white; /** The maximum dependency depth that this resolver will pursue. */ int max_dependency_depth; @@ -130,8 +135,10 @@ */ int* target_fetch_policy; - /** ip6.arpa dname in wireformat, used for qname-minimisation */ - uint8_t* ip6arpa_dname; + /** lock on ratelimit counter */ + lock_basic_type queries_ratelimit_lock; + /** number of queries that have been ratelimited */ + size_t num_queries_ratelimited; }; /** @@ -140,11 +147,11 @@ enum minimisation_state { /** * (Re)start minimisation. Outgoing QNAME should be set to dp->name. - * State entered on new query or after following refferal or CNAME. + * State entered on new query or after following referral or CNAME. */ INIT_MINIMISE_STATE = 0, /** - * QNAME minimisataion ongoing. Increase QNAME on every iteration. + * QNAME minimisation ongoing. Increase QNAME on every iteration. */ MINIMISE_STATE, /** @@ -182,7 +189,7 @@ /** * Each time a delegation point changes for a given query or a * query times out and/or wakes up, this state is (re)visited. - * This state is reponsible for iterating through a list of + * This state is responsible for iterating through a list of * nameserver targets. */ QUERYTARGETS_STATE, @@ -303,9 +310,14 @@ int sent_count; /** number of target queries spawned in [1], for this query and its - * subqueries, the malloced-array is shared, [0] refcount. */ + * subqueries, the malloced-array is shared, [0] refcount. + * in [2] the number of nxdomains is counted. */ 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; + /** if true, already tested for ratelimiting and passed the test */ int ratelimit_ok; @@ -369,6 +381,9 @@ /** QNAME minimisation state, RFC7816 */ 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. @@ -376,7 +391,7 @@ struct query_info qinfo_out; /** - * Count number of QNAME minisation iterations. Used to limit number of + * Count number of QNAME minimisation iterations. Used to limit number of * outgoing queries when QNAME minimisation is enabled. */ int minimise_count; @@ -385,6 +400,11 @@ * Count number of time-outs. Used to prevent resolving failures when * the QNAME minimisation QTYPE is blocked. */ int minimise_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; }; /** @@ -428,7 +448,7 @@ struct outbound_entry* outbound); /** - * Return priming query results to interestes super querystates. + * Return priming query results to interested super querystates. * * Sets the delegation point and delegation message (not nonRD queries). * This is a callback from walk_supers. --- contrib/unbound/libunbound/context.c.orig +++ contrib/unbound/libunbound/context.c @@ -47,6 +47,7 @@ #include "services/localzone.h" #include "services/cache/rrset.h" #include "services/cache/infra.h" +#include "services/authzone.h" #include "util/data/msgreply.h" #include "util/storage/slabhash.h" #include "sldns/sbuffer.h" @@ -54,22 +55,32 @@ int context_finalize(struct ub_ctx* ctx) { + int is_rpz = 0; struct config_file* cfg = ctx->env->cfg; verbosity = cfg->verbosity; - if(ctx->logfile_override) + if(ctx_logfile_overridden && !ctx->logfile_override) { + log_file(NULL); /* clear that override */ + ctx_logfile_overridden = 0; + } + if(ctx->logfile_override) { + ctx_logfile_overridden = 1; log_file(ctx->log_out); - else log_init(cfg->logfile, cfg->use_syslog, NULL); + } else { + log_init(cfg->logfile, cfg->use_syslog, NULL); + } config_apply(cfg); if(!modstack_setup(&ctx->mods, cfg->module_conf, ctx->env)) return UB_INITFAIL; + log_edns_known_options(VERB_ALGO, ctx->env); ctx->local_zones = local_zones_create(); if(!ctx->local_zones) return UB_NOMEM; if(!local_zones_apply_cfg(ctx->local_zones, cfg)) return UB_INITFAIL; - if(!ctx->env->msg_cache || - cfg->msg_cache_size != slabhash_get_size(ctx->env->msg_cache) || - cfg->msg_cache_slabs != ctx->env->msg_cache->size) { + if(!auth_zones_apply_cfg(ctx->env->auth_zones, cfg, 1, &is_rpz)) + return UB_INITFAIL; + if(!slabhash_is_size(ctx->env->msg_cache, cfg->msg_cache_size, + cfg->msg_cache_slabs)) { slabhash_delete(ctx->env->msg_cache); ctx->env->msg_cache = slabhash_create(cfg->msg_cache_slabs, HASH_DEFAULT_STARTARRAY, cfg->msg_cache_size, @@ -126,7 +137,7 @@ struct ctx_query* context_new(struct ub_ctx* ctx, const char* name, int rrtype, int rrclass, - ub_callback_t cb, void* cbarg) + ub_callback_type cb, ub_event_callback_type cb_event, void* cbarg) { struct ctx_query* q = (struct ctx_query*)calloc(1, sizeof(*q)); if(!q) return NULL; @@ -138,8 +149,9 @@ } lock_basic_unlock(&ctx->cfglock); q->node.key = &q->querynum; - q->async = (cb != NULL); + q->async = (cb != NULL || cb_event != NULL); q->cb = cb; + q->cb_event = cb_event; q->cb_arg = cbarg; q->res = (struct ub_result*)calloc(1, sizeof(*q->res)); if(!q->res) { @@ -289,15 +301,17 @@ * o uint32 id * o uint32 error_code * o uint32 msg_security + * o uint32 was_ratelimited * o uint32 length of why_bogus string (+1 for eos); 0 absent. * o why_bogus_string * o the remainder is the answer msg from resolver lookup. * remainder can be length 0. */ + size_t size_of_uint32s = 6 * sizeof(uint32_t); size_t pkt_len = pkt?sldns_buffer_remaining(pkt):0; size_t wlen = (pkt&&q->res->why_bogus)?strlen(q->res->why_bogus)+1:0; uint8_t* p; - *len = sizeof(uint32_t)*5 + pkt_len + wlen; + *len = size_of_uint32s + pkt_len + wlen; p = (uint8_t*)malloc(*len); if(!p) return NULL; sldns_write_uint32(p, UB_LIBCMD_ANSWER); @@ -304,11 +318,12 @@ sldns_write_uint32(p+sizeof(uint32_t), (uint32_t)q->querynum); sldns_write_uint32(p+2*sizeof(uint32_t), (uint32_t)err); sldns_write_uint32(p+3*sizeof(uint32_t), (uint32_t)q->msg_security); - sldns_write_uint32(p+4*sizeof(uint32_t), (uint32_t)wlen); + sldns_write_uint32(p+4*sizeof(uint32_t), (uint32_t)q->res->was_ratelimited); + sldns_write_uint32(p+5*sizeof(uint32_t), (uint32_t)wlen); if(wlen > 0) - memmove(p+5*sizeof(uint32_t), q->res->why_bogus, wlen); + memmove(p+size_of_uint32s, q->res->why_bogus, wlen); if(pkt_len > 0) - memmove(p+5*sizeof(uint32_t)+wlen, + memmove(p+size_of_uint32s+wlen, sldns_buffer_begin(pkt), pkt_len); return p; } @@ -317,10 +332,11 @@ context_deserialize_answer(struct ub_ctx* ctx, uint8_t* p, uint32_t len, int* err) { + size_t size_of_uint32s = 6 * sizeof(uint32_t); struct ctx_query* q = NULL ; int id; size_t wlen; - if(len < 5*sizeof(uint32_t)) return NULL; + if(len < size_of_uint32s) return NULL; log_assert( sldns_read_uint32(p) == UB_LIBCMD_ANSWER); id = (int)sldns_read_uint32(p+sizeof(uint32_t)); q = (struct ctx_query*)rbtree_search(&ctx->queries, &id); @@ -327,11 +343,12 @@ if(!q) return NULL; *err = (int)sldns_read_uint32(p+2*sizeof(uint32_t)); q->msg_security = sldns_read_uint32(p+3*sizeof(uint32_t)); - wlen = (size_t)sldns_read_uint32(p+4*sizeof(uint32_t)); - if(len > 5*sizeof(uint32_t) && wlen > 0) { - if(len >= 5*sizeof(uint32_t)+wlen) + q->res->was_ratelimited = (int)sldns_read_uint32(p+4*sizeof(uint32_t)); + wlen = (size_t)sldns_read_uint32(p+5*sizeof(uint32_t)); + if(len > size_of_uint32s && wlen > 0) { + if(len >= size_of_uint32s+wlen) q->res->why_bogus = (char*)memdup( - p+5*sizeof(uint32_t), wlen); + p+size_of_uint32s, wlen); if(!q->res->why_bogus) { /* pass malloc failure to the user callback */ q->msg_len = 0; @@ -340,9 +357,9 @@ } q->res->why_bogus[wlen-1] = 0; /* zero terminated for sure */ } - if(len > 5*sizeof(uint32_t)+wlen) { - q->msg_len = len - 5*sizeof(uint32_t) - wlen; - q->msg = (uint8_t*)memdup(p+5*sizeof(uint32_t)+wlen, + if(len > size_of_uint32s+wlen) { + q->msg_len = len - size_of_uint32s - wlen; + q->msg = (uint8_t*)memdup(p+size_of_uint32s+wlen, q->msg_len); if(!q->msg) { /* pass malloc failure to the user callback */ @@ -383,12 +400,12 @@ uint8_t* context_serialize_quit(uint32_t* len) { - uint8_t* p = (uint8_t*)malloc(sizeof(uint32_t)); + uint32_t* p = (uint32_t*)malloc(sizeof(uint32_t)); if(!p) return NULL; *len = sizeof(uint32_t); sldns_write_uint32(p, UB_LIBCMD_QUIT); - return p; + return (uint8_t*)p; } enum ub_ctx_cmd context_serial_getcmd(uint8_t* p, uint32_t len) --- contrib/unbound/libunbound/context.h.orig +++ contrib/unbound/libunbound/context.h @@ -45,6 +45,7 @@ #include "util/rbtree.h" #include "services/modstack.h" #include "libunbound/unbound.h" +#include "libunbound/unbound-event.h" #include "util/data/packed_rrset.h" struct libworker; struct tube; @@ -51,6 +52,9 @@ struct sldns_buffer; struct ub_event_base; +/** store that the logfile has a debug override */ +extern int ctx_logfile_overridden; + /** * The context structure * @@ -61,17 +65,17 @@ struct ub_ctx { /* --- pipes --- */ /** mutex on query write pipe */ - lock_basic_t qqpipe_lock; + lock_basic_type qqpipe_lock; /** the query write pipe */ struct tube* qq_pipe; /** mutex on result read pipe */ - lock_basic_t rrpipe_lock; + lock_basic_type rrpipe_lock; /** the result read pipe */ struct tube* rr_pipe; /* --- shared data --- */ /** mutex for access to env.cfg, finalized and dothread */ - lock_basic_t cfglock; + lock_basic_type cfglock; /** * The context has been finalized * This is after config when the first resolve is done. @@ -84,13 +88,13 @@ /** pid of bg worker process */ pid_t bg_pid; /** tid of bg worker thread */ - ub_thread_t bg_tid; + ub_thread_type bg_tid; /** do threading (instead of forking) for async resolution */ int dothread; /** next thread number for new threads */ int thr_next_num; - /** if logfile is overriden */ + /** if logfile is overridden */ int logfile_override; /** what logfile to use instead */ FILE* log_out; @@ -115,6 +119,9 @@ /** event base for event oriented interface */ struct ub_event_base* event_base; + /** true if the event_base is a pluggable base that is malloced + * with a user event base inside, if so, clean up the pluggable alloc*/ + int event_base_malloced; /** libworker for event based interface */ struct libworker* event_worker; @@ -129,7 +136,7 @@ * Used to see if querynum is free for use. * Content of type ctx_query. */ - rbtree_t queries; + rbtree_type queries; }; /** @@ -140,7 +147,7 @@ */ struct ctx_query { /** node in rbtree, must be first entry, key is ptr to the querynum */ - struct rbnode_t node; + struct rbnode_type node; /** query id number, key for node */ int querynum; /** was this an async query? */ @@ -148,8 +155,10 @@ /** was this query cancelled (for bg worker) */ int cancelled; - /** for async query, the callback function */ - ub_callback_t cb; + /** for async query, the callback function of type ub_callback_type */ + ub_callback_type cb; + /** for event callbacks the type is ub_event_callback_type */ + ub_event_callback_type cb_event; /** for async query, the callback user arg */ void* cb_arg; @@ -238,11 +247,13 @@ * @param rrtype: type * @param rrclass: class * @param cb: callback for async, or NULL for sync. + * @param cb_event: event callback for async, or NULL for sync. * @param cbarg: user arg for async queries. * @return new ctx_query or NULL for malloc failure. */ struct ctx_query* context_new(struct ub_ctx* ctx, const char* name, int rrtype, - int rrclass, ub_callback_t cb, void* cbarg); + int rrclass, ub_callback_type cb, ub_event_callback_type cb_event, + void* cbarg); /** * Get a new alloc. Creates a new one or uses a cached one. --- contrib/unbound/libunbound/libunbound.c.orig +++ contrib/unbound/libunbound/libunbound.c @@ -37,7 +37,7 @@ * \file * * This file contains functions to resolve DNS queries and - * validate the answers. Synchonously and asynchronously. + * validate the answers. Synchronously and asynchronously. * */ @@ -62,6 +62,7 @@ #include "services/localzone.h" #include "services/cache/infra.h" #include "services/cache/rrset.h" +#include "services/authzone.h" #include "sldns/sbuffer.h" #ifdef HAVE_PTHREAD #include @@ -78,17 +79,21 @@ #include #endif /* UB_ON_WINDOWS */ +/** store that the logfile has a debug override */ +int ctx_logfile_overridden = 0; + /** create context functionality, but no pipes */ static struct ub_ctx* ub_ctx_create_nopipe(void) { struct ub_ctx* ctx; - unsigned int seed; #ifdef USE_WINSOCK int r; WSADATA wsa_data; #endif - log_init(NULL, 0, NULL); /* logs to stderr */ + checklock_start(); + if(!ctx_logfile_overridden) + log_init(NULL, 0, NULL); /* logs to stderr */ log_ident_set("libunbound"); #ifdef USE_WINSOCK if((r = WSAStartup(MAKEWORD(2,2), &wsa_data)) != 0) { @@ -97,7 +102,7 @@ return NULL; } #endif - verbosity = 0; /* errors only */ + verbosity = NO_VERBOSE; /* errors only */ checklock_start(); ctx = (struct ub_ctx*)calloc(1, sizeof(*ctx)); if(!ctx) { @@ -105,15 +110,12 @@ return NULL; } alloc_init(&ctx->superalloc, NULL, 0); - seed = (unsigned int)time(NULL) ^ (unsigned int)getpid(); - if(!(ctx->seed_rnd = ub_initstate(seed, NULL))) { - seed = 0; + if(!(ctx->seed_rnd = ub_initstate(NULL))) { ub_randfree(ctx->seed_rnd); free(ctx); errno = ENOMEM; return NULL; } - seed = 0; lock_basic_init(&ctx->qqpipe_lock); lock_basic_init(&ctx->rrpipe_lock); lock_basic_init(&ctx->cfglock); @@ -132,6 +134,25 @@ errno = ENOMEM; return NULL; } + /* init edns_known_options */ + if(!edns_known_options_init(ctx->env)) { + config_delete(ctx->env->cfg); + free(ctx->env); + ub_randfree(ctx->seed_rnd); + free(ctx); + errno = ENOMEM; + return NULL; + } + ctx->env->auth_zones = auth_zones_create(); + if(!ctx->env->auth_zones) { + edns_known_options_delete(ctx->env); + config_delete(ctx->env->cfg); + free(ctx->env); + ub_randfree(ctx->seed_rnd); + free(ctx); + errno = ENOMEM; + return NULL; + } ctx->env->alloc = &ctx->superalloc; ctx->env->worker = NULL; ctx->env->need_to_validate = 0; @@ -151,6 +172,7 @@ ub_randfree(ctx->seed_rnd); config_delete(ctx->env->cfg); modstack_desetup(&ctx->mods, ctx->env); + edns_known_options_delete(ctx->env); free(ctx->env); free(ctx); errno = e; @@ -162,6 +184,7 @@ ub_randfree(ctx->seed_rnd); config_delete(ctx->env->cfg); modstack_desetup(&ctx->mods, ctx->env); + edns_known_options_delete(ctx->env); free(ctx->env); free(ctx); errno = e; @@ -199,12 +222,13 @@ ub_ctx_delete(ctx); return NULL; } + ctx->event_base_malloced = 1; return ctx; } /** delete q */ static void -delq(rbnode_t* n, void* ATTR_UNUSED(arg)) +delq(rbnode_type* n, void* ATTR_UNUSED(arg)) { struct ctx_query* q = (struct ctx_query*)n; context_query_delete(q); @@ -298,11 +322,19 @@ rrset_cache_delete(ctx->env->rrset_cache); infra_delete(ctx->env->infra_cache); config_delete(ctx->env->cfg); + edns_known_options_delete(ctx->env); + auth_zones_delete(ctx->env->auth_zones); free(ctx->env); } ub_randfree(ctx->seed_rnd); alloc_clear(&ctx->superalloc); traverse_postorder(&ctx->queries, delq, NULL); + if(ctx_logfile_overridden) { + log_file(NULL); + ctx_logfile_overridden = 0; + } + if(ctx->event_base_malloced) + free(ctx->event_base); free(ctx); #ifdef USE_WINSOCK WSACleanup(); @@ -367,7 +399,6 @@ } if(!cfg_strlist_insert(&ctx->env->cfg->trust_anchor_list, dup)) { lock_basic_unlock(&ctx->cfglock); - free(dup); return UB_NOMEM; } lock_basic_unlock(&ctx->cfglock); @@ -387,7 +418,6 @@ } if(!cfg_strlist_insert(&ctx->env->cfg->trust_anchor_file_list, dup)) { lock_basic_unlock(&ctx->cfglock); - free(dup); return UB_NOMEM; } lock_basic_unlock(&ctx->cfglock); @@ -407,7 +437,6 @@ if(!cfg_strlist_insert(&ctx->env->cfg->auto_trust_anchor_file_list, dup)) { lock_basic_unlock(&ctx->cfglock); - free(dup); return UB_NOMEM; } lock_basic_unlock(&ctx->cfglock); @@ -427,7 +456,6 @@ } if(!cfg_strlist_insert(&ctx->env->cfg->trusted_keys_file_list, dup)) { lock_basic_unlock(&ctx->cfglock); - free(dup); return UB_NOMEM; } lock_basic_unlock(&ctx->cfglock); @@ -448,6 +476,7 @@ { lock_basic_lock(&ctx->cfglock); log_file((FILE*)out); + ctx_logfile_overridden = 1; ctx->logfile_override = 1; ctx->log_out = out; lock_basic_unlock(&ctx->cfglock); @@ -487,7 +516,7 @@ /** process answer from bg worker */ static int process_answer_detail(struct ub_ctx* ctx, uint8_t* msg, uint32_t len, - ub_callback_t* cb, void** cbarg, int* err, + ub_callback_type* cb, void** cbarg, int* err, struct ub_result** res) { struct ctx_query* q; @@ -554,7 +583,7 @@ process_answer(struct ub_ctx* ctx, uint8_t* msg, uint32_t len) { int err; - ub_callback_t cb; + ub_callback_type cb; void* cbarg; struct ub_result* res; int r; @@ -597,7 +626,7 @@ ub_wait(struct ub_ctx* ctx) { int err; - ub_callback_t cb; + ub_callback_type cb; void* cbarg; struct ub_result* res; int r; @@ -665,7 +694,7 @@ } /* create new ctx_query and attempt to add to the list */ lock_basic_unlock(&ctx->cfglock); - q = context_new(ctx, name, rrtype, rrclass, NULL, NULL); + q = context_new(ctx, name, rrtype, rrclass, NULL, NULL, NULL); if(!q) return UB_NOMEM; /* become a resolver thread for a bit */ @@ -693,7 +722,8 @@ int ub_resolve_event(struct ub_ctx* ctx, const char* name, int rrtype, - int rrclass, void* mydata, ub_event_callback_t callback, int* async_id) + int rrclass, void* mydata, ub_event_callback_type callback, + int* async_id) { struct ctx_query* q; int r; @@ -702,7 +732,7 @@ *async_id = 0; lock_basic_lock(&ctx->cfglock); if(!ctx->finalized) { - int r = context_finalize(ctx); + r = context_finalize(ctx); if(r) { lock_basic_unlock(&ctx->cfglock); return r; @@ -721,8 +751,7 @@ ub_comm_base_now(ctx->event_worker->base); /* create new ctx_query and attempt to add to the list */ - q = context_new(ctx, name, rrtype, rrclass, (ub_callback_t)callback, - mydata); + q = context_new(ctx, name, rrtype, rrclass, NULL, callback, mydata); if(!q) return UB_NOMEM; @@ -735,7 +764,7 @@ int ub_resolve_async(struct ub_ctx* ctx, const char* name, int rrtype, - int rrclass, void* mydata, ub_callback_t callback, int* async_id) + int rrclass, void* mydata, ub_callback_type callback, int* async_id) { struct ctx_query* q; uint8_t* msg = NULL; @@ -767,7 +796,7 @@ } /* create new ctx_query and attempt to add to the list */ - q = context_new(ctx, name, rrtype, rrclass, callback, mydata); + q = context_new(ctx, name, rrtype, rrclass, callback, NULL, mydata); if(!q) return UB_NOMEM; @@ -937,7 +966,6 @@ return UB_NOMEM; } if(!cfg_strlist_insert(&s->addrs, dupl)) { - free(dupl); lock_basic_unlock(&ctx->cfglock); errno=ENOMEM; return UB_NOMEM; @@ -946,6 +974,19 @@ return UB_NOERROR; } +int ub_ctx_set_tls(struct ub_ctx* ctx, int tls) +{ + lock_basic_lock(&ctx->cfglock); + if(ctx->finalized) { + lock_basic_unlock(&ctx->cfglock); + errno=EINVAL; + return UB_AFTERFINAL; + } + ctx->env->cfg->ssl_upstream = tls; + lock_basic_unlock(&ctx->cfglock); + return UB_NOERROR; +} + int ub_ctx_set_stub(struct ub_ctx* ctx, const char* zone, const char* addr, int isprime) { @@ -1020,7 +1061,6 @@ } if(!cfg_strlist_insert(&elem->addrs, a)) { lock_basic_unlock(&ctx->cfglock); - free(a); errno = ENOMEM; return UB_NOMEM; } @@ -1118,7 +1158,7 @@ ub_ctx_hosts(struct ub_ctx* ctx, const char* fname) { FILE* in; - char buf[1024], ldata[1024]; + char buf[1024], ldata[2048]; char* parse, *addr, *name, *ins; lock_basic_lock(&ctx->cfglock); if(ctx->finalized) { @@ -1208,7 +1248,6 @@ ins)) { lock_basic_unlock(&ctx->cfglock); fclose(in); - free(ins); errno=ENOMEM; return UB_NOMEM; } --- contrib/unbound/libunbound/libworker.c.orig +++ contrib/unbound/libunbound/libworker.c @@ -55,6 +55,7 @@ #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" @@ -121,7 +122,6 @@ static struct libworker* libworker_setup(struct ub_ctx* ctx, int is_bg, struct ub_event_base* eb) { - unsigned int seed; struct libworker* w = (struct libworker*)calloc(1, sizeof(*w)); struct config_file* cfg = ctx->env->cfg; int* ports; @@ -157,8 +157,9 @@ hints_delete(w->env->hints); w->env->hints = NULL; } - if(cfg->ssl_upstream) { - w->sslctx = connect_sslctx_create(NULL, NULL, NULL); + if(cfg->ssl_upstream || (cfg->tls_cert_bundle && cfg->tls_cert_bundle[0]) || cfg->tls_win_cert) { + w->sslctx = connect_sslctx_create(NULL, NULL, + cfg->tls_cert_bundle, cfg->tls_win_cert); if(!w->sslctx) { /* to make the setup fail after unlock */ hints_delete(w->env->hints); @@ -175,17 +176,13 @@ } w->env->worker = (struct worker*)w; w->env->probe_timer = NULL; - seed = (unsigned int)time(NULL) ^ (unsigned int)getpid() ^ - (((unsigned int)w->thread_num)<<17); - seed ^= (unsigned int)w->env->alloc->next_id; if(!w->is_bg || w->is_bg_thread) { lock_basic_lock(&ctx->cfglock); } - if(!(w->env->rnd = ub_initstate(seed, ctx->seed_rnd))) { + if(!(w->env->rnd = ub_initstate(ctx->seed_rnd))) { if(!w->is_bg || w->is_bg_thread) { lock_basic_unlock(&ctx->cfglock); } - seed = 0; libworker_delete(w); return NULL; } @@ -205,7 +202,6 @@ hash_set_raninit((uint32_t)ub_random(w->env->rnd)); } } - seed = 0; if(eb) w->base = comm_base_create_event(eb); @@ -214,16 +210,16 @@ 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) { - int locked = !w->is_bg || w->is_bg_thread; - libworker_delete(w); - if(locked) { + 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, @@ -232,9 +228,10 @@ cfg->do_tcp?cfg->outgoing_num_tcp:0, w->env->infra_cache, w->env->rnd, cfg->use_caps_bits_for_id, ports, numports, cfg->unwanted_threshold, - cfg->outgoing_tcp_mss, - &libworker_alloc_cleanup, w, cfg->do_udp, w->sslctx, + cfg->outgoing_tcp_mss, &libworker_alloc_cleanup, w, + cfg->do_udp || cfg->udp_upstream_without_downstream, w->sslctx, cfg->delay_close, NULL); + w->env->outnet = w->back; if(!w->is_bg || w->is_bg_thread) { lock_basic_unlock(&ctx->cfglock); } @@ -251,6 +248,7 @@ 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); @@ -294,6 +292,7 @@ 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); @@ -359,6 +358,7 @@ /* 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); @@ -417,25 +417,6 @@ return UB_NOERROR; } -/** get msg reply struct (in temp region) */ -static struct reply_info* -parse_reply(sldns_buffer* pkt, struct regional* region, struct query_info* qi) -{ - struct reply_info* rep; - struct msg_parse* msg; - if(!(msg = regional_alloc(region, sizeof(*msg)))) { - return NULL; - } - memset(msg, 0, sizeof(*msg)); - sldns_buffer_set_position(pkt, 0); - if(parse_packet(pkt, msg, region) != 0) - return 0; - if(!parse_create_msg(pkt, msg, NULL, qi, &rep, region)) { - return 0; - } - return rep; -} - /** insert canonname */ static int fill_canon(struct ub_result* res, uint8_t* s) @@ -509,7 +490,7 @@ struct query_info rq; struct reply_info* rep; res->rcode = LDNS_RCODE_SERVFAIL; - rep = parse_reply(buf, temp, &rq); + rep = parse_reply_in_temp_region(buf, temp, &rq); if(!rep) { log_err("cannot parse buf"); return; /* error parsing buf, or out of memory */ @@ -525,7 +506,8 @@ res->nxdomain = 1; if(msg_security == sec_status_secure) res->secure = 1; - if(msg_security == sec_status_bogus) + if(msg_security == sec_status_bogus || + msg_security == sec_status_secure_sentinel_fail) res->bogus = 1; } @@ -532,8 +514,9 @@ /** fillup fg results */ static void libworker_fillup_fg(struct ctx_query* q, int rcode, sldns_buffer* buf, - enum sec_status s, char* why_bogus) + 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) { @@ -543,7 +526,7 @@ } q->res->rcode = LDNS_RCODE_SERVFAIL; - q->msg_security = 0; + 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) { @@ -557,13 +540,13 @@ void libworker_fg_done_cb(void* arg, int rcode, sldns_buffer* buf, enum sec_status s, - char* why_bogus) + 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); + libworker_fillup_fg(q, rcode, buf, s, why_bogus, was_ratelimited); } /** setup qinfo and edns */ @@ -573,6 +556,7 @@ { 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; @@ -607,16 +591,25 @@ /* 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, &qinfo, &edns, + 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, 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); + 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)) { @@ -634,10 +627,10 @@ void libworker_event_done_cb(void* arg, int rcode, sldns_buffer* buf, - enum sec_status s, char* why_bogus) + enum sec_status s, char* why_bogus, int was_ratelimited) { struct ctx_query* q = (struct ctx_query*)arg; - ub_event_callback_t cb = (ub_event_callback_t)q->cb; + ub_event_callback_type cb = q->cb_event; void* cb_arg = q->cb_arg; int cancelled = q->cancelled; @@ -656,8 +649,8 @@ sec = 1; else if(s == sec_status_secure) sec = 2; - (*cb)(cb_arg, rcode, (void*)sldns_buffer_begin(buf), - (int)sldns_buffer_limit(buf), sec, why_bogus); + (*cb)(cb_arg, rcode, (buf?(void*)sldns_buffer_begin(buf):NULL), + (buf?(int)sldns_buffer_limit(buf):0), sec, why_bogus, was_ratelimited); } } @@ -678,15 +671,23 @@ /* 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, &qinfo, &edns, + 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, 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); + 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; @@ -702,29 +703,37 @@ /** 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 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); + 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--; @@ -743,11 +752,11 @@ void libworker_bg_done_cb(void* arg, int rcode, sldns_buffer* buf, enum sec_status s, - char* why_bogus) + char* why_bogus, int was_ratelimited) { struct ctx_query* q = (struct ctx_query*)arg; - if(q->cancelled) { + 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; @@ -761,12 +770,13 @@ return; } q->msg_security = s; - if(!buf) + 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); + add_bg_result(q->w, q, buf, UB_NOERROR, why_bogus, was_ratelimited); } @@ -791,7 +801,7 @@ return; } if(!setup_qinfo_edns(w, q, &qinfo, &edns)) { - add_bg_result(w, q, NULL, UB_SYNTAX, NULL); + add_bg_result(w, q, NULL, UB_SYNTAX, NULL, 0); return; } qid = 0; @@ -799,20 +809,28 @@ /* 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, &qinfo, &edns, + 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, 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); + 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)) { - add_bg_result(w, q, NULL, UB_NOMEM, NULL); + add_bg_result(w, q, NULL, UB_NOMEM, NULL, 0); } free(qinfo.qname); } @@ -824,11 +842,11 @@ slabhash_clear(w->env->msg_cache); } -struct outbound_entry* libworker_send_query(uint8_t* qname, size_t qnamelen, - uint16_t qtype, uint16_t qclass, uint16_t flags, int dnssec, - int want_dnssec, int nocaps, struct edns_option* opt_list, +struct outbound_entry* libworker_send_query(struct query_info* qinfo, + uint16_t flags, int dnssec, int want_dnssec, int nocaps, struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone, - size_t zonelen, struct module_qstate* q) + size_t zonelen, int ssl_upstream, char* tls_auth_name, + struct module_qstate* q) { struct libworker* w = (struct libworker*)q->env->worker; struct outbound_entry* e = (struct outbound_entry*)regional_alloc( @@ -836,11 +854,10 @@ if(!e) return NULL; e->qstate = q; - e->qsent = outnet_serviced_query(w->back, qname, - qnamelen, qtype, qclass, flags, dnssec, want_dnssec, nocaps, - q->env->cfg->tcp_upstream, q->env->cfg->ssl_upstream, opt_list, - addr, addrlen, zone, zonelen, libworker_handle_service_reply, - e, w->back->udp_buff); + e->qsent = outnet_serviced_query(w->back, qinfo, flags, dnssec, + want_dnssec, nocaps, q->env->cfg->tcp_upstream, ssl_upstream, + tls_auth_name, addr, addrlen, zone, zonelen, q, + libworker_handle_service_reply, e, w->back->udp_buff, q->env); if(!e->qsent) { return NULL; } @@ -955,14 +972,13 @@ log_assert(0); } -struct outbound_entry* worker_send_query(uint8_t* ATTR_UNUSED(qname), - size_t ATTR_UNUSED(qnamelen), uint16_t ATTR_UNUSED(qtype), - uint16_t ATTR_UNUSED(qclass), uint16_t ATTR_UNUSED(flags), - int ATTR_UNUSED(dnssec), int ATTR_UNUSED(want_dnssec), - int ATTR_UNUSED(nocaps), struct edns_option* ATTR_UNUSED(opt_list), - struct sockaddr_storage* ATTR_UNUSED(addr), - socklen_t ATTR_UNUSED(addrlen), uint8_t* ATTR_UNUSED(zone), - size_t ATTR_UNUSED(zonelen), struct module_qstate* ATTR_UNUSED(q)) +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), + struct sockaddr_storage* ATTR_UNUSED(addr), socklen_t ATTR_UNUSED(addrlen), + uint8_t* ATTR_UNUSED(zone), size_t ATTR_UNUSED(zonelen), + int ATTR_UNUSED(ssl_upstream), char* ATTR_UNUSED(tls_auth_name), + struct module_qstate* ATTR_UNUSED(q)) { log_assert(0); return 0; --- contrib/unbound/libunbound/libworker.h.orig +++ contrib/unbound/libunbound/libworker.h @@ -1,5 +1,5 @@ /* - * libunbound/worker.h - worker thread or process that resolves + * libunbound/libworker.h - worker thread or process that resolves * * Copyright (c) 2007, NLnet Labs. All rights reserved. * @@ -59,6 +59,7 @@ struct tube; struct sldns_buffer; struct ub_event_base; +struct query_info; /** * The library-worker status structure @@ -74,6 +75,8 @@ int is_bg; /** is this a bg worker that is threaded (not forked)? */ int is_bg_thread; + /** want to quit, stop handling new content */ + int want_quit; /** copy of the module environment with worker local entries. */ struct module_env* env; --- contrib/unbound/libunbound/python/LICENSE.orig +++ contrib/unbound/libunbound/python/LICENSE @@ -1,28 +0,0 @@ -Copyright (c) 2009, Zdenek Vasicek (vasicek AT fit.vutbr.cz) - Marek Vavrusa (xvavru00 AT stud.fit.vutbr.cz) - -All rights reserved. - -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 organization 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 OWNER 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. --- contrib/unbound/libunbound/python/Makefile.orig +++ contrib/unbound/libunbound/python/Makefile @@ -1,72 +0,0 @@ -# -# Makefile: compilation of pyUnbound and documentation, testing -# -# Copyright (c) 2009, Zdenek Vasicek (vasicek AT fit.vutbr.cz) -# Marek Vavrusa (xvavru00 AT stud.fit.vutbr.cz) -# -# 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 organization 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. - -help: - @echo "Please use \`make ' where is one of" - @echo " testenv to make test environment and run bash " - @echo " useful in case you don't want to install unbound but want to test examples" - @echo " doc to make documentation" - @echo " clean clean all" - -.PHONY: testenv clean doc swig - -#_unbound.so: ../../Makefile - #$(MAKE) -C ../.. - -#../../.libs/libunbound.so.0: ../../Makefile - #$(MAKE) -C ../.. - -clean: - rm -rdf examples/unbound - rm -f _unbound.so libunbound_wrap.o - $(MAKE) -C ../.. clean - -testenv: ../../.libs/libunbound.so.2 ../../.libs/_unbound.so - rm -rdf examples/unbound - cd examples && mkdir unbound && ln -s ../../unbound.py unbound/__init__.py && ln -s ../../_unbound.so unbound/_unbound.so && ln -s ../../../../.libs/libunbound.so.2 unbound/libunbound.so.2 && ls -la - cd examples && if test -f ../../../.libs/_unbound.so; then cp ../../../.libs/_unbound.so . ; fi - @echo "Run a script by typing ./script_name.py" - cd examples && LD_LIBRARY_PATH=unbound bash - rm -rdf examples/unbound examples/_unbound.so - -doc: ../../.libs/libunbound.so.0 _unbound.so - $(MAKE) -C docs html - -#for development only -swig: libunbound.i - swig -python -o libunbound_wrap.c -I../.. libunbound.i - gcc -c libunbound_wrap.c -O9 -fPIC -I../.. -I/usr/include/python2.5 -I. -o libunbound_wrap.o - gcc -shared libunbound_wrap.o -L../../.libs -lunbound -o _unbound.so - --- contrib/unbound/libunbound/python/doc/_static/readme.orig +++ contrib/unbound/libunbound/python/doc/_static/readme @@ -1 +0,0 @@ -this directory exists to pacify sphinx-build. --- contrib/unbound/libunbound/python/doc/conf.py.orig +++ contrib/unbound/libunbound/python/doc/conf.py @@ -1,181 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Unbound documentation build configuration file -# -# This file is execfile()d with the current directory set to its containing dir. -# -# The contents of this file are pickled, so don't put values in the namespace -# that aren't pickleable (module imports are okay, they're removed automatically). -# -# All configuration values have a default value; values that are commented out -# serve to show the default value. - -import sys, os - -# If your extensions are in another directory, add it here. If the directory -# is relative to the documentation root, use os.path.abspath to make it -# absolute, like shown here. -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__),'../'))) -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__),'../../../'))) -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__),'../../../.libs/'))) -#print sys.path - -# General configuration -# --------------------- - -# Add any Sphinx extension module names here, as strings. They can be extensions -# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest'] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The master toctree document. -master_doc = 'index' - -# General substitutions. -project = 'pyUnbound' -copyright = '2009, Zdenek Vasicek, Marek Vavrusa' - -# The default replacements for |version| and |release|, also used in various -# other places throughout the built documents. -# -# The short X.Y version. -version = '1.0' -# The full version, including alpha/beta/rc tags. -release = '1.0.0' - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -today_fmt = '%B %d, %Y' - -# List of documents that shouldn't be included in the build. -#unused_docs = [] - -# List of directories, relative to source directories, that shouldn't be searched -# for source files. -#exclude_dirs = [] - -# The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - - -# Options for HTML output -# ----------------------- - -# The style sheet to use for HTML and HTML Help pages. A file of that name -# must exist either in Sphinx' static/ path, or in one of the custom paths -# given in html_static_path. -html_style = 'default.css' - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (within the static path) to place at the top of -# the sidebar. -#html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -html_use_modindex = False - -# If false, no index is generated. -#html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, the reST sources are included in the HTML build as _sources/. -html_copy_source = False - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = '' - -# Output file base name for HTML help builder. -htmlhelp_basename = 'Unbounddoc' - - -# Options for LaTeX output -# ------------------------ - -# The paper size ('letter' or 'a4'). -#latex_paper_size = 'letter' - -# The font size ('10pt', '11pt' or '12pt'). -#latex_font_size = '10pt' - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, document class [howto/manual]). -latex_documents = [ - ('index', 'Unbound.tex', 'Unbound Documentation', - 'Zdenek Vasicek, Marek Vavrusa', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# Additional stuff for the LaTeX preamble. -#latex_preamble = '' - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_use_modindex = True --- contrib/unbound/libunbound/python/doc/examples/example1a.rst.orig +++ contrib/unbound/libunbound/python/doc/examples/example1a.rst @@ -1,26 +0,0 @@ -.. _example_resolve_name: - -============================== -Resolve a name -============================== - -This basic example shows how to create a context and resolve a host address (DNS record of A type). - -:: - - #!/usr/bin/python - import unbound - - ctx = unbound.ub_ctx() - ctx.resolvconf("/etc/resolv.conf") - - status, result = ctx.resolve("www.google.com") - if status == 0 and result.havedata: - print "Result.data:", result.data.address_list - elif status != 0: - print "Resolve error:", unbound.ub_strerror(status) - -In contrast with C API, the source code is more compact while the performance of C implementation is preserved. -The main advantage is that you need not take care about the deallocation and allocation of context and result structures; pyUnbound module do it automatically for you. - -If only domain name is given, the :meth:`unbound.ub_ctx.resolve` looks for A records in IN class. --- contrib/unbound/libunbound/python/doc/examples/example1b.rst.orig +++ contrib/unbound/libunbound/python/doc/examples/example1b.rst @@ -1,33 +0,0 @@ -.. _example_reverse_lookup: - -============================== -Reverse DNS lookup -============================== - -Reverse DNS lookup involves determining the hostname associated with a given IP address. -This example shows how reverse lookup can be done using unbound module. - -For the reverse DNS records, the special domain in-addr.arpa is reserved. -For example, a host name for the IP address 74.125.43.147 can be obtained by issuing a DNS query for the PTR record for address 147.43.125.74.in-addr.arpa. - -:: - - #!/usr/bin/python - import unbound - - ctx = unbound.ub_ctx() - ctx.resolvconf("/etc/resolv.conf") - - status, result = ctx.resolve(unbound.reverse("74.125.43.147") + ".in-addr.arpa.", unbound.RR_TYPE_PTR, unbound.RR_CLASS_IN) - if status == 0 and result.havedata: - print "Result.data:", result.data.domain_list - elif status != 0: - print "Resolve error:", unbound.ub_strerror(status) - -In order to simplify the python code, unbound module contains function which reverses the hostname components. -This function is defined as follows:: - - def reverse(domain): - return '.'.join([a for a in domain.split(".")][::-1]) - - --- contrib/unbound/libunbound/python/doc/examples/example2.rst.orig +++ contrib/unbound/libunbound/python/doc/examples/example2.rst @@ -1,41 +0,0 @@ -.. _example_setup_ctx: - -============================== -Lookup from threads -============================== - -This example shows how to use unbound module from a threaded program. -In this example, three lookup threads are created which work in background. -Each thread resolves different DNS record. - -:: - - #!/usr/bin/python - from unbound import ub_ctx, RR_TYPE_A, RR_CLASS_IN - from threading import Thread - - ctx = ub_ctx() - ctx.resolvconf("/etc/resolv.conf") - - class LookupThread(Thread): - def __init__(self,ctx, name): - Thread.__init__(self) - self.ctx = ctx - self.name = name - - def run(self): - print "Thread lookup started:",self.name - status, result = self.ctx.resolve(self.name, RR_TYPE_A, RR_CLASS_IN) - if status == 0 and result.havedata: - print " Result:",self.name,":", result.data.address_list - - threads = [] - for name in ["www.fit.vutbr.cz","www.vutbr.cz","www.google.com"]: - thread = LookupThread(ctx, name) - thread.start() - threads.append(thread) - - for thread in threads: - thread.join() - - --- contrib/unbound/libunbound/python/doc/examples/example3.rst.orig +++ contrib/unbound/libunbound/python/doc/examples/example3.rst @@ -1,36 +0,0 @@ -.. _example_asynch: - -============================== -Asynchronous lookup -============================== - -This example performs the name lookup in the background. -The main program keeps running while the name is resolved. - -:: - - #!/usr/bin/python - import time - import unbound - - ctx = unbound.ub_ctx() - ctx.resolvconf("/etc/resolv.conf") - - def call_back(my_data,status,result): - print "Call_back:", my_data - if status == 0 and result.havedata: - print "Result:", result.data.address_list - my_data['done_flag'] = True - - - my_data = {'done_flag':False,'arbitrary':"object"} - status, async_id = ctx.resolve_async("www.seznam.cz", my_data, call_back, unbound.RR_TYPE_A, unbound.RR_CLASS_IN) - - while (status == 0) and (not my_data['done_flag']): - status = ctx.process() - time.sleep(0.1) - - if (status != 0): - print "Resolve error:", unbound.ub_strerror(status) - -The :meth:`unbound.ub_ctx.resolve_async` method is able to pass on any Python object. In this example, we used a dictionary object `my_data`. --- contrib/unbound/libunbound/python/doc/examples/example4.rst.orig +++ contrib/unbound/libunbound/python/doc/examples/example4.rst @@ -1,34 +0,0 @@ -.. _example_examine: - -============================== -DNSSEC validator -============================== - -This example program performs DNSSEC validation of a DNS lookup. - -:: - - #!/usr/bin/python - import os - from unbound import ub_ctx,RR_TYPE_A,RR_CLASS_IN - - ctx = ub_ctx() - ctx.resolvconf("/etc/resolv.conf") - if (os.path.isfile("keys")): - ctx.add_ta_file("keys") #read public keys for DNSSEC verification - - status, result = ctx.resolve("www.nic.cz", RR_TYPE_A, RR_CLASS_IN) - if status == 0 and result.havedata: - - print "Result:", result.data.address_list - - if result.secure: - print "Result is secure" - elif result.bogus: - print "Result is bogus" - else: - print "Result is insecure" - -More detailed informations can be seen in libUnbound DNSSEC tutorial `here`_. - -.. _here: http://www.unbound.net/documentation/libunbound-tutorial-6.html --- contrib/unbound/libunbound/python/doc/examples/example5.rst.orig +++ contrib/unbound/libunbound/python/doc/examples/example5.rst @@ -1,29 +0,0 @@ -.. _example_resolver_only: - -============================== -Resolver only -============================== - -This example program shows how to perform DNS resolution only. -Unbound contains two basic modules: resolver and validator. -In case, the validator is not necessary, the validator module can be turned off using "module-config" option. -This option contains a list of module names separated by the space char. This list determined which modules should be employed and in what order. - -:: - - #!/usr/bin/python - import os - from unbound import ub_ctx,RR_TYPE_A,RR_CLASS_IN - - ctx = ub_ctx() - ctx.set_option("module-config:","iterator") - ctx.resolvconf("/etc/resolv.conf") - - status, result = ctx.resolve("www.google.com", RR_TYPE_A, RR_CLASS_IN) - if status == 0 and result.havedata: - - print "Result:", result.data.address_list - -.. note:: - The :meth:`unbound.ub_ctx.set_option` method must be used before the first resolution (i.e. before :meth:`unbound.ub_ctx.resolve` or :meth:`unbound.ub_ctx.resolve_async` call). - --- contrib/unbound/libunbound/python/doc/examples/example6-1.py.orig +++ contrib/unbound/libunbound/python/doc/examples/example6-1.py @@ -1,27 +0,0 @@ -#!/usr/bin/python -from unbound import ub_ctx,ub_strerror,RR_TYPE_A,RR_CLASS_IN - -ctx = ub_ctx() -ctx.resolvconf("/etc/resolv.conf") - -status, result = ctx.resolve("test.record.xxx", RR_TYPE_A, RR_CLASS_IN) -if status == 0 and result.havedata: - print "Result:", result.data.address_list -else: - print "No record found" - -#define new local zone -status = ctx.zone_add("xxx.","static") -if (status != 0): print "Error zone_add:",status, ub_strerror(status) - -#add RR to the zone -status = ctx.data_add("test.record.xxx. IN A 1.2.3.4") -if (status != 0): print "Error data_add:",status, ub_strerror(status) - -#lookup for an A record -status, result = ctx.resolve("test.record.xxx", RR_TYPE_A, RR_CLASS_IN) -if status == 0 and result.havedata: - print "Result:", result.data.as_address_list() -else: - print "No record found" - --- contrib/unbound/libunbound/python/doc/examples/example6.rst.orig +++ contrib/unbound/libunbound/python/doc/examples/example6.rst @@ -1,11 +0,0 @@ -.. _example_localzone: - -============================== -Local zone manipulation -============================== - -This example program shows how to define local zone containing custom DNS records. - -.. literalinclude:: example6-1.py - :language: python - --- contrib/unbound/libunbound/python/doc/examples/example7-1.py.orig +++ contrib/unbound/libunbound/python/doc/examples/example7-1.py @@ -1,17 +0,0 @@ -#!/usr/bin/python -# vim:fileencoding=utf-8 -# -# IDN (Internationalized Domain Name) lookup support -# -import unbound - -ctx = unbound.ub_ctx() -ctx.resolvconf("/etc/resolv.conf") - -status, result = ctx.resolve(u"www.háčkyčárky.cz", unbound.RR_TYPE_A, unbound.RR_CLASS_IN) -if status == 0 and result.havedata: - print "Result:" - print " raw data:", result.data - for k in result.data.address_list: - print " address:%s" % k - --- contrib/unbound/libunbound/python/doc/examples/example7-2.py.orig +++ contrib/unbound/libunbound/python/doc/examples/example7-2.py @@ -1,16 +0,0 @@ -#!/usr/bin/python -# vim:fileencoding=utf-8 -# -# IDN (Internationalized Domain Name) lookup support (lookup for MX) -# -import unbound - -ctx = unbound.ub_ctx() -ctx.resolvconf("/etc/resolv.conf") - -status, result = ctx.resolve(u"háčkyčárky.cz", unbound.RR_TYPE_MX, unbound.RR_CLASS_IN) -if status == 0 and result.havedata: - print "Result:" - print " raw data:", result.data - for k in result.data.mx_list_idn: - print " priority:%d address:%s" % k --- contrib/unbound/libunbound/python/doc/examples/example7.rst.orig +++ contrib/unbound/libunbound/python/doc/examples/example7.rst @@ -1,18 +0,0 @@ -.. _example_idna: - -================================================= -Internationalized domain name support -================================================= - -Unlike the libUnbound, pyUnbound is able to handle IDN queries. - -.. literalinclude:: example7-1.py - :language: python - -If we use unicode string in :meth:`unbound.ub_ctx.resolve` method, the IDN DNAME conversion (if it is necessary) is performed on background. - -.. literalinclude:: example7-2.py - :language: python - -The :class:`unbound.ub_data` class contains attributes suffix which converts the dname to UTF string. These attributes have the '_idn' suffix. -Apart from this aproach, two conversion functions exist (:func:`unbound.idn2dname` and :func:`unbound.dname2idn`). --- contrib/unbound/libunbound/python/doc/examples/example8-1.py.orig +++ contrib/unbound/libunbound/python/doc/examples/example8-1.py @@ -1,31 +0,0 @@ -#!/usr/bin/python -# vim:fileencoding=utf-8 -# -# Lookup for MX and NS records -# -import unbound - -ctx = unbound.ub_ctx() -ctx.resolvconf("/etc/resolv.conf") - -status, result = ctx.resolve("nic.cz", unbound.RR_TYPE_MX, unbound.RR_CLASS_IN) -if status == 0 and result.havedata: - print "Result:" - print " raw data:", result.data - for k in result.data.mx_list: - print " priority:%d address:%s" % k - -status, result = ctx.resolve("nic.cz", unbound.RR_TYPE_A, unbound.RR_CLASS_IN) -if status == 0 and result.havedata: - print "Result:" - print " raw data:", result.data - for k in result.data.address_list: - print " address:%s" % k - -status, result = ctx.resolve("nic.cz", unbound.RR_TYPE_NS, unbound.RR_CLASS_IN) -if status == 0 and result.havedata: - print "Result:" - print " raw data:", result.data - for k in result.data.domain_list: - print " host: %s" % k - --- contrib/unbound/libunbound/python/doc/examples/example8.rst.orig +++ contrib/unbound/libunbound/python/doc/examples/example8.rst @@ -1,28 +0,0 @@ -.. _example_mxlookup: - -================================================= -Lookup for MX and NS records -================================================= - -The pyUnbound extension provides functions which are able to encode RAW RDATA produces by unbound resolver (see :class:`unbound.ub_data`). - -.. literalinclude:: example8-1.py - :language: python - -Previous example produces following output:: - - Result: - raw data: 00 0F 05 6D 61 69 6C 34 03 6E 69 63 02 63 7A 00;00 14 02 6D 78 05 63 7A 6E 69 63 03 6F 72 67 00;00 0A 04 6D 61 69 6C 03 6E 69 63 02 63 7A 00 - priority:15 address: mail4.nic.cz. - priority:20 address: mx.cznic.org. - priority:10 address: mail.nic.cz. - - Result: - raw data: D9 1F CD 32 - address: 217.31.205.50 - - Result: - raw data: 01 61 02 6E 73 03 6E 69 63 02 63 7A 00;01 65 02 6E 73 03 6E 69 63 02 63 7A 00;01 63 02 6E 73 03 6E 69 63 02 63 7A 00 - host: a.ns.nic.cz. - host: e.ns.nic.cz. - host: c.ns.nic.cz. --- contrib/unbound/libunbound/python/doc/examples/index.rst.orig +++ contrib/unbound/libunbound/python/doc/examples/index.rst @@ -1,14 +0,0 @@ -Examples -============================== - -Here you can find several examples which utilizes the unbound library in Python environment. -Unbound is a caching validator and resolver and can be linked into an application, as a library where can answer DNS queries for the application. -This set of examples shows how to use the functions from Python environment. - -`Tutorials` - -.. toctree:: - :maxdepth: 1 - :glob: - - example* --- contrib/unbound/libunbound/python/doc/index.rst.orig +++ contrib/unbound/libunbound/python/doc/index.rst @@ -1,27 +0,0 @@ -PyUnbound documentation -======================================= - -This project contains an Unbound wrapper providing the thinnest layer over the library possible. -Everything you can do from the libUnbound C API, you can do from Python, even more. - -Contents ----------- -.. toctree:: - :maxdepth: 2 - - intro.rst - install.rst - examples/index.rst - modules/unbound - -Module Documentation ------------------------ - -* Module :mod:`unbound` - -Indices and tables -------------------- - -* :ref:`genindex` -* :ref:`search` - --- contrib/unbound/libunbound/python/doc/install.rst.orig +++ contrib/unbound/libunbound/python/doc/install.rst @@ -1,31 +0,0 @@ -Installation -=================================== - -**Prerequisites** - -Python 2.4 or higher, SWIG 1.3 or higher, GNU make - -**Compiling** - -After downloading, you can compile the pyUnbound library by doing:: - - > tar -xzf unbound-x.x.x-py.tar.gz - > cd unbound-x.x.x - > ./configure --with-pyunbound - > make - -You may want to --with-pythonmodule as well if you want to use python as -a module in the resolver. - -You need GNU make to compile sources; SWIG and Python devel libraries to compile extension module. - - -**Testing** - -If the compilation is successful, you can test the python LDNS extension module by:: - - > cd contrib/python - > make testenv - > ./dns-lookup.py - -You may want to make install in the main directory since make testenv is for debugging. In contrib/examples you can find simple applications written in Python using the Unbound extension. --- contrib/unbound/libunbound/python/doc/intro.rst.orig +++ contrib/unbound/libunbound/python/doc/intro.rst @@ -1,39 +0,0 @@ -Introduction -=================================== - -**Unbound** - - `Unbound`_ is an implementation of a DNS resolver, that performs caching and DNSSEC validation. - Together with unbound, the libunbound library is provided. - This library can be used to convert hostnames to ip addresses, and back, as well as obtain other information. - Since the resolver allows to specify the class and type of a query (A record, NS, MX, ...), this library offers powerful resolving tool. - The library also performs public-key validation of results with DNSSEC. - - .. _Unbound: http://www.unbound.net/documentation - -**pyUnbound** - - The pyUnbound is an extension module for Python which provides an object-oriented interface to libunbound. - It is the first Python module which offers thread-safe caching resolver. - - The interface was designed with the emphasis on the simplicity of use. - There are two main classes :class:`unbound.ub_ctx` (a validation and resolution context) and :class:`unbound.ub_result` which contains the validation and resolution results. - The objects are thread-safe, and a context can be used in non-threaded as well as threaded environment. - Resolution can be performed blocking and non-blocking (i.e. asynchronous). - The asynchronous method returns from the call immediately, so that processing can go on, while the results become available later. - -**Features** - * customizable caching validation resolver for synchronous and asynchronous lookups - * easy to use object interface - * easy to integrate extension module - * designed for thread environment (i.e. thread-safe) - * allows define and customize of local zone and its RR's during the operation (i.e. without restart) - * includes encoding functions to simplify the results retrieval - * Internationalized domain name (`IDN`_) support - - .. _IDN: http://en.wikipedia.org/wiki/Internationalized_domain_name - -**Application area** - * DNS-based applications performing DNS lookups; the caching resolver can reduce overhead - * Applications where the validation of DNS records is required - * Great solution for customizable and dynamic DNS-based white/blacklists (spam rejection, connection rejection, ...) using the dynamic local zone manipulation --- contrib/unbound/libunbound/python/doc/modules/unbound.rst.orig +++ contrib/unbound/libunbound/python/doc/modules/unbound.rst @@ -1,167 +0,0 @@ -Unbound module documentation -================================ - -.. automodule:: unbound - -Class ub_ctx --------------- -.. autoclass:: ub_ctx - :members: - :undoc-members: - - .. automethod:: __init__ - -Class ub_result ----------------------- -.. autoclass:: ub_result - :members: - - .. attribute:: qname - - The original question, name text string. - - .. attribute:: qtype - - The class asked for. - - .. attribute:: canonname - - Canonical name for the result (the final cname). May be empty if no canonical name exists. - - .. attribute:: answer_packet - - The DNS answer packet. Network formatted. Can contain DNSSEC types. - - .. attribute:: havedata - - If there is any data, this property is true. If false, there was no data (nxdomain may be true, rcode can be set). - - .. attribute:: secure - - True, if the result is validated securely. - False, if validation failed or domain queried has no security info. - - It is possible to get a result with no data (havedata is false), - and secure is true. This means that the non-existence of the data - was cryptographically proven (with signatures). - - .. attribute:: bogus - - If the result was not secure (secure==0), and this result is due to a security failure, bogus is true. - This means the data has been actively tampered with, signatures - failed, expected signatures were not present, timestamps on - signatures were out of date and so on. - - If secure==0 and bogus==0, this can happen if the data is not secure - because security is disabled for that domain name. - This means the data is from a domain where data is not signed. - - .. attribute:: nxdomain - - If there was no data, and the domain did not exist, this is true. - If it is false, and there was no data, then the domain name is purported to exist, but the requested data type is not available. - - .. attribute:: rcode - - DNS RCODE for the result. May contain additional error code if there was no data due to an error. - 0 (RCODE_NOERROR) if okay. See predefined `RCODE_` constants. - - RCODE can be represented in display representation form (string) using :attr:`rcode_str` attribute. - -Class ub_data ----------------------- -.. autoclass:: ub_data - :members: - -Functions ----------------------- -.. autofunction:: reverse -.. autofunction:: idn2dname -.. autofunction:: dname2idn - -Predefined constants ------------------------ - -**RCODE** - * RCODE_FORMERR = 1 - * RCODE_NOERROR = 0 - * RCODE_NOTAUTH = 9 - * RCODE_NOTIMPL = 4 - * RCODE_NOTZONE = 10 - * RCODE_NXDOMAIN = 3 - * RCODE_NXRRSET = 8 - * RCODE_REFUSED = 5 - * RCODE_SERVFAIL = 2 - * RCODE_YXDOMAIN = 6 - * RCODE_YXRRSET = 7 - -**RR_CLASS** - * RR_CLASS_ANY = 255 - * RR_CLASS_CH = 3 - * RR_CLASS_HS = 4 - * RR_CLASS_IN = 1 - * RR_CLASS_NONE = 254 - -**RR_TYPE** - * RR_TYPE_A = 1 - * RR_TYPE_A6 = 38 - * RR_TYPE_AAAA = 28 - * RR_TYPE_AFSDB = 18 - * RR_TYPE_ANY = 255 - * RR_TYPE_APL = 42 - * RR_TYPE_ATMA = 34 - * RR_TYPE_AXFR = 252 - * RR_TYPE_CERT = 37 - * RR_TYPE_CNAME = 5 - * RR_TYPE_DHCID = 49 - * RR_TYPE_DLV = 32769 - * RR_TYPE_DNAME = 39 - * RR_TYPE_DNSKEY = 48 - * RR_TYPE_DS = 43 - * RR_TYPE_EID = 31 - * RR_TYPE_GID = 102 - * RR_TYPE_GPOS = 27 - * RR_TYPE_HINFO = 13 - * RR_TYPE_IPSECKEY = 45 - * RR_TYPE_ISDN = 20 - * RR_TYPE_IXFR = 251 - * RR_TYPE_KEY = 25 - * RR_TYPE_KX = 36 - * RR_TYPE_LOC = 29 - * RR_TYPE_MAILA = 254 - * RR_TYPE_MAILB = 253 - * RR_TYPE_MB = 7 - * RR_TYPE_MD = 3 - * RR_TYPE_MF = 4 - * RR_TYPE_MG = 8 - * RR_TYPE_MINFO = 14 - * RR_TYPE_MR = 9 - * RR_TYPE_MX = 15 - * RR_TYPE_NAPTR = 35 - * RR_TYPE_NIMLOC = 32 - * RR_TYPE_NS = 2 - * RR_TYPE_NSAP = 22 - * RR_TYPE_NSAP_PTR = 23 - * RR_TYPE_NSEC = 47 - * RR_TYPE_NSEC3 = 50 - * RR_TYPE_NSEC3PARAMS = 51 - * RR_TYPE_NULL = 10 - * RR_TYPE_NXT = 30 - * RR_TYPE_OPT = 41 - * RR_TYPE_PTR = 12 - * RR_TYPE_PX = 26 - * RR_TYPE_RP = 17 - * RR_TYPE_RRSIG = 46 - * RR_TYPE_RT = 21 - * RR_TYPE_SIG = 24 - * RR_TYPE_SINK = 40 - * RR_TYPE_SOA = 6 - * RR_TYPE_SRV = 33 - * RR_TYPE_SSHFP = 44 - * RR_TYPE_TSIG = 250 - * RR_TYPE_TXT = 16 - * RR_TYPE_UID = 101 - * RR_TYPE_UINFO = 100 - * RR_TYPE_UNSPEC = 103 - * RR_TYPE_WKS = 11 - * RR_TYPE_X25 = 19 --- contrib/unbound/libunbound/python/examples/async-lookup.py.orig +++ contrib/unbound/libunbound/python/examples/async-lookup.py @@ -1,57 +0,0 @@ -#!/usr/bin/python -''' - async-lookup.py : This example shows how to use asynchronous lookups - - Authors: Zdenek Vasicek (vasicek AT fit.vutbr.cz) - Marek Vavrusa (xvavru00 AT stud.fit.vutbr.cz) - - Copyright (c) 2008. 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. - - 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. -''' -from __future__ import print_function -import unbound -import time - -ctx = unbound.ub_ctx() -ctx.resolvconf("/etc/resolv.conf") - -def call_back(my_data,status,result): - print("Call_back:", sorted(my_data)) - if status == 0 and result.havedata: - print("Result:", sorted(result.data.address_list)) - my_data['done_flag'] = True - - -my_data = {'done_flag':False,'arbitrary':"object"} -status, async_id = ctx.resolve_async("www.nic.cz", my_data, call_back, unbound.RR_TYPE_A, unbound.RR_CLASS_IN) - -while (status == 0) and (not my_data['done_flag']): - status = ctx.process() - time.sleep(0.1) - -if (status != 0): - print("Resolve error:", unbound.ub_strerror(status)) --- contrib/unbound/libunbound/python/examples/dns-lookup.py.orig +++ contrib/unbound/libunbound/python/examples/dns-lookup.py @@ -1,45 +0,0 @@ -#!/usr/bin/python -''' - dns-lookup.py : This example shows how to resolve IP address - - Authors: Zdenek Vasicek (vasicek AT fit.vutbr.cz) - Marek Vavrusa (xvavru00 AT stud.fit.vutbr.cz) - - Copyright (c) 2008. 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. - - 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. -''' -from __future__ import print_function -import unbound - -ctx = unbound.ub_ctx() -ctx.resolvconf("/etc/resolv.conf") - -status, result = ctx.resolve("www.nic.cz", unbound.RR_TYPE_A, unbound.RR_CLASS_IN) -if status == 0 and result.havedata: - print("Result:", sorted(result.data.address_list)) -elif status != 0: - print("Error:", unbound.ub_strerror(status)) --- contrib/unbound/libunbound/python/examples/dnssec-valid.py.orig +++ contrib/unbound/libunbound/python/examples/dnssec-valid.py @@ -1,60 +0,0 @@ -#!/usr/bin/python -''' - dnssec-valid.py: DNSSEC validation - - Authors: Zdenek Vasicek (vasicek AT fit.vutbr.cz) - Marek Vavrusa (xvavru00 AT stud.fit.vutbr.cz) - - Copyright (c) 2008. 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. - - 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. -''' -from __future__ import print_function -import os -from unbound import ub_ctx,RR_TYPE_A,RR_CLASS_IN - -ctx = ub_ctx() -ctx.resolvconf("/etc/resolv.conf") - -fw = open("dnssec-valid.txt","wb") -ctx.debugout(fw) -ctx.debuglevel(2) - -if os.path.isfile("keys"): - ctx.add_ta_file("keys") #read public keys for DNSSEC verification - -status, result = ctx.resolve("www.nic.cz", RR_TYPE_A, RR_CLASS_IN) -if status == 0 and result.havedata: - - print("Result:", sorted(result.data.address_list)) - - if result.secure: - print("Result is secure") - elif result.bogus: - print("Result is bogus") - else: - print("Result is insecure") - --- contrib/unbound/libunbound/python/examples/dnssec_test.py.orig +++ contrib/unbound/libunbound/python/examples/dnssec_test.py @@ -1,36 +0,0 @@ -#!/usr/bin/env python -from __future__ import print_function -from unbound import ub_ctx, RR_TYPE_A, RR_TYPE_RRSIG, RR_TYPE_NSEC, RR_TYPE_NSEC3 -import ldns - -def dnssecParse(domain, rrType=RR_TYPE_A): - print("Resolving domain", domain) - s, r = resolver.resolve(domain) - print("status: %s, secure: %s, rcode: %s, havedata: %s, answer_len; %s" % (s, r.secure, r.rcode_str, r.havedata, r.answer_len)) - - s, pkt = ldns.ldns_wire2pkt(r.packet) - if s != 0: - raise RuntimeError("Error parsing DNS packet") - - rrsigs = pkt.rr_list_by_type(RR_TYPE_RRSIG, ldns.LDNS_SECTION_ANSWER) - print("RRSIGs from answer:", sorted(rrsigs)) - - rrsigs = pkt.rr_list_by_type(RR_TYPE_RRSIG, ldns.LDNS_SECTION_AUTHORITY) - print("RRSIGs from authority:", sorted(rrsigs)) - - nsecs = pkt.rr_list_by_type(RR_TYPE_NSEC, ldns.LDNS_SECTION_AUTHORITY) - print("NSECs:", sorted(nsecs)) - - nsec3s = pkt.rr_list_by_type(RR_TYPE_NSEC3, ldns.LDNS_SECTION_AUTHORITY) - print("NSEC3s:", sorted(nsec3s)) - - print("---") - - -resolver = ub_ctx() -resolver.add_ta(". IN DS 19036 8 2 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5") - -dnssecParse("nic.cz") -dnssecParse("nonexistent-domain-blablabla.cz") -dnssecParse("nonexistent-domain-blablabla.root.cz") - --- contrib/unbound/libunbound/python/examples/example8-1.py.orig +++ contrib/unbound/libunbound/python/examples/example8-1.py @@ -1,62 +0,0 @@ -#!/usr/bin/python -# vim:fileencoding=utf-8 -''' - example8-1.py: Example shows how to lookup for MX and NS records - - Authors: Zdenek Vasicek (vasicek AT fit.vutbr.cz) - Marek Vavrusa (xvavru00 AT stud.fit.vutbr.cz) - - Copyright (c) 2008. 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. - - 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. -''' -from __future__ import print_function -import unbound - -ctx = unbound.ub_ctx() -ctx.resolvconf("/etc/resolv.conf") - -status, result = ctx.resolve("nic.cz", unbound.RR_TYPE_MX, unbound.RR_CLASS_IN) -if status == 0 and result.havedata: - print("Result:") - print(" raw data:", result.data) - for k in sorted(result.data.mx_list): - print(" priority:%d address:%s" % k) - -status, result = ctx.resolve("nic.cz", unbound.RR_TYPE_A, unbound.RR_CLASS_IN) -if status == 0 and result.havedata: - print("Result:") - print(" raw data:", result.data) - for k in sorted(result.data.address_list): - print(" address:%s" % k) - -status, result = ctx.resolve("nic.cz", unbound.RR_TYPE_NS, unbound.RR_CLASS_IN) -if status == 0 and result.havedata: - print("Result:") - print(" raw data:", result.data) - for k in sorted(result.data.domain_list): - print(" host: %s" % k) - --- contrib/unbound/libunbound/python/examples/idn-lookup.py.orig +++ contrib/unbound/libunbound/python/examples/idn-lookup.py @@ -1,63 +0,0 @@ -#!/usr/bin/python -# vim:fileencoding=utf-8 -''' - idn-lookup.py: IDN (Internationalized Domain Name) lookup support - - Authors: Zdenek Vasicek (vasicek AT fit.vutbr.cz) - Marek Vavrusa (xvavru00 AT stud.fit.vutbr.cz) - - Copyright (c) 2008. 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. - - 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. -''' -from __future__ import print_function -import unbound -import locale - -ctx = unbound.ub_ctx() -ctx.set_option("module-config:","iterator") #We don't need validation -ctx.resolvconf("/etc/resolv.conf") - -#The unicode IDN string is automatically converted (if necessary) -status, result = ctx.resolve(u"www.háčkyčárky.cz", unbound.RR_TYPE_A, unbound.RR_CLASS_IN) -if status == 0 and result.havedata: - print("Result:") - print(" raw data:", result.data) - for k in sorted(result.data.address_list): - print(" address:%s" % k) - -status, result = ctx.resolve(u"háčkyčárky.cz", unbound.RR_TYPE_MX, unbound.RR_CLASS_IN) -if status == 0 and result.havedata: - print("Result:") - print(" raw data:", result.data) - for k in sorted(result.data.mx_list_idn): - print(" priority:%d address:%s" % k) - -status, result = ctx.resolve(unbound.reverse('217.31.204.66')+'.in-addr.arpa', unbound.RR_TYPE_PTR, unbound.RR_CLASS_IN) -if status == 0 and result.havedata: - print("Result.data:", result.data) - for k in sorted(result.data.domain_list_idn): - print(" dname:%s" % k) --- contrib/unbound/libunbound/python/examples/mx-lookup.py.orig +++ contrib/unbound/libunbound/python/examples/mx-lookup.py @@ -1,54 +0,0 @@ -#!/usr/bin/python -# vim:fileencoding=utf-8 -''' - mx-lookup.py: Lookup for MX records - - Authors: Zdenek Vasicek (vasicek AT fit.vutbr.cz) - Marek Vavrusa (xvavru00 AT stud.fit.vutbr.cz) - - Copyright (c) 2008. 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. - - 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. -''' -from __future__ import print_function -import unbound - -ctx = unbound.ub_ctx() -ctx.resolvconf("/etc/resolv.conf") - -status, result = ctx.resolve("nic.cz", unbound.RR_TYPE_MX, unbound.RR_CLASS_IN) -if status == 0 and result.havedata: - print("Result:") - print(" raw data:", result.data) - for k in sorted(result.data.mx_list): - print(" priority:%d address:%s" % k) - -status, result = ctx.resolve("nic.cz", unbound.RR_TYPE_A, unbound.RR_CLASS_IN) -if status == 0 and result.havedata: - print("Result:") - print(" raw data:", result.data) - for k in sorted(result.data.address_list): - print(" address:%s" % k) --- contrib/unbound/libunbound/python/examples/ns-lookup.py.orig +++ contrib/unbound/libunbound/python/examples/ns-lookup.py @@ -1,48 +0,0 @@ -#!/usr/bin/python -# vim:fileencoding=utf-8 -''' - ns-lookup.py: Example shows how to lookup for NS records - - Authors: Zdenek Vasicek (vasicek AT fit.vutbr.cz) - Marek Vavrusa (xvavru00 AT stud.fit.vutbr.cz) - - Copyright (c) 2008. 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. - - 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. -''' -from __future__ import print_function -import unbound - -ctx = unbound.ub_ctx() -ctx.resolvconf("/etc/resolv.conf") - -status, result = ctx.resolve("vutbr.cz", unbound.RR_TYPE_NS, unbound.RR_CLASS_IN) -if status == 0 and result.havedata: - print("Result:") - print(" raw data:", result.data) - for k in sorted(result.data.domain_list): - print(" host: %s" % k) - --- contrib/unbound/libunbound/python/examples/reverse-lookup.py.orig +++ contrib/unbound/libunbound/python/examples/reverse-lookup.py @@ -1,44 +0,0 @@ -#!/usr/bin/python -''' - reverse-lookup.py: Example shows how to resolve reverse record - - Authors: Zdenek Vasicek (vasicek AT fit.vutbr.cz) - Marek Vavrusa (xvavru00 AT stud.fit.vutbr.cz) - - Copyright (c) 2008. 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. - - 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. -''' -from __future__ import print_function -import unbound - -ctx = unbound.ub_ctx() -ctx.resolvconf("/etc/resolv.conf") - -status, result = ctx.resolve(unbound.reverse("74.125.43.147") + ".in-addr.arpa.", unbound.RR_TYPE_PTR, unbound.RR_CLASS_IN) -if status == 0 and result.havedata: - print("Result.data:", result.data, sorted(result.data.domain_list)) - --- contrib/unbound/libunbound/python/file_py3.i.orig +++ contrib/unbound/libunbound/python/file_py3.i @@ -1,155 +0,0 @@ -/* - * file_py3.i: Typemaps for FILE* for Python 3 - * - * Copyright (c) 2011, Karel Slany (karel.slany AT nic.cz) - * All rights reserved. - * - * 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 organization 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 OWNER 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 -#include -%} - -%types(FILE *); - -//#define SWIG_FILE3_DEBUG - -/* converts basic file descriptor flags onto a string */ -%fragment("fdfl_to_str", "header") { -const char * -fdfl_to_str(int fdfl) { - - static const char * const file_mode[] = {"w+", "w", "r"}; - - if (fdfl & O_RDWR) { - return file_mode[0]; - } else if (fdfl & O_WRONLY) { - return file_mode[1]; - } else { - return file_mode[2]; - } -} -} - -%fragment("is_obj_file", "header") { -int -is_obj_file(PyObject *obj) { - int fd, fdfl; - if (!PyLong_Check(obj) && /* is not an integer */ - PyObject_HasAttrString(obj, "fileno") && /* has fileno method */ - (PyObject_CallMethod(obj, "flush", NULL) != NULL) && /* flush() succeeded */ - ((fd = PyObject_AsFileDescriptor(obj)) != -1) && /* got file descriptor */ - ((fdfl = fcntl(fd, F_GETFL)) != -1) /* got descriptor flags */ - ) { - return 1; - } - else { - return 0; - } -} -} - -%fragment("obj_to_file","header", fragment="fdfl_to_str,is_obj_file") { -FILE * -obj_to_file(PyObject *obj) { - int fd, fdfl; - FILE *fp; - if (is_obj_file(obj)) { - fd = PyObject_AsFileDescriptor(obj); - fdfl = fcntl(fd, F_GETFL); - fp = fdopen(dup(fd), fdfl_to_str(fdfl)); /* the FILE* must be flushed - and closed after being used */ -#ifdef SWIG_FILE3_DEBUG - fprintf(stderr, "opening fd %d (fl %d \"%s\") as FILE %p\n", - fd, fdfl, fdfl_to_str(fdfl), (void *)fp); -#endif - return fp; - } - return NULL; -} -} - -/* returns -1 if error occurred */ -/* caused magic SWIG Syntax errors when was commented out */ -#if 0 -%fragment("dispose_file", "header") { -int -dispose_file(FILE **fp) { -#ifdef SWIG_FILE3_DEBUG - fprintf(stderr, "flushing FILE %p\n", (void *)fp); -#endif - if (*fp == NULL) { - return 0; - } - if ((fflush(*fp) == 0) && /* flush file */ - (fclose(*fp) == 0)) { /* close file */ - *fp = NULL; - return 0; - } - return -1; -} -} -#endif - -%typemap(arginit, noblock = 1) FILE* { - $1 = NULL; -} - -/* - * added due to ub_ctx_debugout since since it is overloaded: - * takes void* and FILE*. In reality only FILE* but the wrapper - * and the function is declared in such way. - */ -%typemap(typecheck, noblock = 1, fragment = "is_obj_file", precedence = SWIG_TYPECHECK_POINTER) FILE* { - $1 = is_obj_file($input); -} - -%typemap(check, noblock = 1) FILE* { - if ($1 == NULL) { - /* The generated wrapper function raises TypeError on mismatching types. */ - SWIG_exception_fail(SWIG_TypeError, "in method '" "$symname" "', argument " - "$argnum"" of type '" "$type""'"); - } -} - -%typemap(in, noblock = 1, fragment = "obj_to_file") FILE* { - $1 = obj_to_file($input); -} - -/* - * Commented out due the way how ub_ctx_debugout() uses the parameter. - * This typemap would cause the FILE* to be closed after return from - * the function. This caused Python interpreter to crash, since the - * function just stores the FILE* internally in ctx and use it for - * logging. So we'll leave the closing of the file on the OS. - */ -/*%typemap(freearg, noblock = 1, fragment = "dispose_file") FILE* { - if (dispose_file(&$1) == -1) { - SWIG_exception_fail(SWIG_IOError, "closing file in method '" "$symname" "', argument " - "$argnum"" of type '" "$type""'"); - } -}*/ --- contrib/unbound/libunbound/python/libunbound.i.orig +++ contrib/unbound/libunbound/python/libunbound.i @@ -1,959 +0,0 @@ -/* - * libunbound.i: pyUnbound module (libunbound wrapper for Python) - * - * Copyright (c) 2009, Zdenek Vasicek (vasicek AT fit.vutbr.cz) - * Marek Vavrusa (xvavru00 AT stud.fit.vutbr.cz) - * - * 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 organization 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. - */ -%module unbound -%{ - #include - #include - #include - #include - #include "libunbound/unbound.h" -%} - -%pythoncode %{ - import encodings.idna - try: - import builtins - except ImportError: - import __builtin__ as builtins - - # Ensure compatibility with older python versions - if 'bytes' not in vars(): - bytes = str - - def ord(s): - if isinstance(s, int): - return s - return builtins.ord(s) -%} - -//%include "doc.i" -#if PY_MAJOR_VERSION >= 3 -%include "file_py3.i" // python 3 FILE * -#else -%include "file.i" -#endif - -%feature("docstring") strerror "Convert error value to a human readable string." - -// ================================================================================ -// ub_resolve - perform resolution and validation -// ================================================================================ -%typemap(in,numinputs=0,noblock=1) (struct ub_result** result) -{ - struct ub_result* newubr; - $1 = &newubr; -} - -/* result generation */ -%typemap(argout,noblock=1) (struct ub_result** result) -{ - if(1) { /* new code block for variable on stack */ - PyObject* tuple; - tuple = PyTuple_New(2); - PyTuple_SetItem(tuple, 0, $result); - if (result == 0) { - PyTuple_SetItem(tuple, 1, SWIG_NewPointerObj(SWIG_as_voidptr(newubr), SWIGTYPE_p_ub_result, SWIG_POINTER_OWN | 0 )); - } else { - PyTuple_SetItem(tuple, 1, Py_None); - } - $result = tuple; - } -} - - -// ================================================================================ -// ub_ctx - validation context -// ================================================================================ -%nodefaultctor ub_ctx; //no default constructor & destructor -%nodefaultdtor ub_ctx; - -%newobject ub_ctx_create; -%delobject ub_ctx_delete; -%rename(_ub_ctx_delete) ub_ctx_delete; - -%newobject ub_resolve; - -%inline %{ - void ub_ctx_free_dbg (struct ub_ctx* c) { - printf("******** UB_CTX free 0x%lX ************\n", (long unsigned int)c); - ub_ctx_delete(c); - } - - //RR types - enum enum_rr_type - { - /** a host address */ - RR_TYPE_A = 1, - /** an authoritative name server */ - RR_TYPE_NS = 2, - /** a mail destination (Obsolete - use MX) */ - RR_TYPE_MD = 3, - /** a mail forwarder (Obsolete - use MX) */ - RR_TYPE_MF = 4, - /** the canonical name for an alias */ - RR_TYPE_CNAME = 5, - /** marks the start of a zone of authority */ - RR_TYPE_SOA = 6, - /** a mailbox domain name (EXPERIMENTAL) */ - RR_TYPE_MB = 7, - /** a mail group member (EXPERIMENTAL) */ - RR_TYPE_MG = 8, - /** a mail rename domain name (EXPERIMENTAL) */ - RR_TYPE_MR = 9, - /** a null RR (EXPERIMENTAL) */ - RR_TYPE_NULL = 10, - /** a well known service description */ - RR_TYPE_WKS = 11, - /** a domain name pointer */ - RR_TYPE_PTR = 12, - /** host information */ - RR_TYPE_HINFO = 13, - /** mailbox or mail list information */ - RR_TYPE_MINFO = 14, - /** mail exchange */ - RR_TYPE_MX = 15, - /** text strings */ - RR_TYPE_TXT = 16, - /** RFC1183 */ - RR_TYPE_RP = 17, - /** RFC1183 */ - RR_TYPE_AFSDB = 18, - /** RFC1183 */ - RR_TYPE_X25 = 19, - /** RFC1183 */ - RR_TYPE_ISDN = 20, - /** RFC1183 */ - RR_TYPE_RT = 21, - /** RFC1706 */ - RR_TYPE_NSAP = 22, - /** RFC1348 */ - RR_TYPE_NSAP_PTR = 23, - /** 2535typecode */ - RR_TYPE_SIG = 24, - /** 2535typecode */ - RR_TYPE_KEY = 25, - /** RFC2163 */ - RR_TYPE_PX = 26, - /** RFC1712 */ - RR_TYPE_GPOS = 27, - /** ipv6 address */ - RR_TYPE_AAAA = 28, - /** LOC record RFC1876 */ - RR_TYPE_LOC = 29, - /** 2535typecode */ - RR_TYPE_NXT = 30, - /** draft-ietf-nimrod-dns-01.txt */ - RR_TYPE_EID = 31, - /** draft-ietf-nimrod-dns-01.txt */ - RR_TYPE_NIMLOC = 32, - /** SRV record RFC2782 */ - RR_TYPE_SRV = 33, - /** http://www.jhsoft.com/rfc/af-saa-0069.000.rtf */ - RR_TYPE_ATMA = 34, - /** RFC2915 */ - RR_TYPE_NAPTR = 35, - /** RFC2230 */ - RR_TYPE_KX = 36, - /** RFC2538 */ - RR_TYPE_CERT = 37, - /** RFC2874 */ - RR_TYPE_A6 = 38, - /** RFC2672 */ - RR_TYPE_DNAME = 39, - /** dnsind-kitchen-sink-02.txt */ - RR_TYPE_SINK = 40, - /** Pseudo OPT record... */ - RR_TYPE_OPT = 41, - /** RFC3123 */ - RR_TYPE_APL = 42, - /** draft-ietf-dnsext-delegation */ - RR_TYPE_DS = 43, - /** SSH Key Fingerprint */ - RR_TYPE_SSHFP = 44, - /** draft-richardson-ipseckey-rr-11.txt */ - RR_TYPE_IPSECKEY = 45, - /** draft-ietf-dnsext-dnssec-25 */ - RR_TYPE_RRSIG = 46, - RR_TYPE_NSEC = 47, - RR_TYPE_DNSKEY = 48, - RR_TYPE_DHCID = 49, - - RR_TYPE_NSEC3 = 50, - RR_TYPE_NSEC3PARAMS = 51, - - RR_TYPE_UINFO = 100, - RR_TYPE_UID = 101, - RR_TYPE_GID = 102, - RR_TYPE_UNSPEC = 103, - - RR_TYPE_TSIG = 250, - RR_TYPE_IXFR = 251, - RR_TYPE_AXFR = 252, - /** A request for mailbox-related records (MB, MG or MR) */ - RR_TYPE_MAILB = 253, - /** A request for mail agent RRs (Obsolete - see MX) */ - RR_TYPE_MAILA = 254, - /** any type (wildcard) */ - RR_TYPE_ANY = 255, - - /* RFC 4431, 5074, DNSSEC Lookaside Validation */ - RR_TYPE_DLV = 32769, - }; - - // RR classes - enum enum_rr_class - { - /** the Internet */ - RR_CLASS_IN = 1, - /** Chaos class */ - RR_CLASS_CH = 3, - /** Hesiod (Dyer 87) */ - RR_CLASS_HS = 4, - /** None class, dynamic update */ - RR_CLASS_NONE = 254, - /** Any class */ - RR_CLASS_ANY = 255, - }; -%} - -%feature("docstring") ub_ctx "Unbound resolving and validation context. - -The validation context is created to hold the resolver status, validation keys and a small cache (containing messages, rrsets, roundtrip times, trusted keys, lameness information). - -**Usage** - ->>> import unbound ->>> ctx = unbound.ub_ctx() ->>> ctx.resolvconf(\"/etc/resolv.conf\") ->>> status, result = ctx.resolve(\"www.google.com\", unbound.RR_TYPE_A, unbound.RR_CLASS_IN) ->>> if status==0 and result.havedata: ->>> print \"Result:\",result.data.address_list -Result: ['74.125.43.147', '74.125.43.99', '74.125.43.103', '74.125.43.104'] -" - -%extend ub_ctx -{ - %pythoncode %{ - def __init__(self): - """Creates a resolving and validation context. - - An exception is invoked if the process of creation an ub_ctx instance fails. - """ - self.this = _unbound.ub_ctx_create() - if not self.this: - raise Exception("Fatal error: unbound context initialization failed") - - #__swig_destroy__ = _unbound.ub_ctx_free_dbg - __swig_destroy__ = _unbound._ub_ctx_delete - - #UB_CTX_METHODS_# - def add_ta(self,ta): - """Add a trust anchor to the given context. - - The trust anchor is a string, on one line, that holds a valid DNSKEY or DS RR. - - :param ta: - string, with zone-format RR on one line. [domainname] [TTL optional] [type] [class optional] [rdata contents] - :returns: (int) 0 if OK, else error. - """ - return _unbound.ub_ctx_add_ta(self,ta) - #parameters: struct ub_ctx *,char *, - #retvals: int - - def add_ta_file(self,fname): - """Add trust anchors to the given context. - - Pass name of a file with DS and DNSKEY records (like from dig or drill). - - :param fname: - filename of file with keyfile with trust anchors. - :returns: (int) 0 if OK, else error. - """ - return _unbound.ub_ctx_add_ta_file(self,fname) - #parameters: struct ub_ctx *,char *, - #retvals: int - - def config(self,fname): - """setup configuration for the given context. - - :param fname: - unbound config file (not all settings applicable). This is a power-users interface that lets you specify all sorts of options. For some specific options, such as adding trust anchors, special routines exist. - :returns: (int) 0 if OK, else error. - """ - return _unbound.ub_ctx_config(self,fname) - #parameters: struct ub_ctx *,char *, - #retvals: int - - def debuglevel(self,d): - """Set debug verbosity for the context Output is directed to stderr. - - :param d: - debug level, 0 is off, 1 is very minimal, 2 is detailed, and 3 is lots. - :returns: (int) 0 if OK, else error. - """ - return _unbound.ub_ctx_debuglevel(self,d) - #parameters: struct ub_ctx *,int, - #retvals: int - - def debugout(self,out): - """Set debug output (and error output) to the specified stream. - - Pass None to disable. Default is stderr. - - :param out: - File stream to log to. - :returns: (int) 0 if OK, else error. - - **Usage:** - - In order to log into file, use - - :: - - ctx = unbound.ub_ctx() - fw = fopen("debug.log") - ctx.debuglevel(3) - ctx.debugout(fw) - - Another option is to print the debug informations to stderr output - - :: - - ctx = unbound.ub_ctx() - ctx.debuglevel(10) - ctx.debugout(sys.stderr) - """ - return _unbound.ub_ctx_debugout(self,out) - #parameters: struct ub_ctx *,void *, - #retvals: int - - def hosts(self,fname="/etc/hosts"): - """Read list of hosts from the filename given. - - Usually "/etc/hosts". These addresses are not flagged as DNSSEC secure when queried for. - - :param fname: - file name string. If None "/etc/hosts" is used. - :returns: (int) 0 if OK, else error. - """ - return _unbound.ub_ctx_hosts(self,fname) - #parameters: struct ub_ctx *,char *, - #retvals: int - - def print_local_zones(self): - """Print the local zones and their content (RR data) to the debug output. - - :returns: (int) 0 if OK, else error. - """ - return _unbound.ub_ctx_print_local_zones(self) - #parameters: struct ub_ctx *, - #retvals: int - - def resolvconf(self,fname="/etc/resolv.conf"): - """Read 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 resolv.conf(5) are ignored. - - :param fname: - file name string. If None "/etc/resolv.conf" is used. - :returns: (int) 0 if OK, else error. - """ - return _unbound.ub_ctx_resolvconf(self,fname) - #parameters: struct ub_ctx *,char *, - #retvals: int - - def set_async(self,dothread): - """Set a context behaviour for asynchronous action. - - :param dothread: - if True, enables threading and a call to :meth:`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 :meth:`async` calls have been made have no effect (delete and re-create the context to change). - :returns: (int) 0 if OK, else error. - """ - return _unbound.ub_ctx_async(self,dothread) - #parameters: struct ub_ctx *,int, - #retvals: int - - def set_fwd(self,addr): - """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 is not DNSSEC-capable, validation may fail. Can be called several times, in that case the addresses are used as backup servers. - - To read the list of nameservers from /etc/resolv.conf (from DHCP or so), use the call :meth:`resolvconf`. - - :param addr: - address, IP4 or IP6 in string format. If the addr is None, forwarding is disabled. - :returns: (int) 0 if OK, else error. - """ - return _unbound.ub_ctx_set_fwd(self,addr) - #parameters: struct ub_ctx *,char *, - #retvals: int - - def set_option(self,opt,val): - """Set an option for the context. - - Changes to the options after :meth:`resolve`, :meth:`resolve_async`, :meth:`zone_add`, :meth:`zone_remove`, :meth:`data_add` or :meth:`data_remove` have no effect (you have to delete and re-create the context). - - :param opt: - option name from the unbound.conf config file format. (not all settings applicable). The name includes the trailing ':' for example set_option("logfile:", "mylog.txt"); This is a power-users interface that lets you specify all sorts of options. For some specific options, such as adding trust anchors, special routines exist. - :param val: - value of the option. - :returns: (int) 0 if OK, else error. - """ - return _unbound.ub_ctx_set_option(self,opt,val) - #parameters: struct ub_ctx *,char *,char *, - #retvals: int - - def trustedkeys(self,fname): - """Add trust anchors to the given context. - - Pass the name of a bind-style config file with trusted-keys{}. - - :param fname: - filename of file with bind-style config entries with trust anchors. - :returns: (int) 0 if OK, else error. - """ - return _unbound.ub_ctx_trustedkeys(self,fname) - #parameters: struct ub_ctx *,char *, - #retvals: int - #_UB_CTX_METHODS# - - def zone_print(self): - """Print local zones using debugout""" - _unbound.ub_ctx_print_local_zones(self) - - def zone_add(self,zonename,zonetype): - """Add new local zone - - :param zonename: zone domain name (e.g. myzone.) - :param zonetype: type of the zone ("static",...) - :returns: (int) 0 if OK, else error. - """ - return _unbound.ub_ctx_zone_add(self,zonename, zonetype) - #parameters: struct ub_ctx *,char*, char* - #retvals: int - - def zone_remove(self,zonename): - """Remove local zone - - If exists, removes local zone with all the RRs. - - :param zonename: zone domain name - :returns: (int) 0 if OK, else error. - """ - return _unbound.ub_ctx_zone_remove(self,zonename) - #parameters: struct ub_ctx *,char* - #retvals: int - - def data_add(self,rrdata): - """Add new local RR data - - :param rrdata: string, in zone-format on one line. [domainname] [TTL optional] [type] [class optional] [rdata contents] - :returns: (int) 0 if OK, else error. - - **Usage** - The local data ... - - :: - - >>> ctx = unbound.ub_ctx() - >>> ctx.zone_add("mydomain.net.","static") - 0 - >>> status = ctx.data_add("test.mydomain.net. IN A 192.168.1.1") - 0 - >>> status, result = ctx.resolve("test.mydomain.net") - >>> if status==0 and result.havedata: - >>> print \"Result:\",result.data.address_list - Result: ['192.168.1.1'] - - """ - return _unbound.ub_ctx_data_add(self,rrdata) - #parameters: struct ub_ctx *,char* - #retvals: int - - def data_remove(self,rrdata): - """Remove local RR data - - If exists, remove resource record from local zone - - :param rrdata: string, in zone-format on one line. [domainname] [TTL optional] [type] [class optional] [rdata contents] - :returns: (int) 0 if OK, else error. - """ - return _unbound.ub_ctx_data_remove(self,rrdata) - #parameters: struct ub_ctx *,char* - #retvals: int - - #UB_METHODS_# - def cancel(self,async_id): - """Cancel an async query in progress. - - Its callback will not be called. - - :param async_id: - which query to cancel. - :returns: (int) 0 if OK, else error. - """ - return _unbound.ub_cancel(self,async_id) - #parameters: struct ub_ctx *,int, - #retvals: int - - def get_fd(self): - """Get file descriptor. - - Wait for it to become readable, at this point answers are returned from the asynchronous validating resolver. Then call the ub_process to continue processing. This routine works immediately after context creation, the fd does not change. - - :returns: (int) -1 on error, or file descriptor to use select(2) with. - """ - return _unbound.ub_fd(self) - #parameters: struct ub_ctx *, - #retvals: int - - def poll(self): - """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: (int) 0 if nothing to read, or nonzero if a result is available. If nonzero, call ctx_process() to do callbacks. - """ - return _unbound.ub_poll(self) - #parameters: struct ub_ctx *, - #retvals: int - - def process(self): - """Call this routine to continue processing results from the validating resolver (when the fd becomes readable). - - Will perform necessary callbacks. - - :returns: (int) 0 if OK, else error. - """ - return _unbound.ub_process(self) - #parameters: struct ub_ctx *, - #retvals: int - - def resolve(self,name,rrtype=RR_TYPE_A,rrclass=RR_CLASS_IN): - """Perform resolution and validation of the target name. - - :param name: - domain name in text format (a string or unicode string). IDN domain name have to be passed as a unicode string. - :param rrtype: - type of RR in host order (optional argument). Default value is RR_TYPE_A (A class). - :param rrclass: - class of RR in host order (optional argument). Default value is RR_CLASS_IN (for internet). - :returns: * (int) 0 if OK, else error. - * (:class:`ub_result`) the result data is returned in a newly allocated result structure. May be None on return, return value is set to an error in that case (out of memory). - """ - if isinstance(name, bytes): #probably IDN - return _unbound.ub_resolve(self,name,rrtype,rrclass) - else: - return _unbound.ub_resolve(self,idn2dname(name),rrtype,rrclass) - #parameters: struct ub_ctx *,char *,int,int, - #retvals: int,struct ub_result ** - - def resolve_async(self,name,mydata,callback,rrtype=RR_TYPE_A,rrclass=RR_CLASS_IN): - """Perform resolution and validation of the target name. - - Asynchronous, after a while, the callback will be called with your data and the result. - If an error happens during processing, your callback will be called with error set to a nonzero value (and result==None). - - :param name: - domain name in text format (a string or unicode string). IDN domain name have to be passed as a unicode string. - :param mydata: - this data is your own data (you can pass arbitrary python object or None) which are passed on to the callback function. - :param callback: - call-back function which is called on completion of the resolution. - :param rrtype: - type of RR in host order (optional argument). Default value is RR_TYPE_A (A class). - :param rrclass: - class of RR in host order (optional argument). Default value is RR_CLASS_IN (for internet). - :returns: * (int) 0 if OK, else error. - * (int) async_id, an identifier number is returned for the query as it is in progress. It can be used to cancel the query. - - **Call-back function:** - The call-back function looks as the follows:: - - def call_back(mydata, status, result): - pass - - **Parameters:** - * `mydata` - mydata object - * `status` - 0 when a result has been found - * `result` - the result structure. The result may be None, in that case err is set. - - """ - if isinstance(name, bytes): #probably IDN - return _unbound._ub_resolve_async(self,name,rrtype,rrclass,mydata,callback) - else: - return _unbound._ub_resolve_async(self,idn2dname(name),rrtype,rrclass,mydata,callback) - #parameters: struct ub_ctx *,char *,int,int,void *,ub_callback_t, - #retvals: int, int - - def wait(self): - """Wait for a context to finish with results. - - Calls after the wait for you. After the wait, there are no more outstanding asynchronous queries. - - :returns: (int) 0 if OK, else error. - """ - return _unbound.ub_wait(self) - #parameters: struct ub_ctx *, - #retvals: int - - #_UB_METHODS# - %} -} - - -// ================================================================================ -// ub_result - validation and resolution results -// ================================================================================ -%nodefaultctor ub_result; //no default constructor & destructor -%nodefaultdtor ub_result; - -%delobject ub_resolve_free; -%rename(_ub_resolve_free) ub_resolve_free; - -%inline %{ - void ub_resolve_free_dbg (struct ub_result* r) { - printf("******** UB_RESOLVE free 0x%lX ************\n", (long unsigned int)r); - ub_resolve_free(r); - } -%} - -%feature("docstring") ub_result "The validation and resolution results." - -//ub_result.rcode -%inline %{ - enum result_enum_rcode { - RCODE_NOERROR = 0, - RCODE_FORMERR = 1, - RCODE_SERVFAIL = 2, - RCODE_NXDOMAIN = 3, - RCODE_NOTIMPL = 4, - RCODE_REFUSED = 5, - RCODE_YXDOMAIN = 6, - RCODE_YXRRSET = 7, - RCODE_NXRRSET = 8, - RCODE_NOTAUTH = 9, - RCODE_NOTZONE = 10 - }; -%} - -%pythoncode %{ - class ub_data: - """Class which makes the resolution results accessible""" - def __init__(self, data): - """Creates ub_data class - :param data: a list of the result data in RAW format - """ - if data == None: - raise Exception("ub_data init: No data") - self.data = data - - def __str__(self): - """Represents data as string""" - return ';'.join([' '.join(map(lambda x:"%02X" % ord(x),a)) for a in self.data]) - - @staticmethod - def dname2str(s, ofs=0, maxlen=0): - """Parses DNAME and produces a list of labels - - :param ofs: where the conversion should start to parse data - :param maxlen: maximum length (0 means parse to the end) - :returns: list of labels (string) - """ - if not s: - return [] - - res = [] - slen = len(s) - if maxlen > 0: - slen = min(slen, maxlen) - - idx = ofs - while (idx < slen): - complen = ord(s[idx]) - # In python 3.x `str()` converts the string to unicode which is the expected text string type - res.append(str(s[idx+1:idx+1+complen].decode())) - idx += complen + 1 - - return res - - def as_raw_data(self): - """Returns a list of RAW strings""" - return self.data - - raw = property(as_raw_data, doc="Returns RAW data (a list of binary encoded strings). See :meth:`as_raw_data`") - - def as_mx_list(self): - """Represents data as a list of MX records (query for RR_TYPE_MX) - - :returns: list of tuples (priority, dname) - """ - return [(256*ord(rdf[0])+ord(rdf[1]),'.'.join([a for a in self.dname2str(rdf,2)])) for rdf in self.data] - - mx_list = property(as_mx_list, doc="Returns a list of tuples containing priority and domain names. See :meth:`as_mx_list`") - - def as_idn_mx_list(self): - """Represents data as a list of MX records (query for RR_TYPE_MX) - - :returns: list of tuples (priority, unicode dname) - """ - return [(256*ord(rdf[0])+ord(rdf[1]),'.'.join([encodings.idna.ToUnicode(a) for a in self.dname2str(rdf,2)])) for rdf in self.data] - - mx_list_idn = property(as_idn_mx_list, doc="Returns a list of tuples containing priority and IDN domain names. See :meth:`as_idn_mx_list`") - - def as_address_list(self): - """Represents data as a list of IP addresses (query for RR_TYPE_PTR) - - :returns: list of strings - """ - return ['.'.join(map(lambda x:str(ord(x)),a)) for a in self.data] - - address_list = property(as_address_list, doc="Returns a list of IP addresses. See :meth:`as_address_list`") - - def as_domain_list(self): - """Represents data as a list of domain names (query for RR_TYPE_A) - - :returns: list of strings - """ - return map(lambda x:'.'.join(self.dname2str(x)), self.data) - - domain_list = property(as_domain_list, doc="Returns a list of domain names. See :meth:`as_domain_list`") - - def as_idn_domain_list(self): - """Represents data as a list of unicode domain names (query for RR_TYPE_A) - - :returns: list of strings - """ - return map(lambda x: '.'.join([encodings.idna.ToUnicode(a) for a in self.dname2str(x)]), self.data) - - domain_list_idn = property(as_idn_domain_list, doc="Returns a list of IDN domain names. See :meth:`as_idn_domain_list`") -%} - -%extend ub_result -{ - - %rename(_data) data; - - PyObject* _ub_result_data(struct ub_result* result) { - PyObject *list; - int i,cnt; - (void)self; - if ((result == 0) || (!result->havedata) || (result->data == 0)) - return Py_None; - - for (cnt=0,i=0;;i++,cnt++) - if (result->data[i] == 0) - break; - - list = PyList_New(cnt); - for (i=0;idata[i],result->len[i])); - - return list; - } - - PyObject* _packet() { - return PyBytes_FromStringAndSize($self->answer_packet, $self->answer_len); - } - - %pythoncode %{ - def __init__(self): - raise Exception("This class can't be created directly.") - - #__swig_destroy__ = _unbound.ub_resolve_free_dbg - __swig_destroy__ = _unbound._ub_resolve_free - - #havedata = property(_unbound.ub_result_havedata_get, _unbound.ub_result_havedata_set, "Havedata property") - - rcode2str = {RCODE_NOERROR:'no error', RCODE_FORMERR:'form error', RCODE_SERVFAIL:'serv fail', RCODE_NXDOMAIN:'nx domain', RCODE_NOTIMPL:'not implemented', RCODE_REFUSED:'refused', RCODE_YXDOMAIN:'yxdomain', RCODE_YXRRSET:'yxrrset', RCODE_NXRRSET:'nxrrset', RCODE_NOTAUTH:'not auth', RCODE_NOTZONE:'not zone'} - - def _get_rcode_str(self): - """Returns rcode in display representation form - - :returns: string - """ - return self.rcode2str[self.rcode] - - __swig_getmethods__["rcode_str"] = _get_rcode_str - if _newclass:rcode_str = _swig_property(_get_rcode_str) - - def _get_raw_data(self): - """Result data, a list of network order DNS rdata items. - - Data are represented as a list of strings. To decode RAW data to the list of IP addresses use :attr:`data` attribute which returns an :class:`ub_data` instance containing conversion function. - """ - return self._ub_result_data(self) - - __swig_getmethods__["rawdata"] = _get_raw_data - rawdata = property(_get_raw_data, doc="Returns raw data, a list of rdata items. To decode RAW data use the :attr:`data` attribute which returns an instance of :class:`ub_data` containing the conversion functions.") - - def _get_data(self): - if not self.havedata: return None - return ub_data(self._ub_result_data(self)) - - __swig_getmethods__["data"] = _get_data - __swig_getmethods__["packet"] = _packet - data = property(_get_data, doc="Returns :class:`ub_data` instance containing various decoding functions or None") - -%} - -} - -%exception ub_resolve -%{ - //printf("resolve_start(%lX)\n",(long unsigned int)arg1); - Py_BEGIN_ALLOW_THREADS - $function - Py_END_ALLOW_THREADS - //printf("resolve_stop()\n"); -%} - -%include "libunbound/unbound.h" - -%inline %{ - //SWIG will see the ub_ctx as a class - struct ub_ctx { - }; -%} - -//ub_ctx_debugout void* parameter correction -int ub_ctx_debugout(struct ub_ctx* ctx, FILE* out); - -// ================================================================================ -// ub_resolve_async - perform asynchronous resolution and validation -// ================================================================================ - -%typemap(in,numinputs=0,noblock=1) (int* async_id) -{ - int asyncid = -1; - $1 = &asyncid; -} - -%apply PyObject* {void* mydata} - -/* result generation */ -%typemap(argout,noblock=1) (int* async_id) -{ - if(1) { /* new code block for variable on stack */ - PyObject* tuple; - tuple = PyTuple_New(2); - PyTuple_SetItem(tuple, 0, $result); - PyTuple_SetItem(tuple, 1, SWIG_From_int(asyncid)); - $result = tuple; - } -} - -// Grab a Python function object as a Python object. -%typemap(in) (PyObject *pyfunc) { - if (!PyCallable_Check($input)) - { - PyErr_SetString(PyExc_TypeError, "Need a callable object!"); - return NULL; - } - $1 = $input; -} - -// Python callback workaround -int _ub_resolve_async(struct ub_ctx* ctx, char* name, int rrtype, int rrclass, void* mydata, PyObject *pyfunc, int* async_id); - -%{ - struct cb_data { - PyObject* data; - PyObject* func; - }; - - static void PythonCallBack(void* iddata, int status, struct ub_result* result) - { - PyObject *arglist; - PyObject *fresult; - struct cb_data* id; - id = (struct cb_data*) iddata; - arglist = Py_BuildValue("(OiO)",id->data,status, SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_ub_result, 0 | 0 )); // Build argument list - fresult = PyEval_CallObject(id->func,arglist); // Call Python - Py_DECREF(id->func); - Py_DECREF(id->data); - free(id); - ub_resolve_free(result); //free ub_result - //ub_resolve_free_dbg(result); //free ub_result - Py_DECREF(arglist); // Trash arglist - Py_XDECREF(fresult); - } - - int _ub_resolve_async(struct ub_ctx* ctx, char* name, int rrtype, int rrclass, PyObject* mydata, PyObject *pyfunc, int* async_id) { - int r; - struct cb_data* id; - id = (struct cb_data*) malloc(sizeof(struct cb_data)); - id->data = mydata; - id->func = pyfunc; - - r = ub_resolve_async(ctx,name,rrtype,rrclass, (void *) id, PythonCallBack, async_id); - Py_INCREF(mydata); - Py_INCREF(pyfunc); - return r; - } - -%} - -%pythoncode %{ - ub_resolve_async = _unbound._ub_resolve_async - - def reverse(domain): - """Reverse domain name - - Usable for reverse lookups when the IP address should be reversed - """ - return '.'.join([a for a in domain.split(".")][::-1]) - - def idn2dname(idnname): - """Converts domain name in IDN format to canonic domain name - - :param idnname: (unicode string) IDN name - :returns: (string) domain name - """ - return '.'.join([encodings.idna.ToASCII(a) if a else '' for a in idnname.split('.')]) - - def dname2idn(name): - """Converts canonic domain name in IDN format to unicode string - - :param name: (string) domain name - :returns: (unicode string) domain name - """ - return '.'.join([encodings.idna.ToUnicode(a) for a in name.split('.')]) - -%} - --- contrib/unbound/libunbound/ubsyms.def.orig +++ contrib/unbound/libunbound/ubsyms.def @@ -20,6 +20,7 @@ ub_ctx_set_fwd ub_ctx_set_option ub_ctx_set_stub +ub_ctx_set_tls ub_ctx_trustedkeys ub_ctx_zone_add ub_ctx_zone_remove --- contrib/unbound/libunbound/unbound-event.h.orig +++ contrib/unbound/libunbound/unbound-event.h @@ -170,7 +170,7 @@ struct ub_event_vmt* vmt; }; -typedef void (*ub_event_callback_t)(void*, int, void*, int, int, char*); +typedef void (*ub_event_callback_type)(void*, int, void*, int, int, char*, int); /** * Create a resolving and validation context. @@ -254,7 +254,8 @@ * @return 0 if OK, else error. */ int ub_resolve_event(struct ub_ctx* ctx, const char* name, int rrtype, - int rrclass, void* mydata, ub_event_callback_t callback, int* async_id); + int rrclass, void* mydata, ub_event_callback_type callback, + int* async_id); #ifdef __cplusplus } --- contrib/unbound/libunbound/unbound.h.orig +++ contrib/unbound/libunbound/unbound.h @@ -37,7 +37,7 @@ * \file * * This file contains functions to resolve DNS queries and - * validate the answers. Synchonously and asynchronously. + * validate the answers. Synchronously and asynchronously. * * Several ways to use this interface from an application wishing * to perform (validated) DNS lookups. @@ -177,7 +177,7 @@ * False, if validation failed or domain queried has no security info. * * It is possible to get a result with no data (havedata is false), - * and secure is true. This means that the non-existance of the data + * and secure is true. This means that the non-existence of the data * was cryptographically proven (with signatures). */ int secure; @@ -204,6 +204,13 @@ char* why_bogus; /** + * If the query or one of its subqueries was ratelimited. Useful if + * ratelimiting is enabled and answer to the client is SERVFAIL as a + * result. + */ + int was_ratelimited; + + /** * TTL for the result, in seconds. If the security is bogus, then * you also cannot trust this value. */ @@ -223,7 +230,7 @@ * This structure is allocated on the heap and needs to be * freed with ub_resolve_free(result); */ -typedef void (*ub_callback_t)(void*, int, struct ub_result*); +typedef void (*ub_callback_type)(void*, int, struct ub_result*); /** * Create a resolving and validation context. @@ -304,6 +311,17 @@ int ub_ctx_set_fwd(struct ub_ctx* ctx, const char* addr); /** + * Use DNS over TLS to send queries to machines set with ub_ctx_set_fwd(). + * + * @param ctx: context. + * At this time it is only possible to set configuration before the + * first resolve is done. + * @param tls: enable or disable DNS over TLS + * @return 0 if OK, else error. + */ +int ub_ctx_set_tls(struct ub_ctx* ctx, int tls); + +/** * Add a stub zone, with given address to send to. This is for custom * root hints or pointing to a local authoritative dns server. * For dns resolvers and the 'DHCP DNS' ip address, use ub_ctx_set_fwd. @@ -519,7 +537,7 @@ * @return 0 if OK, else error. */ int ub_resolve_async(struct ub_ctx* ctx, const char* name, int rrtype, - int rrclass, void* mydata, ub_callback_t callback, int* async_id); + int rrclass, void* mydata, ub_callback_type callback, int* async_id); /** * Cancel an async query in progress. @@ -601,6 +619,204 @@ */ const char* ub_version(void); +/** + * Some global statistics that are not in struct stats_info, + * this struct is shared on a shm segment (shm-key in unbound.conf) + */ +struct ub_shm_stat_info { + int num_threads; + + struct { + long long now_sec, now_usec; + long long up_sec, up_usec; + long long elapsed_sec, elapsed_usec; + } time; + + struct { + long long msg; + long long rrset; + long long val; + long long iter; + long long subnet; + long long ipsecmod; + long long respip; + long long dnscrypt_shared_secret; + long long dnscrypt_nonce; + } mem; +}; + +/** number of qtype that is stored for in array */ +#define UB_STATS_QTYPE_NUM 256 +/** number of qclass that is stored for in array */ +#define UB_STATS_QCLASS_NUM 256 +/** number of rcodes in stats */ +#define UB_STATS_RCODE_NUM 16 +/** number of opcodes in stats */ +#define UB_STATS_OPCODE_NUM 16 +/** number of histogram buckets */ +#define UB_STATS_BUCKET_NUM 40 +/** number of RPZ actions */ +#define UB_STATS_RPZ_ACTION_NUM 10 + +/** per worker statistics. */ +struct ub_server_stats { + /** number of queries from clients received. */ + long long num_queries; + /** number of queries that have been dropped/ratelimited by ip. */ + long long num_queries_ip_ratelimited; + /** number of queries that had a cache-miss. */ + long long num_queries_missed_cache; + /** number of prefetch queries - cachehits with prefetch */ + long long num_queries_prefetch; + + /** + * Sum of the querylistsize of the worker for + * every query that missed cache. To calculate average. + */ + long long sum_query_list_size; + /** max value of query list size reached. */ + long long max_query_list_size; + + /** Extended stats below (bool) */ + int extended; + + /** qtype stats */ + long long qtype[UB_STATS_QTYPE_NUM]; + /** bigger qtype values not in array */ + long long qtype_big; + /** qclass stats */ + long long qclass[UB_STATS_QCLASS_NUM]; + /** bigger qclass values not in array */ + long long qclass_big; + /** query opcodes */ + long long qopcode[UB_STATS_OPCODE_NUM]; + /** number of queries over TCP */ + long long qtcp; + /** number of outgoing queries over TCP */ + long long qtcp_outgoing; + /** number of queries over (DNS over) TLS */ + long long qtls; + /** number of queries over IPv6 */ + long long qipv6; + /** number of queries with QR bit */ + long long qbit_QR; + /** number of queries with AA bit */ + long long qbit_AA; + /** number of queries with TC bit */ + long long qbit_TC; + /** number of queries with RD bit */ + long long qbit_RD; + /** number of queries with RA bit */ + long long qbit_RA; + /** number of queries with Z bit */ + long long qbit_Z; + /** number of queries with AD bit */ + long long qbit_AD; + /** number of queries with CD bit */ + long long qbit_CD; + /** number of queries with EDNS OPT record */ + long long qEDNS; + /** number of queries with EDNS with DO flag */ + long long qEDNS_DO; + /** answer rcodes */ + long long ans_rcode[UB_STATS_RCODE_NUM]; + /** answers with pseudo rcode 'nodata' */ + long long ans_rcode_nodata; + /** answers that were secure (AD) */ + long long ans_secure; + /** answers that were bogus (withheld as SERVFAIL) */ + long long ans_bogus; + /** rrsets marked bogus by validator */ + long long rrset_bogus; + /** number of queries that have been ratelimited by domain recursion. */ + long long queries_ratelimited; + /** unwanted traffic received on server-facing ports */ + long long unwanted_replies; + /** unwanted traffic received on client-facing ports */ + long long unwanted_queries; + /** usage of tcp accept list */ + long long tcp_accept_usage; + /** expired answers served from cache */ + long long ans_expired; + /** histogram data exported to array + * if the array is the same size, no data is lost, and + * if all histograms are same size (is so by default) then + * adding up works well. */ + long long hist[UB_STATS_BUCKET_NUM]; + + /** number of message cache entries */ + long long msg_cache_count; + /** number of rrset cache entries */ + long long rrset_cache_count; + /** number of infra cache entries */ + long long infra_cache_count; + /** number of key cache entries */ + long long key_cache_count; + + /** number of queries that used dnscrypt */ + long long num_query_dnscrypt_crypted; + /** number of queries that queried dnscrypt certificates */ + long long num_query_dnscrypt_cert; + /** number of queries in clear text and not asking for the certificates */ + long long num_query_dnscrypt_cleartext; + /** number of malformed encrypted queries */ + long long num_query_dnscrypt_crypted_malformed; + /** number of queries which did not have a shared secret in cache */ + long long num_query_dnscrypt_secret_missed_cache; + /** number of dnscrypt shared secret cache entries */ + long long shared_secret_cache_count; + /** number of queries which are replays */ + long long num_query_dnscrypt_replay; + /** number of dnscrypt nonces cache entries */ + long long nonce_cache_count; + /** number of queries for unbound's auth_zones, upstream query */ + long long num_query_authzone_up; + /** number of queries for unbound's auth_zones, downstream answers */ + long long num_query_authzone_down; + /** number of times neg cache records were used to generate NOERROR + * responses. */ + long long num_neg_cache_noerror; + /** number of times neg cache records were used to generate NXDOMAIN + * responses. */ + long long num_neg_cache_nxdomain; + /** number of queries answered from edns-subnet specific data */ + long long num_query_subnet; + /** number of queries answered from edns-subnet specific data, and + * the answer was from the edns-subnet cache. */ + long long num_query_subnet_cache; + /** number of bytes in the stream wait buffers */ + long long mem_stream_wait; + /** number of TLS connection resume */ + long long qtls_resume; + /** RPZ action stats */ + long long rpz_action[UB_STATS_RPZ_ACTION_NUM]; +}; + +/** + * Statistics to send over the control pipe when asked + * This struct is made to be memcopied, sent in binary. + * shm mapped with (number+1) at num_threads+1, with first as total + */ +struct ub_stats_info { + /** the thread stats */ + struct ub_server_stats svr; + + /** mesh stats: current number of states */ + long long mesh_num_states; + /** mesh stats: current number of reply (user) states */ + long long mesh_num_reply_states; + /** mesh stats: number of reply states overwritten with a new one */ + long long mesh_jostled; + /** mesh stats: number of incoming queries dropped */ + long long mesh_dropped; + /** mesh stats: replies sent */ + long long mesh_replies_sent; + /** mesh stats: sum of waiting times for the replies */ + long long mesh_replies_sum_wait_sec, mesh_replies_sum_wait_usec; + /** mesh stats: median of waiting times for replies (in sec) */ + double mesh_time_median; +}; + #ifdef __cplusplus } #endif --- contrib/unbound/libunbound/worker.h.orig +++ contrib/unbound/libunbound/worker.h @@ -49,31 +49,31 @@ struct module_qstate; struct tube; struct edns_option; +struct query_info; /** * Worker service routine to send serviced queries to authoritative servers. - * @param qname: query name. (host order) - * @param qnamelen: length in bytes of qname, including trailing 0. - * @param qtype: query type. (host order) - * @param qclass: query class. (host order) + * @param qinfo: query info. * @param flags: host order flags word, with opcode and CD bit. * @param dnssec: if set, EDNS record will have DO bit set. * @param want_dnssec: signatures needed. * @param nocaps: ignore capsforid(if in config), do not perturb qname. - * @param opt_list: EDNS options on outgoing packet. * @param addr: where to. * @param addrlen: length of addr. * @param zone: delegation point name. * @param zonelen: length of zone name wireformat dname. + * @param ssl_upstream: use SSL for upstream queries. + * @param tls_auth_name: if ssl_upstream, use this name with TLS + * authentication. * @param q: wich query state to reactivate upon return. * @return: false on failure (memory or socket related). no query was * sent. */ -struct outbound_entry* libworker_send_query(uint8_t* qname, size_t qnamelen, - uint16_t qtype, uint16_t qclass, uint16_t flags, int dnssec, - int want_dnssec, int nocaps, struct edns_option* opt_list, +struct outbound_entry* libworker_send_query(struct query_info* qinfo, + uint16_t flags, int dnssec, int want_dnssec, int nocaps, struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone, - size_t zonelen, struct module_qstate* q); + size_t zonelen, int ssl_upstream, char* tls_auth_name, + struct module_qstate* q); /** process incoming replies from the network */ int libworker_handle_reply(struct comm_point* c, void* arg, int error, @@ -89,15 +89,15 @@ /** mesh callback with fg results */ void libworker_fg_done_cb(void* arg, int rcode, sldns_buffer* buf, - enum sec_status s, char* why_bogus); + enum sec_status s, char* why_bogus, int was_ratelimited); /** mesh callback with bg results */ void libworker_bg_done_cb(void* arg, int rcode, sldns_buffer* buf, - enum sec_status s, char* why_bogus); + enum sec_status s, char* why_bogus, int was_ratelimited); /** mesh callback with event results */ void libworker_event_done_cb(void* arg, int rcode, struct sldns_buffer* buf, - enum sec_status s, char* why_bogus); + enum sec_status s, char* why_bogus, int was_ratelimited); /** * Worker signal handler function. User argument is the worker itself. @@ -108,28 +108,27 @@ /** * Worker service routine to send serviced queries to authoritative servers. - * @param qname: query name. (host order) - * @param qnamelen: length in bytes of qname, including trailing 0. - * @param qtype: query type. (host order) - * @param qclass: query class. (host order) + * @param qinfo: query info. * @param flags: host order flags word, with opcode and CD bit. * @param dnssec: if set, EDNS record will have DO bit set. * @param want_dnssec: signatures needed. * @param nocaps: ignore capsforid(if in config), do not perturb qname. - * @param opt_list: EDNS options on outgoing packet. * @param addr: where to. * @param addrlen: length of addr. * @param zone: wireformat dname of the zone. * @param zonelen: length of zone name. + * @param ssl_upstream: use SSL for upstream queries. + * @param tls_auth_name: if ssl_upstream, use this name with TLS + * authentication. * @param q: wich query state to reactivate upon return. * @return: false on failure (memory or socket related). no query was * sent. */ -struct outbound_entry* worker_send_query(uint8_t* qname, size_t qnamelen, - uint16_t qtype, uint16_t qclass, uint16_t flags, int dnssec, - int want_dnssec, int nocaps, struct edns_option* opt_list, +struct outbound_entry* worker_send_query(struct query_info* qinfo, + uint16_t flags, int dnssec, int want_dnssec, int nocaps, struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone, - size_t zonelen, struct module_qstate* q); + size_t zonelen, int ssl_upstream, char* tls_auth_name, + struct module_qstate* q); /** * process control messages from the main thread. Frees the control --- contrib/unbound/ltmain.sh.orig +++ contrib/unbound/ltmain.sh @@ -2124,7 +2124,7 @@ # a configuration failure hint, and exit. func_fatal_configuration () { - func__fatal_error ${1+"$@"} \ + func_fatal_error ${1+"$@"} \ "See the $PACKAGE documentation for more information." \ "Fatal configuration error." } @@ -7272,10 +7272,12 @@ # -tp=* Portland pgcc target processor selection # --sysroot=* for sysroot support # -O*, -g*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization + # -specs=* GCC specs files # -stdlib=* select c++ std lib with clang -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \ - -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*) + -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*| \ + -specs=*) func_quote_for_eval "$arg" arg=$func_quote_for_eval_result func_append compile_command " $arg" --- contrib/unbound/respip/respip.c.orig +++ contrib/unbound/respip/respip.c @@ -0,0 +1,1321 @@ +/* + * respip/respip.c - filtering response IP module + */ + +/** + * \file + * + * This file contains a module that inspects a result of recursive resolution + * to see if any IP address record should trigger a special action. + * If applicable these actions can modify the original response. + */ +#include "config.h" + +#include "services/localzone.h" +#include "services/authzone.h" +#include "services/cache/dns.h" +#include "sldns/str2wire.h" +#include "util/config_file.h" +#include "util/fptr_wlist.h" +#include "util/module.h" +#include "util/net_help.h" +#include "util/regional.h" +#include "util/data/msgreply.h" +#include "util/storage/dnstree.h" +#include "respip/respip.h" +#include "services/view.h" +#include "sldns/rrdef.h" + + +/** Subset of resp_addr.node, used for inform-variant logging */ +struct respip_addr_info { + struct sockaddr_storage addr; + socklen_t addrlen; + int net; +}; + +/** Query state regarding the response-ip module. */ +enum respip_state { + /** + * The general state. Unless CNAME chasing takes place, all processing + * is completed in this state without any other asynchronous event. + */ + RESPIP_INIT = 0, + + /** + * A subquery for CNAME chasing is completed. + */ + RESPIP_SUBQUERY_FINISHED +}; + +/** Per query state for the response-ip module. */ +struct respip_qstate { + enum respip_state state; +}; + +struct respip_set* +respip_set_create(void) +{ + struct respip_set* set = calloc(1, sizeof(*set)); + if(!set) + return NULL; + set->region = regional_create(); + if(!set->region) { + free(set); + return NULL; + } + addr_tree_init(&set->ip_tree); + lock_rw_init(&set->lock); + return set; +} + +/** helper traverse to delete resp_addr nodes */ +static void +resp_addr_del(rbnode_type* n, void* ATTR_UNUSED(arg)) +{ + struct resp_addr* r = (struct resp_addr*)n->key; + lock_rw_destroy(&r->lock); +#ifdef THREADS_DISABLED + (void)r; +#endif +} + +void +respip_set_delete(struct respip_set* set) +{ + if(!set) + return; + lock_rw_destroy(&set->lock); + traverse_postorder(&set->ip_tree, resp_addr_del, NULL); + regional_destroy(set->region); + free(set); +} + +struct rbtree_type* +respip_set_get_tree(struct respip_set* set) +{ + if(!set) + return NULL; + return &set->ip_tree; +} + +struct resp_addr* +respip_sockaddr_find_or_create(struct respip_set* set, struct sockaddr_storage* addr, + socklen_t addrlen, int net, int create, const char* ipstr) +{ + struct resp_addr* node; + node = (struct resp_addr*)addr_tree_find(&set->ip_tree, addr, addrlen, net); + if(!node && create) { + node = regional_alloc_zero(set->region, sizeof(*node)); + if(!node) { + log_err("out of memory"); + return NULL; + } + lock_rw_init(&node->lock); + node->action = respip_none; + if(!addr_tree_insert(&set->ip_tree, &node->node, addr, + addrlen, net)) { + /* We know we didn't find it, so this should be + * impossible. */ + log_warn("unexpected: duplicate address: %s", ipstr); + } + } + return node; +} + +void +respip_sockaddr_delete(struct respip_set* set, struct resp_addr* node) +{ + struct resp_addr* prev; + prev = (struct resp_addr*)rbtree_previous((struct rbnode_type*)node); + lock_rw_destroy(&node->lock); + rbtree_delete(&set->ip_tree, node); + /* no free'ing, all allocated in region */ + if(!prev) + addr_tree_init_parents((rbtree_type*)set); + else + addr_tree_init_parents_node(&prev->node); +} + +/** returns the node in the address tree for the specified netblock string; + * non-existent node will be created if 'create' is true */ +static struct resp_addr* +respip_find_or_create(struct respip_set* set, const char* ipstr, int create) +{ + struct sockaddr_storage addr; + int net; + socklen_t addrlen; + + if(!netblockstrtoaddr(ipstr, 0, &addr, &addrlen, &net)) { + log_err("cannot parse netblock: '%s'", ipstr); + return NULL; + } + return respip_sockaddr_find_or_create(set, &addr, addrlen, net, create, + ipstr); +} + +static int +respip_tag_cfg(struct respip_set* set, const char* ipstr, + const uint8_t* taglist, size_t taglen) +{ + struct resp_addr* node; + + if(!(node=respip_find_or_create(set, ipstr, 1))) + return 0; + if(node->taglist) { + log_warn("duplicate response-address-tag for '%s', overridden.", + ipstr); + } + node->taglist = regional_alloc_init(set->region, taglist, taglen); + if(!node->taglist) { + log_err("out of memory"); + return 0; + } + node->taglen = taglen; + return 1; +} + +/** set action for the node specified by the netblock string */ +static int +respip_action_cfg(struct respip_set* set, const char* ipstr, + const char* actnstr) +{ + struct resp_addr* node; + enum respip_action action; + + if(!(node=respip_find_or_create(set, ipstr, 1))) + return 0; + if(node->action != respip_none) { + verbose(VERB_QUERY, "duplicate response-ip action for '%s', overridden.", + ipstr); + } + if(strcmp(actnstr, "deny") == 0) + action = respip_deny; + else if(strcmp(actnstr, "redirect") == 0) + action = respip_redirect; + else if(strcmp(actnstr, "inform") == 0) + action = respip_inform; + else if(strcmp(actnstr, "inform_deny") == 0) + action = respip_inform_deny; + else if(strcmp(actnstr, "inform_redirect") == 0) + action = respip_inform_redirect; + else if(strcmp(actnstr, "always_transparent") == 0) + action = respip_always_transparent; + else if(strcmp(actnstr, "always_refuse") == 0) + action = respip_always_refuse; + else if(strcmp(actnstr, "always_nxdomain") == 0) + action = respip_always_nxdomain; + else if(strcmp(actnstr, "always_nodata") == 0) + action = respip_always_nodata; + else if(strcmp(actnstr, "always_deny") == 0) + action = respip_always_deny; + else { + log_err("unknown response-ip action %s", actnstr); + return 0; + } + node->action = action; + return 1; +} + +/** allocate and initialize an rrset structure; this function is based + * on new_local_rrset() from the localzone.c module */ +static struct ub_packed_rrset_key* +new_rrset(struct regional* region, uint16_t rrtype, uint16_t rrclass) +{ + struct packed_rrset_data* pd; + struct ub_packed_rrset_key* rrset = regional_alloc_zero( + region, sizeof(*rrset)); + if(!rrset) { + log_err("out of memory"); + return NULL; + } + rrset->entry.key = rrset; + pd = 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; + rrset->entry.data = pd; + rrset->rk.dname = regional_alloc_zero(region, 1); + if(!rrset->rk.dname) { + log_err("out of memory"); + return NULL; + } + rrset->rk.dname_len = 1; + rrset->rk.type = htons(rrtype); + rrset->rk.rrset_class = htons(rrclass); + return rrset; +} + +/** enter local data as resource records into a response-ip node */ + +int +respip_enter_rr(struct regional* region, struct resp_addr* raddr, + uint16_t rrtype, uint16_t rrclass, time_t ttl, uint8_t* rdata, + size_t rdata_len, const char* rrstr, const char* netblockstr) +{ + struct packed_rrset_data* pd; + struct sockaddr* sa; + sa = (struct sockaddr*)&raddr->node.addr; + if (rrtype == LDNS_RR_TYPE_CNAME && raddr->data) { + log_err("CNAME response-ip data (%s) can not co-exist with other " + "response-ip data for netblock %s", rrstr, netblockstr); + return 0; + } else if (raddr->data && + raddr->data->rk.type == htons(LDNS_RR_TYPE_CNAME)) { + log_err("response-ip data (%s) can not be added; CNAME response-ip " + "data already in place for netblock %s", rrstr, netblockstr); + return 0; + } else if((rrtype != LDNS_RR_TYPE_CNAME) && + ((sa->sa_family == AF_INET && rrtype != LDNS_RR_TYPE_A) || + (sa->sa_family == AF_INET6 && rrtype != LDNS_RR_TYPE_AAAA))) { + log_err("response-ip data %s record type does not correspond " + "to netblock %s address family", rrstr, netblockstr); + return 0; + } + + if(!raddr->data) { + raddr->data = new_rrset(region, rrtype, rrclass); + if(!raddr->data) + return 0; + } + pd = raddr->data->entry.data; + return rrset_insert_rr(region, pd, rdata, rdata_len, ttl, rrstr); +} + +static int +respip_enter_rrstr(struct regional* region, struct resp_addr* raddr, + const char* rrstr, const char* netblock) +{ + uint8_t* nm; + uint16_t rrtype = 0, rrclass = 0; + time_t ttl = 0; + uint8_t rr[LDNS_RR_BUF_SIZE]; + uint8_t* rdata = NULL; + size_t rdata_len = 0; + char buf[65536]; + char bufshort[64]; + int ret; + if(raddr->action != respip_redirect + && raddr->action != respip_inform_redirect) { + log_err("cannot parse response-ip-data %s: response-ip " + "action for %s is not redirect", rrstr, netblock); + return 0; + } + ret = snprintf(buf, sizeof(buf), ". %s", rrstr); + if(ret < 0 || ret >= (int)sizeof(buf)) { + strlcpy(bufshort, rrstr, sizeof(bufshort)); + log_err("bad response-ip-data: %s...", bufshort); + return 0; + } + if(!rrstr_get_rr_content(buf, &nm, &rrtype, &rrclass, &ttl, rr, sizeof(rr), + &rdata, &rdata_len)) { + log_err("bad response-ip-data: %s", rrstr); + return 0; + } + free(nm); + return respip_enter_rr(region, raddr, rrtype, rrclass, ttl, rdata, + rdata_len, rrstr, netblock); +} + +static int +respip_data_cfg(struct respip_set* set, const char* ipstr, const char* rrstr) +{ + struct resp_addr* node; + + node=respip_find_or_create(set, ipstr, 0); + if(!node || node->action == respip_none) { + log_err("cannot parse response-ip-data %s: " + "response-ip node for %s not found", rrstr, ipstr); + return 0; + } + return respip_enter_rrstr(set->region, node, rrstr, ipstr); +} + +static int +respip_set_apply_cfg(struct respip_set* set, char* const* tagname, int num_tags, + struct config_strbytelist* respip_tags, + struct config_str2list* respip_actions, + struct config_str2list* respip_data) +{ + struct config_strbytelist* p; + struct config_str2list* pa; + struct config_str2list* pd; + + set->tagname = tagname; + set->num_tags = num_tags; + + p = respip_tags; + while(p) { + struct config_strbytelist* np = p->next; + + log_assert(p->str && p->str2); + if(!respip_tag_cfg(set, p->str, p->str2, p->str2len)) { + config_del_strbytelist(p); + return 0; + } + free(p->str); + free(p->str2); + free(p); + p = np; + } + + pa = respip_actions; + while(pa) { + struct config_str2list* np = pa->next; + log_assert(pa->str && pa->str2); + if(!respip_action_cfg(set, pa->str, pa->str2)) { + config_deldblstrlist(pa); + return 0; + } + free(pa->str); + free(pa->str2); + free(pa); + pa = np; + } + + pd = respip_data; + while(pd) { + struct config_str2list* np = pd->next; + log_assert(pd->str && pd->str2); + if(!respip_data_cfg(set, pd->str, pd->str2)) { + config_deldblstrlist(pd); + return 0; + } + free(pd->str); + free(pd->str2); + free(pd); + pd = np; + } + addr_tree_init_parents(&set->ip_tree); + + return 1; +} + +int +respip_global_apply_cfg(struct respip_set* set, struct config_file* cfg) +{ + int ret = respip_set_apply_cfg(set, cfg->tagname, cfg->num_tags, + cfg->respip_tags, cfg->respip_actions, cfg->respip_data); + cfg->respip_data = NULL; + cfg->respip_actions = NULL; + cfg->respip_tags = NULL; + return ret; +} + +/** Iterate through raw view data and apply the view-specific respip + * configuration; at this point we should have already seen all the views, + * so if any of the views that respip data refer to does not exist, that's + * an error. This additional iteration through view configuration data + * is expected to not have significant performance impact (or rather, its + * performance impact is not expected to be prohibitive in the configuration + * processing phase). + */ +int +respip_views_apply_cfg(struct views* vs, struct config_file* cfg, + int* have_view_respip_cfg) +{ + struct config_view* cv; + struct view* v; + int ret; + + for(cv = cfg->views; cv; cv = cv->next) { + + /** if no respip config for this view then there's + * nothing to do; note that even though respip data must go + * with respip action, we're checking for both here because + * we want to catch the case where the respip action is missing + * while the data is present */ + if(!cv->respip_actions && !cv->respip_data) + continue; + + if(!(v = views_find_view(vs, cv->name, 1))) { + log_err("view '%s' unexpectedly missing", cv->name); + return 0; + } + if(!v->respip_set) { + v->respip_set = respip_set_create(); + if(!v->respip_set) { + log_err("out of memory"); + lock_rw_unlock(&v->lock); + return 0; + } + } + ret = respip_set_apply_cfg(v->respip_set, NULL, 0, NULL, + cv->respip_actions, cv->respip_data); + lock_rw_unlock(&v->lock); + if(!ret) { + log_err("Error while applying respip configuration " + "for view '%s'", cv->name); + return 0; + } + *have_view_respip_cfg = (*have_view_respip_cfg || + v->respip_set->ip_tree.count); + cv->respip_actions = NULL; + cv->respip_data = NULL; + } + return 1; +} + +/** + * make a deep copy of 'key' in 'region'. + * This is largely derived from packed_rrset_copy_region() and + * packed_rrset_ptr_fixup(), but differs in the following points: + * + * - It doesn't assume all data in 'key' are in a contiguous memory region. + * Although that would be the case in most cases, 'key' can be passed from + * a lower-level module and it might not build the rrset to meet the + * assumption. In fact, an rrset specified as response-ip-data or generated + * in local_data_find_tag_datas() breaks the assumption. So it would be + * safer not to naively rely on the assumption. On the other hand, this + * function ensures the copied rrset data are in a contiguous region so + * that it won't cause a disruption even if an upper layer module naively + * assumes the memory layout. + * - It doesn't copy RRSIGs (if any) in 'key'. The rrset will be used in + * a reply that was already faked, so it doesn't make much sense to provide + * partial sigs even if they are valid themselves. + * - It doesn't adjust TTLs as it basically has to be a verbatim copy of 'key' + * just allocated in 'region' (the assumption is necessary TTL adjustment + * has been already done in 'key'). + * + * This function returns the copied rrset key on success, and NULL on memory + * allocation failure. + */ +static struct ub_packed_rrset_key* +copy_rrset(const struct ub_packed_rrset_key* key, struct regional* region) +{ + struct ub_packed_rrset_key* ck = regional_alloc(region, + sizeof(struct ub_packed_rrset_key)); + struct packed_rrset_data* d; + struct packed_rrset_data* data = key->entry.data; + size_t dsize, i; + uint8_t* nextrdata; + + /* derived from packed_rrset_copy_region(), but don't use + * packed_rrset_sizeof() and do exclude RRSIGs */ + if(!ck) + return NULL; + ck->id = key->id; + memset(&ck->entry, 0, sizeof(ck->entry)); + ck->entry.hash = key->entry.hash; + ck->entry.key = ck; + ck->rk = key->rk; + ck->rk.dname = regional_alloc_init(region, key->rk.dname, + key->rk.dname_len); + if(!ck->rk.dname) + return NULL; + + if((unsigned)data->count >= 0xffff00U) + return NULL; /* guard against integer overflow in dsize */ + dsize = sizeof(struct packed_rrset_data) + data->count * + (sizeof(size_t)+sizeof(uint8_t*)+sizeof(time_t)); + for(i=0; icount; i++) { + if((unsigned)dsize >= 0x0fffffffU || + (unsigned)data->rr_len[i] >= 0x0fffffffU) + return NULL; /* guard against integer overflow */ + dsize += data->rr_len[i]; + } + d = regional_alloc(region, dsize); + if(!d) + return NULL; + *d = *data; + d->rrsig_count = 0; + ck->entry.data = d; + + /* derived from packed_rrset_ptr_fixup() with copying the data */ + d->rr_len = (size_t*)((uint8_t*)d + sizeof(struct packed_rrset_data)); + d->rr_data = (uint8_t**)&(d->rr_len[d->count]); + d->rr_ttl = (time_t*)&(d->rr_data[d->count]); + nextrdata = (uint8_t*)&(d->rr_ttl[d->count]); + for(i=0; icount; i++) { + d->rr_len[i] = data->rr_len[i]; + d->rr_ttl[i] = data->rr_ttl[i]; + d->rr_data[i] = nextrdata; + memcpy(d->rr_data[i], data->rr_data[i], data->rr_len[i]); + nextrdata += d->rr_len[i]; + } + + return ck; +} + +int +respip_init(struct module_env* env, int id) +{ + (void)env; + (void)id; + return 1; +} + +void +respip_deinit(struct module_env* env, int id) +{ + (void)env; + (void)id; +} + +/** Convert a packed AAAA or A RRset to sockaddr. */ +static int +rdata2sockaddr(const struct packed_rrset_data* rd, uint16_t rtype, size_t i, + struct sockaddr_storage* ss, socklen_t* addrlenp) +{ + /* unbound can accept and cache odd-length AAAA/A records, so we have + * to validate the length. */ + if(rtype == LDNS_RR_TYPE_A && rd->rr_len[i] == 6) { + struct sockaddr_in* sa4 = (struct sockaddr_in*)ss; + + memset(sa4, 0, sizeof(*sa4)); + sa4->sin_family = AF_INET; + memcpy(&sa4->sin_addr, rd->rr_data[i] + 2, + sizeof(sa4->sin_addr)); + *addrlenp = sizeof(*sa4); + return 1; + } else if(rtype == LDNS_RR_TYPE_AAAA && rd->rr_len[i] == 18) { + struct sockaddr_in6* sa6 = (struct sockaddr_in6*)ss; + + memset(sa6, 0, sizeof(*sa6)); + sa6->sin6_family = AF_INET6; + memcpy(&sa6->sin6_addr, rd->rr_data[i] + 2, + sizeof(sa6->sin6_addr)); + *addrlenp = sizeof(*sa6); + return 1; + } + return 0; +} + +/** + * Search the given 'iptree' for response address information that matches + * any of the IP addresses in an AAAA or A in the answer section of the + * response (stored in 'rep'). If found, a pointer to the matched resp_addr + * structure will be returned, and '*rrset_id' is set to the index in + * rep->rrsets for the RRset that contains the matching IP address record + * (the index is normally 0, but can be larger than that if this is a CNAME + * chain or type-ANY response). + * Returns resp_addr holding read lock. + */ +static struct resp_addr* +respip_addr_lookup(const struct reply_info *rep, struct respip_set* rs, + size_t* rrset_id) +{ + size_t i; + struct resp_addr* ra; + struct sockaddr_storage ss; + socklen_t addrlen; + + lock_rw_rdlock(&rs->lock); + for(i=0; ian_numrrsets; i++) { + size_t j; + const struct packed_rrset_data* rd; + uint16_t rtype = ntohs(rep->rrsets[i]->rk.type); + + if(rtype != LDNS_RR_TYPE_A && rtype != LDNS_RR_TYPE_AAAA) + continue; + rd = rep->rrsets[i]->entry.data; + for(j = 0; j < rd->count; j++) { + if(!rdata2sockaddr(rd, rtype, j, &ss, &addrlen)) + continue; + ra = (struct resp_addr*)addr_tree_lookup(&rs->ip_tree, + &ss, addrlen); + if(ra) { + *rrset_id = i; + lock_rw_rdlock(&ra->lock); + lock_rw_unlock(&rs->lock); + return ra; + } + } + } + lock_rw_unlock(&rs->lock); + return NULL; +} + +/* + * Create a new reply_info based on 'rep'. The new info is based on + * the passed 'rep', but ignores any rrsets except for the first 'an_numrrsets' + * RRsets in the answer section. These answer rrsets are copied to the + * new info, up to 'copy_rrsets' rrsets (which must not be larger than + * 'an_numrrsets'). If an_numrrsets > copy_rrsets, the remaining rrsets array + * entries will be kept empty so the caller can fill them later. When rrsets + * are copied, they are shallow copied. The caller must ensure that the + * copied rrsets are valid throughout its lifetime and must provide appropriate + * mutex if it can be shared by multiple threads. + */ +static struct reply_info * +make_new_reply_info(const struct reply_info* rep, struct regional* region, + size_t an_numrrsets, size_t copy_rrsets) +{ + struct reply_info* new_rep; + size_t i; + + /* create a base struct. we specify 'insecure' security status as + * the modified response won't be DNSSEC-valid. In our faked response + * the authority and additional sections will be empty (except possible + * EDNS0 OPT RR in the additional section appended on sending it out), + * so the total number of RRsets is an_numrrsets. */ + new_rep = construct_reply_info_base(region, rep->flags, + rep->qdcount, rep->ttl, rep->prefetch_ttl, + rep->serve_expired_ttl, an_numrrsets, 0, 0, an_numrrsets, + sec_status_insecure); + if(!new_rep) + return NULL; + if(!reply_info_alloc_rrset_keys(new_rep, NULL, region)) + return NULL; + for(i=0; irrsets[i] = rep->rrsets[i]; + + return new_rep; +} + +/** + * See if response-ip or tag data should override the original answer rrset + * (which is rep->rrsets[rrset_id]) and if so override it. + * This is (mostly) equivalent to localzone.c:local_data_answer() but for + * response-ip actions. + * Note that this function distinguishes error conditions from "success but + * not overridden". This is because we want to avoid accidentally applying + * the "no data" action in case of error. + * @param action: action to apply + * @param data: RRset to use for override + * @param qtype: original query type + * @param rep: original reply message + * @param rrset_id: the rrset ID in 'rep' to which the action should apply + * @param new_repp: see respip_rewrite_reply + * @param tag: if >= 0 the tag ID used to determine the action and data + * @param tag_datas: data corresponding to 'tag'. + * @param tag_datas_size: size of 'tag_datas' + * @param tagname: array of tag names, used for logging + * @param num_tags: size of 'tagname', used for logging + * @param redirect_rrsetp: ptr to redirect record + * @param region: region for building new reply + * @return 1 if overridden, 0 if not overridden, -1 on error. + */ +static int +respip_data_answer(enum respip_action action, + struct ub_packed_rrset_key* data, + uint16_t qtype, const struct reply_info* rep, + size_t rrset_id, struct reply_info** new_repp, int tag, + struct config_strlist** tag_datas, size_t tag_datas_size, + char* const* tagname, int num_tags, + struct ub_packed_rrset_key** redirect_rrsetp, struct regional* region) +{ + struct ub_packed_rrset_key* rp = data; + struct reply_info* new_rep; + *redirect_rrsetp = NULL; + + if(action == respip_redirect && tag != -1 && + (size_t)tagrrsets[rrset_id]->rk.dname; + dataqinfo.qname_len = rep->rrsets[rrset_id]->rk.dname_len; + dataqinfo.qtype = ntohs(rep->rrsets[rrset_id]->rk.type); + dataqinfo.qclass = ntohs(rep->rrsets[rrset_id]->rk.rrset_class); + + memset(&r, 0, sizeof(r)); + if(local_data_find_tag_datas(&dataqinfo, tag_datas[tag], &r, + region)) { + verbose(VERB_ALGO, + "response-ip redirect with tag data [%d] %s", + tag, (tagrk.dname = rep->rrsets[rrset_id]->rk.dname; + rp->rk.dname_len = rep->rrsets[rrset_id]->rk.dname_len; + } + + /* Build a new reply with redirect rrset. We keep any preceding CNAMEs + * and replace the address rrset that triggers the action. If it's + * type ANY query, however, no other answer records should be kept + * (note that it can't be a CNAME chain in this case due to + * sanitizing). */ + if(qtype == LDNS_RR_TYPE_ANY) + rrset_id = 0; + new_rep = make_new_reply_info(rep, region, rrset_id + 1, rrset_id); + if(!new_rep) + return -1; + rp->rk.flags |= PACKED_RRSET_FIXEDTTL; /* avoid adjusting TTL */ + new_rep->rrsets[rrset_id] = rp; + + *redirect_rrsetp = rp; + *new_repp = new_rep; + return 1; +} + +/** + * apply response ip action in case where no action data is provided. + * this is similar to localzone.c:lz_zone_answer() but simplified due to + * the characteristics of response ip: + * - 'deny' variants will be handled at the caller side + * - no specific processing for 'transparent' variants: unlike local zones, + * there is no such a case of 'no data but name existing'. so all variants + * just mean 'transparent if no data'. + * @param qtype: query type + * @param action: found action + * @param rep: + * @param new_repp + * @param rrset_id + * @param region: region for building new reply + * @return 1 on success, 0 on error. + */ +static int +respip_nodata_answer(uint16_t qtype, enum respip_action action, + const struct reply_info *rep, size_t rrset_id, + struct reply_info** new_repp, struct regional* region) +{ + struct reply_info* new_rep; + + if(action == respip_refuse || action == respip_always_refuse) { + new_rep = make_new_reply_info(rep, region, 0, 0); + if(!new_rep) + return 0; + FLAGS_SET_RCODE(new_rep->flags, LDNS_RCODE_REFUSED); + *new_repp = new_rep; + return 1; + } else if(action == respip_static || action == respip_redirect || + action == respip_always_nxdomain || + action == respip_always_nodata || + action == respip_inform_redirect) { + /* Since we don't know about other types of the owner name, + * we generally return NOERROR/NODATA unless an NXDOMAIN action + * is explicitly specified. */ + int rcode = (action == respip_always_nxdomain)? + LDNS_RCODE_NXDOMAIN:LDNS_RCODE_NOERROR; + + /* We should empty the answer section except for any preceding + * CNAMEs (in that case rrset_id > 0). Type-ANY case is + * special as noted in respip_data_answer(). */ + if(qtype == LDNS_RR_TYPE_ANY) + rrset_id = 0; + new_rep = make_new_reply_info(rep, region, rrset_id, rrset_id); + if(!new_rep) + return 0; + FLAGS_SET_RCODE(new_rep->flags, rcode); + *new_repp = new_rep; + return 1; + } + + return 1; +} + +/** Populate action info structure with the results of response-ip action + * processing, iff as the result of response-ip processing we are actually + * taking some action. Only action is set if action_only is true. + * Returns true on success, false on failure. + */ +static int +populate_action_info(struct respip_action_info* actinfo, + enum respip_action action, const struct resp_addr* raddr, + const struct ub_packed_rrset_key* ATTR_UNUSED(rrset), + int ATTR_UNUSED(tag), const struct respip_set* ATTR_UNUSED(ipset), + int ATTR_UNUSED(action_only), struct regional* region, int rpz_used, + int rpz_log, char* log_name, int rpz_cname_override) +{ + if(action == respip_none || !raddr) + return 1; + actinfo->action = action; + actinfo->rpz_used = rpz_used; + actinfo->rpz_log = rpz_log; + actinfo->log_name = log_name; + actinfo->rpz_cname_override = rpz_cname_override; + + /* for inform variants, make a copy of the matched address block for + * later logging. We make a copy to proactively avoid disruption if + * and when we allow a dynamic update to the respip tree. */ + if(action == respip_inform || action == respip_inform_deny || + rpz_used) { + struct respip_addr_info* a = + regional_alloc_zero(region, sizeof(*a)); + if(!a) { + log_err("out of memory"); + return 0; + } + a->addr = raddr->node.addr; + a->addrlen = raddr->node.addrlen; + a->net = raddr->node.net; + actinfo->addrinfo = a; + } + + return 1; +} + +static int +respip_use_rpz(struct resp_addr* raddr, struct rpz* r, + enum respip_action* action, + struct ub_packed_rrset_key** data, int* rpz_log, char** log_name, + int* rpz_cname_override, struct regional* region, int* is_rpz) +{ + if(r->action_override == RPZ_DISABLED_ACTION) { + *is_rpz = 0; + return 1; + } + else if(r->action_override == RPZ_NO_OVERRIDE_ACTION) + *action = raddr->action; + else + *action = rpz_action_to_respip_action(r->action_override); + if(r->action_override == RPZ_CNAME_OVERRIDE_ACTION && + r->cname_override) { + *data = r->cname_override; + *rpz_cname_override = 1; + } + *rpz_log = r->log; + if(r->log_name) + if(!(*log_name = regional_strdup(region, r->log_name))) + return 0; + *is_rpz = 1; + return 1; +} + +int +respip_rewrite_reply(const struct query_info* qinfo, + const struct respip_client_info* cinfo, const struct reply_info* rep, + struct reply_info** new_repp, struct respip_action_info* actinfo, + struct ub_packed_rrset_key** alias_rrset, int search_only, + struct regional* region, struct auth_zones* az) +{ + const uint8_t* ctaglist; + size_t ctaglen; + const uint8_t* tag_actions; + size_t tag_actions_size; + struct config_strlist** tag_datas; + size_t tag_datas_size; + struct view* view = NULL; + struct respip_set* ipset = NULL; + size_t rrset_id = 0; + enum respip_action action = respip_none; + int tag = -1; + struct resp_addr* raddr = NULL; + int ret = 1; + struct ub_packed_rrset_key* redirect_rrset = NULL; + struct rpz* r; + struct ub_packed_rrset_key* data = NULL; + int rpz_used = 0; + int rpz_log = 0; + int rpz_cname_override = 0; + char* log_name = NULL; + + if(!cinfo) + goto done; + ctaglist = cinfo->taglist; + ctaglen = cinfo->taglen; + tag_actions = cinfo->tag_actions; + tag_actions_size = cinfo->tag_actions_size; + tag_datas = cinfo->tag_datas; + tag_datas_size = cinfo->tag_datas_size; + view = cinfo->view; + ipset = cinfo->respip_set; + + log_assert(ipset); + + /** Try to use response-ip config from the view first; use + * global response-ip config if we don't have the view or we don't + * have the matching per-view config (and the view allows the use + * of global data in this case). + * Note that we lock the view even if we only use view members that + * currently don't change after creation. This is for safety for + * future possible changes as the view documentation seems to expect + * any of its member can change in the view's lifetime. + * Note also that we assume 'view' is valid in this function, which + * should be safe (see unbound bug #1191) */ + if(view) { + lock_rw_rdlock(&view->lock); + if(view->respip_set) { + if((raddr = respip_addr_lookup(rep, + view->respip_set, &rrset_id))) { + /** for per-view respip directives the action + * can only be direct (i.e. not tag-based) */ + action = raddr->action; + } + } + if(!raddr && !view->isfirst) + goto done; + } + if(!raddr && (raddr = respip_addr_lookup(rep, ipset, + &rrset_id))) { + action = (enum respip_action)local_data_find_tag_action( + raddr->taglist, raddr->taglen, ctaglist, ctaglen, + tag_actions, tag_actions_size, + (enum localzone_type)raddr->action, &tag, + ipset->tagname, ipset->num_tags); + } + lock_rw_rdlock(&az->rpz_lock); + for(r = az->rpz_first; r && !raddr; r = r->next) { + if(!r->taglist || taglist_intersect(r->taglist, + r->taglistlen, ctaglist, ctaglen)) { + if((raddr = respip_addr_lookup(rep, + r->respip_set, &rrset_id))) { + if(!respip_use_rpz(raddr, r, &action, &data, + &rpz_log, &log_name, &rpz_cname_override, + region, &rpz_used)) { + log_err("out of memory"); + lock_rw_unlock(&raddr->lock); + lock_rw_unlock(&az->rpz_lock); + return 0; + } + if(!rpz_used) { + lock_rw_unlock(&raddr->lock); + raddr = NULL; + actinfo->rpz_disabled++; + } + } + } + } + lock_rw_unlock(&az->rpz_lock); + if(raddr && !search_only) { + int result = 0; + + /* first, see if we have response-ip or tag action for the + * action except for 'always' variants. */ + if(action != respip_always_refuse + && action != respip_always_transparent + && action != respip_always_nxdomain + && action != respip_always_nodata + && action != respip_always_deny + && (result = respip_data_answer(action, + (data) ? data : raddr->data, qinfo->qtype, rep, + rrset_id, new_repp, tag, tag_datas, tag_datas_size, + ipset->tagname, ipset->num_tags, &redirect_rrset, + region)) < 0) { + ret = 0; + goto done; + } + + /* if no action data applied, take action specific to the + * action without data. */ + if(!result && !respip_nodata_answer(qinfo->qtype, action, rep, + rrset_id, new_repp, region)) { + ret = 0; + goto done; + } + } + done: + if(view) { + lock_rw_unlock(&view->lock); + } + if(ret) { + /* If we're redirecting the original answer to a + * CNAME, record the CNAME rrset so the caller can take + * the appropriate action. Note that we don't check the + * action type; it should normally be 'redirect', but it + * can be of other type when a data-dependent tag action + * uses redirect response-ip data. + */ + if(redirect_rrset && + redirect_rrset->rk.type == ntohs(LDNS_RR_TYPE_CNAME) && + qinfo->qtype != LDNS_RR_TYPE_ANY) + *alias_rrset = redirect_rrset; + /* on success, populate respip result structure */ + ret = populate_action_info(actinfo, action, raddr, + redirect_rrset, tag, ipset, search_only, region, + rpz_used, rpz_log, log_name, rpz_cname_override); + } + if(raddr) { + lock_rw_unlock(&raddr->lock); + } + return ret; +} + +static int +generate_cname_request(struct module_qstate* qstate, + struct ub_packed_rrset_key* alias_rrset) +{ + struct module_qstate* subq = NULL; + struct query_info subqi; + + memset(&subqi, 0, sizeof(subqi)); + get_cname_target(alias_rrset, &subqi.qname, &subqi.qname_len); + if(!subqi.qname) + return 0; /* unexpected: not a valid CNAME RDATA */ + subqi.qtype = qstate->qinfo.qtype; + subqi.qclass = qstate->qinfo.qclass; + fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub)); + return (*qstate->env->attach_sub)(qstate, &subqi, BIT_RD, 0, 0, &subq); +} + +void +respip_operate(struct module_qstate* qstate, enum module_ev event, int id, + struct outbound_entry* outbound) +{ + struct respip_qstate* rq = (struct respip_qstate*)qstate->minfo[id]; + + log_query_info(VERB_QUERY, "respip operate: query", &qstate->qinfo); + (void)outbound; + + if(event == module_event_new || event == module_event_pass) { + if(!rq) { + rq = regional_alloc_zero(qstate->region, sizeof(*rq)); + if(!rq) + goto servfail; + rq->state = RESPIP_INIT; + qstate->minfo[id] = rq; + } + if(rq->state == RESPIP_SUBQUERY_FINISHED) { + qstate->ext_state[id] = module_finished; + return; + } + verbose(VERB_ALGO, "respip: pass to next module"); + qstate->ext_state[id] = module_wait_module; + } else if(event == module_event_moddone) { + /* If the reply may be subject to response-ip rewriting + * according to the query type, check the actions. If a + * rewrite is necessary, we'll replace the reply in qstate + * with the new one. */ + enum module_ext_state next_state = module_finished; + + if((qstate->qinfo.qtype == LDNS_RR_TYPE_A || + qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA || + qstate->qinfo.qtype == LDNS_RR_TYPE_ANY) && + qstate->return_msg && qstate->return_msg->rep) { + struct reply_info* new_rep = qstate->return_msg->rep; + struct ub_packed_rrset_key* alias_rrset = NULL; + struct respip_action_info actinfo = {0}; + actinfo.action = respip_none; + + if(!respip_rewrite_reply(&qstate->qinfo, + qstate->client_info, qstate->return_msg->rep, + &new_rep, &actinfo, &alias_rrset, 0, + qstate->region, qstate->env->auth_zones)) { + goto servfail; + } + if(actinfo.action != respip_none) { + /* save action info for logging on a + * per-front-end-query basis */ + if(!(qstate->respip_action_info = + regional_alloc_init(qstate->region, + &actinfo, sizeof(actinfo)))) + { + log_err("out of memory"); + goto servfail; + } + } else { + qstate->respip_action_info = NULL; + } + if (actinfo.action == respip_always_deny || + (new_rep == qstate->return_msg->rep && + (actinfo.action == respip_deny || + actinfo.action == respip_inform_deny))) { + /* for deny-variant actions (unless response-ip + * data is applied), mark the query state so + * the response will be dropped for all + * clients. */ + qstate->is_drop = 1; + } else if(alias_rrset) { + if(!generate_cname_request(qstate, alias_rrset)) + goto servfail; + next_state = module_wait_subquery; + } + qstate->return_msg->rep = new_rep; + } + qstate->ext_state[id] = next_state; + } else + qstate->ext_state[id] = module_finished; + + return; + + servfail: + qstate->return_rcode = LDNS_RCODE_SERVFAIL; + qstate->return_msg = NULL; +} + +int +respip_merge_cname(struct reply_info* base_rep, + const struct query_info* qinfo, const struct reply_info* tgt_rep, + const struct respip_client_info* cinfo, int must_validate, + struct reply_info** new_repp, struct regional* region, + struct auth_zones* az) +{ + struct reply_info* new_rep; + struct reply_info* tmp_rep = NULL; /* just a placeholder */ + struct ub_packed_rrset_key* alias_rrset = NULL; /* ditto */ + uint16_t tgt_rcode; + size_t i, j; + struct respip_action_info actinfo = {0}; + actinfo.action = respip_none; + + /* If the query for the CNAME target would result in an unusual rcode, + * we generally translate it as a failure for the base query + * (which would then be translated into SERVFAIL). The only exception + * is NXDOMAIN and YXDOMAIN, which are passed to the end client(s). + * The YXDOMAIN case would be rare but still possible (when + * DNSSEC-validated DNAME has been cached but synthesizing CNAME + * can't be generated due to length limitation) */ + tgt_rcode = FLAGS_GET_RCODE(tgt_rep->flags); + if((tgt_rcode != LDNS_RCODE_NOERROR && + tgt_rcode != LDNS_RCODE_NXDOMAIN && + tgt_rcode != LDNS_RCODE_YXDOMAIN) || + (must_validate && tgt_rep->security <= sec_status_bogus)) { + return 0; + } + + /* see if the target reply would be subject to a response-ip action. */ + if(!respip_rewrite_reply(qinfo, cinfo, tgt_rep, &tmp_rep, &actinfo, + &alias_rrset, 1, region, az)) + return 0; + if(actinfo.action != respip_none) { + log_info("CNAME target of redirect response-ip action would " + "be subject to response-ip action, too; stripped"); + *new_repp = base_rep; + return 1; + } + + /* Append target reply to the base. Since we cannot assume + * tgt_rep->rrsets is valid throughout the lifetime of new_rep + * or it can be safely shared by multiple threads, we need to make a + * deep copy. */ + new_rep = make_new_reply_info(base_rep, region, + base_rep->an_numrrsets + tgt_rep->an_numrrsets, + base_rep->an_numrrsets); + if(!new_rep) + return 0; + for(i=0,j=base_rep->an_numrrsets; ian_numrrsets; i++,j++) { + new_rep->rrsets[j] = copy_rrset(tgt_rep->rrsets[i], region); + if(!new_rep->rrsets[j]) + return 0; + } + + FLAGS_SET_RCODE(new_rep->flags, tgt_rcode); + *new_repp = new_rep; + return 1; +} + +void +respip_inform_super(struct module_qstate* qstate, int id, + struct module_qstate* super) +{ + struct respip_qstate* rq = (struct respip_qstate*)super->minfo[id]; + struct reply_info* new_rep = NULL; + + rq->state = RESPIP_SUBQUERY_FINISHED; + + /* respip subquery should have always been created with a valid reply + * in super. */ + log_assert(super->return_msg && super->return_msg->rep); + + /* return_msg can be NULL when, e.g., the sub query resulted in + * SERVFAIL, in which case we regard it as a failure of the original + * query. Other checks are probably redundant, but we check them + * for safety. */ + if(!qstate->return_msg || !qstate->return_msg->rep || + qstate->return_rcode != LDNS_RCODE_NOERROR) + goto fail; + + if(!respip_merge_cname(super->return_msg->rep, &qstate->qinfo, + qstate->return_msg->rep, super->client_info, + super->env->need_to_validate, &new_rep, super->region, + qstate->env->auth_zones)) + goto fail; + super->return_msg->rep = new_rep; + return; + + fail: + super->return_rcode = LDNS_RCODE_SERVFAIL; + super->return_msg = NULL; + return; +} + +void +respip_clear(struct module_qstate* qstate, int id) +{ + qstate->minfo[id] = NULL; +} + +size_t +respip_get_mem(struct module_env* env, int id) +{ + (void)env; + (void)id; + return 0; +} + +/** + * The response-ip function block + */ +static struct module_func_block respip_block = { + "respip", + &respip_init, &respip_deinit, &respip_operate, &respip_inform_super, + &respip_clear, &respip_get_mem +}; + +struct module_func_block* +respip_get_funcblock(void) +{ + return &respip_block; +} + +enum respip_action +resp_addr_get_action(const struct resp_addr* addr) +{ + return addr ? addr->action : respip_none; +} + +struct ub_packed_rrset_key* +resp_addr_get_rrset(struct resp_addr* addr) +{ + return addr ? addr->data : NULL; +} + +int +respip_set_is_empty(const struct respip_set* set) +{ + return set ? set->ip_tree.count == 0 : 1; +} + +void +respip_inform_print(struct respip_action_info* respip_actinfo, uint8_t* qname, + uint16_t qtype, uint16_t qclass, struct local_rrset* local_alias, + struct comm_reply* repinfo) +{ + char srcip[128], respip[128], txt[512]; + unsigned port; + struct respip_addr_info* respip_addr = respip_actinfo->addrinfo; + size_t txtlen = 0; + const char* actionstr = NULL; + + if(local_alias) + qname = local_alias->rrset->rk.dname; + port = (unsigned)((repinfo->addr.ss_family == AF_INET) ? + ntohs(((struct sockaddr_in*)&repinfo->addr)->sin_port) : + ntohs(((struct sockaddr_in6*)&repinfo->addr)->sin6_port)); + addr_to_str(&repinfo->addr, repinfo->addrlen, srcip, sizeof(srcip)); + addr_to_str(&respip_addr->addr, respip_addr->addrlen, + respip, sizeof(respip)); + if(respip_actinfo->rpz_log) { + txtlen += snprintf(txt+txtlen, sizeof(txt)-txtlen, "%s", + "RPZ applied "); + if(respip_actinfo->rpz_cname_override) + actionstr = rpz_action_to_string( + RPZ_CNAME_OVERRIDE_ACTION); + else + actionstr = rpz_action_to_string( + respip_action_to_rpz_action( + respip_actinfo->action)); + } + if(respip_actinfo->log_name) { + txtlen += snprintf(txt+txtlen, sizeof(txt)-txtlen, + "[%s] ", respip_actinfo->log_name); + } + snprintf(txt+txtlen, sizeof(txt)-txtlen, + "%s/%d %s %s@%u", respip, respip_addr->net, + (actionstr) ? actionstr : "inform", srcip, port); + log_nametypeclass(NO_VERBOSE, txt, qname, qtype, qclass); +} --- contrib/unbound/respip/respip.h.orig +++ contrib/unbound/respip/respip.h @@ -0,0 +1,297 @@ +/* + * respip/respip.h - IP-based response modification module + */ + +/** + * \file + * + * This file contains a module that selectively modifies query responses + * based on their AAAA/A IP addresses. + */ + +#ifndef RESPIP_RESPIP_H +#define RESPIP_RESPIP_H + +#include "util/module.h" +#include "services/localzone.h" +#include "util/locks.h" + +/** + * Conceptual set of IP addresses for response AAAA or A records that should + * trigger special actions. + */ +struct respip_set { + struct regional* region; + struct rbtree_type ip_tree; + lock_rw_type lock; /* lock on the respip tree */ + char* const* tagname; /* shallow copy of tag names, for logging */ + int num_tags; /* number of tagname entries */ +}; + + +/** An address span with response control information */ +struct resp_addr { + /** node in address tree */ + struct addr_tree_node node; + /** lock on the node item */ + lock_rw_type lock; + /** tag bitlist */ + uint8_t* taglist; + /** length of the taglist (in bytes) */ + size_t taglen; + /** action for this address span */ + enum respip_action action; + /** "local data" for this node */ + struct ub_packed_rrset_key* data; +}; + + +/** + * Forward declaration for the structure that represents a tree of view data. + */ + +struct views; + +struct respip_addr_info; + +/** + * Client-specific attributes that can affect IP-based actions. + * This is essentially a subset of acl_addr (except for respip_set) but + * defined as a separate structure to avoid dependency on the daemon-specific + * structure. + * respip_set is supposed to refer to the response-ip set for the global view. + */ +struct respip_client_info { + uint8_t* taglist; + size_t taglen; + uint8_t* tag_actions; + size_t tag_actions_size; + struct config_strlist** tag_datas; + size_t tag_datas_size; + struct view* view; + struct respip_set* respip_set; +}; + +/** + * Data items representing the result of response-ip processing. + * Note: this structure currently only define a few members, but exists + * as a separate struct mainly for the convenience of custom extensions. + */ +struct respip_action_info { + enum respip_action action; + int rpz_used; + int rpz_log; + int rpz_disabled; + char* log_name; + int rpz_cname_override; + struct respip_addr_info* addrinfo; /* set only for inform variants */ +}; + +/** + * Forward declaration for the structure that represents a node in the + * respip_set address tree + */ +struct resp_addr; + +/** + * Create response IP set. + * @return new struct or NULL on error. + */ +struct respip_set* respip_set_create(void); + +/** + * Delete response IP set. + * @param set: to delete. + */ +void respip_set_delete(struct respip_set* set); + +/** + * Apply response-ip config settings to the global (default) view. + * It assumes exclusive access to set (no internal locks). + * @param set: processed global respip config data + * @param cfg: config data. + * @return 1 on success, 0 on error. + */ +int respip_global_apply_cfg(struct respip_set* set, struct config_file* cfg); + +/** + * Apply response-ip config settings in named views. + * @param vs: view structures with processed config data + * @param cfg: config data. + * @param have_view_respip_cfg: set to true if any named view has respip + * configuration; otherwise set to false + * @return 1 on success, 0 on error. + */ +int respip_views_apply_cfg(struct views* vs, struct config_file* cfg, + int* have_view_respip_cfg); + +/** + * Merge two replies to build a complete CNAME chain. + * It appends the content of 'tgt_rep' to 'base_rep', assuming (but not + * checking) the former ends with a CNAME and the latter resolves its target. + * A merged new reply will be built using 'region' and *new_repp will point + * to the new one on success. + * If the target reply would also be subject to a response-ip action for + * 'cinfo', this function uses 'base_rep' as the merged reply, ignoring + * 'tgt_rep'. This is for avoiding cases like a CNAME loop or failure of + * applying an action to an address. + * RRSIGs in 'tgt_rep' will be excluded in the merged reply, as the resulting + * reply is assumed to be faked due to a response-ip action and can't be + * considered secure in terms of DNSSEC. + * The caller must ensure that neither 'base_rep' nor 'tgt_rep' can be modified + * until this function returns. + * @param base_rep: the reply info containing an incomplete CNAME. + * @param qinfo: query info corresponding to 'base_rep'. + * @param tgt_rep: the reply info that completes the CNAME chain. + * @param cinfo: client info corresponding to 'base_rep'. + * @param must_validate: whether 'tgt_rep' must be DNSSEC-validated. + * @param new_repp: pointer placeholder for the merged reply. will be intact + * on error. + * @param region: allocator to build *new_repp. + * @param az: auth zones containing RPZ information. + * @return 1 on success, 0 on error. + */ +int respip_merge_cname(struct reply_info* base_rep, + const struct query_info* qinfo, const struct reply_info* tgt_rep, + const struct respip_client_info* cinfo, int must_validate, + struct reply_info** new_repp, struct regional* region, + struct auth_zones* az); + +/** + * See if any IP-based action should apply to any IP address of AAAA/A answer + * record in the reply. If so, apply the action. In some cases it rewrites + * the reply rrsets, in which case *new_repp will point to the updated reply + * info. Depending on the action, some of the rrsets in 'rep' will be + * shallow-copied into '*new_repp'; the caller must ensure that the rrsets + * in 'rep' are valid throughout the lifetime of *new_repp, and it must + * provide appropriate mutex if the rrsets can be shared by multiple threads. + * @param qinfo: query info corresponding to the reply. + * @param cinfo: client-specific info to identify the best matching action. + * can be NULL. + * @param rep: original reply info. must not be NULL. + * @param new_repp: can be set to the rewritten reply info (intact on failure). + * @param actinfo: result of response-ip processing + * @param alias_rrset: must not be NULL. + * @param search_only: if true, only check if an action would apply. actionp + * will be set (or intact) accordingly but the modified reply won't be built. + * @param az: auth zones containing RPZ information. + * @param region: allocator to build *new_repp. + * @return 1 on success, 0 on error. + */ +int respip_rewrite_reply(const struct query_info* qinfo, + const struct respip_client_info* cinfo, + const struct reply_info *rep, struct reply_info** new_repp, + struct respip_action_info* actinfo, + struct ub_packed_rrset_key** alias_rrset, + int search_only, struct regional* region, struct auth_zones* az); + +/** + * Get the response-ip function block. + * @return: function block with function pointers to response-ip methods. + */ +struct module_func_block* respip_get_funcblock(void); + +/** response-ip init */ +int respip_init(struct module_env* env, int id); + +/** response-ip deinit */ +void respip_deinit(struct module_env* env, int id); + +/** response-ip operate on a query */ +void respip_operate(struct module_qstate* qstate, enum module_ev event, int id, + struct outbound_entry* outbound); + +/** inform response-ip super */ +void respip_inform_super(struct module_qstate* qstate, int id, + struct module_qstate* super); + +/** response-ip cleanup query state */ +void respip_clear(struct module_qstate* qstate, int id); + +/** + * returns address of the IP address tree of the specified respip set; + * returns NULL for NULL input; exists for test purposes only + */ +struct rbtree_type* respip_set_get_tree(struct respip_set* set); + +/** + * returns respip action for the specified node in the respip address + * returns respip_none for NULL input; exists for test purposes only + */ +enum respip_action resp_addr_get_action(const struct resp_addr* addr); + +/** + * returns rrset portion of the specified node in the respip address + * tree; returns NULL for NULL input; exists for test purposes only + */ +struct ub_packed_rrset_key* resp_addr_get_rrset(struct resp_addr* addr); + +/** response-ip alloc size routine */ +size_t respip_get_mem(struct module_env* env, int id); + +/** + * respip set emptiness test + * @param set respip set to test + * @return 0 if the specified set exists (non-NULL) and is non-empty; + * otherwise returns 1 + */ +int respip_set_is_empty(const struct respip_set* set); + +/** + * print log information for a query subject to an inform or inform-deny + * response-ip action. + * @param respip_actinfo: response-ip information that causes the action + * @param qname: query name in the context, will be ignored if local_alias is + * non-NULL. + * @param qtype: query type, in host byte order. + * @param qclass: query class, in host byte order. + * @param local_alias: set to a local alias if the query matches an alias in + * a local zone. In this case its owner name will be considered the actual + * query name. + * @param repinfo: reply info containing the client's source address and port. + */ +void respip_inform_print(struct respip_action_info* respip_actinfo, + uint8_t* qname, uint16_t qtype, uint16_t qclass, + struct local_rrset* local_alias, struct comm_reply* repinfo); + +/** + * Find resp_addr in tree, create and add to tree if it does not exist. + * @param set: struct containing the tree and region to alloc new node on. + * should hold write lock. + * @param addr: address to look up. + * @param addrlen: length of addr. + * @param net: netblock to lookup. + * @param create: create node if it does not exist when 1. + * @param ipstr: human redable ip string, for logging. + * @return newly created of found node, not holding lock. + */ +struct resp_addr* +respip_sockaddr_find_or_create(struct respip_set* set, struct sockaddr_storage* addr, + socklen_t addrlen, int net, int create, const char* ipstr); + +/** + * Add RR to resp_addr's RRset. Create RRset if not existing. + * @param region: region to alloc RR(set). + * @param raddr: resp_addr containing RRset. Must hold write lock. + * @param rrtype: RR type. + * @param rrclass: RR class. + * @param ttl: TTL. + * @param rdata: RDATA. + * @param rdata_len: length of rdata. + * @param rrstr: RR as string, for logging + * @param netblockstr: netblock as string, for logging + * @return 0 on error + */ +int +respip_enter_rr(struct regional* region, struct resp_addr* raddr, + uint16_t rrtype, uint16_t rrclass, time_t ttl, uint8_t* rdata, + size_t rdata_len, const char* rrstr, const char* netblockstr); + +/** + * Delete resp_addr node from tree. + * @param set: struct containing tree. Must hold write lock. + * @param node: node to delete. Not locked. + */ +void +respip_sockaddr_delete(struct respip_set* set, struct resp_addr* node); +#endif /* RESPIP_RESPIP_H */ --- contrib/unbound/services/authzone.c.orig +++ contrib/unbound/services/authzone.c @@ -0,0 +1,6948 @@ +/* + * 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_secalgo.h" +#include + +/** 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 tranfers 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->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; irep->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 additonal 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->prev) + z->rpz->prev->next = z->rpz->next; + else + az->rpz_first = z->rpz->next; + if(z->rpz->next) + z->rpz->next->prev = z->rpz->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)); + lock_rw_wrlock(&z->lock); + /* z lock protects all, except rbtree itself, which is 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; icount + 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; icount + 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; icount+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; icount+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; icount+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; icount+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; icount+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; icount+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; icount+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; + idata->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->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->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; iname, 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; +} + +/** read all auth zones from file (if they have) */ +static int +auth_zones_read_zones(struct auth_zones* az, struct config_file* cfg) +{ + 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; + } + lock_rw_unlock(&z->lock); + } + lock_rw_unlock(&az->lock); + return 1; +} + +/** 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). */ +static 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 */ + lock_rw_wrlock(&az->lock); + if(!(z=auth_zones_find_or_add_zone(az, c->name))) { + lock_rw_unlock(&az->lock); + return 0; + } + if(c->masters || c->urls) { + if(!(x=auth_zones_find_or_add_xfer(az, z))) { + lock_rw_unlock(&az->lock); + lock_rw_unlock(&z->lock); + return 0; + } + } + 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); + return 0; + } + z->for_downstream = c->for_downstream; + z->for_upstream = c->for_upstream; + z->fallback_enabled = c->fallback_enabled; + if(c->isrpz && !z->rpz){ + if(!(z->rpz = rpz_create(c))){ + fatal_exit("Could not setup RPZ zones"); + return 0; + } + lock_rw_wrlock(&az->rpz_lock); + z->rpz->next = az->rpz_first; + if(az->rpz_first) + az->rpz_first->prev = z->rpz; + az->rpz_first = z->rpz; + 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 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)) + 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; + *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)) && + /* 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; + } + /* not equal to qname and has type DNAME */ + if(n->namelen != qinfo->qname_len && + (*rrset=az_domain_rrset(n, LDNS_RR_TYPE_DNAME))) { + /* this is ce and the lowernode is nonexisting */ + *ce = n; + return 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; icount; 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) +{ + uint32_t minimum; + 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 = sldns_read_uint32(d->rr_data[0]+(d->rr_len[0]-4)); + d->ttl = (time_t)minimum; + d->rr_ttl[0] = (time_t)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; irep->rrsets[i]->rk.dname, oldname) + == 0) { + msg->rep->rrsets[i]->rk.dname = newname; + msg->rep->rrsets[i]->rk.dname_len = newlen; + } + } +} + +/** 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; idata->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 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) + || !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)) + edns->opt_list = NULL; + error_encode(buf, rcode|BIT_AA, qinfo, + *(uint16_t*)sldns_buffer_begin(buf), + sldns_buffer_read_u16_at(buf, 2), edns); +} + +int auth_zones_answer(struct auth_zones* az, struct module_env* env, + struct query_info* qinfo, struct edns_data* edns, + struct 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) && + 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_probe->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 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 */ +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 1; + } + return 0; +} + +/** process $TTL for http */ +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; + pstate->default_ttl = sldns_str2period( + sldns_strip_ws(line+5), &end); + 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) +{ + while(chunkline_get_line_collated(chunk, chunk_pos, buf)) { + if(chunkline_is_comment_line_or_empty(buf)) { + /* a comment, go to next line */ + continue; + } + if(http_parse_origin(buf, pstate)) { + continue; /* $ORIGIN has been handled */ + } + if(http_parse_ttl(buf, pstate)) { + 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; idefault_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; idata); 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; + 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(http_parse_origin(scratch_buffer, &pstate)) { + continue; /* $ORIGIN has been handled */ + } + if(http_parse_ttl(scratch_buffer, &pstate)) { + 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; + } + 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); +} + +/** 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 */ + /* release xfr lock, then, while holding az->lock grab both + * z->lock and xfr->lock */ + lock_basic_unlock(&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); + /* the zone is gone, ignore xfr results */ + lock_basic_lock(&xfr->lock); + return 0; + } + lock_rw_wrlock(&z->lock); + lock_basic_lock(&xfr->lock); + lock_rw_unlock(&env->auth_zones->lock); + + /* 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; + } + 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)) { + /* 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 = NULL; + 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)) { + 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); + 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); + } + return 1; + } + + /* perform AXFR/IXFR */ + /* set the packet to be written */ + /* create new ID */ + xfr->task_transfer->id = (uint16_t)(ub_random(env->rnd)&0xffff); + 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; icount; 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->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")); + } + } + } 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->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->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->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 = (uint16_t)(ub_random(env->rnd)&0xffff); + 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)) { + 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->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->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 successfull probe, we need + * to send the next one */ + xfr_probe_nextmaster(xfr); + xfr_probe_send_or_end(xfr, env); + return 0; +} + +/** lookup a host name for its addresses, if needed */ +static int +xfr_probe_lookup_host(struct auth_xfer* xfr, struct module_env* env) +{ + struct sockaddr_storage addr; + socklen_t addrlen = 0; + struct auth_master* master = xfr->task_probe->lookup_target; + struct query_info qinfo; + uint16_t qflags = BIT_RD; + uint8_t dname[LDNS_MAX_DOMAINLEN+1]; + struct edns_data edns; + sldns_buffer* buf = env->scratch_buffer; + if(!master) return 0; + if(extstrtoaddr(master->host, &addr, &addrlen)) { + /* not needed, host is in IP addr format */ + return 0; + } + 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 = NULL; + 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)) { + 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->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")); + } + } + } 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->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("index.html"); + 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); + 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); + 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); + 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; + } +} --- contrib/unbound/services/authzone.h.orig +++ contrib/unbound/services/authzone.h @@ -0,0 +1,683 @@ +/* + * services/authzone.h - 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. + */ + +#ifndef SERVICES_AUTHZONE_H +#define SERVICES_AUTHZONE_H +#include "util/rbtree.h" +#include "util/locks.h" +#include "services/mesh.h" +#include "services/rpz.h" +struct ub_packed_rrset_key; +struct regional; +struct config_file; +struct config_auth; +struct query_info; +struct dns_msg; +struct edns_data; +struct module_env; +struct worker; +struct comm_point; +struct comm_timer; +struct comm_reply; +struct auth_rrset; +struct auth_nextprobe; +struct auth_probe; +struct auth_transfer; +struct auth_master; +struct auth_chunk; + +/** + * Authoritative zones, shared. + */ +struct auth_zones { + /** lock on the authzone trees */ + lock_rw_type lock; + /** rbtree of struct auth_zone */ + rbtree_type ztree; + /** rbtree of struct auth_xfer */ + rbtree_type xtree; + /** do we have downstream enabled */ + int have_downstream; + /** number of queries upstream */ + size_t num_query_up; + /** number of queries downstream */ + size_t num_query_down; + /** first rpz item in linked list */ + struct rpz* rpz_first; + /** rw lock for rpz linked list, needed when iterating or editing linked + * list. */ + lock_rw_type rpz_lock; +}; + +/** + * Auth zone. Authoritative data, that is fetched from instead of sending + * packets to the internet. + */ +struct auth_zone { + /** rbtree node, key is name and class */ + rbnode_type node; + + /** zone name, in uncompressed wireformat */ + uint8_t* name; + /** length of zone name */ + size_t namelen; + /** number of labels in zone name */ + int namelabs; + /** the class of this zone, in host byteorder. + * uses 'dclass' to not conflict with c++ keyword class. */ + uint16_t dclass; + + /** lock on the data in the structure + * For the node, parent, name, namelen, namelabs, dclass, you + * need to also hold the zones_tree lock to change them (or to + * delete this zone) */ + lock_rw_type lock; + + /** auth data for this zone + * rbtree of struct auth_data */ + rbtree_type data; + + /** zonefile name (or NULL for no zonefile) */ + char* zonefile; + /** fallback to the internet on failure or ttl-expiry of auth zone */ + int fallback_enabled; + /** the zone has expired (enabled by the xfer worker), fallback + * happens if that option is enabled. */ + int zone_expired; + /** zone is a slave zone (it has masters) */ + int zone_is_slave; + /** for downstream: this zone answers queries towards the downstream + * clients */ + int for_downstream; + /** for upstream: this zone answers queries that unbound intends to + * send upstream. */ + int for_upstream; + /** RPZ zones */ + struct rpz* rpz; + /** zone has been deleted */ + int zone_deleted; + /** deletelist pointer, unused normally except during delete */ + struct auth_zone* delete_next; +}; + +/** + * Auth data. One domain name, and the RRs to go with it. + */ +struct auth_data { + /** rbtree node, key is name only */ + rbnode_type node; + /** domain name */ + uint8_t* name; + /** length of name */ + size_t namelen; + /** number of labels in name */ + int namelabs; + /** the data rrsets, with different types, linked list. + * if the list if NULL the node would be an empty non-terminal, + * but in this data structure such nodes that represent an empty + * non-terminal are not needed; they just don't exist. + */ + struct auth_rrset* rrsets; +}; + +/** + * A auth data RRset + */ +struct auth_rrset { + /** next in list */ + struct auth_rrset* next; + /** RR type in host byteorder */ + uint16_t type; + /** RRset data item */ + struct packed_rrset_data* data; +}; + +/** + * Authoritative zone transfer structure. + * Create and destroy needs the auth_zones* biglock. + * The structure consists of different tasks. Each can be unowned (-1) or + * owner by a worker (worker-num). A worker can pick up a task and then do + * it. This means the events (timeouts, sockets) are for that worker. + * + * (move this to tasks). + * They don't have locks themselves, the worker (that owns it) uses it, + * also as part of callbacks, hence it has separate zonename pointers for + * lookup in the main zonetree. If the zone has no transfers, this + * structure is not created. + */ +struct auth_xfer { + /** rbtree node, key is name and class */ + rbnode_type node; + + /** lock on this structure, and on the workernum elements of the + * tasks. First hold the tree-lock in auth_zones, find the auth_xfer, + * lock this lock. Then a worker can reassign itself to fill up + * one of the tasks. + * Once it has the task assigned to it, the worker can access the + * other elements of the task structure without a lock, because that + * is necessary for the eventloop and callbacks from that. */ + lock_basic_type lock; + + /** zone name, in uncompressed wireformat */ + uint8_t* name; + /** length of zone name */ + size_t namelen; + /** number of labels in zone name */ + int namelabs; + /** the class of this zone, in host byteorder. + * uses 'dclass' to not conflict with c++ keyword class. */ + uint16_t dclass; + + /** task to wait for next-probe-timeout, + * once timeouted, see if a SOA probe is needed, or already + * in progress */ + struct auth_nextprobe* task_nextprobe; + + /** task for SOA probe. Check if the zone can be updated */ + struct auth_probe* task_probe; + + /** Task for transfer. Transferring and updating the zone. This + * includes trying (potentially) several upstream masters. Downloading + * and storing the zone */ + struct auth_transfer* task_transfer; + + /** a notify was received, but a zone transfer or probe was already + * acted on. + * However, the zone transfer could signal a newer serial number. + * The serial number of that notify is saved below. The transfer and + * probe tasks should check this once done to see if they need to + * restart the transfer task for the newer notify serial. + * Hold the lock to access this member (and the serial). + */ + int notify_received; + /** true if the notify_received has a serial number */ + int notify_has_serial; + /** serial number of the notify */ + uint32_t notify_serial; + /** the list of masters for checking notifies. This list is + * empty on start, and a copy of the list from the probe_task when + * it is done looking them up. */ + struct auth_master* allow_notify_list; + + /* protected by the lock on the structure, information about + * the loaded authority zone. */ + /** is the zone currently considered expired? after expiry also older + * serial numbers are allowed (not just newer) */ + int zone_expired; + /** do we have a zone (if 0, no zone data at all) */ + int have_zone; + + /** current serial (from SOA), if we have no zone, 0 */ + uint32_t serial; + /** retry time (from SOA), time to wait with next_probe + * if no master responds */ + time_t retry; + /** refresh time (from SOA), time to wait with next_probe + * if everything is fine */ + time_t refresh; + /** expiry time (from SOA), time until zone data is not considered + * valid any more, if no master responds within this time, either + * with the current zone or a new zone. */ + time_t expiry; + + /** zone lease start time (start+expiry is expiration time). + * this is renewed every SOA probe and transfer. On zone load + * from zonefile it is also set (with probe set soon to check) */ + time_t lease_time; +}; + +/** + * The next probe task. + * This task consists of waiting for the probetimeout. It is a task because + * it needs an event in the eventtable. Once the timeout has passed, that + * worker can (potentially) become the auth_probe worker, or if another worker + * is already doing that, do nothing. Tasks becomes unowned. + * The probe worker, if it detects nothing has to be done picks up this task, + * if unowned. + */ +struct auth_nextprobe { + /* Worker pointer. NULL means unowned. */ + struct worker* worker; + /* module env for this task */ + struct module_env* env; + + /** increasing backoff for failures */ + time_t backoff; + /** Timeout for next probe (for SOA) */ + time_t next_probe; + /** timeout callback for next_probe or expiry(if that is sooner). + * it is on the worker's event_base */ + struct comm_timer* timer; +}; + +/** + * The probe task. + * Send a SOA UDP query to see if the zone needs to be updated (or similar, + * potential, HTTP probe query) and check serial number. + * If yes, start the auth_transfer task. If no, make sure auth_nextprobe + * timeout wait task is running. + * Needs to be a task, because the UDP query needs an event entry. + * This task could also be started by eg. a NOTIFY being received, even though + * another worker is performing the nextprobe task (and that worker keeps + * waiting uninterrupted). + */ +struct auth_probe { + /* Worker pointer. NULL means unowned. */ + struct worker* worker; + /* module env for this task */ + struct module_env* env; + + /** list of upstream masters for this zone, from config */ + struct auth_master* masters; + + /** for the hostname lookups, which master is current */ + struct auth_master* lookup_target; + /** are we looking up A or AAAA, first A, then AAAA (if ip6 enabled) */ + int lookup_aaaa; + /** we only want to do lookups for making config work (for notify), + * don't proceed with UDP SOA probe queries */ + int only_lookup; + /** we have seen a new lease this scan, because one of the masters + * replied with the current SOA serial version */ + int have_new_lease; + + /** once notified, or the timeout has been reached. a scan starts. */ + /** the scan specific target (notify source), or NULL if none */ + struct auth_master* scan_specific; + /** scan tries all the upstream masters. the scan current target. + * or NULL if not working on sequential scan */ + struct auth_master* scan_target; + /** if not NULL, the specific addr for the current master */ + struct auth_addr* scan_addr; + + /** dns id of packet in flight */ + uint16_t id; + /** the SOA probe udp event. + * on the workers event base. */ + struct comm_point* cp; + /** is the cp for ip6 or ip4 */ + int cp_is_ip6; + /** timeout for packets. + * on the workers event base. */ + struct comm_timer* timer; + /** timeout in msec */ + int timeout; +}; + +/** + * The transfer task. + * Once done, make sure the nextprobe waiting task is running, whether done + * with failure or success. If failure, use shorter timeout for wait time. + */ +struct auth_transfer { + /* Worker pointer. NULL means unowned. */ + struct worker* worker; + /* module env for this task */ + struct module_env* env; + + /** xfer data that has been transferred, the data is applied + * once the transfer has completed correctly */ + struct auth_chunk* chunks_first; + /** last element in chunks list (to append new data at the end) */ + struct auth_chunk* chunks_last; + + /** list of upstream masters for this zone, from config */ + struct auth_master* masters; + + /** for the hostname lookups, which master is current */ + struct auth_master* lookup_target; + /** are we looking up A or AAAA, first A, then AAAA (if ip6 enabled) */ + int lookup_aaaa; + + /** once notified, or the timeout has been reached. a scan starts. */ + /** the scan specific target (notify source), or NULL if none */ + struct auth_master* scan_specific; + /** scan tries all the upstream masters. the scan current target. + * or NULL if not working on sequential scan */ + struct auth_master* scan_target; + /** what address we are scanning for the master, or NULL if the + * master is in IP format itself */ + struct auth_addr* scan_addr; + /** the zone transfer in progress (or NULL if in scan). It is + * from this master */ + struct auth_master* master; + + /** failed ixfr transfer, retry with axfr (to the current master), + * the IXFR was 'REFUSED', 'SERVFAIL', 'NOTIMPL' or the contents of + * the IXFR did not apply cleanly (out of sync, delete of nonexistent + * data or add of duplicate data). Flag is cleared once the retry + * with axfr is done. */ + int ixfr_fail; + /** we saw an ixfr-indicating timeout, count of them */ + int ixfr_possible_timeout_count; + /** we are doing IXFR right now */ + int on_ixfr; + /** did we detect the current AXFR/IXFR serial number yet, 0 not yet, + * 1 we saw the first, 2 we saw the second, 3 must be last SOA in xfr*/ + int got_xfr_serial; + /** number of RRs scanned for AXFR/IXFR detection */ + size_t rr_scan_num; + /** we are doing an IXFR but we detected an AXFR contents */ + int on_ixfr_is_axfr; + /** the serial number for the current AXFR/IXFR incoming reply, + * for IXFR, the outermost SOA records serial */ + uint32_t incoming_xfr_serial; + + /** dns id of AXFR query */ + uint16_t id; + /** the transfer (TCP) to the master. + * on the workers event base. */ + struct comm_point* cp; + /** timeout for the transfer. + * on the workers event base. */ + struct comm_timer* timer; +}; + +/** list of addresses */ +struct auth_addr { + /** next in list */ + struct auth_addr* next; + /** IP address */ + struct sockaddr_storage addr; + /** addr length */ + socklen_t addrlen; +}; + +/** auth zone master upstream, and the config settings for it */ +struct auth_master { + /** next master in list */ + struct auth_master* next; + /** master IP address (and port), or hostname, string */ + char* host; + /** for http, filename */ + char* file; + /** use HTTP for this master */ + int http; + /** use IXFR for this master */ + int ixfr; + /** this is an allow notify member, the master can send notifies + * to us, but we don't send SOA probes, or zone transfer from it */ + int allow_notify; + /** use ssl for channel */ + int ssl; + /** the port number (for urls) */ + int port; + /** if the host is a hostname, the list of resolved addrs, if any*/ + struct auth_addr* list; +}; + +/** auth zone master zone transfer data chunk */ +struct auth_chunk { + /** next chunk in list */ + struct auth_chunk* next; + /** the data from this chunk, this is what was received. + * for an IXFR that means results from comm_net tcp actions, + * packets. also for an AXFR. For HTTP a zonefile chunk. */ + uint8_t* data; + /** length of allocated data */ + size_t len; +}; + +/** + * Create auth zones structure + */ +struct auth_zones* auth_zones_create(void); + +/** + * Apply configuration to auth zones. Reads zonefiles. + * @param az: auth zones structure + * @param cfg: config to apply. + * @param setup: if true, also sets up values in the auth zones structure + * @param is_rpz: set to 1 if at least one RPZ zone is configured. + * @return false on failure. + */ +int auth_zones_apply_cfg(struct auth_zones* az, struct config_file* cfg, + int setup, int* is_rpz); + +/** initial pick up of worker timeouts, ties events to worker event loop + * @param az: auth zones structure + * @param env: worker env, of first worker that receives the events (if any) + * in its eventloop. + */ +void auth_xfer_pickup_initial(struct auth_zones* az, struct module_env* env); + +/** + * Cleanup auth zones. This removes all events from event bases. + * Stops the xfr tasks. But leaves zone data. + * @param az: auth zones structure. + */ +void auth_zones_cleanup(struct auth_zones* az); + +/** + * Delete auth zones structure + */ +void auth_zones_delete(struct auth_zones* az); + +/** + * Write auth zone data to file, in zonefile format. + */ +int auth_zone_write_file(struct auth_zone* z, const char* fname); + +/** + * Use auth zones to lookup the answer to a query. + * The query is from the iterator. And the auth zones attempts to provide + * the answer instead of going to the internet. + * + * @param az: auth zones structure. + * @param qinfo: query info to lookup. + * @param region: region to use to allocate the reply in. + * @param msg: reply is stored here (if one). + * @param fallback: if true, fallback to making a query to the internet. + * @param dp_nm: name of delegation point to look for. This zone is used + * to answer the query. + * If the dp_nm is not found, fallback is set to true and false returned. + * @param dp_nmlen: length of dp_nm. + * @return 0: failure (an error of some sort, like servfail). + * if 0 and fallback is true, fallback to the internet. + * if 0 and fallback is false, like getting servfail. + * If true, an answer is available. + */ +int auth_zones_lookup(struct auth_zones* az, struct query_info* qinfo, + struct regional* region, struct dns_msg** msg, int* fallback, + uint8_t* dp_nm, size_t dp_nmlen); + +/** + * Answer query from auth zone. Create authoritative answer. + * @param az: auth zones structure. + * @param env: the module environment. + * @param qinfo: query info (parsed). + * @param edns: edns info (parsed). + * @param buf: buffer with query ID and flags, also for reply. + * @param repinfo: reply information for a communication point. + * @param temp: temporary storage region. + * @return false if not answered + */ +int auth_zones_answer(struct auth_zones* az, struct module_env* env, + struct query_info* qinfo, struct edns_data* edns, + struct comm_reply* repinfo, struct sldns_buffer* buf, struct regional* temp); + +/** + * Find the auth zone that is above the given qname. + * Return NULL when there is no auth_zone above the give name, otherwise + * returns the closest auth_zone above the qname that pertains to it. + * @param az: auth zones structure. + * @param name: query to look up for. + * @param name_len: length of name. + * @param dclass: class of zone to find. + * @return NULL or auth_zone that pertains to the query. + */ +struct auth_zone* auth_zones_find_zone(struct auth_zones* az, + uint8_t* name, size_t name_len, uint16_t dclass); + +/** find an auth zone by name (exact match by name or NULL returned) */ +struct auth_zone* auth_zone_find(struct auth_zones* az, uint8_t* nm, + size_t nmlen, uint16_t dclass); + +/** find an xfer zone by name (exact match by name or NULL returned) */ +struct auth_xfer* auth_xfer_find(struct auth_zones* az, uint8_t* nm, + size_t nmlen, uint16_t dclass); + +/** create an auth zone. returns wrlocked zone. caller must have wrlock + * on az. returns NULL on malloc failure */ +struct auth_zone* auth_zone_create(struct auth_zones* az, uint8_t* nm, + size_t nmlen, uint16_t dclass); + +/** set auth zone zonefile string. caller must have lock on zone */ +int auth_zone_set_zonefile(struct auth_zone* z, char* zonefile); + +/** set auth zone fallback. caller must have lock on zone. + * fallbackstr is "yes" or "no". false on parse failure. */ +int auth_zone_set_fallback(struct auth_zone* z, char* fallbackstr); + +/** see if the auth zone for the name can fallback + * @param az: auth zones + * @param nm: name of delegation point. + * @param nmlen: length of nm. + * @param dclass: class of zone to look for. + * @return true if fallback_enabled is true. false if not. + * if the zone does not exist, fallback is true (more lenient) + * also true if zone does not do upstream requests. + */ +int auth_zones_can_fallback(struct auth_zones* az, uint8_t* nm, size_t nmlen, + uint16_t dclass); + +/** process notify for auth zones. + * first checks the access list. Then processes the notify. This starts + * the probe sequence or it notes the serial number (if any) + * @param az: auth zones structure. + * @param env: module env of the worker that is handling the notify. it will + * pick up the task probe (or transfer), unless already in progress by + * another worker. + * @param nm: name of the zone. Uncompressed. from query. + * @param nmlen: length of name. + * @param dclass: class of zone. + * @param addr: source address of notify + * @param addrlen: length of addr. + * @param has_serial: if true, the notify has a serial attached. + * @param serial: the serial number, if has_serial is true. + * @param refused: is set to true on failure to note refused access. + * @return fail on failures (refused is false) and when access is + * denied (refused is true). True when processed. + */ +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); + +/** process notify packet and read serial number from SOA. + * returns 0 if no soa record in the notify */ +int auth_zone_parse_notify_serial(struct sldns_buffer* pkt, uint32_t *serial); + +/** for the zone and if not already going, starts the probe sequence. + * false if zone cannot be found. This is like a notify arrived and was + * accepted for that zone. */ +int auth_zones_startprobesequence(struct auth_zones* az, + struct module_env* env, uint8_t* nm, size_t nmlen, uint16_t dclass); + +/** read auth zone from zonefile. caller must lock zone. false on failure */ +int auth_zone_read_zonefile(struct auth_zone* z, struct config_file* cfg); + +/** find serial number of zone or false if none (no SOA record) */ +int auth_zone_get_serial(struct auth_zone* z, uint32_t* serial); + +/** compare auth_zones for sorted rbtree */ +int auth_zone_cmp(const void* z1, const void* z2); + +/** compare auth_data for sorted rbtree */ +int auth_data_cmp(const void* z1, const void* z2); + +/** compare auth_xfer for sorted rbtree */ +int auth_xfer_cmp(const void* z1, const void* z2); + +/** Create auth_xfer structure. + * Caller must have wrlock on az. Returns locked xfer zone. + * @param az: zones structure. + * @param z: zone with name and class + * @return xfer zone or NULL + */ +struct auth_xfer* auth_xfer_create(struct auth_zones* az, struct auth_zone* z); + +/** + * Set masters in auth xfer structure from config. + * @param list: pointer to start of list. The malloced list is returned here. + * @param c: the config items to copy over. + * @param with_http: if true, http urls are also included, before the masters. + * @return false on failure. + */ +int xfer_set_masters(struct auth_master** list, struct config_auth* c, + int with_http); + +/** xfer nextprobe timeout callback, this is part of task_nextprobe */ +void auth_xfer_timer(void* arg); + +/** callback for commpoint udp replies to task_probe */ +int auth_xfer_probe_udp_callback(struct comm_point* c, void* arg, int err, + struct comm_reply* repinfo); +/** callback for task_transfer tcp connections */ +int auth_xfer_transfer_tcp_callback(struct comm_point* c, void* arg, int err, + struct comm_reply* repinfo); +/** callback for task_transfer http connections */ +int auth_xfer_transfer_http_callback(struct comm_point* c, void* arg, int err, + struct comm_reply* repinfo); +/** xfer probe timeout callback, part of task_probe */ +void auth_xfer_probe_timer_callback(void* arg); +/** xfer transfer timeout callback, part of task_transfer */ +void auth_xfer_transfer_timer_callback(void* arg); +/** mesh callback for task_probe on lookup of host names */ +void auth_xfer_probe_lookup_callback(void* arg, int rcode, + struct sldns_buffer* buf, enum sec_status sec, char* why_bogus, + int was_ratelimited); +/** mesh callback for task_transfer on lookup of host names */ +void auth_xfer_transfer_lookup_callback(void* arg, int rcode, + struct sldns_buffer* buf, enum sec_status sec, char* why_bogus, + int was_ratelimited); + +/* + * Compares two 32-bit serial numbers as defined in RFC1982. Returns + * <0 if a < b, 0 if a == b, and >0 if a > b. The result is undefined + * if a != b but neither is greater or smaller (see RFC1982 section + * 3.2.). + */ +int compare_serial(uint32_t a, uint32_t b); + +#endif /* SERVICES_AUTHZONE_H */ --- contrib/unbound/services/cache/dns.c.orig +++ contrib/unbound/services/cache/dns.c @@ -40,9 +40,12 @@ */ #include "config.h" #include "iterator/iter_delegpt.h" +#include "iterator/iter_utils.h" #include "validator/val_nsec.h" +#include "validator/val_utils.h" #include "services/cache/dns.h" #include "services/cache/rrset.h" +#include "util/data/msgparse.h" #include "util/data/msgreply.h" #include "util/data/packed_rrset.h" #include "util/data/dname.h" @@ -71,15 +74,15 @@ time_t leeway, int pside, struct reply_info* qrep, struct regional* region) { - size_t i; - /* see if rrset already exists in cache, if not insert it. */ - for(i=0; irrset_count; i++) { - rep->ref[i].key = rep->rrsets[i]; - rep->ref[i].id = rep->rrsets[i]->id; - /* update ref if it was in the cache */ + size_t i; + /* see if rrset already exists in cache, if not insert it. */ + for(i=0; irrset_count; i++) { + rep->ref[i].key = rep->rrsets[i]; + rep->ref[i].id = rep->rrsets[i]->id; + /* update ref if it was in the cache */ switch(rrset_cache_update(env->rrset_cache, &rep->ref[i], - env->alloc, now + ((ntohs(rep->ref[i].key->rk.type)== - LDNS_RR_TYPE_NS && !pside)?0:leeway))) { + env->alloc, now + ((ntohs(rep->ref[i].key->rk.type)== + LDNS_RR_TYPE_NS && !pside)?0:leeway))) { case 0: /* ref unchanged, item inserted */ break; case 2: /* ref updated, cache is superior */ @@ -98,16 +101,61 @@ } } /* no break: also copy key item */ + /* the line below is matched by gcc regex and silences + * the fallthrough warning */ + /* fallthrough */ case 1: /* ref updated, item inserted */ - rep->rrsets[i] = rep->ref[i].key; + rep->rrsets[i] = rep->ref[i].key; } - } + } } +/** delete message from message cache */ +void +msg_cache_remove(struct module_env* env, uint8_t* qname, size_t qnamelen, + uint16_t qtype, uint16_t qclass, uint16_t flags) +{ + struct query_info k; + hashvalue_type h; + + k.qname = qname; + k.qname_len = qnamelen; + k.qtype = qtype; + k.qclass = qclass; + k.local_alias = NULL; + h = query_info_hash(&k, flags); + slabhash_remove(env->msg_cache, h, &k); +} + +/** remove servfail msg cache entry */ +static void +msg_del_servfail(struct module_env* env, struct query_info* qinfo, + uint32_t flags) +{ + struct msgreply_entry* e; + /* see if the entry is servfail, and then remove it, so that + * lookups move from the cacheresponse stage to the recursionresponse + * stage */ + e = msg_cache_lookup(env, qinfo->qname, qinfo->qname_len, + qinfo->qtype, qinfo->qclass, flags, 0, 0); + if(!e) return; + /* we don't check for the ttl here, also expired servfail entries + * are removed. If the user uses serve-expired, they would still be + * used to answer from cache */ + if(FLAGS_GET_RCODE(((struct reply_info*)e->entry.data)->flags) + != LDNS_RCODE_SERVFAIL) { + lock_rw_unlock(&e->entry.lock); + return; + } + lock_rw_unlock(&e->entry.lock); + msg_cache_remove(env, qinfo->qname, qinfo->qname_len, qinfo->qtype, + qinfo->qclass, flags); +} + void dns_cache_store_msg(struct module_env* env, struct query_info* qinfo, - hashvalue_t hash, struct reply_info* rep, time_t leeway, int pside, - struct reply_info* qrep, struct regional* region) + hashvalue_type hash, struct reply_info* rep, time_t leeway, int pside, + struct reply_info* qrep, uint32_t flags, struct regional* region) { struct msgreply_entry* e; time_t ttl = rep->ttl; @@ -123,11 +171,17 @@ * unnecessary, because the cache gets locked per rrset. */ reply_info_set_ttls(rep, *env->now); store_rrsets(env, rep, *env->now, leeway, pside, qrep, region); - if(ttl == 0) { + if(ttl == 0 && !(flags & DNSCACHE_STORE_ZEROTTL)) { /* we do not store the message, but we did store the RRs, * which could be useful for delegation information */ verbose(VERB_ALGO, "TTL 0: dropped msg from cache"); free(rep); + /* if the message is SERVFAIL in cache, remove that SERVFAIL, + * so that the TTL 0 response can be returned for future + * responses (i.e. don't get answered by the servfail from + * cache, but instead go to recursion to get this TTL0 + * response). */ + msg_del_servfail(env, qinfo, flags); return; } @@ -182,18 +236,19 @@ } /** lookup message in message cache */ -static struct msgreply_entry* +struct msgreply_entry* msg_cache_lookup(struct module_env* env, uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, uint16_t flags, time_t now, int wr) { struct lruhash_entry* e; struct query_info k; - hashvalue_t h; + hashvalue_type h; k.qname = qname; k.qname_len = qnamelen; k.qtype = qtype; k.qclass = qclass; + k.local_alias = NULL; h = query_info_hash(&k, flags); e = slabhash_lookup(env->msg_cache, h, &k, wr); @@ -218,7 +273,7 @@ akey = rrset_cache_lookup(env->rrset_cache, ns->name, ns->namelen, LDNS_RR_TYPE_A, qclass, 0, now, 0); if(akey) { - if(!delegpt_add_rrset_A(dp, region, akey, 0)) { + if(!delegpt_add_rrset_A(dp, region, akey, 0, NULL)) { lock_rw_unlock(&akey->entry.lock); return 0; } @@ -238,7 +293,7 @@ akey = rrset_cache_lookup(env->rrset_cache, ns->name, ns->namelen, LDNS_RR_TYPE_AAAA, qclass, 0, now, 0); if(akey) { - if(!delegpt_add_rrset_AAAA(dp, region, akey, 0)) { + if(!delegpt_add_rrset_AAAA(dp, region, akey, 0, NULL)) { lock_rw_unlock(&akey->entry.lock); return 0; } @@ -272,7 +327,8 @@ akey = rrset_cache_lookup(env->rrset_cache, ns->name, ns->namelen, LDNS_RR_TYPE_A, qclass, 0, now, 0); if(akey) { - if(!delegpt_add_rrset_A(dp, region, akey, ns->lame)) { + if(!delegpt_add_rrset_A(dp, region, akey, ns->lame, + NULL)) { lock_rw_unlock(&akey->entry.lock); return 0; } @@ -292,7 +348,8 @@ akey = rrset_cache_lookup(env->rrset_cache, ns->name, ns->namelen, LDNS_RR_TYPE_AAAA, qclass, 0, now, 0); if(akey) { - if(!delegpt_add_rrset_AAAA(dp, region, akey, ns->lame)) { + if(!delegpt_add_rrset_AAAA(dp, region, akey, ns->lame, + NULL)) { lock_rw_unlock(&akey->entry.lock); return 0; } @@ -361,6 +418,7 @@ msg->qinfo.qname_len = qnamelen; msg->qinfo.qtype = qtype; msg->qinfo.qclass = 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)); @@ -389,8 +447,7 @@ return 1; } -/** add rrset to answer section */ -static int +int dns_msg_ansadd(struct dns_msg* msg, struct regional* region, struct ub_packed_rrset_key* rrset, time_t now) { @@ -477,32 +534,52 @@ return msg; } -/** generate dns_msg from cached message */ -static struct dns_msg* -tomsg(struct module_env* env, struct query_info* q, struct reply_info* r, - struct regional* region, time_t now, struct regional* scratch) +struct dns_msg* +tomsg(struct module_env* env, struct query_info* q, struct reply_info* r, + struct regional* region, time_t now, int allow_expired, + struct regional* scratch) { struct dns_msg* msg; size_t i; - if(now > r->ttl) - return NULL; + int is_expired = 0; + time_t now_control = now; + if(now > r->ttl) { + /* Check if we are allowed to serve expired */ + if(allow_expired) { + if(env->cfg->serve_expired_ttl && + r->serve_expired_ttl < now) { + return NULL; + } + } else { + return NULL; + } + /* Change the current time so we can pass the below TTL checks when + * serving expired data. */ + now_control = r->ttl - env->cfg->serve_expired_reply_ttl; + is_expired = 1; + } + msg = gen_dns_msg(region, q, r->rrset_count); - if(!msg) - return NULL; + if(!msg) return NULL; msg->rep->flags = r->flags; msg->rep->qdcount = r->qdcount; - msg->rep->ttl = r->ttl - now; + msg->rep->ttl = is_expired + ?SERVE_EXPIRED_REPLY_TTL + :r->ttl - now; if(r->prefetch_ttl > now) msg->rep->prefetch_ttl = r->prefetch_ttl - now; - else msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl); + else + msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl); + msg->rep->serve_expired_ttl = msg->rep->ttl + SERVE_EXPIRED_TTL; msg->rep->security = r->security; msg->rep->an_numrrsets = r->an_numrrsets; msg->rep->ns_numrrsets = r->ns_numrrsets; msg->rep->ar_numrrsets = r->ar_numrrsets; msg->rep->rrset_count = r->rrset_count; - msg->rep->authoritative = r->authoritative; - if(!rrset_array_lock(r->ref, r->rrset_count, now)) + msg->rep->authoritative = r->authoritative; + if(!rrset_array_lock(r->ref, r->rrset_count, now_control)) { return NULL; + } if(r->an_numrrsets > 0 && (r->rrsets[0]->rk.type == htons( LDNS_RR_TYPE_CNAME) || r->rrsets[0]->rk.type == htons( LDNS_RR_TYPE_DNAME)) && !reply_check_cname_chain(q, r)) { @@ -516,7 +593,7 @@ return NULL; } for(i=0; irep->rrset_count; i++) { - msg->rep->rrsets[i] = packed_rrset_copy_region(r->rrsets[i], + msg->rep->rrsets[i] = packed_rrset_copy_region(r->rrsets[i], region, now); if(!msg->rep->rrsets[i]) { rrset_array_unlock(r->ref, r->rrset_count); @@ -523,8 +600,11 @@ return NULL; } } - rrset_array_unlock_touch(env->rrset_cache, scratch, r->ref, + if(env) + rrset_array_unlock_touch(env->rrset_cache, scratch, r->ref, r->rrset_count); + else + rrset_array_unlock(r->ref, r->rrset_count); return msg; } @@ -546,6 +626,7 @@ msg->rep->qdcount = 1; msg->rep->ttl = d->ttl - now; msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl); + msg->rep->serve_expired_ttl = msg->rep->ttl + SERVE_EXPIRED_TTL; msg->rep->security = sec_status_unchecked; msg->rep->an_numrrsets = 1; msg->rep->ns_numrrsets = 0; @@ -560,7 +641,7 @@ /** synthesize DNAME+CNAME response from cached DNAME item */ static struct dns_msg* synth_dname_msg(struct ub_packed_rrset_key* rrset, struct regional* region, - time_t now, struct query_info* q) + time_t now, struct query_info* q, enum sec_status* sec_status) { struct dns_msg* msg; struct ub_packed_rrset_key* ck; @@ -572,8 +653,9 @@ return NULL; /* only allow validated (with DNSSEC) DNAMEs used from cache * for insecure DNAMEs, query again. */ - if(d->security != sec_status_secure) - return NULL; + *sec_status = d->security; + /* return sec status, so the status of the CNAME can be checked + * by the calling routine. */ msg = gen_dns_msg(region, q, 2); /* DNAME + CNAME RRset */ if(!msg) return NULL; @@ -582,6 +664,7 @@ msg->rep->qdcount = 1; msg->rep->ttl = d->ttl - now; msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl); + msg->rep->serve_expired_ttl = msg->rep->ttl + SERVE_EXPIRED_TTL; msg->rep->security = sec_status_unchecked; msg->rep->an_numrrsets = 1; msg->rep->ns_numrrsets = 0; @@ -640,6 +723,7 @@ newd->rr_ttl[0] = newd->ttl; msg->rep->ttl = newd->ttl; msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(newd->ttl); + msg->rep->serve_expired_ttl = newd->ttl + SERVE_EXPIRED_TTL; sldns_write_uint16(newd->rr_data[0], newlen); memmove(newd->rr_data[0] + sizeof(uint16_t), newname, newlen); msg->rep->an_numrrsets ++; @@ -661,6 +745,19 @@ int i, num=6; /* number of RR types to look up */ log_assert(lookup[num] == 0); + if(env->cfg->deny_any) { + /* return empty message */ + msg = dns_msg_create(qname, qnamelen, qtype, qclass, + region, 0); + if(!msg) { + return NULL; + } + /* set NOTIMPL for RFC 8482 */ + msg->rep->flags |= LDNS_RCODE_NOTIMPL; + msg->rep->security = sec_status_indeterminate; + return msg; + } + for(i=0; inow; struct ub_packed_rrset_key* rrset; @@ -716,12 +814,13 @@ k.qname_len = qnamelen; k.qtype = qtype; k.qclass = qclass; + k.local_alias = NULL; h = query_info_hash(&k, flags); e = slabhash_lookup(env->msg_cache, h, &k, 0); if(e) { struct msgreply_entry* key = (struct msgreply_entry*)e->key; struct reply_info* data = (struct reply_info*)e->data; - struct dns_msg* msg = tomsg(env, &key->key, data, region, now, + struct dns_msg* msg = tomsg(env, &key->key, data, region, now, 0, scratch); if(msg) { lock_rw_unlock(&e->lock); @@ -734,26 +833,59 @@ /* see if a DNAME exists. Checked for first, to enforce that DNAMEs * are more important, the CNAME is resynthesized and thus * consistent with the DNAME */ - if( (rrset=find_closest_of_type(env, qname, qnamelen, qclass, now, + if(!no_partial && + (rrset=find_closest_of_type(env, qname, qnamelen, qclass, now, LDNS_RR_TYPE_DNAME, 1))) { /* synthesize a DNAME+CNAME message based on this */ - struct dns_msg* msg = synth_dname_msg(rrset, region, now, &k); + enum sec_status sec_status = sec_status_unchecked; + struct dns_msg* msg = synth_dname_msg(rrset, region, now, &k, + &sec_status); if(msg) { + struct ub_packed_rrset_key* cname_rrset; lock_rw_unlock(&rrset->entry.lock); - return msg; + /* now, after unlocking the DNAME rrset lock, + * check the sec_status, and see if we need to look + * up the CNAME record associated before it can + * be used */ + /* normally, only secure DNAMEs allowed from cache*/ + if(sec_status == sec_status_secure) + return msg; + /* but if we have a CNAME cached with this name, then we + * have previously already allowed this name to pass. + * the next cache lookup is going to fetch that CNAME itself, + * but it is better to have the (unsigned)DNAME + CNAME in + * that case */ + cname_rrset = rrset_cache_lookup( + env->rrset_cache, qname, qnamelen, + LDNS_RR_TYPE_CNAME, qclass, 0, now, 0); + if(cname_rrset) { + /* CNAME already synthesized by + * synth_dname_msg routine, so we can + * straight up return the msg */ + lock_rw_unlock(&cname_rrset->entry.lock); + return msg; + } + } else { + lock_rw_unlock(&rrset->entry.lock); } - lock_rw_unlock(&rrset->entry.lock); } /* see if we have CNAME for this domain, * but not for DS records (which are part of the parent) */ - if( qtype != LDNS_RR_TYPE_DS && + if(!no_partial && qtype != LDNS_RR_TYPE_DS && (rrset=rrset_cache_lookup(env->rrset_cache, qname, qnamelen, LDNS_RR_TYPE_CNAME, qclass, 0, now, 0))) { - struct dns_msg* msg = rrset_msg(rrset, region, now, &k); - if(msg) { - lock_rw_unlock(&rrset->entry.lock); - return msg; + uint8_t* wc = NULL; + size_t wl; + /* if the rrset is not a wildcard expansion, with wcname */ + /* because, if we return that CNAME rrset on its own, it is + * missing the NSEC or NSEC3 proof */ + if(!(val_rrset_wildcard(rrset, &wc, &wl) && wc != NULL)) { + struct dns_msg* msg = rrset_msg(rrset, region, now, &k); + if(msg) { + lock_rw_unlock(&rrset->entry.lock); + return msg; + } } lock_rw_unlock(&rrset->entry.lock); } @@ -790,33 +922,37 @@ * Empty nonterminals are NOERROR, so an NXDOMAIN for foo * means bla.foo also does not exist. The DNSSEC proofs are * the same. We search upwards for NXDOMAINs. */ - if(env->cfg->harden_below_nxdomain) - while(!dname_is_root(k.qname)) { - dname_remove_label(&k.qname, &k.qname_len); - h = query_info_hash(&k, flags); - e = slabhash_lookup(env->msg_cache, h, &k, 0); - if(!e && k.qtype != LDNS_RR_TYPE_NS && - env->cfg->qname_minimisation) { - k.qtype = LDNS_RR_TYPE_NS; + if(env->cfg->harden_below_nxdomain) { + while(!dname_is_root(k.qname)) { + dname_remove_label(&k.qname, &k.qname_len); h = query_info_hash(&k, flags); e = slabhash_lookup(env->msg_cache, h, &k, 0); - } - if(e) { - struct reply_info* data = (struct reply_info*)e->data; - struct dns_msg* msg; - if(FLAGS_GET_RCODE(data->flags) == LDNS_RCODE_NXDOMAIN - && data->security == sec_status_secure - && (msg=tomsg(env, &k, data, region, now, scratch))){ + if(!e && k.qtype != LDNS_RR_TYPE_A && + env->cfg->qname_minimisation) { + k.qtype = LDNS_RR_TYPE_A; + h = query_info_hash(&k, flags); + e = slabhash_lookup(env->msg_cache, h, &k, 0); + } + if(e) { + struct reply_info* data = (struct reply_info*)e->data; + struct dns_msg* msg; + if(FLAGS_GET_RCODE(data->flags) == LDNS_RCODE_NXDOMAIN + && data->security == sec_status_secure + && (data->an_numrrsets == 0 || + ntohs(data->rrsets[0]->rk.type) != LDNS_RR_TYPE_CNAME) + && (msg=tomsg(env, &k, data, region, now, 0, scratch))) { + lock_rw_unlock(&e->lock); + msg->qinfo.qname=qname; + msg->qinfo.qname_len=qnamelen; + /* check that DNSSEC really works out */ + msg->rep->security = sec_status_unchecked; + iter_scrub_nxdomain(msg); + return msg; + } lock_rw_unlock(&e->lock); - msg->qinfo.qname=qname; - msg->qinfo.qname_len=qnamelen; - /* check that DNSSEC really works out */ - msg->rep->security = sec_status_unchecked; - return msg; } - lock_rw_unlock(&e->lock); + k.qtype = qtype; } - k.qtype = qtype; } /* fill common RR types for ANY response to avoid requery */ @@ -827,10 +963,10 @@ return NULL; } -int +int dns_cache_store(struct module_env* env, struct query_info* msgqinf, struct reply_info* msgrep, int is_referral, time_t leeway, int pside, - struct regional* region, uint16_t flags) + struct regional* region, uint32_t flags) { struct reply_info* rep = NULL; /* alloc, malloc properly (not in region, like msg is) */ @@ -837,7 +973,7 @@ rep = reply_info_copy(msgrep, env->alloc, NULL); if(!rep) return 0; - /* ttl must be relative ;i.e. 0..86400 not time(0)+86400. + /* ttl must be relative ;i.e. 0..86400 not time(0)+86400. * the env->now is added to message and RRsets in this routine. */ /* the leeway is used to invalidate other rrsets earlier */ @@ -862,7 +998,7 @@ } else { /* store msg, and rrsets */ struct query_info qinf; - hashvalue_t h; + hashvalue_type h; qinf = *msgqinf; qinf.qname = memdup(msgqinf->qname, msgqinf->qname_len); @@ -875,9 +1011,9 @@ * Not AA from cache. Not CD in cache (depends on client bit). */ rep->flags |= (BIT_RA | BIT_QR); rep->flags &= ~(BIT_AA | BIT_CD); - h = query_info_hash(&qinf, flags); + h = query_info_hash(&qinf, (uint16_t)flags); dns_cache_store_msg(env, &qinf, h, rep, leeway, pside, msgrep, - region); + flags, region); /* qname is used inside query_info_entrysetup, and set to * NULL. If it has not been used, free it. free(0) is safe. */ free(qinf.qname); --- contrib/unbound/services/cache/dns.h.orig +++ contrib/unbound/services/cache/dns.h @@ -49,6 +49,12 @@ struct regional; struct delegpt; +/** Flags to control behavior of dns_cache_store() and dns_cache_store_msg(). + * Must be an unsigned 32-bit value larger than 0xffff */ + +/** Allow caching a DNS message with a zero TTL. */ +#define DNSCACHE_STORE_ZEROTTL 0x100000 + /** * Region allocated message reply */ @@ -80,11 +86,13 @@ * @param region: region to allocate better entries from cache into. * (used when is_referral is false). * @param flags: flags with BIT_CD for AAAA queries in dns64 translation. + * The higher 16 bits are used internally to customize the cache policy. + * (See DNSCACHE_STORE_xxx flags). * @return 0 on alloc error (out of memory). */ int dns_cache_store(struct module_env* env, struct query_info* qinf, struct reply_info* rep, int is_referral, time_t leeway, int pside, - struct regional* region, uint16_t flags); + struct regional* region, uint32_t flags); /** * Store message in the cache. Stores in message cache and rrset cache. @@ -103,11 +111,12 @@ * from the parentside of the zonecut. This means that the type NS * can be updated to full TTL even in prefetch situations. * @param qrep: message that can be altered with better rrs from cache. + * @param flags: customization flags for the cache policy. * @param region: to allocate into for qmsg. */ void dns_cache_store_msg(struct module_env* env, struct query_info* qinfo, - hashvalue_t hash, struct reply_info* rep, time_t leeway, int pside, - struct reply_info* qrep, struct regional* region); + hashvalue_type hash, struct reply_info* rep, time_t leeway, int pside, + struct reply_info* qrep, uint32_t flags, struct regional* region); /** * Find a delegation from the cache. @@ -126,6 +135,23 @@ uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, struct regional* region, struct dns_msg** msg, time_t timenow); +/** + * generate dns_msg from cached message + * @param env: module environment with the DNS cache. NULL if the LRU from cache + * does not need to be touched. + * @param q: query info, contains qname that will make up the dns message. + * @param r: reply info that, together with qname, will make up the dns message. + * @param region: where to allocate dns message. + * @param now: the time now, for check if TTL on cache entry is ok. + * @param allow_expired: if true and serve-expired is enabled, it will allow + * for expired dns_msg to be generated based on the configured serve-expired + * logic. + * @param scratch: where to allocate temporary data. + * */ +struct dns_msg* tomsg(struct module_env* env, struct query_info* q, + struct reply_info* r, struct regional* region, time_t now, + int allow_expired, struct regional* scratch); + /** * Find cached message * @param env: module environment with the DNS cache. @@ -136,6 +162,8 @@ * @param flags: flags with BIT_CD for AAAA queries in dns64 translation. * @param region: where to allocate result. * @param scratch: where to allocate temporary data. + * @param no_partial: if true, only complete messages and not a partial + * one (with only the start of the CNAME chain and not the rest). * @return new response message (alloced in region, rrsets do not have IDs). * or NULL on error or if not found in cache. * TTLs are made relative to the current time. @@ -142,7 +170,8 @@ */ struct dns_msg* dns_cache_lookup(struct module_env* env, uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, - uint16_t flags, struct regional* region, struct regional* scratch); + uint16_t flags, struct regional* region, struct regional* scratch, + int no_partial); /** * find and add A and AAAA records for missing nameservers in delegpt @@ -182,6 +211,18 @@ struct ub_packed_rrset_key* rrset, time_t now); /** + * Add rrset to authority section in unpacked dns_msg message. Must have enough + * space left, does not grow the array. + * @param msg: msg to put it in. + * @param region: region to alloc in + * @param rrset: to add in authority section + * @param now: now. + * @return true if worked, false on fail + */ +int dns_msg_ansadd(struct dns_msg* msg, struct regional* region, + struct ub_packed_rrset_key* rrset, time_t now); + +/** * Adjust the prefetch_ttl for a cached message. This adds a value to the * prefetch ttl - postponing the time when it will be prefetched for future * incoming queries. @@ -194,4 +235,22 @@ int dns_cache_prefetch_adjust(struct module_env* env, struct query_info* qinfo, time_t adjust, uint16_t flags); +/** lookup message in message cache + * the returned nonNULL entry is locked and has to be unlocked by the caller */ +struct msgreply_entry* msg_cache_lookup(struct module_env* env, + uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, + uint16_t flags, time_t now, int wr); + +/** + * Remove entry from the message cache. For unwanted entries. + * @param env: with message cache. + * @param qname: query name, in wireformat + * @param qnamelen: length of qname, including terminating 0. + * @param qtype: query type, host order. + * @param qclass: query class, host order. + * @param flags: flags + */ +void msg_cache_remove(struct module_env* env, uint8_t* qname, size_t qnamelen, + uint16_t qtype, uint16_t qclass, uint16_t flags); + #endif /* SERVICES_CACHE_DNS_H */ --- contrib/unbound/services/cache/infra.c.orig +++ contrib/unbound/services/cache/infra.c @@ -41,6 +41,8 @@ #include "config.h" #include "sldns/rrdef.h" #include "sldns/str2wire.h" +#include "sldns/sbuffer.h" +#include "sldns/wire2str.h" #include "services/cache/infra.h" #include "util/storage/slabhash.h" #include "util/storage/lookup3.h" @@ -61,6 +63,10 @@ /** ratelimit value for delegation point */ int infra_dp_ratelimit = 0; +/** ratelimit value for client ip addresses, + * in queries per second. */ +int infra_ip_ratelimit = 0; + size_t infra_sizefunc(void* k, void* ATTR_UNUSED(d)) { @@ -211,6 +217,18 @@ return 1; } +/** setup domain limits tree (0 on failure) */ +static int +setup_domain_limits(struct infra_cache* infra, struct config_file* cfg) +{ + name_tree_init(&infra->domain_limits); + if(!infra_ratelimit_cfg_insert(infra, cfg)) { + return 0; + } + name_tree_init_parents(&infra->domain_limits); + return 1; +} + struct infra_cache* infra_create(struct config_file* cfg) { @@ -226,29 +244,33 @@ return NULL; } infra->host_ttl = cfg->host_ttl; - name_tree_init(&infra->domain_limits); infra_dp_ratelimit = cfg->ratelimit; - if(cfg->ratelimit != 0) { - infra->domain_rates = slabhash_create(cfg->ratelimit_slabs, - INFRA_HOST_STARTSIZE, cfg->ratelimit_size, - &rate_sizefunc, &rate_compfunc, &rate_delkeyfunc, - &rate_deldatafunc, NULL); - if(!infra->domain_rates) { - infra_delete(infra); - return NULL; - } - /* insert config data into ratelimits */ - if(!infra_ratelimit_cfg_insert(infra, cfg)) { - infra_delete(infra); - return NULL; - } - name_tree_init_parents(&infra->domain_limits); + infra->domain_rates = slabhash_create(cfg->ratelimit_slabs, + INFRA_HOST_STARTSIZE, cfg->ratelimit_size, + &rate_sizefunc, &rate_compfunc, &rate_delkeyfunc, + &rate_deldatafunc, NULL); + if(!infra->domain_rates) { + infra_delete(infra); + return NULL; } + /* insert config data into ratelimits */ + if(!setup_domain_limits(infra, cfg)) { + infra_delete(infra); + return NULL; + } + infra_ip_ratelimit = cfg->ip_ratelimit; + infra->client_ip_rates = slabhash_create(cfg->ip_ratelimit_slabs, + INFRA_HOST_STARTSIZE, cfg->ip_ratelimit_size, &ip_rate_sizefunc, + &ip_rate_compfunc, &ip_rate_delkeyfunc, &ip_rate_deldatafunc, NULL); + if(!infra->client_ip_rates) { + infra_delete(infra); + return NULL; + } return infra; } /** delete domain_limit entries */ -static void domain_limit_free(rbnode_t* n, void* ATTR_UNUSED(arg)) +static void domain_limit_free(rbnode_type* n, void* ATTR_UNUSED(arg)) { if(n) { free(((struct domain_limit_data*)n)->node.name); @@ -264,6 +286,7 @@ slabhash_delete(infra->hosts); slabhash_delete(infra->domain_rates); traverse_postorder(&infra->domain_limits, domain_limit_free, NULL); + slabhash_delete(infra->client_ip_rates); free(infra); } @@ -274,31 +297,54 @@ if(!infra) return infra_create(cfg); infra->host_ttl = cfg->host_ttl; + infra_dp_ratelimit = cfg->ratelimit; + infra_ip_ratelimit = cfg->ip_ratelimit; maxmem = cfg->infra_cache_numhosts * (sizeof(struct infra_key)+ sizeof(struct infra_data)+INFRA_BYTES_NAME); - if(maxmem != slabhash_get_size(infra->hosts) || - cfg->infra_cache_slabs != infra->hosts->size) { + /* divide cachesize by slabs and multiply by slabs, because if the + * cachesize is not an even multiple of slabs, that is the resulting + * size of the slabhash */ + if(!slabhash_is_size(infra->hosts, maxmem, cfg->infra_cache_slabs) || + !slabhash_is_size(infra->domain_rates, cfg->ratelimit_size, + cfg->ratelimit_slabs) || + !slabhash_is_size(infra->client_ip_rates, cfg->ip_ratelimit_size, + cfg->ip_ratelimit_slabs)) { infra_delete(infra); infra = infra_create(cfg); + } else { + /* reapply domain limits */ + traverse_postorder(&infra->domain_limits, domain_limit_free, + NULL); + if(!setup_domain_limits(infra, cfg)) { + infra_delete(infra); + return NULL; + } } return infra; } -/** calculate the hash value for a host key */ -static hashvalue_t -hash_addr(struct sockaddr_storage* addr, socklen_t addrlen) +/** calculate the hash value for a host key + * set use_port to a non-0 number to use the port in + * the hash calculation; 0 to ignore the port.*/ +static hashvalue_type +hash_addr(struct sockaddr_storage* addr, socklen_t addrlen, + int use_port) { - hashvalue_t h = 0xab; + hashvalue_type h = 0xab; /* select the pieces to hash, some OS have changing data inside */ if(addr_is_ip6(addr, addrlen)) { struct sockaddr_in6* in6 = (struct sockaddr_in6*)addr; h = hashlittle(&in6->sin6_family, sizeof(in6->sin6_family), h); - h = hashlittle(&in6->sin6_port, sizeof(in6->sin6_port), h); + if(use_port){ + h = hashlittle(&in6->sin6_port, sizeof(in6->sin6_port), h); + } h = hashlittle(&in6->sin6_addr, INET6_SIZE, h); } else { struct sockaddr_in* in = (struct sockaddr_in*)addr; h = hashlittle(&in->sin_family, sizeof(in->sin_family), h); - h = hashlittle(&in->sin_port, sizeof(in->sin_port), h); + if(use_port){ + h = hashlittle(&in->sin_port, sizeof(in->sin_port), h); + } h = hashlittle(&in->sin_addr, INET_SIZE, h); } return h; @@ -305,10 +351,10 @@ } /** calculate infra hash for a key */ -static hashvalue_t +static hashvalue_type hash_infra(struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* name) { - return dname_query_hash(name, hash_addr(addr, addrlen)); + return dname_query_hash(name, hash_addr(addr, addrlen, 1)); } /** lookup version that does not check host ttl (you check it) */ @@ -726,12 +772,36 @@ return infra_dp_ratelimit; } +size_t ip_rate_sizefunc(void* k, void* ATTR_UNUSED(d)) +{ + struct ip_rate_key* key = (struct ip_rate_key*)k; + return sizeof(*key) + sizeof(struct ip_rate_data) + + lock_get_mem(&key->entry.lock); +} + +int ip_rate_compfunc(void* key1, void* key2) +{ + struct ip_rate_key* k1 = (struct ip_rate_key*)key1; + struct ip_rate_key* k2 = (struct ip_rate_key*)key2; + return sockaddr_cmp_addr(&k1->addr, k1->addrlen, + &k2->addr, k2->addrlen); +} + +void ip_rate_delkeyfunc(void* k, void* ATTR_UNUSED(arg)) +{ + struct ip_rate_key* key = (struct ip_rate_key*)k; + if(!key) + return; + lock_rw_destroy(&key->entry.lock); + free(key); +} + /** find data item in array, for write access, caller unlocks */ static struct lruhash_entry* infra_find_ratedata(struct infra_cache* infra, uint8_t* name, size_t namelen, int wr) { struct rate_key key; - hashvalue_t h = dname_query_hash(name, 0xab); + hashvalue_type h = dname_query_hash(name, 0xab); memset(&key, 0, sizeof(key)); key.name = name; key.namelen = namelen; @@ -739,11 +809,25 @@ return slabhash_lookup(infra->domain_rates, h, &key, wr); } +/** find data item in array for ip addresses */ +static struct lruhash_entry* infra_find_ip_ratedata(struct infra_cache* infra, + struct comm_reply* repinfo, int wr) +{ + struct ip_rate_key key; + hashvalue_type h = hash_addr(&(repinfo->addr), + repinfo->addrlen, 0); + memset(&key, 0, sizeof(key)); + key.addr = repinfo->addr; + key.addrlen = repinfo->addrlen; + key.entry.hash = h; + return slabhash_lookup(infra->client_ip_rates, h, &key, wr); +} + /** create rate data item for name, number 1 in now */ static void infra_create_ratedata(struct infra_cache* infra, uint8_t* name, size_t namelen, time_t timenow) { - hashvalue_t h = dname_query_hash(name, 0xab); + hashvalue_type h = dname_query_hash(name, 0xab); struct rate_key* k = (struct rate_key*)calloc(1, sizeof(*k)); struct rate_data* d = (struct rate_data*)calloc(1, sizeof(*d)); if(!k || !d) { @@ -767,6 +851,30 @@ slabhash_insert(infra->domain_rates, h, &k->entry, d, NULL); } +/** create rate data item for ip address */ +static void infra_ip_create_ratedata(struct infra_cache* infra, + struct comm_reply* repinfo, time_t timenow) +{ + hashvalue_type h = hash_addr(&(repinfo->addr), + repinfo->addrlen, 0); + struct ip_rate_key* k = (struct ip_rate_key*)calloc(1, sizeof(*k)); + struct ip_rate_data* d = (struct ip_rate_data*)calloc(1, sizeof(*d)); + if(!k || !d) { + free(k); + free(d); + return; /* alloc failure */ + } + k->addr = repinfo->addr; + k->addrlen = repinfo->addrlen; + lock_rw_init(&k->entry.lock); + k->entry.hash = h; + k->entry.key = k; + k->entry.data = d; + d->qps[0] = 1; + d->timestamp[0] = timenow; + slabhash_insert(infra->client_ip_rates, h, &k->entry, d, NULL); +} + /** find the second and return its rate counter, if none, remove oldest */ static int* infra_rate_find_second(void* data, time_t t) { @@ -801,7 +909,8 @@ } int infra_ratelimit_inc(struct infra_cache* infra, uint8_t* name, - size_t namelen, time_t timenow) + size_t namelen, time_t timenow, struct query_info* qinfo, + struct comm_reply* replylist) { int lim, max; struct lruhash_entry* entry; @@ -811,6 +920,8 @@ /* find ratelimit */ lim = infra_find_ratelimit(infra, name, namelen); + if(!lim) + return 1; /* disabled for this domain */ /* find or insert ratedata */ entry = infra_find_ratedata(infra, name, namelen, 1); @@ -822,9 +933,19 @@ lock_rw_unlock(&entry->lock); if(premax < lim && max >= lim) { - char buf[257]; + char buf[257], qnm[257], ts[12], cs[12], ip[128]; dname_str(name, buf); - verbose(VERB_OPS, "ratelimit exceeded %s %d", buf, lim); + dname_str(qinfo->qname, qnm); + sldns_wire2str_type_buf(qinfo->qtype, ts, sizeof(ts)); + sldns_wire2str_class_buf(qinfo->qclass, cs, sizeof(cs)); + ip[0]=0; + if(replylist) { + addr_to_str((struct sockaddr_storage *)&replylist->addr, + replylist->addrlen, ip, sizeof(ip)); + verbose(VERB_OPS, "ratelimit exceeded %s %d query %s %s %s from %s", buf, lim, qnm, cs, ts, ip); + } else { + verbose(VERB_OPS, "ratelimit exceeded %s %d query %s %s %s", buf, lim, qnm, cs, ts); + } } return (max < lim); } @@ -859,6 +980,8 @@ /* find ratelimit */ lim = infra_find_ratelimit(infra, name, namelen); + if(!lim) + return 0; /* disabled for this domain */ /* find current rate */ entry = infra_find_ratedata(infra, name, namelen, 0); @@ -875,6 +998,58 @@ { size_t s = sizeof(*infra) + slabhash_get_mem(infra->hosts); if(infra->domain_rates) s += slabhash_get_mem(infra->domain_rates); + if(infra->client_ip_rates) s += slabhash_get_mem(infra->client_ip_rates); /* ignore domain_limits because walk through tree is big */ return s; } + +int infra_ip_ratelimit_inc(struct infra_cache* infra, + struct comm_reply* repinfo, time_t timenow, struct sldns_buffer* buffer) +{ + int max; + struct lruhash_entry* entry; + + /* not enabled */ + if(!infra_ip_ratelimit) { + return 1; + } + /* find or insert ratedata */ + entry = infra_find_ip_ratedata(infra, repinfo, 1); + if(entry) { + int premax = infra_rate_max(entry->data, timenow); + int* cur = infra_rate_find_second(entry->data, timenow); + (*cur)++; + max = infra_rate_max(entry->data, timenow); + lock_rw_unlock(&entry->lock); + + if(premax < infra_ip_ratelimit && max >= infra_ip_ratelimit) { + char client_ip[128], qnm[LDNS_MAX_DOMAINLEN+1+12+12]; + addr_to_str((struct sockaddr_storage *)&repinfo->addr, + repinfo->addrlen, client_ip, sizeof(client_ip)); + qnm[0]=0; + if(sldns_buffer_limit(buffer)>LDNS_HEADER_SIZE && + LDNS_QDCOUNT(sldns_buffer_begin(buffer))!=0) { + (void)sldns_wire2str_rrquestion_buf( + sldns_buffer_at(buffer, LDNS_HEADER_SIZE), + sldns_buffer_limit(buffer)-LDNS_HEADER_SIZE, + qnm, sizeof(qnm)); + if(strlen(qnm)>0 && qnm[strlen(qnm)-1]=='\n') + qnm[strlen(qnm)-1] = 0; /*remove newline*/ + if(strchr(qnm, '\t')) + *strchr(qnm, '\t') = ' '; + if(strchr(qnm, '\t')) + *strchr(qnm, '\t') = ' '; + verbose(VERB_OPS, "ip_ratelimit exceeded %s %d %s", + client_ip, infra_ip_ratelimit, qnm); + } else { + verbose(VERB_OPS, "ip_ratelimit exceeded %s %d (no query name)", + client_ip, infra_ip_ratelimit); + } + } + return (max <= infra_ip_ratelimit); + } + + /* create */ + infra_ip_create_ratedata(infra, repinfo, timenow); + return 1; +} --- contrib/unbound/services/cache/infra.h.orig +++ contrib/unbound/services/cache/infra.h @@ -36,7 +36,10 @@ /** * \file * - * This file contains the infrastructure cache. + * This file contains the infrastructure cache, as well as rate limiting. + * Note that there are two sorts of rate-limiting here: + * - Pre-cache, per-query rate limiting (query ratelimits) + * - Post-cache, per-domain name rate limiting (infra-ratelimits) */ #ifndef SERVICES_CACHE_INFRA_H @@ -44,6 +47,8 @@ #include "util/storage/lruhash.h" #include "util/storage/dnstree.h" #include "util/rtt.h" +#include "util/netevent.h" +#include "util/data/msgreply.h" struct slabhash; struct config_file; @@ -112,7 +117,9 @@ /** hash table with query rates per name: rate_key, rate_data */ struct slabhash* domain_rates; /** ratelimit settings for domains, struct domain_limit_data */ - rbtree_t domain_limits; + rbtree_type domain_limits; + /** hash table with query rates per client ip: ip_rate_key, ip_rate_data */ + struct slabhash* client_ip_rates; }; /** ratelimit, unless overridden by domain_limits, 0 is off */ @@ -142,6 +149,21 @@ size_t namelen; }; +/** ip ratelimit, 0 is off */ +extern int infra_ip_ratelimit; + +/** + * key for ip_ratelimit lookups, a source IP. + */ +struct ip_rate_key { + /** lruhash key entry */ + struct lruhash_entry entry; + /** client ip information */ + struct sockaddr_storage addr; + /** length of address */ + socklen_t addrlen; +}; + /** number of seconds to track qps rate */ #define RATE_WINDOW 2 @@ -160,6 +182,8 @@ time_t timestamp[RATE_WINDOW]; }; +#define ip_rate_data rate_data + /** infra host cache default hash lookup size */ #define INFRA_HOST_STARTSIZE 32 /** bytes per zonename reserved in the hostcache, dnamelen(zonename.com.) */ @@ -342,12 +366,15 @@ * @param name: zone name * @param namelen: zone name length * @param timenow: what time it is now. + * @param qinfo: for logging, query name. + * @param replylist: for logging, querier's address (if any). * @return 1 if it could be incremented. 0 if the increment overshot the * ratelimit or if in the previous second the ratelimit was exceeded. * Failures like alloc failures are not returned (probably as 1). */ int infra_ratelimit_inc(struct infra_cache* infra, uint8_t* name, - size_t namelen, time_t timenow); + size_t namelen, time_t timenow, struct query_info* qinfo, + struct comm_reply* replylist); /** * Decrement the query rate counter for a delegation point. @@ -377,10 +404,22 @@ /** find the maximum rate stored, not too old. 0 if no information. */ int infra_rate_max(void* data, time_t now); -/** find the ratelimit in qps for a domain */ +/** find the ratelimit in qps for a domain. 0 if no limit for domain. */ int infra_find_ratelimit(struct infra_cache* infra, uint8_t* name, size_t namelen); +/** Update query ratelimit hash and decide + * whether or not a query should be dropped. + * @param infra: infra cache + * @param repinfo: information about client + * @param timenow: what time it is now. + * @param buffer: with query for logging. + * @return 1 if it could be incremented. 0 if the increment overshot the + * ratelimit and the query should be dropped. */ +int infra_ip_ratelimit_inc(struct infra_cache* infra, + struct comm_reply* repinfo, time_t timenow, + struct sldns_buffer* buffer); + /** * Get memory used by the infra cache. * @param infra: infrastructure cache. @@ -413,4 +452,16 @@ /** delete data */ void rate_deldatafunc(void* d, void* arg); +/* calculate size for the client ip hashtable */ +size_t ip_rate_sizefunc(void* k, void* d); + +/* compare two addresses */ +int ip_rate_compfunc(void* key1, void* key2); + +/* delete key, and destroy the lock */ +void ip_rate_delkeyfunc(void* d, void* arg); + +/* delete data */ +#define ip_rate_deldatafunc rate_deldatafunc + #endif /* SERVICES_CACHE_INFRA_H */ --- contrib/unbound/services/cache/rrset.c.orig +++ contrib/unbound/services/cache/rrset.c @@ -47,6 +47,7 @@ #include "util/data/msgreply.h" #include "util/regional.h" #include "util/alloc.h" +#include "util/net_help.h" void rrset_markdel(void* key) @@ -80,8 +81,8 @@ struct rrset_cache* rrset_cache_adjust(struct rrset_cache *r, struct config_file* cfg, struct alloc_cache* alloc) { - if(!r || !cfg || cfg->rrset_cache_slabs != r->table.size || - cfg->rrset_cache_size != slabhash_get_size(&r->table)) + if(!r || !cfg || !slabhash_is_size(&r->table, cfg->rrset_cache_size, + cfg->rrset_cache_slabs)) { rrset_cache_delete(r); r = rrset_cache_create(cfg, alloc); @@ -91,7 +92,7 @@ void rrset_cache_touch(struct rrset_cache* r, struct ub_packed_rrset_key* key, - hashvalue_t hash, rrset_id_t id) + hashvalue_type hash, rrset_id_type id) { struct lruhash* table = slabhash_gettable(&r->table, hash); /* @@ -186,7 +187,7 @@ { struct lruhash_entry* e; struct ub_packed_rrset_key* k = ref->key; - hashvalue_t h = k->entry.hash; + hashvalue_type h = k->entry.hash; uint16_t rrset_type = ntohs(k->rk.type); int equal = 0; log_assert(ref->id != 0 && k->id != 0); @@ -237,6 +238,39 @@ return 0; } +void rrset_cache_update_wildcard(struct rrset_cache* rrset_cache, + struct ub_packed_rrset_key* rrset, uint8_t* ce, size_t ce_len, + struct alloc_cache* alloc, time_t timenow) +{ + struct rrset_ref ref; + uint8_t wc_dname[LDNS_MAX_DOMAINLEN+3]; + rrset = packed_rrset_copy_alloc(rrset, alloc, timenow); + if(!rrset) { + log_err("malloc failure in rrset_cache_update_wildcard"); + return; + } + /* ce has at least one label less then qname, we can therefore safely + * add the wildcard label. */ + wc_dname[0] = 1; + wc_dname[1] = (uint8_t)'*'; + memmove(wc_dname+2, ce, ce_len); + + free(rrset->rk.dname); + rrset->rk.dname_len = ce_len + 2; + rrset->rk.dname = (uint8_t*)memdup(wc_dname, rrset->rk.dname_len); + if(!rrset->rk.dname) { + alloc_special_release(alloc, rrset); + log_err("memdup failure in rrset_cache_update_wildcard"); + return; + } + + rrset->entry.hash = rrset_key_hash(&rrset->rk); + ref.key = rrset; + ref.id = rrset->id; + /* ignore ret: if it was in the cache, ref updated */ + (void)rrset_cache_update(rrset_cache, &ref, alloc, timenow); +} + struct ub_packed_rrset_key* rrset_cache_lookup(struct rrset_cache* r, uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, uint32_t flags, time_t timenow, @@ -303,10 +337,10 @@ rrset_array_unlock_touch(struct rrset_cache* r, struct regional* scratch, struct rrset_ref* ref, size_t count) { - hashvalue_t* h; + hashvalue_type* h; size_t i; - if(count > RR_COUNT_MAX || !(h = (hashvalue_t*)regional_alloc(scratch, - sizeof(hashvalue_t)*count))) { + if(count > RR_COUNT_MAX || !(h = (hashvalue_type*)regional_alloc( + scratch, sizeof(hashvalue_type)*count))) { log_warn("rrset LRU: memory allocation failed"); h = NULL; } else /* store hash values */ --- contrib/unbound/services/cache/rrset.h.orig +++ contrib/unbound/services/cache/rrset.h @@ -102,7 +102,7 @@ * @param id: used to check that the item is unchanged and not deleted. */ void rrset_cache_touch(struct rrset_cache* r, struct ub_packed_rrset_key* key, - hashvalue_t hash, rrset_id_t id); + hashvalue_type hash, rrset_id_type id); /** * Update an rrset in the rrset cache. Stores the information for later use. @@ -134,6 +134,24 @@ struct alloc_cache* alloc, time_t timenow); /** + * Update or add an rrset in the rrset cache using a wildcard dname. + * Generates wildcard dname by prepending the wildcard label to the closest + * encloser. Will lookup if the rrset is in the cache and perform an update if + * necessary. + * + * @param rrset_cache: the rrset cache. + * @param rrset: which rrset to cache as wildcard. This rrset is left + * untouched. + * @param ce: the closest encloser, will be uses to generate the wildcard dname. + * @param ce_len: the closest encloser lenght. + * @param alloc: how to allocate (and deallocate) the special rrset key. + * @param timenow: current time (to see if ttl in cache is expired). + */ +void rrset_cache_update_wildcard(struct rrset_cache* rrset_cache, + struct ub_packed_rrset_key* rrset, uint8_t* ce, size_t ce_len, + struct alloc_cache* alloc, time_t timenow); + +/** * Lookup rrset. You obtain read/write lock. You must unlock before lookup * anything of else. * @param r: the rrset cache. --- contrib/unbound/services/listen_dnsport.c.orig +++ contrib/unbound/services/listen_dnsport.c @@ -53,6 +53,9 @@ #include "util/config_file.h" #include "util/net_help.h" #include "sldns/sbuffer.h" +#include "services/mesh.h" +#include "util/fptr_wlist.h" +#include "util/locks.h" #ifdef HAVE_NETDB_H #include @@ -63,9 +66,25 @@ #include #endif +#ifdef HAVE_SYSTEMD +#include +#endif + /** number of queued TCP connections for listen() */ #define TCP_BACKLOG 256 +/** number of simultaneous requests a client can have */ +#define TCP_MAX_REQ_SIMULTANEOUS 32 + +#ifndef THREADS_DISABLED +/** lock on the counter of stream buffer memory */ +static lock_basic_type stream_wait_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; + /** * Debug print of the getaddrinfo returned address. * @param addr: the address returned. @@ -96,14 +115,74 @@ } } +#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 freebind, int use_systemd) { int s; -#if defined(SO_REUSEADDR) || defined(SO_REUSEPORT) || defined(IPV6_USE_MIN_MTU) || defined(IP_TRANSPARENT) || defined(IP_BINDANY) || defined(IP_FREEBIND) +#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 @@ -118,12 +197,22 @@ #ifndef IPV6_V6ONLY (void)v6only; #endif -#if !defined(IP_TRANSPARENT) && !defined(IP_BINDANY) +#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 @@ -144,6 +233,11 @@ *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, @@ -168,6 +262,26 @@ } #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, @@ -184,6 +298,7 @@ /* this option is not essential, we can continue */ *reuseport = 0; } +# endif /* SO_REUSEPORT_LB */ #else (void)reuseport; #endif /* defined(SO_REUSEPORT) */ @@ -202,7 +317,14 @@ log_warn("setsockopt(.. IP%s_BINDANY ..) failed: %s", (family==AF_INET6?"V6":""), strerror(errno)); } -#endif /* IP_TRANSPARENT || IP_BINDANY */ +#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 && @@ -465,7 +587,11 @@ } # endif /* IPv4 MTU */ } - if(bind(s, (struct sockaddr*)addr, addrlen) != 0) { + if( +#ifdef HAVE_SYSTEMD + !got_fd_from_systemd && +#endif + bind(s, (struct sockaddr*)addr, addrlen) != 0) { *noproto = 0; *inuse = 0; #ifndef USE_WINSOCK @@ -474,7 +600,12 @@ /* detect freebsd jail with no ipv6 permission */ if(family==AF_INET6 && errno==EINVAL) *noproto = 1; - else if(errno != EADDRINUSE) { + 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); } @@ -482,13 +613,14 @@ close(s); #else /* USE_WINSOCK */ if(WSAGetLastError() != WSAEADDRINUSE && - WSAGetLastError() != WSAEADDRNOTAVAIL) { + WSAGetLastError() != WSAEADDRNOTAVAIL && + !(WSAGetLastError() == WSAEACCES && verbosity < 4 && !listen)) { log_err_addr("can't bind socket", wsa_strerror(WSAGetLastError()), (struct sockaddr_storage*)addr, addrlen); } closesocket(s); -#endif +#endif /* USE_WINSOCK */ return -1; } if(!fd_set_nonblock(s)) { @@ -506,16 +638,19 @@ int create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto, - int* reuseport, int transparent, int mss, int freebind) + int* reuseport, int transparent, int mss, int freebind, int use_systemd) { int s; -#if defined(SO_REUSEADDR) || defined(SO_REUSEPORT) || defined(IPV6_V6ONLY) || defined(IP_TRANSPARENT) || defined(IP_BINDANY) || defined(IP_FREEBIND) +#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) +#if !defined(IP_TRANSPARENT) && !defined(IP_BINDANY) && !defined(SO_BINDANY) (void)transparent; #endif #if !defined(IP_FREEBIND) @@ -523,6 +658,15 @@ #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) { @@ -560,6 +704,11 @@ 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) { @@ -636,8 +785,19 @@ log_warn("setsockopt(.. IP%s_BINDANY ..) failed: %s", (addr->ai_family==AF_INET6?"V6":""), strerror(errno)); } -#endif /* IP_TRANSPARENT || IP_BINDANY */ - if(bind(s, addr->ai_addr, addr->ai_addrlen) != 0) { +#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 */ + 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) @@ -688,7 +848,19 @@ #endif if ((setsockopt(s, IPPROTO_TCP, TCP_FASTOPEN, &qlen, sizeof(qlen))) == -1 ) { - log_err("Setting TCP Fast Open as server failed: %s", strerror(errno)); +#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; @@ -695,11 +867,21 @@ } int -create_local_accept_sock(const char *path, int* noproto) +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 @@ -720,7 +902,7 @@ /* The socket already exists and cannot be removed */ log_err("Cannot remove old local socket %s (%s)", path, strerror(errno)); - return -1; + goto err; } if (bind(s, (struct sockaddr *)&usock, @@ -727,22 +909,35 @@ (socklen_t)sizeof(struct sockaddr_un)) == -1) { log_err("Cannot bind local socket %s (%s)", path, strerror(errno)); - return -1; + goto err; } if (!fd_set_nonblock(s)) { log_err("Cannot set non-blocking mode"); - return -1; + goto err; } if (listen(s, TCP_BACKLOG) == -1) { log_err("can't listen: %s", strerror(errno)); - return -1; + goto err; } (void)noproto; /*unused*/ return s; + +err: +#ifndef USE_WINSOCK + close(s); #else + closesocket(s); +#endif + return -1; + +#ifdef HAVE_SYSTEMD + } +#endif +#else + (void)use_systemd; (void)path; log_err("Local sockets are not supported"); *noproto = 1; @@ -757,7 +952,7 @@ 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 freebind) + int* reuseport, int transparent, int tcp_mss, int freebind, int use_systemd) { struct addrinfo *res = NULL; int r, s, inuse, noproto; @@ -785,7 +980,7 @@ 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); + reuseport, transparent, freebind, use_systemd); if(s == -1 && inuse) { log_err("bind: address already in use"); } else if(s == -1 && noproto && hints->ai_family == AF_INET6){ @@ -793,7 +988,7 @@ } } else { s = create_tcp_accept_sock(res, v6only, &noproto, reuseport, - transparent, tcp_mss, freebind); + transparent, tcp_mss, freebind, use_systemd); if(s == -1 && noproto && hints->ai_family == AF_INET6){ *noip6 = 1; } @@ -806,7 +1001,7 @@ 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 freebind) + int* reuseport, int transparent, int tcp_mss, int freebind, int use_systemd) { char* s = strchr(ifname, '@'); if(s) { @@ -828,10 +1023,10 @@ (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, freebind); + rcv, snd, reuseport, transparent, tcp_mss, freebind, use_systemd); } return make_sock(stype, ifname, port, hints, v6only, noip6, rcv, snd, - reuseport, transparent, tcp_mss, freebind); + reuseport, transparent, tcp_mss, freebind, use_systemd); } /** @@ -881,7 +1076,7 @@ } # else log_err("no IPV6_RECVPKTINFO and no IPV6_PKTINFO option, please " - "disable interface-automatic in config"); + "disable interface-automatic or do-ip6 in config"); return 0; # endif /* defined IPV6_RECVPKTINFO */ @@ -902,7 +1097,7 @@ } # else log_err("no IP_SENDSRCADDR or IP_PKTINFO option, please disable " - "interface-automatic in config"); + "interface-automatic or do-ip4 in config"); return 0; # endif /* IP_PKTINFO */ @@ -910,6 +1105,26 @@ 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. @@ -923,26 +1138,40 @@ * @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 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 use_systemd: if true, fetch sockets from systemd. + * @param dnscrypt_port: dnscrypt service port number * @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, int* reuseport, int transparent, - int tcp_mss, int freebind) + size_t rcv, size_t snd, int ssl_port, + struct config_strlist* tls_additional_port, int* reuseport, + int transparent, int tcp_mss, int freebind, int use_systemd, + int dnscrypt_port) { int s, noip6=0; +#ifdef USE_DNSCRYPT + int is_dnscrypt = ((strchr(ifname, '@') && + atoi(strchr(ifname, '@')+1) == dnscrypt_port) || + (!strchr(ifname, '@') && atoi(port) == dnscrypt_port)); +#else + int is_dnscrypt = 0; + (void)dnscrypt_port; +#endif + if(!do_udp && !do_tcp) return 0; if(do_auto) { if((s = make_sock_port(SOCK_DGRAM, ifname, port, hints, 1, &noip6, rcv, snd, reuseport, transparent, - tcp_mss, freebind)) == -1) { + tcp_mss, freebind, use_systemd)) == -1) { if(noip6) { log_warn("IPv6 protocol not available"); return 1; @@ -958,7 +1187,8 @@ #endif return 0; } - if(!port_insert(list, s, listen_type_udpancil)) { + if(!port_insert(list, s, + is_dnscrypt?listen_type_udpancil_dnscrypt:listen_type_udpancil)) { #ifndef USE_WINSOCK close(s); #else @@ -970,7 +1200,7 @@ /* regular udp socket */ if((s = make_sock_port(SOCK_DGRAM, ifname, port, hints, 1, &noip6, rcv, snd, reuseport, transparent, - tcp_mss, freebind)) == -1) { + tcp_mss, freebind, use_systemd)) == -1) { if(noip6) { log_warn("IPv6 protocol not available"); return 1; @@ -977,7 +1207,8 @@ } return 0; } - if(!port_insert(list, s, listen_type_udp)) { + if(!port_insert(list, s, + is_dnscrypt?listen_type_udp_dnscrypt:listen_type_udp)) { #ifndef USE_WINSOCK close(s); #else @@ -987,12 +1218,11 @@ } } if(do_tcp) { - int is_ssl = ((strchr(ifname, '@') && - atoi(strchr(ifname, '@')+1) == ssl_port) || - (!strchr(ifname, '@') && atoi(port) == ssl_port)); + int is_ssl = if_is_ssl(ifname, port, ssl_port, + tls_additional_port); if((s = make_sock_port(SOCK_STREAM, ifname, port, hints, 1, &noip6, 0, 0, reuseport, transparent, tcp_mss, - freebind)) == -1) { + freebind, use_systemd)) == -1) { if(noip6) { /*log_warn("IPv6 protocol not available");*/ return 1; @@ -1002,7 +1232,7 @@ if(is_ssl) verbose(VERB_ALGO, "setup TCP for SSL service"); if(!port_insert(list, s, is_ssl?listen_type_ssl: - listen_type_tcp)) { + (is_dnscrypt?listen_type_tcp_dnscrypt:listen_type_tcp))) { #ifndef USE_WINSOCK close(s); #else @@ -1035,8 +1265,9 @@ struct listen_dnsport* listen_create(struct comm_base* base, struct listen_port* ports, - size_t bufsize, int tcp_accept_count, void* sslctx, - struct dt_env* dtenv, comm_point_callback_t* cb, void *cb_arg) + size_t bufsize, int tcp_accept_count, int tcp_idle_timeout, + 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)); @@ -1044,25 +1275,39 @@ 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; } + if(!stream_wait_lock_inited) { + lock_basic_init(&stream_wait_count_lock); + stream_wait_lock_inited = 1; + } /* create comm points as needed */ while(ports) { struct comm_point* cp = NULL; - if(ports->ftype == listen_type_udp) + if(ports->ftype == listen_type_udp || + ports->ftype == listen_type_udp_dnscrypt) cp = comm_point_create_udp(base, ports->fd, front->udp_buff, cb, cb_arg); - else if(ports->ftype == listen_type_tcp) + else if(ports->ftype == listen_type_tcp || + ports->ftype == listen_type_tcp_dnscrypt) cp = comm_point_create_tcp(base, ports->fd, - tcp_accept_count, bufsize, cb, cb_arg); + tcp_accept_count, tcp_idle_timeout, + tcp_conn_limit, bufsize, front->udp_buff, + cb, cb_arg); else if(ports->ftype == listen_type_ssl) { cp = comm_point_create_tcp(base, ports->fd, - tcp_accept_count, bufsize, cb, cb_arg); + tcp_accept_count, tcp_idle_timeout, + tcp_conn_limit, bufsize, front->udp_buff, + cb, cb_arg); cp->ssl = sslctx; - } else if(ports->ftype == listen_type_udpancil) + } else if(ports->ftype == listen_type_udpancil || + ports->ftype == listen_type_udpancil_dnscrypt) cp = comm_point_create_udp_ancil(base, ports->fd, front->udp_buff, cb, cb_arg); if(!cp) { @@ -1072,6 +1317,21 @@ } 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); @@ -1107,8 +1367,18 @@ 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); + if(stream_wait_lock_inited) { + stream_wait_lock_inited = 0; + lock_basic_destroy(&stream_wait_count_lock); + } } struct listen_port* @@ -1148,9 +1418,10 @@ do_auto, cfg->do_udp, do_tcp, &hints, portbuf, &list, cfg->so_rcvbuf, cfg->so_sndbuf, - cfg->ssl_port, reuseport, - cfg->ip_transparent, - cfg->tcp_mss, cfg->ip_freebind)) { + cfg->ssl_port, cfg->tls_additional_port, + reuseport, cfg->ip_transparent, + cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd, + cfg->dnscrypt_port)) { listening_ports_free(list); return NULL; } @@ -1161,9 +1432,10 @@ do_auto, cfg->do_udp, do_tcp, &hints, portbuf, &list, cfg->so_rcvbuf, cfg->so_sndbuf, - cfg->ssl_port, reuseport, - cfg->ip_transparent, - cfg->tcp_mss, cfg->ip_freebind)) { + cfg->ssl_port, cfg->tls_additional_port, + reuseport, cfg->ip_transparent, + cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd, + cfg->dnscrypt_port)) { listening_ports_free(list); return NULL; } @@ -1176,9 +1448,10 @@ if(!ports_create_if(cfg->ifs[i], 0, cfg->do_udp, do_tcp, &hints, portbuf, &list, cfg->so_rcvbuf, cfg->so_sndbuf, - cfg->ssl_port, reuseport, - cfg->ip_transparent, - cfg->tcp_mss, cfg->ip_freebind)) { + cfg->ssl_port, cfg->tls_additional_port, + reuseport, cfg->ip_transparent, + cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd, + cfg->dnscrypt_port)) { listening_ports_free(list); return NULL; } @@ -1189,9 +1462,10 @@ if(!ports_create_if(cfg->ifs[i], 0, cfg->do_udp, do_tcp, &hints, portbuf, &list, cfg->so_rcvbuf, cfg->so_sndbuf, - cfg->ssl_port, reuseport, - cfg->ip_transparent, - cfg->tcp_mss, cfg->ip_freebind)) { + cfg->ssl_port, cfg->tls_additional_port, + reuseport, cfg->ip_transparent, + cfg->tcp_mss, cfg->ip_freebind, cfg->use_systemd, + cfg->dnscrypt_port)) { listening_ports_free(list); return NULL; } @@ -1219,10 +1493,16 @@ 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); - struct listen_list* p; +#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); @@ -1256,3 +1536,373 @@ } } +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->num_open_req + req->num_done_req < TCP_MAX_REQ_SIMULTANEOUS && + !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, + req->cp->tcp_timeout_msec); + } else if(rd) { + req->cp->tcp_is_reading = 1; + comm_point_stop_listening(req->cp); + comm_point_start_listening(req->cp, -1, + req->cp->tcp_timeout_msec); + /* 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, + req->cp->tcp_timeout_msec); + 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, c->tcp_timeout_msec); + 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, + req->cp->tcp_timeout_msec); + 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; +} --- contrib/unbound/services/listen_dnsport.h.orig +++ contrib/unbound/services/listen_dnsport.h @@ -47,6 +47,7 @@ struct config_file; struct addrinfo; struct sldns_buffer; +struct tcl_list; /** * Listening for queries structure. @@ -59,7 +60,9 @@ /** buffer shared by UDP connections, since there is only one datagram at any time. */ struct sldns_buffer* udp_buff; - +#ifdef USE_DNSCRYPT + struct sldns_buffer* dnscrypt_udp_buff; +#endif /** list of comm points used to get incoming events */ struct listen_list* cps; }; @@ -85,7 +88,14 @@ /** udp ipv6 (v4mapped) for use with ancillary data */ listen_type_udpancil, /** ssl over tcp type */ - listen_type_ssl + listen_type_ssl, + /** udp type + dnscrypt*/ + listen_type_udp_dnscrypt, + /** tcp type + dnscrypt */ + listen_type_tcp_dnscrypt, + /** udp ipv6 (v4mapped) for use with ancillary data + dnscrypt*/ + listen_type_udpancil_dnscrypt + }; /** @@ -128,6 +138,8 @@ * @param bufsize: size of datagram buffer. * @param tcp_accept_count: max number of simultaneous TCP connections * from clients. + * @param tcp_idle_timeout: idle timeout for TCP connections in msec. + * @param tcp_conn_limit: TCP connection limit info. * @param sslctx: nonNULL if ssl context. * @param dtenv: nonNULL if dnstap enabled. * @param cb: callback function when a request arrives. It is passed @@ -136,9 +148,10 @@ * @return: the malloced listening structure, ready for use. NULL on error. */ struct listen_dnsport* listen_create(struct comm_base* base, - struct listen_port* ports, size_t bufsize, int tcp_accept_count, - void* sslctx, struct dt_env *dtenv, comm_point_callback_t* cb, - void* cb_arg); + struct listen_port* ports, size_t bufsize, + int tcp_accept_count, int tcp_idle_timeout, + struct tcl_list* tcp_conn_limit, void* sslctx, + struct dt_env *dtenv, comm_point_callback_type* cb, void* cb_arg); /** * delete the listening structure @@ -191,11 +204,12 @@ * listening UDP port. Set to false on return if it failed to do so. * @param transparent: set IP_TRANSPARENT socket option. * @param freebind: set IP_FREEBIND socket option. + * @param use_systemd: if true, fetch sockets from systemd. * @return: the socket. -1 on error. */ 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 snd, int listen, int* reuseport, int transparent, int freebind, int use_systemd); /** * Create and bind TCP listening socket @@ -207,10 +221,11 @@ * @param transparent: set IP_TRANSPARENT socket option. * @param mss: maximum segment size of the socket. if zero, leaves the default. * @param freebind: set IP_FREEBIND socket option. + * @param use_systemd: if true, fetch sockets from systemd. * @return: the socket. -1 on error. */ int create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto, - int* reuseport, int transparent, int mss, int freebind); + int* reuseport, int transparent, int mss, int freebind, int use_systemd); /** * Create and bind local listening socket @@ -217,8 +232,139 @@ * @param path: path to the socket. * @param noproto: on error, this is set true if cause is that local sockets * are not supported. + * @param use_systemd: if true, fetch sockets from systemd. * @return: the socket. -1 on error. */ -int create_local_accept_sock(const char* path, int* noproto); +int create_local_accept_sock(const char* path, int* noproto, int use_systemd); +/** + * TCP request info. List of requests outstanding on the channel, that + * are asked for but not yet answered back. + */ +struct tcp_req_info { + /** the TCP comm point for this. Its buffer is used for read/write */ + struct comm_point* cp; + /** the buffer to use to spool reply from mesh into, + * it can then be copied to the result list and written. + * it is a pointer to the shared udp buffer. */ + struct sldns_buffer* spool_buffer; + /** are we in worker_handle function call (for recursion callback)*/ + int in_worker_handle; + /** is the comm point dropped (by worker handle). + * That means we have to disconnect the channel. */ + int is_drop; + /** is the comm point set to send_reply (by mesh new client in worker + * handle), if so answer is available in c.buffer */ + int is_reply; + /** read channel has closed, just write pending results */ + int read_is_closed; + /** read again */ + int read_again; + /** number of outstanding requests */ + int num_open_req; + /** list of outstanding requests */ + struct tcp_req_open_item* open_req_list; + /** number of pending writeable results */ + int num_done_req; + /** list of pending writable result packets, malloced one at a time */ + struct tcp_req_done_item* done_req_list; +}; + +/** + * List of open items in TCP channel + */ +struct tcp_req_open_item { + /** next in list */ + struct tcp_req_open_item* next; + /** the mesh area of the mesh_state */ + struct mesh_area* mesh; + /** the mesh state */ + struct mesh_state* mesh_state; +}; + +/** + * List of done items in TCP channel + */ +struct tcp_req_done_item { + /** next in list */ + struct tcp_req_done_item* next; + /** the buffer with packet contents */ + uint8_t* buf; + /** length of the buffer */ + size_t len; +}; + +/** + * Create tcp request info structure that keeps track of open + * requests on the TCP channel that are resolved at the same time, + * and the pending results that have to get written back to that client. + * @param spoolbuf: shared buffer + * @return new structure or NULL on alloc failure. + */ +struct tcp_req_info* tcp_req_info_create(struct sldns_buffer* spoolbuf); + +/** + * Delete tcp request structure. Called by owning commpoint. + * Removes mesh entry references and stored results from the lists. + * @param req: the tcp request info + */ +void tcp_req_info_delete(struct tcp_req_info* req); + +/** + * Clear tcp request structure. Removes list entries, sets it up ready + * for the next connection. + * @param req: tcp request info structure. + */ +void tcp_req_info_clear(struct tcp_req_info* req); + +/** + * Remove mesh state entry from list in tcp_req_info. + * caller has to manage the mesh state reply entry in the mesh state. + * @param req: the tcp req info that has the entry removed from the list. + * @param m: the state removed from the list. + */ +void tcp_req_info_remove_mesh_state(struct tcp_req_info* req, + struct mesh_state* m); + +/** + * Handle write done of the last result packet + * @param req: the tcp req info. + */ +void tcp_req_info_handle_writedone(struct tcp_req_info* req); + +/** + * Handle read done of a new request from the client + * @param req: the tcp req info. + */ +void tcp_req_info_handle_readdone(struct tcp_req_info* req); + +/** + * Add mesh state to the tcp req list of open requests. + * So the comm_reply can be removed off the mesh reply list when + * the tcp channel has to be closed (for other reasons then that that + * request was done, eg. channel closed by client or some format error). + * @param req: tcp req info structure. It keeps track of the simultaneous + * requests and results on a tcp (or TLS) channel. + * @param mesh: mesh area for the state. + * @param m: mesh state to add. + * @return 0 on failure (malloc failure). + */ +int tcp_req_info_add_meshstate(struct tcp_req_info* req, + struct mesh_area* mesh, struct mesh_state* m); + +/** + * Send reply on tcp simultaneous answer channel. May queue it up. + * @param req: request info structure. + */ +void tcp_req_info_send_reply(struct tcp_req_info* req); + +/** the read channel has closed + * @param req: request. remaining queries are looked up and answered. + * @return zero if nothing to do, just close the tcp. + */ +int tcp_req_info_handle_read_close(struct tcp_req_info* req); + +/** get the size of currently used tcp stream wait buffers (in bytes) */ +size_t tcp_req_info_get_stream_buffer_size(void); + #endif /* LISTEN_DNSPORT_H */ --- contrib/unbound/services/localzone.c.orig +++ contrib/unbound/services/localzone.c @@ -41,7 +41,6 @@ #include "config.h" #include "services/localzone.h" #include "sldns/str2wire.h" -#include "sldns/sbuffer.h" #include "util/regional.h" #include "util/config_file.h" #include "util/data/dname.h" @@ -53,6 +52,10 @@ #include "util/data/msgparse.h" #include "util/as112.h" +/* maximum RRs in an RRset, to cap possible 'endless' list RRs. + * with 16 bytes for an A record, a 64K packet has about 4000 max */ +#define LOCALZONE_RRSET_COUNT_MAX 4096 + struct local_zones* local_zones_create(void) { @@ -69,7 +72,7 @@ /** helper traverse to delete zones */ static void -lzdel(rbnode_t* n, void* ATTR_UNUSED(arg)) +lzdel(rbnode_type* n, void* ATTR_UNUSED(arg)) { struct local_zone* z = (struct local_zone*)n->key; local_zone_delete(z); @@ -154,13 +157,13 @@ z->namelen = len; z->namelabs = labs; lock_rw_init(&z->lock); - z->region = regional_create(); + z->region = regional_create_custom(sizeof(struct regional)); if(!z->region) { free(z); return NULL; } rbtree_init(&z->data, &local_data_cmp); - lock_protect(&z->lock, &z->parent, sizeof(*z)-sizeof(rbnode_t)); + lock_protect(&z->lock, &z->parent, sizeof(*z)-sizeof(rbnode_type)); /* also the zones->lock protects node, parent, name*, class */ return z; } @@ -181,13 +184,19 @@ lock_rw_wrlock(&zones->lock); lock_rw_wrlock(&z->lock); if(!rbtree_insert(&zones->ztree, &z->node)) { - log_warn("duplicate local-zone"); + struct local_zone* oldz; + char str[256]; + dname_str(nm, str); + log_warn("duplicate local-zone %s", str); lock_rw_unlock(&z->lock); - local_zone_delete(z); + /* save zone name locally before deallocation, + * otherwise, nm is gone if we zone_delete now. */ + oldz = z; /* find the correct zone, so not an error for duplicate */ z = local_zones_find(zones, nm, len, labs, c); lock_rw_wrlock(&z->lock); lock_rw_unlock(&zones->lock); + local_zone_delete(oldz); return z; } lock_rw_unlock(&zones->lock); @@ -220,9 +229,8 @@ return z; } -/** return name and class and rdata of rr; parses string */ -static int -get_rr_content(const char* str, uint8_t** nm, uint16_t* type, +int +rrstr_get_rr_content(const char* str, uint8_t** nm, uint16_t* type, uint16_t* dclass, time_t* ttl, uint8_t* rr, size_t len, uint8_t** rdata, size_t* rdata_len) { @@ -250,7 +258,8 @@ /** return name and class of rr; parses string */ static int -get_rr_nameclass(const char* str, uint8_t** nm, uint16_t* dclass) +get_rr_nameclass(const char* str, uint8_t** nm, uint16_t* dclass, + uint16_t* dtype) { uint8_t rr[LDNS_RR_BUF_SIZE]; size_t len = sizeof(rr), dname_len = 0; @@ -264,6 +273,7 @@ } *nm = memdup(rr, dname_len); *dclass = sldns_wirerr_get_class(rr, len, dname_len); + *dtype = sldns_wirerr_get_type(rr, len, dname_len); if(!*nm) { log_err("out of memory"); return 0; @@ -275,10 +285,12 @@ * Find an rrset in local data structure. * @param data: local data domain name structure. * @param type: type to look for (host order). + * @param alias_ok: 1 if matching a non-exact, alias type such as CNAME is + * allowed. otherwise 0. * @return rrset pointer or NULL if not found. */ static struct local_rrset* -local_data_find_type(struct local_data* data, uint16_t type) +local_data_find_type(struct local_data* data, uint16_t type, int alias_ok) { struct local_rrset* p; type = htons(type); @@ -285,6 +297,8 @@ 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; } @@ -340,9 +354,9 @@ } /** insert RR into RRset data structure; Wastes a couple of bytes */ -static int -insert_rr(struct regional* region, struct packed_rrset_data* pd, - uint8_t* rdata, size_t rdata_len, time_t ttl) +int +rrset_insert_rr(struct regional* region, struct packed_rrset_data* pd, + uint8_t* rdata, size_t rdata_len, time_t ttl, const char* rrstr) { size_t* oldlen = pd->rr_len; time_t* oldttl = pd->rr_ttl; @@ -349,6 +363,11 @@ uint8_t** olddata = pd->rr_data; /* add RR to rrset */ + if(pd->count > LOCALZONE_RRSET_COUNT_MAX) { + log_warn("RRset '%s' has more than %d records, record ignored", + rrstr, LOCALZONE_RRSET_COUNT_MAX); + return 1; + } pd->count++; pd->rr_len = regional_alloc(region, sizeof(*pd->rr_len)*pd->count); pd->rr_ttl = regional_alloc(region, sizeof(*pd->rr_ttl)*pd->count); @@ -375,10 +394,31 @@ return 1; } -/** find a data node by exact name */ -static struct local_data* -lz_find_node(struct local_zone* z, uint8_t* nm, size_t nmlen, int nmlabs) +/** Delete RR from local-zone RRset, wastes memory as the deleted RRs cannot be + * free'd (regionally alloc'd) */ +int +local_rrset_remove_rr(struct packed_rrset_data* pd, size_t index) { + log_assert(pd->count > 0); + if(index >= pd->count) { + log_warn("Trying to remove RR with out of bound index"); + return 0; + } + if(index + 1 < pd->count) { + /* not removing last element */ + size_t nexti = index + 1; + size_t num = pd->count - nexti; + memmove(pd->rr_len+index, pd->rr_len+nexti, sizeof(*pd->rr_len)*num); + memmove(pd->rr_ttl+index, pd->rr_ttl+nexti, sizeof(*pd->rr_ttl)*num); + memmove(pd->rr_data+index, pd->rr_data+nexti, sizeof(*pd->rr_data)*num); + } + pd->count--; + return 1; +} + +struct local_data* +local_zone_find_data(struct local_zone* z, uint8_t* nm, size_t nmlen, int nmlabs) +{ struct local_data key; key.node.key = &key; key.name = nm; @@ -392,7 +432,7 @@ lz_find_create_node(struct local_zone* z, uint8_t* nm, size_t nmlen, int nmlabs, struct local_data** res) { - struct local_data* ld = lz_find_node(z, nm, nmlen, nmlabs); + struct local_data* ld = local_zone_find_data(z, nm, nmlen, nmlabs); if(!ld) { /* create a domain name to store rr. */ ld = (struct local_data*)regional_alloc_zero(z->region, @@ -423,43 +463,38 @@ return 1; } -/** enter data RR into auth zone */ -static int -lz_enter_rr_into_zone(struct local_zone* z, const char* rrstr) +int +local_zone_enter_rr(struct local_zone* z, uint8_t* nm, size_t nmlen, + int nmlabs, uint16_t rrtype, uint16_t rrclass, time_t ttl, + uint8_t* rdata, size_t rdata_len, const char* rrstr) { - uint8_t* nm; - size_t nmlen; - int nmlabs; struct local_data* node; struct local_rrset* rrset; struct packed_rrset_data* pd; - uint16_t rrtype = 0, rrclass = 0; - time_t ttl = 0; - uint8_t rr[LDNS_RR_BUF_SIZE]; - uint8_t* rdata; - size_t rdata_len; - if(!get_rr_content(rrstr, &nm, &rrtype, &rrclass, &ttl, rr, sizeof(rr), - &rdata, &rdata_len)) { - log_err("bad local-data: %s", rrstr); - return 0; - } - log_assert(z->dclass == rrclass); - if(z->type == local_zone_redirect && - query_dname_compare(z->name, nm) != 0) { - log_err("local-data in redirect zone must reside at top of zone" - ", not at %s", rrstr); - free(nm); - return 0; - } - nmlabs = dname_count_size_labels(nm, &nmlen); + if(!lz_find_create_node(z, nm, nmlen, nmlabs, &node)) { - free(nm); return 0; } log_assert(node); - free(nm); - rrset = local_data_find_type(node, rrtype); + /* Reject it if we would end up having CNAME and other data (including + * another CNAME) for a redirect zone. */ + if((z->type == local_zone_redirect || + z->type == local_zone_inform_redirect) && node->rrsets) { + const char* othertype = NULL; + if (rrtype == LDNS_RR_TYPE_CNAME) + othertype = "other"; + else if (node->rrsets->rrset->rk.type == + htons(LDNS_RR_TYPE_CNAME)) { + othertype = "CNAME"; + } + if(othertype) { + log_err("local-data '%s' in redirect zone must not " + "coexist with %s local-data", rrstr, othertype); + return 0; + } + } + rrset = local_data_find_type(node, rrtype, 0); if(!rrset) { rrset = new_local_rrset(z->region, node, rrtype, rrclass); if(!rrset) @@ -479,26 +514,59 @@ verbose(VERB_ALGO, "ignoring duplicate RR: %s", rrstr); return 1; } - return insert_rr(z->region, pd, rdata, rdata_len, ttl); + return rrset_insert_rr(z->region, pd, rdata, rdata_len, ttl, rrstr); } +/** enter data RR into auth zone */ +int +lz_enter_rr_into_zone(struct local_zone* z, const char* rrstr) +{ + uint8_t* nm; + size_t nmlen; + int nmlabs, ret; + uint16_t rrtype = 0, rrclass = 0; + time_t ttl = 0; + uint8_t rr[LDNS_RR_BUF_SIZE]; + uint8_t* rdata; + size_t rdata_len; + if(!rrstr_get_rr_content(rrstr, &nm, &rrtype, &rrclass, &ttl, rr, + sizeof(rr), &rdata, &rdata_len)) { + log_err("bad local-data: %s", rrstr); + return 0; + } + log_assert(z->dclass == rrclass); + if((z->type == local_zone_redirect || + z->type == local_zone_inform_redirect) && + query_dname_compare(z->name, nm) != 0) { + log_err("local-data in redirect zone must reside at top of zone" + ", not at %s", rrstr); + free(nm); + return 0; + } + nmlabs = dname_count_size_labels(nm, &nmlen); + ret = local_zone_enter_rr(z, nm, nmlen, nmlabs, rrtype, rrclass, ttl, + rdata, rdata_len, rrstr); + free(nm); + return ret; +} + /** enter a data RR into auth data; a zone for it must exist */ static int lz_enter_rr_str(struct local_zones* zones, const char* rr) { uint8_t* rr_name; - uint16_t rr_class; + uint16_t rr_class, rr_type; size_t len; int labs; struct local_zone* z; int r; - if(!get_rr_nameclass(rr, &rr_name, &rr_class)) { + if(!get_rr_nameclass(rr, &rr_name, &rr_class, &rr_type)) { log_err("bad rr %s", rr); return 0; } labs = dname_count_size_labels(rr_name, &len); lock_rw_rdlock(&zones->lock); - z = local_zones_lookup(zones, rr_name, len, labs, rr_class); + z = local_zones_lookup(zones, rr_name, len, labs, rr_class, rr_type); if(!z) { lock_rw_unlock(&zones->lock); fatal_exit("internal error: no zone for rr %s", rr); @@ -595,7 +663,7 @@ /* create netblock addr_tree if not present yet */ if(!z->override_tree) { - z->override_tree = (struct rbtree_t*)regional_alloc_zero( + z->override_tree = (struct rbtree_type*)regional_alloc_zero( z->region, sizeof(*z->override_tree)); if(!z->override_tree) { lock_rw_unlock(&z->lock); @@ -684,9 +752,9 @@ return 0; } -/** enter AS112 default zone */ +/** enter (AS112) empty default zone */ static int -add_as112_default(struct local_zones* zones, struct config_file* cfg, +add_empty_default(struct local_zones* zones, struct config_file* cfg, const char* name) { struct local_zone* z; @@ -711,12 +779,15 @@ } /** enter default zones */ -static int -lz_enter_defaults(struct local_zones* zones, struct config_file* cfg) +int local_zone_enter_defaults(struct local_zones* zones, struct config_file* cfg) { struct local_zone* z; const char** zstr; + /* Do not add any default */ + if(cfg->local_zones_disable_default) + return 1; + /* this list of zones is from RFC 6303 and RFC 7686 */ /* block localhost level zones first, then onion and later the LAN zones */ @@ -724,7 +795,7 @@ /* localhost. zone */ if(!lz_exists(zones, "localhost.") && !lz_nodefault(cfg, "localhost.")) { - if(!(z=lz_enter_zone(zones, "localhost.", "static", + if(!(z=lz_enter_zone(zones, "localhost.", "redirect", LDNS_RR_CLASS_IN)) || !lz_enter_rr_into_zone(z, "localhost. 10800 IN NS localhost.") || @@ -778,26 +849,24 @@ lock_rw_unlock(&z->lock); } /* onion. zone (RFC 7686) */ - if(!lz_exists(zones, "onion.") && - !lz_nodefault(cfg, "onion.")) { - if(!(z=lz_enter_zone(zones, "onion.", "static", - LDNS_RR_CLASS_IN)) || - !lz_enter_rr_into_zone(z, - "onion. 10800 IN NS localhost.") || - !lz_enter_rr_into_zone(z, - "onion. 10800 IN SOA localhost. nobody.invalid. " - "1 3600 1200 604800 10800")) { - log_err("out of memory adding default zone"); - if(z) { lock_rw_unlock(&z->lock); } - return 0; - } - lock_rw_unlock(&z->lock); + if(!add_empty_default(zones, cfg, "onion.")) { + log_err("out of memory adding default zone"); + return 0; } - + /* test. zone (RFC 6761) */ + if(!add_empty_default(zones, cfg, "test.")) { + log_err("out of memory adding default zone"); + return 0; + } + /* invalid. zone (RFC 6761) */ + if(!add_empty_default(zones, cfg, "invalid.")) { + log_err("out of memory adding default zone"); + return 0; + } /* block AS112 zones, unless asked not to */ if(!cfg->unblock_lan_zones) { for(zstr = as112_zones; *zstr; zstr++) { - if(!add_as112_default(zones, cfg, *zstr)) { + if(!add_empty_default(zones, cfg, *zstr)) { log_err("out of memory adding default zone"); return 0; } @@ -875,16 +944,17 @@ init_parents(zones); /* to enable local_zones_lookup() */ for(p = cfg->local_data; p; p = p->next) { uint8_t* rr_name; - uint16_t rr_class; + uint16_t rr_class, rr_type; size_t len; int labs; - if(!get_rr_nameclass(p->str, &rr_name, &rr_class)) { + if(!get_rr_nameclass(p->str, &rr_name, &rr_class, &rr_type)) { log_err("Bad local-data RR %s", p->str); return 0; } labs = dname_count_size_labels(rr_name, &len); lock_rw_rdlock(&zones->lock); - if(!local_zones_lookup(zones, rr_name, len, labs, rr_class)) { + if(!local_zones_lookup(zones, rr_name, len, labs, rr_class, + rr_type)) { if(!have_name) { dclass = rr_class; nm = rr_name; @@ -986,7 +1056,7 @@ return 0; } /* apply default zones+content (unless disabled, or overridden) */ - if(!lz_enter_defaults(zones, cfg)) { + if(!local_zone_enter_defaults(zones, cfg)) { return 0; } /* enter local zone overrides */ @@ -1015,21 +1085,26 @@ struct local_zone* local_zones_lookup(struct local_zones* zones, - uint8_t* name, size_t len, int labs, uint16_t dclass) + uint8_t* name, size_t len, int labs, uint16_t dclass, uint16_t dtype) { return local_zones_tags_lookup(zones, name, len, labs, - dclass, NULL, 0, 1); + dclass, dtype, NULL, 0, 1); } struct local_zone* local_zones_tags_lookup(struct local_zones* zones, - uint8_t* name, size_t len, int labs, uint16_t dclass, + uint8_t* name, size_t len, int labs, uint16_t dclass, uint16_t dtype, uint8_t* taglist, size_t taglen, int ignoretags) { - rbnode_t* res = NULL; + rbnode_type* res = NULL; struct local_zone *result; struct local_zone key; int m; + /* for type DS use a zone higher when on a zonecut */ + if(dtype == LDNS_RR_TYPE_DS && !dname_is_root(name)) { + dname_remove_label(&name, &len); + labs--; + } key.node.key = &key; key.dclass = dclass; key.name = name; @@ -1068,6 +1143,22 @@ return (struct local_zone*)rbtree_search(&zones->ztree, &key); } +struct local_zone* +local_zones_find_le(struct local_zones* zones, + uint8_t* name, size_t len, int labs, uint16_t dclass, + int* exact) +{ + struct local_zone key; + rbnode_type *node; + key.node.key = &key; + key.dclass = dclass; + key.name = name; + key.namelen = len; + key.namelabs = labs; + *exact = rbtree_find_less_equal(&zones->ztree, &key, &node); + return (struct local_zone*)node; +} + /** print all RRsets in local zone */ static void local_zone_out(struct local_zone* z) @@ -1076,7 +1167,7 @@ struct local_rrset* p; RBTREE_FOR(d, struct local_data*, &z->data) { for(p = d->rrsets; p; p = p->next) { - log_nametypeclass(0, "rrset", d->name, + log_nametypeclass(NO_VERBOSE, "rrset", d->name, ntohs(p->rrset->rk.type), ntohs(p->rrset->rk.rrset_class)); } @@ -1089,57 +1180,11 @@ lock_rw_rdlock(&zones->lock); log_info("number of auth zones %u", (unsigned)zones->ztree.count); RBTREE_FOR(z, struct local_zone*, &zones->ztree) { + char buf[64]; lock_rw_rdlock(&z->lock); - switch(z->type) { - case local_zone_deny: - log_nametypeclass(0, "deny zone", - z->name, 0, z->dclass); - break; - case local_zone_refuse: - log_nametypeclass(0, "refuse zone", - z->name, 0, z->dclass); - break; - case local_zone_redirect: - log_nametypeclass(0, "redirect zone", - z->name, 0, z->dclass); - break; - case local_zone_transparent: - log_nametypeclass(0, "transparent zone", - z->name, 0, z->dclass); - break; - case local_zone_typetransparent: - log_nametypeclass(0, "typetransparent zone", - z->name, 0, z->dclass); - break; - case local_zone_static: - log_nametypeclass(0, "static zone", - z->name, 0, z->dclass); - break; - case local_zone_inform: - log_nametypeclass(0, "inform zone", - z->name, 0, z->dclass); - break; - case local_zone_inform_deny: - log_nametypeclass(0, "inform_deny zone", - z->name, 0, z->dclass); - break; - case local_zone_always_transparent: - log_nametypeclass(0, "always_transparent zone", - z->name, 0, z->dclass); - break; - case local_zone_always_refuse: - log_nametypeclass(0, "always_refuse zone", - z->name, 0, z->dclass); - break; - case local_zone_always_nxdomain: - log_nametypeclass(0, "always_nxdomain zone", - z->name, 0, z->dclass); - break; - default: - log_nametypeclass(0, "badtyped zone", - z->name, 0, z->dclass); - break; - } + snprintf(buf, sizeof(buf), "%s zone", + local_zone_type2str(z->type)); + log_nametypeclass(NO_VERBOSE, buf, z->name, 0, z->dclass); local_zone_out(z); lock_rw_unlock(&z->lock); } @@ -1148,9 +1193,10 @@ /** encode answer consisting of 1 rrset */ static int -local_encode(struct query_info* qinfo, struct edns_data* edns, - sldns_buffer* buf, struct regional* temp, - struct ub_packed_rrset_key* rrset, int ansec, int rcode) +local_encode(struct query_info* qinfo, struct module_env* env, + 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 reply_info rep; uint16_t udpsize; @@ -1168,23 +1214,40 @@ edns->udp_size = EDNS_ADVERTISED_SIZE; edns->ext_rcode = 0; edns->bits &= EDNS_DO; - if(!edns_opt_inplace_reply(edns, temp) || - !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)) + if(!inplace_cb_reply_local_call(env, qinfo, NULL, &rep, rcode, edns, + repinfo, temp) || !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); + sldns_buffer_read_u16_at(buf, 2), edns); + } return 1; } +/** encode local error answer */ +static void +local_error_encode(struct query_info* qinfo, struct module_env* env, + struct edns_data* edns, struct comm_reply* repinfo, sldns_buffer* buf, + struct regional* temp, int rcode, int r) +{ + 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)) + edns->opt_list = NULL; + error_encode(buf, r, qinfo, *(uint16_t*)sldns_buffer_begin(buf), + sldns_buffer_read_u16_at(buf, 2), edns); +} + /** find local data tag string match for the given type in the list */ -static int -find_tag_datas(struct query_info* qinfo, struct config_strlist* list, - struct ub_packed_rrset_key* r, struct regional* temp, - uint8_t* zname, size_t zlen) +int +local_data_find_tag_datas(const struct query_info* qinfo, + struct config_strlist* list, struct ub_packed_rrset_key* r, + struct regional* temp) { struct config_strlist* p; char buf[65536]; @@ -1193,11 +1256,13 @@ int res; struct packed_rrset_data* d; for(p=list; p; p=p->next) { + uint16_t rdr_type; + len = sizeof(rr); /* does this element match the type? */ snprintf(buf, sizeof(buf), ". %s", p->str); res = sldns_str2wire_rr_buf(buf, rr, &len, NULL, 3600, - zname, zlen, NULL, 0); + NULL, 0, NULL, 0); if(res != 0) /* parse errors are already checked before, in * acllist check_data, skip this for robustness */ @@ -1204,7 +1269,8 @@ continue; if(len < 1 /* . */ + 8 /* typeclassttl*/ + 2 /*rdatalen*/) continue; - if(sldns_wirerr_get_type(rr, len, 1) != qinfo->qtype) + rdr_type = sldns_wirerr_get_type(rr, len, 1); + if(rdr_type != qinfo->qtype && rdr_type != LDNS_RR_TYPE_CNAME) continue; /* do we have entries already? if not setup key */ @@ -1212,7 +1278,7 @@ r->entry.key = r; r->rk.dname = qinfo->qname; r->rk.dname_len = qinfo->qname_len; - r->rk.type = htons(qinfo->qtype); + r->rk.type = htons(rdr_type); r->rk.rrset_class = htons(qinfo->qclass); r->rk.flags = 0; d = (struct packed_rrset_data*)regional_alloc_zero( @@ -1258,7 +1324,7 @@ sldns_wirerr_get_rdatawl(rr, len, 1), d->rr_len[d->count]); if(!d->rr_data[d->count]) - if(!d) return 0; /* out of memory */ + return 0; /* out of memory */ d->count++; } if(r->rk.dname) @@ -1266,14 +1332,37 @@ return 0; } -/** answer local data match */ static int -local_data_answer(struct local_zone* z, struct query_info* qinfo, - struct edns_data* edns, sldns_buffer* buf, struct regional* temp, - int labs, struct local_data** ldp, enum localzone_type lz_type, - int tag, struct config_strlist** tag_datas, size_t tag_datas_size, - char** tagname, int num_tags) +find_tag_datas(struct query_info* qinfo, struct config_strlist* list, + struct ub_packed_rrset_key* r, struct regional* temp) { + int result = local_data_find_tag_datas(qinfo, list, r, temp); + + /* If we've found a non-exact alias type of local data, make a shallow + * copy of the RRset and remember it in qinfo to complete the alias + * chain later. */ + if(result && qinfo->qtype != LDNS_RR_TYPE_CNAME && + r->rk.type == htons(LDNS_RR_TYPE_CNAME)) { + qinfo->local_alias = + regional_alloc_zero(temp, sizeof(struct local_rrset)); + if(!qinfo->local_alias) + return 0; /* out of memory */ + qinfo->local_alias->rrset = + regional_alloc_init(temp, r, sizeof(*r)); + if(!qinfo->local_alias->rrset) + return 0; /* out of memory */ + } + return result; +} + +int +local_data_answer(struct local_zone* z, struct module_env* env, + struct query_info* qinfo, struct edns_data* edns, + struct comm_reply* repinfo, sldns_buffer* buf, + struct regional* temp, int labs, struct local_data** ldp, + enum localzone_type lz_type, int tag, struct config_strlist** tag_datas, + size_t tag_datas_size, char** tagname, int num_tags) +{ struct local_data key; struct local_data* ld; struct local_rrset* lr; @@ -1281,7 +1370,8 @@ key.name = qinfo->qname; key.namelen = qinfo->qname_len; key.namelabs = labs; - if(lz_type == local_zone_redirect) { + if(lz_type == local_zone_redirect || + lz_type == local_zone_inform_redirect) { key.name = z->name; key.namelen = z->namelen; key.namelabs = z->namelabs; @@ -1288,11 +1378,17 @@ if(tag != -1 && (size_t)tagname, z->namelen)) { + if(find_tag_datas(qinfo, tag_datas[tag], &r, temp)) { verbose(VERB_ALGO, "redirect with tag data [%d] %s", tag, (taglocal_alias) + return 1; + return local_encode(qinfo, env, edns, repinfo, buf, temp, &r, 1, LDNS_RCODE_NOERROR); } } @@ -1302,38 +1398,131 @@ if(!ld) { return 0; } - lr = local_data_find_type(ld, qinfo->qtype); + lr = local_data_find_type(ld, qinfo->qtype, 1); if(!lr) return 0; - if(lz_type == local_zone_redirect) { + + /* Special case for alias matching. See local_data_answer(). */ + if((lz_type == local_zone_redirect || + lz_type == local_zone_inform_redirect) && + qinfo->qtype != LDNS_RR_TYPE_CNAME && + lr->rrset->rk.type == htons(LDNS_RR_TYPE_CNAME)) { + uint8_t* ctarget; + size_t ctargetlen = 0; + + qinfo->local_alias = + regional_alloc_zero(temp, sizeof(struct local_rrset)); + if(!qinfo->local_alias) + return 0; /* out of memory */ + qinfo->local_alias->rrset = regional_alloc_init( + temp, lr->rrset, sizeof(*lr->rrset)); + if(!qinfo->local_alias->rrset) + return 0; /* out of memory */ + qinfo->local_alias->rrset->rk.dname = qinfo->qname; + qinfo->local_alias->rrset->rk.dname_len = qinfo->qname_len; + get_cname_target(lr->rrset, &ctarget, &ctargetlen); + if(!ctargetlen) + return 0; /* invalid cname */ + if(dname_is_wild(ctarget)) { + /* synthesize cname target */ + struct packed_rrset_data* d; + /* -3 for wildcard label and root label from qname */ + size_t newtargetlen = qinfo->qname_len + ctargetlen - 3; + + log_assert(ctargetlen >= 3); + log_assert(qinfo->qname_len >= 1); + + if(newtargetlen > LDNS_MAX_DOMAINLEN) { + qinfo->local_alias = NULL; + local_error_encode(qinfo, env, edns, repinfo, + buf, temp, LDNS_RCODE_YXDOMAIN, + (LDNS_RCODE_YXDOMAIN|BIT_AA)); + return 1; + } + memset(&qinfo->local_alias->rrset->entry, 0, + sizeof(qinfo->local_alias->rrset->entry)); + qinfo->local_alias->rrset->entry.key = + qinfo->local_alias->rrset; + qinfo->local_alias->rrset->entry.hash = + rrset_key_hash(&qinfo->local_alias->rrset->rk); + d = (struct packed_rrset_data*)regional_alloc_zero(temp, + sizeof(struct packed_rrset_data) + sizeof(size_t) + + sizeof(uint8_t*) + sizeof(time_t) + sizeof(uint16_t) + + newtargetlen); + if(!d) + return 0; /* out of memory */ + qinfo->local_alias->rrset->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] = newtargetlen + sizeof(uint16_t); + packed_rrset_ptr_fixup(d); + d->rr_ttl[0] = d->ttl; + sldns_write_uint16(d->rr_data[0], newtargetlen); + /* write qname */ + memmove(d->rr_data[0] + sizeof(uint16_t), qinfo->qname, + qinfo->qname_len - 1); + /* write cname target wilcard wildcard label */ + memmove(d->rr_data[0] + sizeof(uint16_t) + + qinfo->qname_len - 1, ctarget + 2, + ctargetlen - 2); + } + return 1; + } + if(lz_type == local_zone_redirect || + lz_type == local_zone_inform_redirect) { /* convert rrset name to query name; like a wildcard */ struct ub_packed_rrset_key r = *lr->rrset; r.rk.dname = qinfo->qname; r.rk.dname_len = qinfo->qname_len; - return local_encode(qinfo, edns, buf, temp, &r, 1, + return local_encode(qinfo, env, edns, repinfo, buf, temp, &r, 1, LDNS_RCODE_NOERROR); } - return local_encode(qinfo, edns, buf, temp, lr->rrset, 1, + return local_encode(qinfo, env, edns, repinfo, buf, temp, lr->rrset, 1, LDNS_RCODE_NOERROR); } -/** - * answer in case where no exact match is found - * @param z: zone for query - * @param qinfo: query - * @param edns: edns from query - * @param buf: buffer for answer. - * @param temp: temp region for encoding - * @param ld: local data, if NULL, no such name exists in localdata. - * @param lz_type: type of the local zone - * @return 1 if a reply is to be sent, 0 if not. - */ +/** + * See if the local zone does not cover the name, eg. the name is not + * in the zone and the zone is transparent */ static int -lz_zone_answer(struct local_zone* z, struct query_info* qinfo, - struct edns_data* edns, sldns_buffer* buf, struct regional* temp, +local_zone_does_not_cover(struct local_zone* z, struct query_info* qinfo, + int labs) +{ + struct local_data key; + struct local_data* ld = NULL; + struct local_rrset* lr = NULL; + if(z->type == local_zone_always_transparent) + return 1; + if(z->type != local_zone_transparent + && z->type != local_zone_typetransparent + && z->type != local_zone_inform) + return 0; + key.node.key = &key; + key.name = qinfo->qname; + key.namelen = qinfo->qname_len; + key.namelabs = labs; + ld = (struct local_data*)rbtree_search(&z->data, &key.node); + if(z->type == local_zone_transparent || z->type == local_zone_inform) + return (ld == NULL); + if(ld) + lr = local_data_find_type(ld, qinfo->qtype, 1); + /* local_zone_typetransparent */ + return (lr == NULL); +} + +int +local_zones_zone_answer(struct local_zone* z, struct module_env* env, + struct query_info* qinfo, struct edns_data* edns, + struct comm_reply* repinfo, sldns_buffer* buf, struct regional* temp, struct local_data* ld, enum localzone_type lz_type) { - if(lz_type == local_zone_deny || lz_type == local_zone_inform_deny) { + if(lz_type == local_zone_deny || + lz_type == local_zone_always_deny || + lz_type == local_zone_inform_deny) { /** no reply at all, signal caller by clearing buffer. */ sldns_buffer_clear(buf); sldns_buffer_flip(buf); @@ -1340,13 +1529,14 @@ return 1; } else if(lz_type == local_zone_refuse || lz_type == local_zone_always_refuse) { - error_encode(buf, (LDNS_RCODE_REFUSED|BIT_AA), qinfo, - *(uint16_t*)sldns_buffer_begin(buf), - sldns_buffer_read_u16_at(buf, 2), edns); + local_error_encode(qinfo, env, edns, repinfo, buf, temp, + LDNS_RCODE_REFUSED, (LDNS_RCODE_REFUSED|BIT_AA)); return 1; } else if(lz_type == local_zone_static || lz_type == local_zone_redirect || - lz_type == local_zone_always_nxdomain) { + lz_type == local_zone_inform_redirect || + lz_type == local_zone_always_nxdomain || + lz_type == local_zone_always_nodata) { /* for static, reply nodata or nxdomain * for redirect, reply nodata */ /* no additional section processing, @@ -1354,14 +1544,15 @@ * or using closest match for NSEC. * or using closest match for returning delegation downwards */ - int rcode = (ld || lz_type == local_zone_redirect)? + int rcode = (ld || lz_type == local_zone_redirect || + lz_type == local_zone_inform_redirect || + lz_type == local_zone_always_nodata)? LDNS_RCODE_NOERROR:LDNS_RCODE_NXDOMAIN; if(z->soa) - return local_encode(qinfo, edns, buf, temp, + return local_encode(qinfo, env, edns, repinfo, buf, temp, z->soa, 0, rcode); - error_encode(buf, (rcode|BIT_AA), qinfo, - *(uint16_t*)sldns_buffer_begin(buf), - sldns_buffer_read_u16_at(buf, 2), edns); + local_error_encode(qinfo, env, edns, repinfo, buf, temp, rcode, + (rcode|BIT_AA)); return 1; } else if(lz_type == local_zone_typetransparent || lz_type == local_zone_always_transparent) { @@ -1375,11 +1566,10 @@ if(ld && ld->rrsets) { int rcode = LDNS_RCODE_NOERROR; if(z->soa) - return local_encode(qinfo, edns, buf, temp, + return local_encode(qinfo, env, edns, repinfo, buf, temp, z->soa, 0, rcode); - error_encode(buf, (rcode|BIT_AA), qinfo, - *(uint16_t*)sldns_buffer_begin(buf), - sldns_buffer_read_u16_at(buf, 2), edns); + local_error_encode(qinfo, env, edns, repinfo, buf, temp, rcode, + (rcode|BIT_AA)); return 1; } @@ -1397,19 +1587,17 @@ uint16_t port = ntohs(((struct sockaddr_in*)&repinfo->addr)->sin_port); dname_str(z->name, zname); addr_to_str(&repinfo->addr, repinfo->addrlen, ip, sizeof(ip)); - snprintf(txt, sizeof(txt), "%s inform %s@%u", zname, ip, + snprintf(txt, sizeof(txt), "%s %s %s@%u", zname, local_zone_type2str(z->type), ip, (unsigned)port); - log_nametypeclass(0, txt, qinfo->qname, qinfo->qtype, qinfo->qclass); + log_nametypeclass(NO_VERBOSE, txt, qinfo->qname, qinfo->qtype, qinfo->qclass); } static enum localzone_type lz_type(uint8_t *taglist, size_t taglen, uint8_t *taglist2, size_t taglen2, uint8_t *tagactions, size_t tagactionssize, enum localzone_type lzt, - struct comm_reply* repinfo, struct rbtree_t* override_tree, int* tag, - char** tagname, int num_tags) + struct comm_reply* repinfo, struct rbtree_type* override_tree, + int* tag, char** tagname, int num_tags) { - size_t i, j; - uint8_t tagmatch; struct local_zone_override* lzo; if(repinfo && override_tree) { lzo = (struct local_zone_override*)addr_tree_lookup( @@ -1422,6 +1610,19 @@ } if(!taglist || !taglist2) return lzt; + return local_data_find_tag_action(taglist, taglen, taglist2, taglen2, + tagactions, tagactionssize, lzt, tag, tagname, num_tags); +} + +enum localzone_type +local_data_find_tag_action(const uint8_t* taglist, size_t taglen, + const uint8_t* taglist2, size_t taglen2, const uint8_t* tagactions, + size_t tagactionssize, enum localzone_type lzt, int* tag, + char* const* tagname, int num_tags) +{ + size_t i, j; + uint8_t tagmatch; + for(i=0; i0; j++) { @@ -1448,12 +1649,12 @@ } int -local_zones_answer(struct local_zones* zones, struct query_info* qinfo, - struct edns_data* edns, sldns_buffer* buf, struct regional* temp, - struct comm_reply* repinfo, uint8_t* taglist, size_t taglen, - uint8_t* tagactions, size_t tagactionssize, +local_zones_answer(struct local_zones* zones, 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, uint8_t* tagactions, size_t tagactionssize, struct config_strlist** tag_datas, size_t tag_datas_size, - char** tagname, int num_tags) + char** tagname, int num_tags, struct view* view) { /* see if query is covered by a zone, * if so: - try to match (exact) local data @@ -1460,42 +1661,92 @@ * - look at zone type for negative response. */ int labs = dname_count_labels(qinfo->qname); struct local_data* ld = NULL; - struct local_zone* z; - enum localzone_type lzt; + struct local_zone* z = NULL; + enum localzone_type lzt = local_zone_transparent; int r, tag = -1; - lock_rw_rdlock(&zones->lock); - z = local_zones_tags_lookup(zones, qinfo->qname, - qinfo->qname_len, labs, qinfo->qclass, taglist, taglen, 0); + + if(view) { + lock_rw_rdlock(&view->lock); + if(view->local_zones && + (z = local_zones_lookup(view->local_zones, + qinfo->qname, qinfo->qname_len, labs, + qinfo->qclass, qinfo->qtype))) { + lock_rw_rdlock(&z->lock); + lzt = z->type; + } + if(lzt == local_zone_noview) { + lock_rw_unlock(&z->lock); + z = NULL; + } + if(z && (lzt == local_zone_transparent || + lzt == local_zone_typetransparent || + lzt == local_zone_inform || + lzt == local_zone_always_transparent) && + local_zone_does_not_cover(z, qinfo, labs)) { + lock_rw_unlock(&z->lock); + z = NULL; + } + if(view->local_zones && !z && !view->isfirst){ + lock_rw_unlock(&view->lock); + return 0; + } + if(z && verbosity >= VERB_ALGO) { + char zname[255+1]; + dname_str(z->name, zname); + verbose(VERB_ALGO, "using localzone %s %s from view %s", + zname, local_zone_type2str(lzt), view->name); + } + lock_rw_unlock(&view->lock); + } if(!z) { + /* try global local_zones tree */ + lock_rw_rdlock(&zones->lock); + if(!(z = local_zones_tags_lookup(zones, qinfo->qname, + qinfo->qname_len, labs, qinfo->qclass, qinfo->qtype, + taglist, taglen, 0))) { + lock_rw_unlock(&zones->lock); + return 0; + } + lock_rw_rdlock(&z->lock); + lzt = lz_type(taglist, taglen, z->taglist, z->taglen, + tagactions, tagactionssize, z->type, repinfo, + z->override_tree, &tag, tagname, num_tags); lock_rw_unlock(&zones->lock); - return 0; + if(z && verbosity >= VERB_ALGO) { + char zname[255+1]; + dname_str(z->name, zname); + verbose(VERB_ALGO, "using localzone %s %s", zname, + local_zone_type2str(lzt)); + } } - lock_rw_rdlock(&z->lock); - lock_rw_unlock(&zones->lock); - - lzt = lz_type(taglist, taglen, z->taglist, z->taglen, tagactions, - tagactionssize, z->type, repinfo, z->override_tree, &tag, - tagname, num_tags); - - if((lzt == local_zone_inform || lzt == local_zone_inform_deny) - && repinfo) + if((env->cfg->log_local_actions || + lzt == local_zone_inform || + lzt == local_zone_inform_deny || + lzt == local_zone_inform_redirect) + && repinfo) lz_inform_print(z, qinfo, repinfo); - if(lzt != local_zone_always_refuse && lzt != local_zone_always_transparent + if(lzt != local_zone_always_refuse + && lzt != local_zone_always_transparent && lzt != local_zone_always_nxdomain - && local_data_answer(z, qinfo, edns, buf, temp, labs, &ld, lzt, - tag, tag_datas, tag_datas_size, tagname, num_tags)) { + && lzt != local_zone_always_nodata + && lzt != local_zone_always_deny + && local_data_answer(z, env, qinfo, edns, repinfo, buf, temp, labs, + &ld, lzt, tag, tag_datas, tag_datas_size, tagname, num_tags)) { lock_rw_unlock(&z->lock); - return 1; + /* We should tell the caller that encode is deferred if we found + * a local alias. */ + return !qinfo->local_alias; } - r = lz_zone_answer(z, qinfo, edns, buf, temp, ld, lzt); + r = local_zones_zone_answer(z, env, qinfo, edns, repinfo, buf, temp, ld, lzt); lock_rw_unlock(&z->lock); - return r; + return r && !qinfo->local_alias; /* see above */ } const char* local_zone_type2str(enum localzone_type t) { switch(t) { + case local_zone_unset: return "unset"; case local_zone_deny: return "deny"; case local_zone_refuse: return "refuse"; case local_zone_redirect: return "redirect"; @@ -1505,9 +1756,14 @@ case local_zone_nodefault: return "nodefault"; case local_zone_inform: return "inform"; case local_zone_inform_deny: return "inform_deny"; + case local_zone_inform_redirect: return "inform_redirect"; case local_zone_always_transparent: return "always_transparent"; case local_zone_always_refuse: return "always_refuse"; case local_zone_always_nxdomain: return "always_nxdomain"; + case local_zone_always_nodata: return "always_nodata"; + case local_zone_always_deny: return "always_deny"; + case local_zone_noview: return "noview"; + case local_zone_invalid: return "invalid"; } return "badtyped"; } @@ -1530,6 +1786,8 @@ *t = local_zone_inform; else if(strcmp(type, "inform_deny") == 0) *t = local_zone_inform_deny; + else if(strcmp(type, "inform_redirect") == 0) + *t = local_zone_inform_redirect; else if(strcmp(type, "always_transparent") == 0) *t = local_zone_always_transparent; else if(strcmp(type, "always_refuse") == 0) @@ -1536,6 +1794,14 @@ *t = local_zone_always_refuse; else if(strcmp(type, "always_nxdomain") == 0) *t = local_zone_always_nxdomain; + else if(strcmp(type, "always_nodata") == 0) + *t = local_zone_always_nodata; + else if(strcmp(type, "always_deny") == 0) + *t = local_zone_always_deny; + else if(strcmp(type, "noview") == 0) + *t = local_zone_noview; + else if(strcmp(type, "nodefault") == 0) + *t = local_zone_nodefault; else return 0; return 1; } @@ -1615,12 +1881,12 @@ local_zones_add_RR(struct local_zones* zones, const char* rr) { uint8_t* rr_name; - uint16_t rr_class; + uint16_t rr_class, rr_type; size_t len; int labs; struct local_zone* z; int r; - if(!get_rr_nameclass(rr, &rr_name, &rr_class)) { + if(!get_rr_nameclass(rr, &rr_name, &rr_class, &rr_type)) { return 0; } labs = dname_count_size_labels(rr_name, &len); @@ -1627,7 +1893,7 @@ /* could first try readlock then get writelock if zone does not exist, * but we do not add enough RRs (from multiple threads) to optimize */ lock_rw_wrlock(&zones->lock); - z = local_zones_lookup(zones, rr_name, len, labs, rr_class); + z = local_zones_lookup(zones, rr_name, len, labs, rr_class, rr_type); if(!z) { z = local_zones_add_zone(zones, rr_name, len, labs, rr_class, local_zone_transparent); @@ -1675,10 +1941,27 @@ return; dname_remove_label(&name, &len); labs--; - d = lz_find_node(z, name, len, labs); + d = local_zone_find_data(z, name, len, labs); } } +/** find and remove type from list in domain struct */ +static void +del_local_rrset(struct local_data* d, uint16_t dtype) +{ + struct local_rrset* prev=NULL, *p=d->rrsets; + 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 d->rrsets = p->next; + /* no memory recycling for zone deletions ... */ +} + void local_zones_del_data(struct local_zones* zones, uint8_t* name, size_t len, int labs, uint16_t dclass) { @@ -1685,8 +1968,24 @@ /* find zone */ struct local_zone* z; struct local_data* d; + + /* remove DS */ lock_rw_rdlock(&zones->lock); - z = local_zones_lookup(zones, name, len, labs, dclass); + z = local_zones_lookup(zones, name, len, labs, dclass, LDNS_RR_TYPE_DS); + if(z) { + lock_rw_wrlock(&z->lock); + d = local_zone_find_data(z, name, len, labs); + if(d) { + del_local_rrset(d, LDNS_RR_TYPE_DS); + del_empty_term(z, d, name, len, labs); + } + lock_rw_unlock(&z->lock); + } + lock_rw_unlock(&zones->lock); + + /* remove other types */ + lock_rw_rdlock(&zones->lock); + z = local_zones_lookup(zones, name, len, labs, dclass, 0); if(!z) { /* no such zone, we're done */ lock_rw_unlock(&zones->lock); @@ -1696,7 +1995,7 @@ lock_rw_unlock(&zones->lock); /* find the domain */ - d = lz_find_node(z, name, len, labs); + d = local_zone_find_data(z, name, len, labs); if(d) { /* no memory recycling for zone deletions ... */ d->rrsets = NULL; --- contrib/unbound/services/localzone.h.orig +++ contrib/unbound/services/localzone.h @@ -44,6 +44,10 @@ #include "util/rbtree.h" #include "util/locks.h" #include "util/storage/dnstree.h" +#include "util/module.h" +#include "services/view.h" +#include "sldns/sbuffer.h" +struct packed_rrset_data; struct ub_packed_rrset_key; struct regional; struct config_file; @@ -59,8 +63,10 @@ * local-data directly. */ enum localzone_type { + /** unset type, used for unset tag_action elements */ + local_zone_unset = 0, /** drop query */ - local_zone_deny = 0, + local_zone_deny, /** answer with error */ local_zone_refuse, /** answer nxdomain or nodata */ @@ -78,12 +84,22 @@ local_zone_inform, /** log client address, and block (drop) */ local_zone_inform_deny, + /** log client address, and direct */ + local_zone_inform_redirect, /** resolve normally, even when there is local data */ local_zone_always_transparent, /** answer with error, even when there is local data */ local_zone_always_refuse, /** answer with nxdomain, even when there is local data */ - local_zone_always_nxdomain + local_zone_always_nxdomain, + /** answer with noerror/nodata, even when there is local data */ + local_zone_always_nodata, + /** drop query, even when there is local data */ + local_zone_always_deny, + /** answer not from the view, but global or no-answer */ + local_zone_noview, + /** Invalid type, cannot be used to generate answer */ + local_zone_invalid }; /** @@ -91,9 +107,9 @@ */ struct local_zones { /** lock on the localzone tree */ - lock_rw_t lock; + lock_rw_type lock; /** rbtree of struct local_zone */ - rbtree_t ztree; + rbtree_type ztree; }; /** @@ -101,7 +117,7 @@ */ struct local_zone { /** rbtree node, key is name and class */ - rbnode_t node; + rbnode_type node; /** parent zone, if any. */ struct local_zone* parent; @@ -119,7 +135,7 @@ * For the node, parent, name, namelen, namelabs, dclass, you * need to also hold the zones_tree lock to change them (or to * delete this zone) */ - lock_rw_t lock; + lock_rw_type lock; /** how to process zone */ enum localzone_type type; @@ -129,7 +145,7 @@ size_t taglen; /** netblock addr_tree with struct local_zone_override information * or NULL if there are no override elements */ - struct rbtree_t* override_tree; + struct rbtree_type* override_tree; /** in this region the zone's data is allocated. * the struct local_zone itself is malloced. */ @@ -136,7 +152,7 @@ struct regional* region; /** local data for this zone * rbtree of struct local_data */ - rbtree_t data; + rbtree_type data; /** if data contains zone apex SOA data, this is a ptr to it. */ struct ub_packed_rrset_key* soa; }; @@ -146,7 +162,7 @@ */ struct local_data { /** rbtree node, key is name only */ - rbnode_t node; + rbnode_type node; /** domain name */ uint8_t* name; /** length of name */ @@ -230,6 +246,7 @@ * @param len: length of name. * @param labs: labelcount of name. * @param dclass: class to lookup. + * @param dtype: type to lookup, if type DS a zone higher is used for zonecuts. * @param taglist: taglist to lookup. * @param taglen: lenth of taglist. * @param ignoretags: lookup zone by name and class, regardless the @@ -237,7 +254,7 @@ * @return closest local_zone or NULL if no covering zone is found. */ struct local_zone* local_zones_tags_lookup(struct local_zones* zones, - uint8_t* name, size_t len, int labs, uint16_t dclass, + uint8_t* name, size_t len, int labs, uint16_t dclass, uint16_t dtype, uint8_t* taglist, size_t taglen, int ignoretags); /** @@ -248,10 +265,12 @@ * @param len: length of name. * @param labs: labelcount of name. * @param dclass: class to lookup. + * @param dtype: type of the record, if type DS then a zone higher up is found + * pass 0 to just plain find a zone for a name. * @return closest local_zone or NULL if no covering zone is found. */ struct local_zone* local_zones_lookup(struct local_zones* zones, - uint8_t* name, size_t len, int labs, uint16_t dclass); + uint8_t* name, size_t len, int labs, uint16_t dclass, uint16_t dtype); /** * Debug helper. Print all zones @@ -264,6 +283,7 @@ * Answer authoritatively for local zones. * Takes care of locking. * @param zones: the stored zones (shared, read only). + * @param env: the module environment. * @param qinfo: query info (parsed). * @param edns: edns info (parsed). * @param buf: buffer with query ID and flags, also for reply. @@ -277,17 +297,45 @@ * @param tag_datas_size: size of tag_datas array. * @param tagname: array of tag name strings (for debug output). * @param num_tags: number of items in tagname array. + * @param view: answer using this view. May be NULL. * @return true if answer is in buffer. false if query is not answered * by authority data. If the reply should be dropped altogether, the return * value is true, but the buffer is cleared (empty). + * It can also return true if a non-exact alias answer is found. In this + * case qinfo->local_alias points to the corresponding alias RRset but the + * answer is NOT encoded in buffer. It's the caller's responsibility to + * complete the alias chain (if needed) and encode the final set of answer. + * Data pointed to by qinfo->local_alias is allocated in 'temp' or refers to + * configuration data. So the caller will need to make a deep copy of it + * if it needs to keep it beyond the lifetime of 'temp' or a dynamic update + * to local zone data. */ -int local_zones_answer(struct local_zones* zones, struct query_info* qinfo, - struct edns_data* edns, struct sldns_buffer* buf, struct regional* temp, - struct comm_reply* repinfo, uint8_t* taglist, size_t taglen, - uint8_t* tagactions, size_t tagactionssize, +int local_zones_answer(struct local_zones* zones, struct module_env* env, + struct query_info* qinfo, struct edns_data* edns, struct sldns_buffer* buf, + struct regional* temp, struct comm_reply* repinfo, uint8_t* taglist, + size_t taglen, uint8_t* tagactions, size_t tagactionssize, struct config_strlist** tag_datas, size_t tag_datas_size, - char** tagname, int num_tags); + char** tagname, int num_tags, struct view* view); +/** + * Answer using the local zone only (not local data used). + * @param z: zone for query. + * @param env: module environment. + * @param qinfo: query. + * @param edns: edns from query. + * @param repinfo: source address for checks. may be NULL. + * @param buf: buffer for answer. + * @param temp: temp region for encoding. + * @param ld: local data, if NULL, no such name exists in localdata. + * @param lz_type: type of the local zone. + * @return 1 if a reply is to be sent, 0 if not. + */ +int +local_zones_zone_answer(struct local_zone* z, struct module_env* env, + struct query_info* qinfo, struct edns_data* edns, + struct comm_reply* repinfo, sldns_buffer* buf, struct regional* temp, + struct local_data* ld, enum localzone_type lz_type); + /** * Parse the string into localzone type. * @@ -319,6 +367,22 @@ uint8_t* name, size_t len, int labs, uint16_t dclass); /** + * Find zone that with exactly or smaller name/class + * User must lock the tree or result zone. + * @param zones: the zones tree + * @param name: dname to lookup + * @param len: length of name. + * @param labs: labelcount of name. + * @param dclass: class to lookup. + * @param exact: 1 on return is this is an exact match. + * @return the exact or smaller local_zone or NULL. + */ +struct local_zone* +local_zones_find_le(struct local_zones* zones, + uint8_t* name, size_t len, int labs, uint16_t dclass, + int* exact); + +/** * Add a new zone. Caller must hold the zones lock. * Adjusts the other zones as well (parent pointers) after insertion. * The zone must NOT exist (returns NULL and logs error). @@ -375,4 +439,193 @@ */ int parse_dname(const char* str, uint8_t** res, size_t* len, int* labs); +/** + * Find local data tag string match for the given type (in qinfo) in the list. + * If found, 'r' will be filled with corresponding rrset information. + * @param qinfo: contains name, type, and class for the data + * @param list: stores local tag data to be searched + * @param r: rrset key to be filled for matched data + * @param temp: region to allocate rrset in 'r' + * @return 1 if a match is found and rrset is built; otherwise 0 including + * errors. + */ +int local_data_find_tag_datas(const struct query_info* qinfo, + struct config_strlist* list, struct ub_packed_rrset_key* r, + struct regional* temp); + +/** + * See if two sets of tag lists (in the form of bitmap) have the same tag that + * has an action. If so, '*tag' will be set to the found tag index, and the + * corresponding action will be returned in the form of local zone type. + * Otherwise the passed type (lzt) will be returned as the default action. + * Pointers except tagactions must not be NULL. + * @param taglist: 1st list of tags + * @param taglen: size of taglist in bytes + * @param taglist2: 2nd list of tags + * @param taglen2: size of taglist2 in bytes + * @param tagactions: local data actions for tags. May be NULL. + * @param tagactionssize: length of the tagactions. + * @param lzt: default action (local zone type) if no tag action is found. + * @param tag: see above. + * @param tagname: array of tag name strings (for debug output). + * @param num_tags: number of items in tagname array. + * @return found tag action or the default action. + */ +enum localzone_type local_data_find_tag_action(const uint8_t* taglist, + size_t taglen, const uint8_t* taglist2, size_t taglen2, + const uint8_t* tagactions, size_t tagactionssize, + enum localzone_type lzt, int* tag, char* const* tagname, int num_tags); + +/** + * Enter defaults to local zone. + * @param zones: to add defaults to + * @param cfg: containing list of zones to exclude from default set. + * @return 1 on success; 0 otherwise. + */ +int local_zone_enter_defaults(struct local_zones* zones, + struct config_file* cfg); + +/** + * Parses resource record string into wire format, also returning its field values. + * @param str: input resource record + * @param nm: domain name field + * @param type: record type field + * @param dclass: record class field + * @param ttl: ttl field + * @param rr: buffer for the parsed rr in wire format + * @param len: buffer length + * @param rdata: rdata field + * @param rdata_len: rdata field length + * @return 1 on success; 0 otherwise. + */ +int rrstr_get_rr_content(const char* str, uint8_t** nm, uint16_t* type, + uint16_t* dclass, time_t* ttl, uint8_t* rr, size_t len, + uint8_t** rdata, size_t* rdata_len); + +/** + * Insert specified rdata into the specified resource record. + * @param region: allocator + * @param pd: data portion of the destination resource record + * @param rdata: source rdata + * @param rdata_len: source rdata length + * @param ttl: time to live + * @param rrstr: resource record in text form (for logging) + * @return 1 on success; 0 otherwise. + */ +int rrset_insert_rr(struct regional* region, struct packed_rrset_data* pd, + uint8_t* rdata, size_t rdata_len, time_t ttl, const char* rrstr); + +/** + * Remove RR from rrset that is created using localzone's rrset_insert_rr. + * @param pd: the RRset containing the RR to remove + * @param index: index of RR to remove + * @return: 1 on success; 0 otherwise. + */ +int +local_rrset_remove_rr(struct packed_rrset_data* pd, size_t index); + +/** + * Valid response ip actions for the IP-response-driven-action feature; + * defined here instead of in the respip module to enable sharing of enum + * values with the localzone_type enum. + * Note that these values except 'none' are the same as localzone types of + * the 'same semantics'. It's intentional as we use these values via + * access-control-tags, which can be shared for both response ip actions and + * local zones. + */ +enum respip_action { + /** no respip action */ + respip_none = local_zone_unset, + /** don't answer */ + respip_deny = local_zone_deny, + /** redirect as per provided data */ + respip_redirect = local_zone_redirect, + /** log query source and answer query */ + respip_inform = local_zone_inform, + /** log query source and don't answer query */ + respip_inform_deny = local_zone_inform_deny, + /** log query source and redirect */ + respip_inform_redirect = local_zone_inform_redirect, + /** resolve normally, even when there is response-ip data */ + respip_always_transparent = local_zone_always_transparent, + /** answer with 'refused' response */ + respip_always_refuse = local_zone_always_refuse, + /** answer with 'no such domain' response */ + respip_always_nxdomain = local_zone_always_nxdomain, + /** answer with nodata response */ + respip_always_nodata = local_zone_always_nodata, + /** answer with nodata response */ + respip_always_deny = local_zone_always_deny, + + /* The rest of the values are only possible as + * access-control-tag-action */ + + /** serves response data (if any), else, drops queries. */ + respip_refuse = local_zone_refuse, + /** serves response data, else, nodata answer. */ + respip_static = local_zone_static, + /** gives response data (if any), else nodata answer. */ + respip_transparent = local_zone_transparent, + /** gives response data (if any), else nodata answer. */ + respip_typetransparent = local_zone_typetransparent, + /** type invalid */ + respip_invalid = local_zone_invalid, +}; + +/** + * Get local data from local zone and encode answer. + * @param z: local zone to use + * @param env: module env + * @param qinfo: qinfo + * @param edns: edns data, for message encoding + * @param repinfo: reply info, for message encoding + * @param buf: commpoint buffer + * @param temp: scratchpad region + * @param labs: number of labels in qname + * @param ldp: where to store local data + * @param lz_type: type of local zone + * @param tag: matching tag index + * @param tag_datas: alc specific tag data list + * @param tag_datas_size: size of tag_datas + * @param tagname: list of names of tags, for logging purpose + * @param num_tags: number of tags + * @return 1 on success + */ +int +local_data_answer(struct local_zone* z, struct module_env* env, + struct query_info* qinfo, struct edns_data* edns, + struct comm_reply* repinfo, sldns_buffer* buf, + struct regional* temp, int labs, struct local_data** ldp, + enum localzone_type lz_type, int tag, struct config_strlist** tag_datas, + size_t tag_datas_size, char** tagname, int num_tags); + +/** + * Add RR to local zone. + * @param z: local zone to add RR to + * @param nm: dname of RR + * @param nmlen: length of nm + * @param nmlabs: number of labels of nm + * @param rrtype: RR type + * @param rrclass: RR class + * @param ttl: TTL of RR to add + * @param rdata: RDATA of RR to add + * @param rdata_len: length of rdata + * @param rrstr: RR in string format, for logging + * @return: 1 on success + */ +int +local_zone_enter_rr(struct local_zone* z, uint8_t* nm, size_t nmlen, + int nmlabs, uint16_t rrtype, uint16_t rrclass, time_t ttl, + uint8_t* rdata, size_t rdata_len, const char* rrstr); + +/** + * Find a data node by exact name for a local zone + * @param z: local_zone containing data tree + * @param nm: name of local-data element to find + * @param nmlen: length of nm + * @param nmlabs: labs of nm + * @return local_data on exact match, NULL otherwise. + */ +struct local_data* +local_zone_find_data(struct local_zone* z, uint8_t* nm, size_t nmlen, int nmlabs); #endif /* SERVICES_LOCALZONE_H */ --- contrib/unbound/services/mesh.c.orig +++ contrib/unbound/services/mesh.c @@ -46,6 +46,7 @@ #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" @@ -55,7 +56,13 @@ #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" /** subtract timers and the values do not overflow or become negative */ static void @@ -79,7 +86,7 @@ #ifndef S_SPLINT_S d->tv_sec += add->tv_sec; d->tv_usec += add->tv_usec; - if(d->tv_usec > 1000000 ) { + if(d->tv_usec >= 1000000 ) { d->tv_usec -= 1000000; d->tv_sec++; } @@ -121,12 +128,70 @@ #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; + 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; + 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) @@ -147,7 +212,10 @@ if(!(a->s.query_flags&BIT_CD) && (b->s.query_flags&BIT_CD)) return 1; - return query_info_compare(&a->s.qinfo, &b->s.qinfo); + 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 @@ -183,6 +251,7 @@ mesh->num_forever_states = 0; mesh->stats_jostled = 0; mesh->stats_dropped = 0; + mesh->ans_expired = 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 @@ -195,7 +264,7 @@ /** help mesh delete delete mesh states */ static void -mesh_delete_helper(rbnode_t* n) +mesh_delete_helper(rbnode_type* n) { struct mesh_state* mstate = (struct mesh_state*)n->key; /* perform a full delete, not only 'cleanup' routine, @@ -278,14 +347,114 @@ 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, - uint16_t qflags, struct edns_data* edns, struct comm_reply* rep, - uint16_t qid) + struct respip_client_info* cinfo, uint16_t qflags, + struct edns_data* edns, struct comm_reply* rep, uint16_t qid) { - struct mesh_state* s = mesh_area_find(mesh, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0); + struct mesh_state* s = NULL; + int unique = unique_mesh_state(edns->opt_list, 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)) { @@ -292,7 +461,7 @@ verbose(VERB_ALGO, "Too many queries. dropping " "incoming query."); comm_point_drop_reply(rep); - mesh->stats_dropped ++; + mesh->stats_dropped++; return; } /* for this new reply state, the reply address is free, @@ -302,8 +471,8 @@ 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++; - comm_point_drop_reply(rep); return; } } @@ -310,18 +479,38 @@ /* see if it already exists, if not, create one */ if(!s) { #ifdef UNBOUND_DEBUG - struct rbnode_t* n; + struct rbnode_type* n; #endif - s = mesh_state_create(mesh->env, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0); + 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(!edns_opt_inplace_reply(edns, mesh->env->scratch)) - edns->opt_list = NULL; - error_encode(rep->c->buffer, LDNS_RCODE_SERVFAIL, + if(!inplace_cb_reply_servfail_call(mesh->env, qinfo, NULL, NULL, + LDNS_RCODE_SERVFAIL, edns, rep, mesh->env->scratch)) + edns->opt_list = NULL; + error_encode(r_buffer, LDNS_RCODE_SERVFAIL, qinfo, qid, qflags, edns); comm_point_send_reply(rep); return; } + if(unique) + mesh_state_make_unique(s); + /* copy the edns options we got from the front */ + if(edns->opt_list) { + s->s.edns_opts_front_in = edns_opt_copy_region(edns->opt_list, + s->s.region); + if(!s->s.edns_opts_front_in) { + 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)) + edns->opt_list = NULL; + error_encode(r_buffer, LDNS_RCODE_SERVFAIL, + qinfo, qid, qflags, edns); + comm_point_send_reply(rep); + return; + } + } + #ifdef UNBOUND_DEBUG n = #else @@ -333,22 +522,28 @@ mesh->num_detached_states++; added = 1; } - if(!s->reply_list && !s->cb_list && s->super_set.count == 0) - was_detached = 1; - if(!s->reply_list && !s->cb_list) + 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->qname)) { - log_err("mesh_new_client: out of memory; SERVFAIL"); - if(!edns_opt_inplace_reply(edns, mesh->env->scratch)) - edns->opt_list = NULL; - error_encode(rep->c->buffer, LDNS_RCODE_SERVFAIL, - qinfo, qid, qflags, edns); - comm_point_send_reply(rep); - if(added) - mesh_state_delete(&s->s); - return; + 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; + } + } + /* 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); @@ -373,28 +568,56 @@ } 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)) + edns->opt_list = 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_t cb, void* cb_arg) + uint16_t qid, mesh_cb_func_type cb, void* cb_arg) { - struct mesh_state* s = mesh_area_find(mesh, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0); + struct mesh_state* s = NULL; + int unique = unique_mesh_state(edns->opt_list, 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_t* n; + struct rbnode_type* n; #endif - s = mesh_state_create(mesh->env, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0); + s = mesh_state_create(mesh->env, qinfo, NULL, + qflags&(BIT_RD|BIT_CD), 0, 0); if(!s) { return 0; } + if(unique) + mesh_state_make_unique(s); + if(edns->opt_list) { + s->s.edns_opts_front_in = edns_opt_copy_region(edns->opt_list, + s->s.region); + if(!s->s.edns_opts_front_in) { + return 0; + } + } #ifdef UNBOUND_DEBUG n = #else @@ -406,16 +629,22 @@ mesh->num_detached_states++; added = 1; } - if(!s->reply_list && !s->cb_list && s->super_set.count == 0) - was_detached = 1; - if(!s->reply_list && !s->cb_list) + 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; + 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)) { + return 0; + } /* update statistics */ if(was_detached) { log_assert(mesh->num_detached_states > 0); @@ -430,12 +659,18 @@ return 1; } -void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo, - uint16_t qflags, time_t leeway) +/* Internal backend routine of mesh_new_prefetch(). It takes one additional + * parameter, 'run', which controls whether to run the prefetch state + * immediately. When this function is called internally 'run' could be + * 0 (false), in which case the new state is only made runnable so it + * will not be run recursively on top of the current state. */ +static void mesh_schedule_prefetch(struct mesh_area* mesh, + struct query_info* qinfo, uint16_t qflags, time_t leeway, int run) { - struct mesh_state* s = mesh_area_find(mesh, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0); + struct mesh_state* s = mesh_area_find(mesh, NULL, qinfo, + qflags&(BIT_RD|BIT_CD), 0, 0); #ifdef UNBOUND_DEBUG - struct rbnode_t* n; + struct rbnode_type* n; #endif /* already exists, and for a different purpose perhaps. * if mesh_no_list, keep it that way. */ @@ -452,7 +687,9 @@ mesh->stats_dropped ++; return; } - s = mesh_state_create(mesh->env, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0); + + 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; @@ -483,9 +720,27 @@ s->list_select = mesh_jostle_list; } } + + if(!run) { +#ifdef UNBOUND_DEBUG + n = +#else + (void) +#endif + rbtree_insert(&mesh->run, &s->run_node); + log_assert(n != NULL); + return; + } + mesh_run(mesh, s, module_event_new, NULL); } +void mesh_new_prefetch(struct mesh_area* mesh, struct query_info* qinfo, + uint16_t qflags, time_t leeway) +{ + mesh_schedule_prefetch(mesh, qinfo, qflags, leeway, 1); +} + void mesh_report_reply(struct mesh_area* mesh, struct outbound_entry* e, struct comm_reply* reply, int what) { @@ -499,9 +754,10 @@ mesh_run(mesh, e->qstate->mesh_info, event, e); } -struct mesh_state* -mesh_state_create(struct module_env* env, struct query_info* qinfo, - uint16_t qflags, int prime, int valrec) +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; @@ -525,9 +781,11 @@ 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); @@ -535,6 +793,14 @@ 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; @@ -547,14 +813,38 @@ 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; + /* init modules */ for(i=0; imesh->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; } +int +mesh_state_is_unique(struct mesh_state* mstate) +{ + return mstate->unique != NULL; +} + +void +mesh_state_make_unique(struct mesh_state* mstate) +{ + mstate->unique = mstate; +} + void mesh_state_cleanup(struct mesh_state* mstate) { @@ -563,18 +853,30 @@ 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; + struct mesh_reply* rep = mstate->reply_list; struct mesh_cb* cb; - for(rep=mstate->reply_list; rep; rep=rep->next) { + /* 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--; } - for(cb=mstate->cb_list; cb; cb=cb->next) { + 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); + sec_status_unchecked, NULL, 0); + log_assert(mesh->num_reply_addrs > 0); mesh->num_reply_addrs--; } } @@ -642,7 +944,7 @@ } /** find cycle for already looked up mesh_state */ -static int +static int mesh_detect_cycle_found(struct module_qstate* qstate, struct mesh_state* dep_m) { struct mesh_state* cyc_m = qstate->mesh_info; @@ -662,7 +964,7 @@ struct mesh_area* mesh = qstate->env->mesh; struct mesh_state_ref* ref, lookup; #ifdef UNBOUND_DEBUG - struct rbnode_t* n; + struct rbnode_type* n; #endif lookup.node.key = &lookup; lookup.s = qstate->mesh_info; @@ -684,26 +986,26 @@ rbtree_init(&qstate->mesh_info->sub_set, &mesh_state_ref_compare); } -int mesh_attach_sub(struct module_qstate* qstate, struct query_info* qinfo, - uint16_t qflags, int prime, int valrec, struct module_qstate** newq) +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; - struct mesh_state* sub = mesh_area_find(mesh, qinfo, qflags, prime, - valrec); - int was_detached; - if(mesh_detect_cycle_found(qstate, sub)) { + *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) { + if(!*sub) { #ifdef UNBOUND_DEBUG - struct rbnode_t* n; + struct rbnode_type* n; #endif /* create a new one */ - sub = mesh_state_create(qstate->env, qinfo, qflags, prime, + *sub = mesh_state_create(qstate->env, qinfo, NULL, qflags, prime, valrec); - if(!sub) { + if(!*sub) { log_err("mesh_attach_sub: out of memory"); return 0; } @@ -712,7 +1014,7 @@ #else (void) #endif - rbtree_insert(&mesh->all, &sub->node); + rbtree_insert(&mesh->all, &(*sub)->node); log_assert(n != NULL); /* set detached (it is now) */ mesh->num_detached_states++; @@ -722,11 +1024,22 @@ #else (void) #endif - rbtree_insert(&mesh->run, &sub->run_node); + rbtree_insert(&mesh->run, &(*sub)->run_node); log_assert(n != NULL); - *newq = &sub->s; + *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; @@ -744,7 +1057,7 @@ int mesh_state_attachment(struct mesh_state* super, struct mesh_state* sub) { #ifdef UNBOUND_DEBUG - struct rbnode_t* n; + 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 */ @@ -791,7 +1104,8 @@ { int secure; char* reason = NULL; - /* bogus messages are not made into servfail, sec_status passed + 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; @@ -798,14 +1112,25 @@ else secure = 0; if(!rep && rcode == LDNS_RCODE_NOERROR) rcode = LDNS_RCODE_SERVFAIL; - if(!rcode && rep->security == sec_status_bogus) { - if(!(reason = errinf_to_str(&m->s))) + if(!rcode && (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)) + r->edns.opt_list = NULL; + } else { + if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep, rcode, + &r->edns, NULL, m->s.region)) + r->edns.opt_list = NULL; + } fptr_ok(fptr_whitelist_mesh_cb(r->cb)); - (*r->cb)(r->cb_arg, rcode, r->buf, sec_status_unchecked, NULL); + (*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); @@ -813,8 +1138,10 @@ r->edns.udp_size = EDNS_ADVERTISED_SIZE; r->edns.ext_rcode = 0; r->edns.bits &= EDNS_DO; - if(!edns_opt_inplace_reply(&r->edns, m->s.region) || - !reply_info_answer_encode(&m->s.qinfo, rep, r->qid, + + if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep, + LDNS_RCODE_NOERROR, &r->edns, NULL, m->s.region) || + !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)) @@ -821,14 +1148,15 @@ { fptr_ok(fptr_whitelist_mesh_cb(r->cb)); (*r->cb)(r->cb_arg, LDNS_RCODE_SERVFAIL, r->buf, - sec_status_unchecked, NULL); + 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->security, reason); + rep->security, reason, was_ratelimited); } } free(reason); + log_assert(m->s.env->mesh->num_reply_addrs > 0); m->s.env->mesh->num_reply_addrs--; } @@ -838,19 +1166,26 @@ * @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 mesh_reply* prev) + 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; + /* Copy the client's EDNS for later restore, to make sure the edns + * compare is with the correct edns options. */ + struct edns_data edns_bak = r->edns; /* 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_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++; @@ -861,7 +1196,13 @@ if(!rep && rcode == LDNS_RCODE_NOERROR) rcode = LDNS_RCODE_SERVFAIL; /* send the reply */ - if(prev && prev->qflags == r->qflags && + /* 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. */ + if(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 && @@ -868,18 +1209,26 @@ edns_opt_list_compare(prev->edns.opt_list, r->edns.opt_list) == 0) { /* if the previous reply is identical to this one, fix ID */ - if(prev->query_reply.c->buffer != r->query_reply.c->buffer) - sldns_buffer_copy(r->query_reply.c->buffer, - prev->query_reply.c->buffer); - sldns_buffer_write_at(r->query_reply.c->buffer, 0, - &r->qid, sizeof(uint16_t)); - sldns_buffer_write_at(r->query_reply.c->buffer, 12, - r->qname, m->s.qinfo.qname_len); + 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); comm_point_send_reply(&r->query_reply); } else if(rcode) { m->s.qinfo.qname = r->qname; - error_encode(r->query_reply.c->buffer, rcode, &m->s.qinfo, - r->qid, r->qflags, &r->edns); + 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, NULL, m->s.region)) + r->edns.opt_list = NULL; + } else { + if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep, rcode, + &r->edns, NULL, m->s.region)) + r->edns.opt_list = NULL; + } + error_encode(r_buffer, rcode, &m->s.qinfo, r->qid, + r->qflags, &r->edns); comm_point_send_reply(&r->query_reply); } else { size_t udp_size = r->edns.udp_size; @@ -888,19 +1237,28 @@ r->edns.ext_rcode = 0; r->edns.bits &= EDNS_DO; m->s.qinfo.qname = r->qname; - if(!edns_opt_inplace_reply(&r->edns, m->s.region) || - !reply_info_answer_encode(&m->s.qinfo, rep, r->qid, - r->qflags, r->query_reply.c->buffer, 0, 1, - m->s.env->scratch, udp_size, &r->edns, - (int)(r->edns.bits & EDNS_DO), secure)) + m->s.qinfo.local_alias = r->local_alias; + if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep, + LDNS_RCODE_NOERROR, &r->edns, NULL, m->s.region) || + !apply_edns_options(&r->edns, &edns_bak, + m->s.env->cfg, r->query_reply.c, + m->s.region) || + !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)) { - error_encode(r->query_reply.c->buffer, - LDNS_RCODE_SERVFAIL, &m->s.qinfo, r->qid, - r->qflags, &r->edns); + if(!inplace_cb_reply_servfail_call(m->s.env, &m->s.qinfo, &m->s, + rep, LDNS_RCODE_SERVFAIL, &r->edns, NULL, m->s.region)) + r->edns.opt_list = NULL; + error_encode(r_buffer, LDNS_RCODE_SERVFAIL, + &m->s.qinfo, r->qid, r->qflags, &r->edns); } + r->edns = edns_bak; comm_point_send_reply(&r->query_reply); } /* 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); @@ -910,29 +1268,120 @@ 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-> - query_reply.c->buffer, 2)); + 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-> - query_reply.c->buffer)) == 0) + 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.addr, + r->query_reply.addrlen, duration, 0, r_buffer); + } } void mesh_query_done(struct mesh_state* mstate) { - struct mesh_reply* r; + struct mesh_reply* r, *reply_list = NULL; 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); - for(r = mstate->reply_list; r; r = r->next) { - mesh_send_reply(mstate, mstate->s.return_rcode, rep, r, prev); - prev = r; + /* 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 asnwer 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); + } + } + if(mstate->reply_list) { + /* set the reply_list to NULL during the mesh_query_done + * processing, so that calls back into the mesh from + * tcp_req_info (deciding to drop the reply and thus + * unregister the mesh_reply from the mstate) are stopped + * because the list is empty. + * The mstate is then likely not a reply_state, and maybe + * also a detached_state. + */ + reply_list = 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++; + } + for(r = reply_list; r; r = r->next) { + /* 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); + if(mstate->s.env->cfg->stat_extended && + mstate->s.respip_action_info->rpz_used) { + if(mstate->s.respip_action_info->rpz_disabled) + mstate->s.env->mesh->rpz_action[RPZ_DISABLED_ACTION]++; + if(mstate->s.respip_action_info->rpz_cname_override) + mstate->s.env->mesh->rpz_action[RPZ_CNAME_OVERRIDE_ACTION]++; + else + mstate->s.env->mesh->rpz_action[respip_action_to_rpz_action( + mstate->s.respip_action_info->action)]++; + } + } + + /* if this query is determined to be dropped during the + * mesh processing, this is the point to take that action. */ + if(mstate->s.is_drop) { + comm_point_drop_reply(&r->query_reply); + } 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; + } + } mstate->replies_sent = 1; - for(c = mstate->cb_list; c; c = c->next) { + 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); } } @@ -949,11 +1398,14 @@ 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 query_info* qinfo, uint16_t qflags, int prime, int valrec) + struct respip_client_info* cinfo, struct query_info* qinfo, + uint16_t qflags, int prime, int valrec) { struct mesh_state key; struct mesh_state* result; @@ -963,6 +1415,11 @@ 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; @@ -969,7 +1426,7 @@ } int mesh_state_add_cb(struct mesh_state* s, struct edns_data* edns, - sldns_buffer* buf, mesh_cb_func_t cb, void* cb_arg, + 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, @@ -996,7 +1453,8 @@ } int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns, - struct comm_reply* rep, uint16_t qid, uint16_t qflags, uint8_t* qname) + 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)); @@ -1014,17 +1472,84 @@ r->qflags = qflags; r->start_time = *s->s.env->now_tv; r->next = s->reply_list; - r->qname = regional_alloc_init(s->s.region, qname, + r->qname = regional_alloc_init(s->s.region, qinfo->qname, s->s.qinfo.qname_len); if(!r->qname) return 0; + + /* 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 + * individualy 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 tranfer of control. + * Handles module to modules transfer of control. * Handles module finished. * @param mesh: the mesh area. * @param mstate: currently active mesh state. @@ -1044,8 +1569,9 @@ mstate->num_activated++; if(mstate->num_activated > MESH_MAX_ACTIVATION) { /* module is looping. Stop it. */ - log_err("internal error: looping module stopped"); - log_query_info(VERB_QUERY, "pass error for qstate", + 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; } @@ -1060,11 +1586,16 @@ return mesh_continue(mesh, mstate, module_error, ev); } if(s == module_restart_next) { - 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; + 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; @@ -1079,11 +1610,32 @@ /* error is bad, handle pass back up below */ mstate->s.return_rcode = LDNS_RCODE_SERVFAIL; } - if(s == module_error || s == module_finished) { + if(s == module_error) { + mesh_query_done(mstate); + mesh_walk_supers(mesh, mstate); + mesh_state_delete(&mstate->s); + return 0; + } + if(s == module_finished) { if(mstate->s.curmod == 0) { + struct query_info* qinfo = NULL; + uint16_t qflags; + mesh_query_done(mstate); mesh_walk_supers(mesh, mstate); + + /* If the answer to the query needs to be refetched + * from an external DNS server, we'll need to schedule + * a prefetch after removing the current state, so + * we need to make a copy of the query info here. */ + if(mstate->s.need_refetch) + mesh_copy_qinfo(mstate, &qinfo, &qflags); + mesh_state_delete(&mstate->s); + if(qinfo) { + mesh_schedule_prefetch(mesh, qinfo, qflags, + 0, 1); + } return 0; } /* pass along the locus of control */ @@ -1189,7 +1741,9 @@ timehist_clear(mesh->histogram); mesh->ans_secure = 0; mesh->ans_bogus = 0; - memset(&mesh->ans_rcode[0], 0, sizeof(size_t)*16); + mesh->ans_expired = 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; } @@ -1212,8 +1766,9 @@ uint16_t flags, int prime, int valrec) { struct mesh_area* mesh = qstate->env->mesh; - struct mesh_state* dep_m = mesh_area_find(mesh, qinfo, flags, prime, - valrec); + struct mesh_state* dep_m = NULL; + if(!mesh_state_is_unique(qstate->mesh_info)) + dep_m = mesh_area_find(mesh, NULL, qinfo, flags, prime, valrec); return mesh_detect_cycle_found(qstate, dep_m); } @@ -1239,3 +1794,212 @@ 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)) + 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; + int must_validate = (!(qstate->query_flags&BIT_CD) + || qstate->env->cfg->ignore_cd) && qstate->env->need_to_validate; + 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(qstate->blacklist || 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); + + r = 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++; + } + } + for(; r; r = r->next) { + /* 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); + + if(qstate->env->cfg->stat_extended && actinfo.rpz_used) { + if(actinfo.rpz_disabled) + qstate->env->mesh->rpz_action[RPZ_DISABLED_ACTION]++; + if(actinfo.rpz_cname_override) + qstate->env->mesh->rpz_action[RPZ_CNAME_OVERRIDE_ACTION]++; + else + qstate->env->mesh->rpz_action[ + respip_action_to_rpz_action(actinfo.action)]++; + } + } + + 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. */ + mesh->ans_expired++; + + } + 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); + } +} --- contrib/unbound/services/mesh.h.orig +++ contrib/unbound/services/mesh.h @@ -51,6 +51,8 @@ #include "util/data/msgparse.h" #include "util/module.h" #include "services/modstack.h" +#include "services/rpz.h" +#include "libunbound/unbound.h" struct sldns_buffer; struct mesh_state; struct mesh_reply; @@ -59,12 +61,13 @@ struct reply_info; struct outbound_entry; struct timehist; +struct respip_client_info; /** * Maximum number of mesh state activations. Any more is likely an * infinite loop in the module. It is then terminated. */ -#define MESH_MAX_ACTIVATION 3000 +#define MESH_MAX_ACTIVATION 10000 /** * Max number of references-to-references-to-references.. search size. @@ -83,9 +86,9 @@ struct module_env* env; /** set of runnable queries (mesh_state.run_node) */ - rbtree_t run; + rbtree_type run; /** rbtree of all current queries (mesh_state.node)*/ - rbtree_t all; + rbtree_type all; /** count of the total number of mesh_reply entries */ size_t num_reply_addrs; @@ -109,6 +112,8 @@ size_t stats_jostled; /** stats, cumulative number of incoming client msgs dropped */ size_t stats_dropped; + /** stats, number of expired replies sent */ + size_t ans_expired; /** number of replies sent */ size_t replies_sent; /** sum of waiting times for the replies */ @@ -120,9 +125,11 @@ /** (extended stats) bogus replies */ size_t ans_bogus; /** (extended stats) rcodes in replies */ - size_t ans_rcode[16]; + size_t ans_rcode[UB_STATS_RCODE_NUM]; /** (extended stats) rcode nodata in replies */ size_t ans_nodata; + /** (extended stats) type of applied RPZ action */ + size_t rpz_action[UB_STATS_RPZ_ACTION_NUM]; /** backup of query if other operations recurse and need the * network buffers */ @@ -141,6 +148,11 @@ struct mesh_state* jostle_last; /** timeout for jostling. if age is lower, it does not get jostled. */ struct timeval jostle_max; + + /** If we need to use response ip (value passed from daemon)*/ + int use_response_ip; + /** If we need to use RPZ (value passed from daemon) */ + int use_rpz; }; /** @@ -154,9 +166,9 @@ */ struct mesh_state { /** node in mesh_area all tree, key is this struct. Must be first. */ - rbnode_t node; + rbnode_type node; /** node in mesh_area runnable tree, key is this struct */ - rbnode_t run_node; + rbnode_type run_node; /** the query state. Note that the qinfo and query_flags * may not change. */ struct module_qstate s; @@ -166,10 +178,10 @@ struct mesh_cb* cb_list; /** set of superstates (that want this state's result) * contains struct mesh_state_ref* */ - rbtree_t super_set; + rbtree_type super_set; /** set of substates (that this state needs to continue) * contains struct mesh_state_ref* */ - rbtree_t sub_set; + rbtree_type sub_set; /** number of activations for the mesh state */ size_t num_activated; @@ -180,6 +192,8 @@ /** if this state is in the forever list, jostle list, or neither */ enum mesh_list_select { mesh_no_list, mesh_forever_list, mesh_jostle_list } list_select; + /** pointer to this state for uniqueness or NULL */ + struct mesh_state* unique; /** true if replies have been sent out (at end for alignment) */ uint8_t replies_sent; @@ -191,7 +205,7 @@ */ struct mesh_state_ref { /** node in rbtree for set, key is this structure */ - rbnode_t node; + rbnode_type node; /** the mesh state */ struct mesh_state* s; }; @@ -214,14 +228,17 @@ uint16_t qflags; /** qname from this query. len same as mesh qinfo. */ uint8_t* qname; + /** same as that in query_info. */ + struct local_rrset* local_alias; }; /** * Mesh result callback func. - * called as func(cb_arg, rcode, buffer_with_reply, security, why_bogus); + * called as func(cb_arg, rcode, buffer_with_reply, security, why_bogus, + * was_ratelimited); */ -typedef void (*mesh_cb_func_t)(void*, int, struct sldns_buffer*, enum sec_status, - char*); +typedef void (*mesh_cb_func_type)(void* cb_arg, int rcode, struct sldns_buffer*, + enum sec_status, char* why_bogus, int was_ratelimited); /** * Callback to result routine @@ -237,11 +254,10 @@ uint16_t qflags; /** buffer for reply */ struct sldns_buffer* buf; - /** callback routine for results. if rcode != 0 buf has message. - * called as cb(cb_arg, rcode, buf, sec_state); + * called as cb(cb_arg, rcode, buf, sec_state, why_bogus, was_ratelimited); */ - mesh_cb_func_t cb; + mesh_cb_func_type cb; /** user arg for callback */ void* cb_arg; }; @@ -270,6 +286,10 @@ * * @param mesh: the mesh. * @param qinfo: query from client. + * @param cinfo: additional information associated with the query client. + * 'cinfo' itself is ephemeral but data pointed to by its members + * can be assumed to be valid and unchanged until the query processing is + * completed. * @param qflags: flags from client query. * @param edns: edns data from client query. * @param rep: where to reply to. @@ -276,8 +296,8 @@ * @param qid: query id to reply with. */ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo, - uint16_t qflags, struct edns_data* edns, struct comm_reply* rep, - uint16_t qid); + struct respip_client_info* cinfo, uint16_t qflags, + struct edns_data* edns, struct comm_reply* rep, uint16_t qid); /** * New query with callback. Create new query state if needed, and @@ -296,7 +316,7 @@ */ int mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo, uint16_t qflags, struct edns_data* edns, struct sldns_buffer* buf, - uint16_t qid, mesh_cb_func_t cb, void* cb_arg); + uint16_t qid, mesh_cb_func_type cb, void* cb_arg); /** * New prefetch message. Create new query state if needed. @@ -362,6 +382,35 @@ 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 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); + +/** * Query state is done, send messages to reply entries. * Encode messages using reply entry values and the querystate (with original * qinfo), using given reply_info. @@ -405,6 +454,7 @@ * Does not put the mesh state into rbtrees and so on. * @param env: module environment to set. * @param qinfo: query info that the mesh is for. + * @param cinfo: control info for the query client (can be NULL). * @param qflags: flags for query (RD / CD flag). * @param prime: if true, it is a priming query, set is_priming on mesh state. * @param valrec: if true, it is a validation recursion query, and sets @@ -411,10 +461,26 @@ * is_valrec on the mesh state. * @return: new mesh state or NULL on allocation error. */ -struct mesh_state* mesh_state_create(struct module_env* env, - struct query_info* qinfo, uint16_t qflags, int prime, int valrec); +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); /** + * Check if the mesh state is unique. + * A unique mesh state uses it's unique member to point to itself, else NULL. + * @param mstate: mesh state to check. + * @return true if the mesh state is unique, false otherwise. + */ +int mesh_state_is_unique(struct mesh_state* mstate); + +/** + * Make a mesh state unique. + * A unique mesh state uses it's unique member to point to itself. + * @param mstate: mesh state to check. + */ +void mesh_state_make_unique(struct mesh_state* mstate); + +/** * Cleanup a mesh state and its query state. Does not do rbtree or * reference cleanup. * @param mstate: mesh state to cleanup. Its pointer may no longer be used @@ -432,6 +498,8 @@ * Find a mesh state in the mesh area. Pass relevant flags. * * @param mesh: the mesh area to look in. + * @param cinfo: if non-NULL client specific info that may affect IP-based + * actions that apply to the query result. * @param qinfo: what query * @param qflags: if RD / CD bit is set or not. * @param prime: if it is a priming query. @@ -438,8 +506,9 @@ * @param valrec: if it is a validation-recursion query. * @return: mesh state or NULL if not found. */ -struct mesh_state* mesh_area_find(struct mesh_area* mesh, - struct query_info* qinfo, uint16_t qflags, int prime, int valrec); +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); /** * Setup attachment super/sub relation between super and sub mesh state. @@ -459,11 +528,12 @@ * @param rep: comm point reply info. * @param qid: ID of reply. * @param qflags: original query flags. - * @param qname: original query name. + * @param qinfo: original query info. * @return: 0 on alloc error. */ -int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns, - struct comm_reply* rep, uint16_t qid, uint16_t qflags, uint8_t* qname); +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); /** * Create new callback structure and attach it to a mesh state. @@ -478,8 +548,8 @@ * @return: 0 on alloc error. */ int mesh_state_add_cb(struct mesh_state* s, struct edns_data* edns, - struct sldns_buffer* buf, mesh_cb_func_t cb, void* cb_arg, uint16_t qid, - uint16_t qflags); + struct sldns_buffer* buf, mesh_cb_func_type cb, void* cb_arg, + uint16_t qid, uint16_t qflags); /** * Run the mesh. Run all runnable mesh states. Which can create new @@ -574,4 +644,32 @@ void mesh_list_remove(struct mesh_state* m, struct mesh_state** fp, struct mesh_state** lp); +/** + * Remove mesh reply entry from the reply entry list. Searches for + * the comm_point pointer. + * @param mesh: to update the counters. + * @param m: the mesh state. + * @param cp: the comm_point to remove from the list. + */ +void mesh_state_remove_reply(struct mesh_area* mesh, struct mesh_state* m, + struct comm_point* cp); + +/** Callback for when the serve expired client timer has run out. Tries to + * find an expired answer in the cache and reply that to the client. + * @param arg: the argument passed to the callback. + */ +void mesh_serve_expired_callback(void* arg); + +/** + * Try to get a (expired) cached answer. + * This needs to behave like the worker's answer_from_cache() in order to have + * the same behavior as when replying from cache. + * @param qstate: the module qstate. + * @param lookup_qinfo: the query info to look for in the cache. + * @return dns_msg if a cached answer was found, otherwise NULL. + */ +struct dns_msg* +mesh_serve_expired_lookup(struct module_qstate* qstate, + struct query_info* lookup_qinfo); + #endif /* SERVICES_MESH_H */ --- contrib/unbound/services/modstack.c.orig +++ contrib/unbound/services/modstack.c @@ -46,6 +46,7 @@ #include "dns64/dns64.h" #include "iterator/iterator.h" #include "validator/validator.h" +#include "respip/respip.h" #ifdef WITH_PYTHONMODULE #include "pythonmod/pythonmod.h" @@ -53,6 +54,15 @@ #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 /** count number of modules (words) in the string */ static int @@ -106,8 +116,14 @@ for(i=0; inum; i++) { stack->mod[i] = module_factory(&module_conf); if(!stack->mod[i]) { - log_err("Unknown value for next module: '%s'", - module_conf); + char md[256]; + snprintf(md, sizeof(md), "%s", module_conf); + if(strchr(md, ' ')) *(strchr(md, ' ')) = 0; + if(strchr(md, '\t')) *(strchr(md, '\t')) = 0; + log_err("Unknown value in module-config, module: '%s'." + " This module is not present (not compiled in)," + " See the list of linked modules with unbound -h", + md); return 0; } } @@ -122,13 +138,23 @@ static const char* names[] = { "dns64", #ifdef WITH_PYTHONMODULE - "python", + "python", #endif #ifdef USE_CACHEDB "cachedb", #endif - "validator", - "iterator", +#ifdef USE_IPSECMOD + "ipsecmod", +#endif +#ifdef CLIENT_SUBNET + "subnetcache", +#endif +#ifdef USE_IPSET + "ipset", +#endif + "respip", + "validator", + "iterator", NULL}; return names; } @@ -143,18 +169,28 @@ static struct module_func_block* (*fb[])(void) = { &dns64_get_funcblock, #ifdef WITH_PYTHONMODULE - &pythonmod_get_funcblock, + &pythonmod_get_funcblock, #endif #ifdef USE_CACHEDB &cachedb_get_funcblock, #endif - &val_get_funcblock, - &iter_get_funcblock, +#ifdef USE_IPSECMOD + &ipsecmod_get_funcblock, +#endif +#ifdef CLIENT_SUBNET + &subnetmod_get_funcblock, +#endif +#ifdef USE_IPSET + &ipset_get_funcblock, +#endif + &respip_get_funcblock, + &val_get_funcblock, + &iter_get_funcblock, NULL}; return fb; } -struct +struct module_func_block* module_factory(const char** str) { int i = 0; @@ -216,9 +252,21 @@ modstack_find(struct module_stack* stack, const char* name) { int i; - for(i=0; inum; i++) { + for(i=0; inum; i++) { if(strcmp(stack->mod[i]->name, name) == 0) return i; } return -1; } + +size_t +mod_get_mem(struct module_env* env, const char* name) +{ + int m = modstack_find(&env->mesh->mods, name); + if(m != -1) { + fptr_ok(fptr_whitelist_mod_get_mem(env->mesh-> + mods.mod[m]->get_mem)); + return (*env->mesh->mods.mod[m]->get_mem)(env, m); + } + return 0; +} --- contrib/unbound/services/modstack.h.orig +++ contrib/unbound/services/modstack.h @@ -110,4 +110,7 @@ */ int modstack_find(struct module_stack* stack, const char* name); +/** fetch memory for a module by name, returns 0 if module not there */ +size_t mod_get_mem(struct module_env* env, const char* name); + #endif /* SERVICES_MODSTACK_H */ --- contrib/unbound/services/outside_network.c.orig +++ contrib/unbound/services/outside_network.c @@ -48,6 +48,7 @@ #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" @@ -62,6 +63,9 @@ #ifdef HAVE_OPENSSL_SSL_H #include #endif +#ifdef HAVE_X509_VERIFY_PARAM_SET1_HOST +#include +#endif #ifdef HAVE_NETDB_H #include @@ -198,18 +202,17 @@ return 1; } -/** use next free buffer to service a tcp query */ -static int -outnet_tcp_take_into_use(struct waiting_tcp* w, uint8_t* pkt, size_t pkt_len) +/** get TCP file descriptor for address, returns -1 on failure, + * tcp_mss is 0 or maxseg size to set for TCP packets. */ +int +outnet_get_tcp_fd(struct sockaddr_storage* addr, socklen_t addrlen, int tcp_mss) { - struct pending_tcp* pend = w->outnet->tcp_free; int s; - log_assert(pend); - log_assert(pkt); - log_assert(w->addrlen > 0); - /* open socket */ +#ifdef SO_REUSEADDR + int on = 1; +#endif #ifdef INET6 - if(addr_is_ip6(&w->addr, w->addrlen)) + if(addr_is_ip6(addr, addrlen)) s = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP); else #endif @@ -217,21 +220,28 @@ if(s == -1) { #ifndef USE_WINSOCK log_err_addr("outgoing tcp: socket", strerror(errno), - &w->addr, w->addrlen); + addr, addrlen); #else log_err_addr("outgoing tcp: socket", - wsa_strerror(WSAGetLastError()), &w->addr, w->addrlen); + wsa_strerror(WSAGetLastError()), addr, addrlen); #endif - return 0; + return -1; } - if (w->outnet->tcp_mss > 0) { +#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 + + if(tcp_mss > 0) { #if defined(IPPROTO_TCP) && defined(TCP_MAXSEG) if(setsockopt(s, IPPROTO_TCP, TCP_MAXSEG, - (void*)&w->outnet->tcp_mss, - (socklen_t)sizeof(w->outnet->tcp_mss)) < 0) { + (void*)&tcp_mss, (socklen_t)sizeof(tcp_mss)) < 0) { verbose(VERB_ALGO, "outgoing tcp:" - " setsockopt(.. SO_REUSEADDR ..) failed"); + " setsockopt(.. TCP_MAXSEG ..) failed"); } #else verbose(VERB_ALGO, "outgoing tcp:" @@ -239,6 +249,53 @@ #endif /* defined(IPPROTO_TCP) && defined(TCP_MAXSEG) */ } + return s; +} + +/** connect tcp connection to addr, 0 on failure */ +int +outnet_tcp_connect(int s, struct sockaddr_storage* addr, socklen_t addrlen) +{ + if(connect(s, (struct sockaddr*)addr, addrlen) == -1) { +#ifndef USE_WINSOCK +#ifdef EINPROGRESS + if(errno != EINPROGRESS) { +#endif + if(tcp_connect_errno_needs_log( + (struct sockaddr*)addr, addrlen)) + log_err_addr("outgoing tcp: connect", + strerror(errno), addr, addrlen); + close(s); + return 0; +#ifdef EINPROGRESS + } +#endif +#else /* USE_WINSOCK */ + if(WSAGetLastError() != WSAEINPROGRESS && + WSAGetLastError() != WSAEWOULDBLOCK) { + closesocket(s); + return 0; + } +#endif + } + return 1; +} + +/** use next free buffer to service a tcp query */ +static int +outnet_tcp_take_into_use(struct waiting_tcp* w, uint8_t* pkt, size_t pkt_len) +{ + struct pending_tcp* pend = w->outnet->tcp_free; + int s; + log_assert(pend); + log_assert(pkt); + log_assert(w->addrlen > 0); + /* open socket */ + s = outnet_get_tcp_fd(&w->addr, w->addrlen, w->outnet->tcp_mss); + + if(s == -1) + return 0; + if(!pick_outgoing_tcp(w, s)) return 0; @@ -258,6 +315,13 @@ 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; @@ -292,6 +356,10 @@ #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) { @@ -299,10 +367,51 @@ 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(w->tls_auth_name) { +#ifdef HAVE_SSL + (void)SSL_set_tlsext_host_name(pend->c->ssl, w->tls_auth_name); +#endif + } +#ifdef HAVE_SSL_SET1_HOST + if(w->tls_auth_name) { + SSL_set_verify(pend->c->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(pend->c->ssl, w->tls_auth_name)) { + log_err("SSL_set1_host failed"); + pend->c->fd = s; + SSL_free(pend->c->ssl); + pend->c->ssl = NULL; + comm_point_close(pend->c); + 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(w->tls_auth_name) { + X509_VERIFY_PARAM* param = SSL_get0_param(pend->c->ssl); + X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); + if(!X509_VERIFY_PARAM_set1_host(param, w->tls_auth_name, strlen(w->tls_auth_name))) { + log_err("X509_VERIFY_PARAM_set1_host failed"); + pend->c->fd = s; + SSL_free(pend->c->ssl); + pend->c->ssl = NULL; + comm_point_close(pend->c); + return 0; + } + SSL_set_verify(pend->c->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 */ } w->pkt = NULL; w->next_waiting = (void*)pend; @@ -334,7 +443,7 @@ if(outnet->tcp_wait_last == w) outnet->tcp_wait_last = NULL; if(!outnet_tcp_take_into_use(w, w->pkt, w->pkt_len)) { - comm_point_callback_t* cb = w->cb; + comm_point_callback_type* cb = w->cb; void* cb_arg = w->cb_arg; waiting_tcp_delete(w); fptr_ok(fptr_whitelist_pending_tcp(cb)); @@ -343,9 +452,9 @@ } } -/** decomission a tcp buffer, closes commpoint and frees waiting_tcp entry */ +/** decommission a tcp buffer, closes commpoint and frees waiting_tcp entry */ static void -decomission_pending_tcp(struct outside_network* outnet, +decommission_pending_tcp(struct outside_network* outnet, struct pending_tcp* pend) { if(pend->c->ssl) { @@ -385,7 +494,7 @@ } fptr_ok(fptr_whitelist_pending_tcp(pend->query->cb)); (void)(*pend->query->cb)(c, pend->query->cb_arg, error, reply_info); - decomission_pending_tcp(outnet, pend); + decommission_pending_tcp(outnet, pend); return 0; } @@ -676,7 +785,7 @@ outnet->delay_tv.tv_usec = (delayclose%1000)*1000; } #endif - if(numavailports == 0) { + if(numavailports == 0 || num_ports == 0) { log_err("no outgoing ports available"); outside_network_delete(outnet); return NULL; @@ -775,7 +884,7 @@ /** helper pending delete */ static void -pending_node_del(rbnode_t* node, void* arg) +pending_node_del(rbnode_type* node, void* arg) { struct pending* pend = (struct pending*)node; struct outside_network* outnet = (struct outside_network*)arg; @@ -784,12 +893,13 @@ /** helper serviced delete */ static void -serviced_node_del(rbnode_t* node, void* ATTR_UNUSED(arg)) +serviced_node_del(rbnode_type* node, void* ATTR_UNUSED(arg)) { struct serviced_query* sq = (struct serviced_query*)node; struct service_callback* p = sq->cblist, *np; free(sq->qbuf); free(sq->zone); + free(sq->tls_auth_name); edns_opt_list_free(sq->opt_list); while(p) { np = p->next; @@ -960,6 +1070,8 @@ 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); @@ -966,13 +1078,13 @@ } fd = create_udp_sock(AF_INET6, SOCK_DGRAM, (struct sockaddr*)&sa, addrlen, 1, inuse, &noproto, - 0, 0, 0, NULL, 0, freebind); + 0, 0, 0, NULL, 0, freebind, 0); } else { struct sockaddr_in* sa = (struct sockaddr_in*)addr; sa->sin_port = (in_port_t)htons((uint16_t)port); fd = create_udp_sock(AF_INET, SOCK_DGRAM, (struct sockaddr*)addr, addrlen, 1, inuse, &noproto, - 0, 0, 0, NULL, 0, 0); + 0, 0, 0, NULL, 0, 0, 0); } return fd; } @@ -1124,7 +1236,7 @@ struct pending* pending_udp_query(struct serviced_query* sq, struct sldns_buffer* packet, - int timeout, comm_point_callback_t* cb, void* cb_arg) + int timeout, comm_point_callback_type* cb, void* cb_arg) { struct pending* pend = (struct pending*)calloc(1, sizeof(*pend)); if(!pend) return NULL; @@ -1174,7 +1286,7 @@ { struct waiting_tcp* w = (struct waiting_tcp*)arg; struct outside_network* outnet = w->outnet; - comm_point_callback_t* cb; + comm_point_callback_type* cb; void* cb_arg; if(w->pkt) { /* it is on the waiting list */ @@ -1182,6 +1294,13 @@ } else { /* it was in use */ struct pending_tcp* pend=(struct pending_tcp*)w->next_waiting; + 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->query = NULL; pend->next_free = outnet->tcp_free; @@ -1197,7 +1316,7 @@ struct waiting_tcp* pending_tcp_query(struct serviced_query* sq, sldns_buffer* packet, - int timeout, comm_point_callback_t* callback, void* callback_arg) + int timeout, comm_point_callback_type* callback, void* callback_arg) { struct pending_tcp* pend = sq->outnet->tcp_free; struct waiting_tcp* w; @@ -1223,9 +1342,10 @@ w->cb = callback; w->cb_arg = callback_arg; w->ssl_upstream = sq->ssl_upstream; + w->tls_auth_name = sq->tls_auth_name; #ifndef S_SPLINT_S - tv.tv_sec = timeout; - tv.tv_usec = 0; + tv.tv_sec = timeout/1000; + tv.tv_usec = (timeout%1000)*1000; #endif comm_timer_set(w->timer, &tv); if(pend) { @@ -1296,12 +1416,12 @@ 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, - struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone, - size_t zonelen, int qtype, struct edns_option* opt_list) + char* tls_auth_name, struct sockaddr_storage* addr, socklen_t addrlen, + uint8_t* zone, size_t zonelen, int qtype, struct edns_option* opt_list) { struct serviced_query* sq = (struct serviced_query*)malloc(sizeof(*sq)); #ifdef UNBOUND_DEBUG - rbnode_t* ins; + rbnode_type* ins; #endif if(!sq) return NULL; @@ -1325,6 +1445,17 @@ sq->nocaps = nocaps; sq->tcp_upstream = tcp_upstream; sq->ssl_upstream = ssl_upstream; + if(tls_auth_name) { + sq->tls_auth_name = strdup(tls_auth_name); + if(!sq->tls_auth_name) { + free(sq->zone); + free(sq->qbuf); + free(sq); + return NULL; + } + } else { + sq->tls_auth_name = NULL; + } memcpy(&sq->addr, addr, addrlen); sq->addrlen = addrlen; sq->opt_list = NULL; @@ -1331,6 +1462,7 @@ if(opt_list) { sq->opt_list = edns_opt_copy_alloc(opt_list); if(!sq->opt_list) { + free(sq->tls_auth_name); free(sq->zone); free(sq->qbuf); free(sq); @@ -1381,7 +1513,6 @@ /* clear up the pending query */ if(sq->status == serviced_query_UDP_EDNS || sq->status == serviced_query_UDP || - sq->status == serviced_query_PROBE_EDNS || sq->status == serviced_query_UDP_EDNS_FRAG || sq->status == serviced_query_UDP_EDNS_fallback) { struct pending* p = (struct pending*)sq->pending; @@ -1395,7 +1526,7 @@ struct waiting_tcp* p = (struct waiting_tcp*) sq->pending; if(p->pkt == NULL) { - decomission_pending_tcp(sq->outnet, + decommission_pending_tcp(sq->outnet, (struct pending_tcp*)p->next_waiting); } else { waiting_list_remove(sq->outnet, p); @@ -1508,15 +1639,7 @@ sq->last_rtt = rtt; verbose(VERB_ALGO, "EDNS lookup known=%d vs=%d", edns_lame_known, vs); if(sq->status == serviced_initial) { - if(edns_lame_known == 0 && rtt > 5000 && rtt < 10001) { - /* perform EDNS lame probe - check if server is - * EDNS lame (EDNS queries to it are dropped) */ - verbose(VERB_ALGO, "serviced query: send probe to see " - " if use of EDNS causes timeouts"); - /* even 700 msec may be too small */ - rtt = 1000; - sq->status = serviced_query_PROBE_EDNS; - } else if(vs != -1) { + if(vs != -1) { sq->status = serviced_query_UDP_EDNS; } else { sq->status = serviced_query_UDP; @@ -1538,20 +1661,24 @@ static int serviced_check_qname(sldns_buffer* pkt, uint8_t* qbuf, size_t qbuflen) { - uint8_t* d1 = sldns_buffer_at(pkt, 12); + 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++; - if(sldns_buffer_limit(pkt) < 12+1+4) /* packet too small for qname */ - return 0; while(len1 != 0 || len2 != 0) { if(LABEL_IS_PTR(len1)) { - d1 = sldns_buffer_at(pkt, PTR_OFFSET(len1, *d1)); + /* 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; @@ -1563,6 +1690,9 @@ 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); @@ -1587,7 +1717,7 @@ uint8_t *backup_p = NULL; size_t backlen = 0; #ifdef UNBOUND_DEBUG - rbnode_t* rem = + rbnode_type* rem = #else (void) #endif @@ -1716,7 +1846,12 @@ } if(sq->tcp_upstream || sq->ssl_upstream) { struct timeval now = *sq->outnet->now_tv; - if(now.tv_sec > sq->last_sent_time.tv_sec || + 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 */ @@ -1726,7 +1861,7 @@ 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 < TCP_AUTH_QUERY_TIMEOUT*1000) { + 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)) @@ -1758,7 +1893,7 @@ if(!sq->pending) { /* delete from tree so that a retry by above layer does not * clash with this entry */ - log_err("serviced_tcp_initiate: failed to send tcp query"); + verbose(VERB_ALGO, "serviced_tcp_initiate: failed to send tcp query"); serviced_callbacks(sq, NETEVENT_CLOSED, NULL, NULL); } } @@ -1767,18 +1902,26 @@ static int serviced_tcp_send(struct serviced_query* sq, sldns_buffer* buff) { - int vs, rtt; + 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; - sq->pending = pending_tcp_query(sq, buff, TCP_AUTH_QUERY_TIMEOUT, + if(sq->tcp_upstream || sq->ssl_upstream) { + timeout = rtt; + if(rtt >= UNKNOWN_SERVER_NICENESS && rtt < TCP_AUTH_QUERY_TIMEOUT) + timeout = TCP_AUTH_QUERY_TIMEOUT; + } else { + timeout = TCP_AUTH_QUERY_TIMEOUT; + } + sq->pending = pending_tcp_query(sq, buff, timeout, serviced_tcp_callback, sq); return sq->pending != NULL; } @@ -1828,17 +1971,9 @@ struct serviced_query* sq = (struct serviced_query*)arg; struct outside_network* outnet = sq->outnet; struct timeval now = *sq->outnet->now_tv; - int fallback_tcp = 0; sq->pending = NULL; /* removed after callback */ if(error == NETEVENT_TIMEOUT) { - int rto = 0; - if(sq->status == serviced_query_PROBE_EDNS) { - /* non-EDNS probe failed; we do not know its status, - * keep trying with EDNS, timeout may not be caused - * by EDNS. */ - sq->status = serviced_query_UDP_EDNS; - } if(sq->status == serviced_query_UDP_EDNS && sq->last_rtt < 5000) { /* fallback to 1480/1280 */ sq->status = serviced_query_UDP_EDNS_FRAG; @@ -1854,9 +1989,9 @@ sq->status = serviced_query_UDP_EDNS; } sq->retry++; - if(!(rto=infra_rtt_update(outnet->infra, &sq->addr, sq->addrlen, + 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))) + (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, @@ -1866,20 +2001,14 @@ } return 0; } - if(rto >= RTT_MAX_TIMEOUT) { - fallback_tcp = 1; - /* UDP does not work, fallback to TCP below */ - } else { - serviced_callbacks(sq, NETEVENT_TIMEOUT, c, rep); - return 0; - } - } else if(error != NETEVENT_NOERROR) { + } + 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 - if(outnet->dtenv && + if(error == NETEVENT_NOERROR && outnet->dtenv && (outnet->dtenv->log_resolver_response_messages || outnet->dtenv->log_forwarder_response_messages)) dt_msg_send_outside_response(outnet->dtenv, &sq->addr, c->type, @@ -1886,9 +2015,8 @@ sq->zone, sq->zonelen, sq->qbuf, sq->qbuflen, &sq->last_sent_time, sq->outnet->now_tv, c->buffer); #endif - if(!fallback_tcp) { - if( (sq->status == serviced_query_UDP_EDNS - ||sq->status == serviced_query_UDP_EDNS_FRAG) + 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 @@ -1902,19 +2030,7 @@ serviced_callbacks(sq, NETEVENT_CLOSED, c, rep); } return 0; - } else if(sq->status == serviced_query_PROBE_EDNS) { - /* probe without EDNS succeeds, so we conclude that this - * host likely has EDNS packets dropped */ - log_addr(VERB_DETAIL, "timeouts, concluded that connection to " - "host drops EDNS packets", &sq->addr, sq->addrlen); - /* only store noEDNS in cache if domain is noDNSSEC */ - if(!sq->want_dnssec) - 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"); - } - sq->status = serviced_query_UDP; - } else if(sq->status == serviced_query_UDP_EDNS && + } 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", @@ -1924,7 +2040,7 @@ log_err("Out of memory caching edns works"); } sq->edns_lame_known = 1; - } else if(sq->status == serviced_query_UDP_EDNS_fallback && + } 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)) == @@ -1942,12 +2058,12 @@ } } else { log_addr(VERB_ALGO, "serviced query: EDNS fails, but " - "not stored because need DNSSEC for", &sq->addr, + "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 || + } + 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 */ @@ -1963,11 +2079,10 @@ sq->last_rtt, (time_t)now.tv_sec)) log_err("out of memory noting rtt."); } - } - } /* end of if_!fallback_tcp */ + } /* 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_tcp) { + if(LDNS_TC_WIRE(sldns_buffer_begin(c->buffer))) { /* fallback to TCP */ /* this discards partial UDP contents */ if(sq->status == serviced_query_UDP_EDNS || @@ -1986,17 +2101,22 @@ struct serviced_query* outnet_serviced_query(struct outside_network* outnet, - uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, - uint16_t flags, int dnssec, int want_dnssec, int nocaps, - int tcp_upstream, int ssl_upstream, struct edns_option* opt_list, + struct query_info* qinfo, uint16_t flags, 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, comm_point_callback_t* callback, void* callback_arg, - sldns_buffer* buff) + size_t zonelen, struct module_qstate* qstate, + comm_point_callback_type* callback, void* callback_arg, sldns_buffer* buff, + struct module_env* env) { struct serviced_query* sq; struct service_callback* cb; - serviced_gen_query(buff, qname, qnamelen, qtype, qclass, flags); - sq = lookup_serviced(outnet, buff, dnssec, addr, addrlen, opt_list); + if(!inplace_cb_query_call(env, qinfo, flags, addr, addrlen, zone, zonelen, + qstate, qstate->region)) + return NULL; + serviced_gen_query(buff, qinfo->qname, qinfo->qname_len, qinfo->qtype, + qinfo->qclass, flags); + sq = lookup_serviced(outnet, buff, dnssec, addr, addrlen, + qstate->edns_opts_back_out); /* 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). */ @@ -2005,8 +2125,9 @@ if(!sq) { /* make new serviced query entry */ sq = serviced_create(outnet, buff, dnssec, want_dnssec, nocaps, - tcp_upstream, ssl_upstream, addr, addrlen, zone, - zonelen, (int)qtype, opt_list); + tcp_upstream, ssl_upstream, tls_auth_name, addr, + addrlen, zone, zonelen, (int)qinfo->qtype, + qstate->edns_opts_back_out); if(!sq) { free(cb); return NULL; @@ -2015,9 +2136,7 @@ if(outnet->do_udp && !(tcp_upstream || ssl_upstream)) { if(!serviced_udp_send(sq, buff)) { (void)rbtree_delete(outnet->serviced, sq); - free(sq->qbuf); - free(sq->zone); - free(sq); + serviced_node_del(&sq->node, NULL); free(cb); return NULL; } @@ -2024,9 +2143,7 @@ } else { if(!serviced_tcp_send(sq, buff)) { (void)rbtree_delete(outnet->serviced, sq); - free(sq->qbuf); - free(sq->zone); - free(sq); + serviced_node_del(&sq->node, NULL); free(cb); return NULL; } @@ -2068,6 +2185,258 @@ } } +/** 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; + struct port_if* pif; + + /* create fd */ + 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; + 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]; + } + + if(addr_is_ip6(to_addr, to_addrlen)) { + struct sockaddr_in6 sa = *(struct sockaddr_in6*)addr; + sa.sin6_port = (in_port_t)htons((uint16_t)port); + fd = create_udp_sock(AF_INET6, SOCK_DGRAM, + (struct sockaddr*)&sa, addrlen, 1, &inuse, &noproto, + 0, 0, 0, NULL, 0, freebind, 0); + } else { + struct sockaddr_in* sa = (struct sockaddr_in*)addr; + sa->sin_port = (in_port_t)htons((uint16_t)port); + fd = create_udp_sock(AF_INET, SOCK_DGRAM, + (struct sockaddr*)addr, addrlen, 1, &inuse, &noproto, + 0, 0, 0, NULL, 0, freebind, 0); + } + if(fd != -1) { + return fd; + } + if(!inuse) { + return -1; + } + } + /* too many tries */ + log_err("cannot send probe, ports are in use"); + return -1; +} + +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, + cb, cb_arg); + 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_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); + X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); + 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); + 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.addrlen = to_addrlen; + memcpy(&cp->repinfo.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 http request headers in buffer for sending query to destination */ +static int +setup_http_request(sldns_buffer* buf, char* host, char* path) +{ + 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); + sldns_buffer_printf(buf, "User-Agent: unbound/%s\r\n", + PACKAGE_VERSION); + /* 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) +{ + /* 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); + 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.addrlen = to_addrlen; + memcpy(&cp->repinfo.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)) { + 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) @@ -2158,7 +2527,6 @@ s += sizeof(*sb); if(sq->status == serviced_query_UDP_EDNS || sq->status == serviced_query_UDP || - sq->status == serviced_query_PROBE_EDNS || sq->status == serviced_query_UDP_EDNS_FRAG || sq->status == serviced_query_UDP_EDNS_fallback) { s += sizeof(struct pending); --- contrib/unbound/services/outside_network.h.orig +++ contrib/unbound/services/outside_network.h @@ -59,6 +59,9 @@ struct serviced_query; struct dt_env; struct edns_option; +struct module_env; +struct module_qstate; +struct query_info; /** * Send queries to outside servers and wait for answers from servers. @@ -120,9 +123,9 @@ struct pending* udp_wait_last; /** pending udp answers. sorted by id, addr */ - rbtree_t* pending; + rbtree_type* pending; /** serviced queries, sorted by qbuf, addr, dnssec */ - rbtree_t* serviced; + rbtree_type* serviced; /** host cache, pointer but not owned by outnet. */ struct infra_cache* infra; /** where to get random numbers */ @@ -207,7 +210,7 @@ */ struct pending { /** redblacktree entry, key is the pending struct(id, addr). */ - rbnode_t node; + rbnode_type node; /** the ID for the query. int so that a value out of range can * be used to signify a pending that is for certain not present in * the rbtree. (and for which deletion is safe). */ @@ -221,7 +224,7 @@ /** timeout event */ struct comm_timer* timer; /** callback for the timeout, error or reply to the message */ - comm_point_callback_t* cb; + comm_point_callback_type* cb; /** callback user argument */ void* cb_arg; /** the outside network it is part of */ @@ -282,11 +285,13 @@ /** length of query packet. */ size_t pkt_len; /** callback for the timeout, error or reply to the message */ - comm_point_callback_t* cb; + comm_point_callback_type* cb; /** callback user argument */ void* cb_arg; /** if it uses ssl upstream */ int ssl_upstream; + /** ref to the tls_auth_name from the serviced_query */ + char* tls_auth_name; }; /** @@ -296,7 +301,7 @@ /** next in callback list */ struct service_callback* next; /** callback function */ - comm_point_callback_t* cb; + comm_point_callback_type* cb; /** user argument for callback function */ void* cb_arg; }; @@ -314,7 +319,7 @@ */ struct serviced_query { /** The rbtree node, key is this record */ - rbnode_t node; + rbnode_type node; /** The query that needs to be answered. Starts with flags u16, * then qdcount, ..., including qname, qtype, qclass. Does not include * EDNS record. */ @@ -329,6 +334,9 @@ int nocaps; /** tcp upstream used, use tcp, or ssl_upstream for SSL */ int tcp_upstream, ssl_upstream; + /** the name of the tls authentication name, eg. 'ns.example.com' + * or NULL */ + char* tls_auth_name; /** where to send it */ struct sockaddr_storage addr; /** length of addr field in use. */ @@ -351,8 +359,6 @@ serviced_query_TCP_EDNS, /** TCP without EDNS sent */ serviced_query_TCP, - /** probe to test EDNS lameness (EDNS is dropped) */ - serviced_query_PROBE_EDNS, /** probe to test noEDNS0 (EDNS gives FORMERRorNOTIMP) */ serviced_query_UDP_EDNS_fallback, /** probe to test TCP noEDNS0 (EDNS gives FORMERRorNOTIMP) */ @@ -368,7 +374,7 @@ int retry; /** time last UDP was sent */ struct timeval last_sent_time; - /** rtt of last (UDP) message */ + /** rtt of last message */ int last_rtt; /** do we know edns probe status already, for UDP_EDNS queries */ int edns_lame_known; @@ -440,7 +446,7 @@ * @return: NULL on error for malloc or socket. Else the pending query object. */ struct pending* pending_udp_query(struct serviced_query* sq, - struct sldns_buffer* packet, int timeout, comm_point_callback_t* callback, + struct sldns_buffer* packet, int timeout, comm_point_callback_type* callback, void* callback_arg); /** @@ -448,7 +454,7 @@ * checks id. * @param sq: serviced query. * @param packet: wireformat query to send to destination. copied from. - * @param timeout: in seconds from now. + * @param timeout: in milliseconds from now. * Timer starts running now. Timer may expire if all buffers are used, * without any query been sent to the server yet. * @param callback: function to call on error, timeout or reply. @@ -456,7 +462,7 @@ * @return: false on error for malloc or socket. Else the pending TCP object. */ struct waiting_tcp* pending_tcp_query(struct serviced_query* sq, - struct sldns_buffer* packet, int timeout, comm_point_callback_t* callback, + struct sldns_buffer* packet, int timeout, comm_point_callback_type* callback, void* callback_arg); /** @@ -471,10 +477,7 @@ * Perform a serviced query to the authoritative servers. * Duplicate efforts are detected, and EDNS, TCP and UDP retry is performed. * @param outnet: outside network, with rbtree of serviced queries. - * @param qname: what qname to query. - * @param qnamelen: length of qname in octets including 0 root label. - * @param qtype: rrset type to query (host format) - * @param qclass: query class. (host format) + * @param qinfo: query info. * @param flags: flags u16 (host format), includes opcode, CD bit. * @param dnssec: if set, DO bit is set in EDNS queries. * If the value includes BIT_CD, CD bit is set when in EDNS queries. @@ -484,10 +487,8 @@ * @param nocaps: ignore use_caps_for_id and use unperturbed qname. * @param tcp_upstream: use TCP for upstream queries. * @param ssl_upstream: use SSL for upstream queries. - * @param opt_list: pass edns option list (deep copied into serviced query) - * these options are set on the outgoing packets. - * @param callback: callback function. - * @param callback_arg: user argument to callback function. + * @param tls_auth_name: when ssl_upstream is true, use this name to check + * the server's peer certificate. * @param addr: to which server to send the query. * @param addrlen: length of addr. * @param zone: name of the zone of the delegation point. wireformat dname. @@ -494,17 +495,22 @@ This is the delegation point name for which the server is deemed authoritative. * @param zonelen: length of zone. + * @param qstate: module qstate. Mainly for inspecting the available + * edns_opts_lists. + * @param callback: callback function. + * @param callback_arg: user argument to callback function. * @param buff: scratch buffer to create query contents in. Empty on exit. + * @param env: the module environment. * @return 0 on error, or pointer to serviced query that is used to answer * this serviced query may be shared with other callbacks as well. */ struct serviced_query* outnet_serviced_query(struct outside_network* outnet, - uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, - uint16_t flags, int dnssec, int want_dnssec, int nocaps, - int tcp_upstream, int ssl_upstream, struct edns_option* opt_list, + struct query_info* qinfo, uint16_t flags, 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, comm_point_callback_t* callback, void* callback_arg, - struct sldns_buffer* buff); + size_t zonelen, struct module_qstate* qstate, + comm_point_callback_type* callback, void* callback_arg, + struct sldns_buffer* buff, struct module_env* env); /** * Remove service query callback. @@ -532,6 +538,72 @@ */ size_t serviced_get_mem(struct serviced_query* sq); +/** get TCP file descriptor for address, returns -1 on failure, + * tcp_mss is 0 or maxseg size to set for TCP packets. */ +int outnet_get_tcp_fd(struct sockaddr_storage* addr, socklen_t addrlen, int tcp_mss); + +/** + * Create udp commpoint suitable for sending packets to the destination. + * @param outnet: outside_network with the comm_base it is attached to, + * with the outgoing interfaces chosen from, and rnd gen for random. + * @param cb: callback function for the commpoint. + * @param cb_arg: callback argument for cb. + * @param to_addr: intended destination. + * @param to_addrlen: length of to_addr. + * @return commpoint that you can comm_point_send_udp_msg with, or NULL. + */ +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); + +/** + * Create tcp commpoint suitable for communication to the destination. + * It also performs connect() to the to_addr. + * @param outnet: outside_network with the comm_base it is attached to, + * and the tcp_mss. + * @param cb: callback function for the commpoint. + * @param cb_arg: callback argument for cb. + * @param to_addr: intended destination. + * @param to_addrlen: length of to_addr. + * @param query: initial packet to send writing, in buffer. It is copied + * to the commpoint buffer that is created. + * @param timeout: timeout for the TCP connection. + * timeout in milliseconds, or -1 for no (change to the) timeout. + * So seconds*1000. + * @param ssl: set to true for TLS. + * @param host: hostname for host name verification of TLS (or NULL if no TLS). + * @return tcp_out commpoint, or NULL. + */ +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, + struct sldns_buffer* query, int timeout, int ssl, char* host); + +/** + * Create http commpoint suitable for communication to the destination. + * Creates the http request buffer. It also performs connect() to the to_addr. + * @param outnet: outside_network with the comm_base it is attached to, + * and the tcp_mss. + * @param cb: callback function for the commpoint. + * @param cb_arg: callback argument for cb. + * @param to_addr: intended destination. + * @param to_addrlen: length of to_addr. + * @param timeout: timeout for the TCP connection. + * timeout in milliseconds, or -1 for no (change to the) timeout. + * So seconds*1000. + * @param ssl: set to true for https. + * @param host: hostname to use for the destination. part of http request. + * @param path: pathname to lookup, eg. name of the file on the destination. + * @return http_out commpoint, or NULL. + */ +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); + +/** connect tcp connection to addr, 0 on failure */ +int outnet_tcp_connect(int s, struct sockaddr_storage* addr, socklen_t addrlen); + /** callback for incoming udp answers from the network */ int outnet_udp_cb(struct comm_point* c, void* arg, int error, struct comm_reply *reply_info); --- contrib/unbound/services/rpz.c.orig +++ contrib/unbound/services/rpz.c @@ -0,0 +1,1015 @@ +/* + * 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" + +/** string for RPZ action enum */ +const char* +rpz_action_to_string(enum rpz_action a) +{ + switch(a) { + case RPZ_NXDOMAIN_ACTION: return "nxdomain"; + case RPZ_NODATA_ACTION: return "nodata"; + case RPZ_PASSTHRU_ACTION: return "passthru"; + case RPZ_DROP_ACTION: return "drop"; + case RPZ_TCP_ONLY_ACTION: return "tcp_only"; + case RPZ_INVALID_ACTION: return "invalid"; + case RPZ_LOCAL_DATA_ACTION: return "local_data"; + case RPZ_DISABLED_ACTION: return "disabled"; + case RPZ_CNAME_OVERRIDE_ACTION: return "cname_override"; + case RPZ_NO_OVERRIDE_ACTION: return "no_override"; + } + return "unknown"; +} + +/** 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; + 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 "qname"; + case RPZ_CLIENT_IP_TRIGGER: return "client_ip"; + case RPZ_RESPONSE_IP_TRIGGER: return "response_ip"; + case RPZ_NSDNAME_TRIGGER: return "nsdname"; + case RPZ_NSIP_TRIGGER: return "nsip"; + case RPZ_INVALID_TRIGGER: return "invalid"; + } + return "unknown"; +} + +/** + * 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; +} + +/** + * 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: + 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_INVALID_ACTION: /* fallthrough */ + case RPZ_TCP_ONLY_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_INVALID_ACTION: /* fallthrough */ + case RPZ_TCP_ONLY_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_invalid: + 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_invalid: + 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; +} + +void rpz_delete(struct rpz* r) +{ + if(!r) + return; + local_zones_delete(r->local_zones); + respip_set_delete(r->respip_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); + respip_set_delete(r->respip_set); + if(!(r->local_zones = local_zones_create())){ + return 0; + } + if(!(r->respip_set = respip_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); +} + +/** 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; + } + if(!(r->respip_set = respip_set_create())) { + 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("cannot parse RPZ 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; + 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->respip_set) + respip_set_delete(r->respip_set); + if(r->taglist) + free(r->taglist); + 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 */ +} + +/** Insert RR into RPZ's local-zone */ +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) +{ + struct local_zone* z; + enum localzone_type tp = local_zone_always_transparent; + int dnamelabs = dname_count_labels(dname); + char* rrstr; + int newzone = 0; + + if(a == RPZ_TCP_ONLY_ACTION || a == RPZ_INVALID_ACTION) { + verbose(VERB_ALGO, "RPZ: skipping unsupported action: %s", + rpz_action_to_string(a)); + free(dname); + return; + } + + lock_rw_wrlock(&r->local_zones->lock); + /* exact match */ + z = local_zones_find(r->local_zones, dname, dnamelen, dnamelabs, + LDNS_RR_CLASS_IN); + if(z && a != RPZ_LOCAL_DATA_ACTION) { + rrstr = sldns_wire2str_rr(rr, rr_len); + if(!rrstr) { + log_err("malloc error while inserting RPZ qname " + "trigger"); + free(dname); + lock_rw_unlock(&r->local_zones->lock); + return; + } + verbose(VERB_ALGO, "RPZ: skipping duplicate record: '%s'", + rrstr); + free(rrstr); + free(dname); + lock_rw_unlock(&r->local_zones->lock); + return; + } + if(!z) { + tp = rpz_action_to_localzone_type(a); + if(!(z = local_zones_add_zone(r->local_zones, dname, dnamelen, + dnamelabs, rrclass, tp))) { + log_warn("RPZ create failed"); + lock_rw_unlock(&r->local_zones->lock); + /* dname will be free'd in failed local_zone_create() */ + return; + } + newzone = 1; + } + if(a == RPZ_LOCAL_DATA_ACTION) { + rrstr = sldns_wire2str_rr(rr, rr_len); + if(!rrstr) { + log_err("malloc error while inserting RPZ qname " + "trigger"); + free(dname); + lock_rw_unlock(&r->local_zones->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(&r->local_zones->lock); + return; +} + +/** 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 resp_addr* node; + struct sockaddr_storage addr; + socklen_t addrlen; + int net, af; + char* rrstr; + enum respip_action respa = rpz_action_to_respip_action(a); + + if(a == RPZ_TCP_ONLY_ACTION || a == RPZ_INVALID_ACTION || + respa == respip_invalid) { + verbose(VERB_ALGO, "RPZ: skipping unsupported action: %s", + rpz_action_to_string(a)); + return 0; + } + + if(!netblockdnametoaddr(dname, dnamelen, &addr, &addrlen, &net, &af)) + return 0; + + lock_rw_wrlock(&r->respip_set->lock); + rrstr = sldns_wire2str_rr(rr, rr_len); + if(!rrstr) { + log_err("malloc error while inserting RPZ respip trigger"); + lock_rw_unlock(&r->respip_set->lock); + return 0; + } + if(!(node=respip_sockaddr_find_or_create(r->respip_set, &addr, addrlen, + net, 1, rrstr))) { + lock_rw_unlock(&r->respip_set->lock); + free(rrstr); + return 0; + } + + lock_rw_wrlock(&node->lock); + lock_rw_unlock(&r->respip_set->lock); + node->action = respa; + + if(a == RPZ_LOCAL_DATA_ACTION) { + respip_enter_rr(r->respip_set->region, node, rrtype, + rrclass, ttl, rdata, rdata_len, rrstr, ""); + } + lock_rw_unlock(&node->lock); + free(rrstr); + return 1; +} + +int +rpz_insert_rr(struct rpz* r, 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; + + log_assert(dnamelen >= aznamelen); + if(!(policydname = calloc(1, (dnamelen-aznamelen)+1))) + 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) { + 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 { + free(policydname); + verbose(VERB_ALGO, "RPZ: skipping unsupported trigger: %s", + rpz_trigger_to_string(t)); + } + return 1; +} + +/** + * Find RPZ local-zone by qname. + * @param r: rpz containing local-zone tree + * @param qname: qname + * @param qname_len: length of qname + * @param qclass: qclass + * @param only_exact: if 1 only excact (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 rpz* r, 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, ce_labs; + uint8_t wc[LDNS_MAX_DOMAINLEN+1]; + int exact; + struct local_zone* z = NULL; + if(wr) { + lock_rw_wrlock(&r->local_zones->lock); + } else { + lock_rw_rdlock(&r->local_zones->lock); + } + z = local_zones_find_le(r->local_zones, qname, qname_len, + dname_count_labels(qname), + LDNS_RR_CLASS_IN, &exact); + if(!z || (only_exact && !exact)) { + lock_rw_unlock(&r->local_zones->lock); + return NULL; + } + if(wr) { + lock_rw_wrlock(&z->lock); + } else { + lock_rw_rdlock(&z->lock); + } + if(!zones_keep_lock) { + lock_rw_unlock(&r->local_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 */ || !*ce /* root */) { + lock_rw_unlock(&z->lock); + if(zones_keep_lock) { + lock_rw_unlock(&r->local_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(&r->local_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(&r->local_zones->lock); + } else { + lock_rw_rdlock(&r->local_zones->lock); + } + } + z = local_zones_find_le(r->local_zones, wc, + ce_len+2, ce_labs+1, qclass, &exact); + if(!z || !exact) { + lock_rw_unlock(&r->local_zones->lock); + return NULL; + } + if(wr) { + lock_rw_wrlock(&z->lock); + } else { + lock_rw_rdlock(&z->lock); + } + if(!zones_keep_lock) { + lock_rw_unlock(&r->local_zones->lock); + } + return z; +} + +/** + * 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: lenth 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'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) +{ + struct local_zone* z; + int delete_zone = 1; + z = rpz_find_zone(r, 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)) { + return; + } + lock_rw_unlock(&z->lock); + if(delete_zone) { + local_zones_del_zone(r->local_zones, z); + } + lock_rw_unlock(&r->local_zones->lock); + return; +} + +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); +} + +void +rpz_remove_rr(struct rpz* r, 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(!(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_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); + } + free(policydname); +} + +/** print log information for an applied RPZ policy. Based on local-zone's + * lz_inform_print(). + */ +static void +log_rpz_apply(uint8_t* dname, enum rpz_action a, struct query_info* qinfo, + struct comm_reply* repinfo, char* log_name) +{ + char ip[128], txt[512]; + char dnamestr[LDNS_MAX_DOMAINLEN+1]; + uint16_t port = ntohs(((struct sockaddr_in*)&repinfo->addr)->sin_port); + dname_str(dname, dnamestr); + addr_to_str(&repinfo->addr, repinfo->addrlen, ip, sizeof(ip)); + if(log_name) + snprintf(txt, sizeof(txt), "RPZ applied [%s] %s %s %s@%u", + log_name, dnamestr, rpz_action_to_string(a), ip, + (unsigned)port); + else + snprintf(txt, sizeof(txt), "RPZ applied %s %s %s@%u", + dnamestr, rpz_action_to_string(a), ip, (unsigned)port); + log_nametypeclass(0, txt, qinfo->qname, qinfo->qtype, qinfo->qclass); +} + +int +rpz_apply_qname_trigger(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) +{ + struct rpz* r; + int ret; + enum localzone_type lzt; + struct local_zone* z = NULL; + struct local_data* ld = NULL; + lock_rw_rdlock(&az->rpz_lock); + for(r = az->rpz_first; r; r = r->next) { + if(!r->taglist || taglist_intersect(r->taglist, + r->taglistlen, taglist, taglen)) { + z = rpz_find_zone(r, qinfo->qname, qinfo->qname_len, + qinfo->qclass, 0, 0, 0); + if(z && r->action_override == RPZ_DISABLED_ACTION) { + if(r->log) + log_rpz_apply(z->name, + r->action_override, + qinfo, repinfo, r->log_name); + /* TODO only register stats when stats_extended? + * */ + stats->rpz_action[r->action_override]++; + lock_rw_unlock(&z->lock); + z = NULL; + } + if(z) + break; + } + } + lock_rw_unlock(&az->rpz_lock); + if(!z) + return 0; + + + 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_CNAME_OVERRIDE_ACTION) { + qinfo->local_alias = + regional_alloc_zero(temp, sizeof(struct local_rrset)); + if(!qinfo->local_alias) { + lock_rw_unlock(&z->lock); + 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) { + lock_rw_unlock(&z->lock); + 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(z->name, RPZ_CNAME_OVERRIDE_ACTION, + qinfo, repinfo, r->log_name); + stats->rpz_action[RPZ_CNAME_OVERRIDE_ACTION]++; + lock_rw_unlock(&z->lock); + 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(z->name, + localzone_type_to_rpz_action(lzt), qinfo, + repinfo, r->log_name); + stats->rpz_action[localzone_type_to_rpz_action(lzt)]++; + lock_rw_unlock(&z->lock); + return !qinfo->local_alias; + } + + ret = local_zones_zone_answer(z, env, qinfo, edns, repinfo, buf, temp, + 0 /* no local data used */, lzt); + if(r->log) + log_rpz_apply(z->name, localzone_type_to_rpz_action(lzt), + qinfo, repinfo, r->log_name); + stats->rpz_action[localzone_type_to_rpz_action(lzt)]++; + lock_rw_unlock(&z->lock); + + return ret; +} --- contrib/unbound/services/rpz.h.orig +++ contrib/unbound/services/rpz.h @@ -0,0 +1,201 @@ +/* + * services/rpz.h - 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. + */ + +#ifndef SERVICES_RPZ_H +#define SERVICES_RPZ_H + +#include "services/localzone.h" +#include "util/locks.h" +#include "util/log.h" +#include "util/config_file.h" +#include "services/authzone.h" +#include "sldns/sbuffer.h" +#include "daemon/stats.h" +#include "respip/respip.h" + +/** + * RPZ triggers, only the QNAME trigger is currently supported in Unbound. + */ +enum rpz_trigger { + RPZ_QNAME_TRIGGER = 0, + /* unsupported triggers */ + RPZ_CLIENT_IP_TRIGGER, /* rpz-client-ip */ + RPZ_RESPONSE_IP_TRIGGER, /* rpz-ip */ + RPZ_NSDNAME_TRIGGER, /* rpz-nsdname */ + RPZ_NSIP_TRIGGER, /* rpz-nsip */ + RPZ_INVALID_TRIGGER, /* dname does not contain valid trigger */ +}; + +/** + * RPZ actions. + */ +enum rpz_action { + RPZ_NXDOMAIN_ACTION = 0,/* CNAME . */ + RPZ_NODATA_ACTION, /* CNAME *. */ + RPZ_PASSTHRU_ACTION, /* CNAME rpz-passthru. */ + RPZ_DROP_ACTION, /* CNAME rpz-drop. */ + RPZ_TCP_ONLY_ACTION, /* CNAME rpz-tcp-only. */ + RPZ_INVALID_ACTION, /* CNAME with (child of) TLD starting with + "rpz-" in target, SOA, NS, DNAME and + DNSSEC-related records. */ + RPZ_LOCAL_DATA_ACTION, /* anything else */ + /* RPZ override actions */ + RPZ_DISABLED_ACTION, /* RPZ action disabled using override */ + RPZ_NO_OVERRIDE_ACTION, /* RPZ action no override*/ + RPZ_CNAME_OVERRIDE_ACTION, /* RPZ CNAME action override*/ +}; + +/** + * RPZ containing policies. Pointed to from corresponding auth-zone. Part of a + * linked list to keep configuration order. Iterating or changing the linked + * list requires the rpz_lock from struct auth_zones. + */ +struct rpz { + struct local_zones* local_zones; + struct respip_set* respip_set; + uint8_t* taglist; + size_t taglistlen; + enum rpz_action action_override; + struct ub_packed_rrset_key* cname_override; + int log; + char* log_name; + struct rpz* next; + struct rpz* prev; + struct regional* region; +}; + +/** + * Create policy from RR and add to this RPZ. + * @param r: the rpz to add the policy to. + * @param aznamelen: the length of the auth-zone name + * @param dname: dname of the RR + * @param dnamelen: length of the dname + * @param rr_type: RR type of the RR + * @param rr_class: RR class of the RR + * @param rr_ttl: TTL of the RR + * @param rdatawl: rdata of the RR, prepended with the rdata size + * @param rdatalen: length if the RR, including the prepended rdata size + * @param rr: the complete RR, for logging purposes + * @param rr_len: the length of the complete RR + * @return: 0 on error + */ +int rpz_insert_rr(struct rpz* r, 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); + +/** + * Delete policy matching RR, used for IXFR. + * @param r: the rpz to add the policy to. + * @param aznamelen: the length of the auth-zone name + * @param dname: dname of the RR + * @param dnamelen: length of the dname + * @param rr_type: RR type of the RR + * @param rr_class: RR class of the RR + * @param rdatawl: rdata of the RR, prepended with the rdata size + * @param rdatalen: length if the RR, including the prepended rdata size + */ +void rpz_remove_rr(struct rpz* r, size_t aznamelen, uint8_t* dname, + size_t dnamelen, uint16_t rr_type, uint16_t rr_class, uint8_t* rdatawl, + size_t rdatalen); + +/** + * Walk over the RPZ zones to find and apply a QNAME trigger policy. + * @param az: auth_zones struct, containing first RPZ item and RPZ lock + * @param env: module env + * @param qinfo: qinfo containing qname and qtype + * @param edns: edns data + * @param buf: buffer to write answer to + * @param temp: scratchpad + * @param repinfo: reply info + * @param taglist: taglist to lookup. + * @param taglen: lenth of taglist. + * @param stats: worker stats struct + * @return: 1 if client answer is ready, 0 to continue resolving + */ +int rpz_apply_qname_trigger(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); + +/** + * Delete RPZ + * @param r: RPZ struct to delete + */ +void rpz_delete(struct rpz* r); + +/** + * Clear local-zones and respip data in RPZ, used after reloading file or + * AXFR/HTTP transfer. + * @param r: RPZ to use + */ +int rpz_clear(struct rpz* r); + +/** + * Create RPZ. RPZ must be added to linked list after creation. + * @return: the newly created RPZ + */ +struct rpz* rpz_create(struct config_auth* p); + +/** + * String for RPZ action enum + * @param a: RPZ action to get string for + * @return: string for RPZ action + */ +const char* rpz_action_to_string(enum rpz_action a); + +enum rpz_action +respip_action_to_rpz_action(enum respip_action a); + +/** + * Prepare RPZ after procesing feed content. + * @param r: RPZ to use + */ +void rpz_finish_config(struct rpz* r); + +/** + * Classify respip action for RPZ action + * @param a: RPZ action + * @return: the respip action + */ +enum respip_action +rpz_action_to_respip_action(enum rpz_action a); + +#endif /* SERVICES_RPZ_H */ --- contrib/unbound/services/view.c.orig +++ contrib/unbound/services/view.c @@ -0,0 +1,248 @@ +/* + * services/view.c - named views containing local zones authority service. + * + * 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 functions to enable named views that can hold local zone + * authority service. + */ +#include "config.h" +#include "services/view.h" +#include "services/localzone.h" +#include "util/config_file.h" + +int +view_cmp(const void* v1, const void* v2) +{ + struct view* a = (struct view*)v1; + struct view* b = (struct view*)v2; + + return strcmp(a->name, b->name); +} + +struct views* +views_create(void) +{ + struct views* v = (struct views*)calloc(1, + sizeof(*v)); + if(!v) + return NULL; + rbtree_init(&v->vtree, &view_cmp); + lock_rw_init(&v->lock); + lock_protect(&v->lock, &v->vtree, sizeof(v->vtree)); + return v; +} + +/** This prototype is defined in in respip.h, but we want to avoid + * unnecessary dependencies */ +void respip_set_delete(struct respip_set *set); + +void +view_delete(struct view* v) +{ + if(!v) + return; + lock_rw_destroy(&v->lock); + local_zones_delete(v->local_zones); + respip_set_delete(v->respip_set); + free(v->name); + free(v); +} + +static void +delviewnode(rbnode_type* n, void* ATTR_UNUSED(arg)) +{ + struct view* v = (struct view*)n; + view_delete(v); +} + +void +views_delete(struct views* v) +{ + if(!v) + return; + lock_rw_destroy(&v->lock); + traverse_postorder(&v->vtree, delviewnode, NULL); + free(v); +} + +/** create a new view */ +static struct view* +view_create(char* name) +{ + struct view* v = (struct view*)calloc(1, sizeof(*v)); + if(!v) + return NULL; + v->node.key = v; + if(!(v->name = strdup(name))) { + free(v); + return NULL; + } + lock_rw_init(&v->lock); + lock_protect(&v->lock, &v->name, sizeof(*v)-sizeof(rbnode_type)); + return v; +} + +/** enter a new view returns with WRlock */ +static struct view* +views_enter_view_name(struct views* vs, char* name) +{ + struct view* v = view_create(name); + if(!v) { + log_err("out of memory"); + return NULL; + } + + /* add to rbtree */ + lock_rw_wrlock(&vs->lock); + lock_rw_wrlock(&v->lock); + if(!rbtree_insert(&vs->vtree, &v->node)) { + log_warn("duplicate view: %s", name); + lock_rw_unlock(&v->lock); + view_delete(v); + lock_rw_unlock(&vs->lock); + return NULL; + } + lock_rw_unlock(&vs->lock); + return v; +} + +int +views_apply_cfg(struct views* vs, struct config_file* cfg) +{ + struct config_view* cv; + struct view* v; + struct config_file lz_cfg; + /* Check existence of name in first view (last in config). Rest of + * views are already checked when parsing config. */ + if(cfg->views && !cfg->views->name) { + log_err("view without a name"); + return 0; + } + for(cv = cfg->views; cv; cv = cv->next) { + /* create and enter view */ + if(!(v = views_enter_view_name(vs, cv->name))) + return 0; + v->isfirst = cv->isfirst; + if(cv->local_zones || cv->local_data) { + if(!(v->local_zones = local_zones_create())){ + lock_rw_unlock(&v->lock); + return 0; + } + memset(&lz_cfg, 0, sizeof(lz_cfg)); + lz_cfg.local_zones = cv->local_zones; + lz_cfg.local_data = cv->local_data; + lz_cfg.local_zones_nodefault = + cv->local_zones_nodefault; + if(v->isfirst) { + /* Do not add defaults to view-specific + * local-zone when global local zone will be + * used. */ + struct config_strlist* nd; + lz_cfg.local_zones_disable_default = 1; + /* Add nodefault zones to list of zones to add, + * so they will be used as if they are + * configured as type transparent */ + for(nd = cv->local_zones_nodefault; nd; + nd = nd->next) { + char* nd_str, *nd_type; + nd_str = strdup(nd->str); + if(!nd_str) { + log_err("out of memory"); + lock_rw_unlock(&v->lock); + return 0; + } + nd_type = strdup("nodefault"); + if(!nd_type) { + log_err("out of memory"); + free(nd_str); + lock_rw_unlock(&v->lock); + return 0; + } + if(!cfg_str2list_insert( + &lz_cfg.local_zones, nd_str, + nd_type)) { + log_err("failed to insert " + "default zones into " + "local-zone list"); + lock_rw_unlock(&v->lock); + return 0; + } + } + } + if(!local_zones_apply_cfg(v->local_zones, &lz_cfg)){ + lock_rw_unlock(&v->lock); + return 0; + } + /* local_zones, local_zones_nodefault and local_data + * are free'd from config_view by local_zones_apply_cfg. + * Set pointers to NULL. */ + cv->local_zones = NULL; + cv->local_data = NULL; + cv->local_zones_nodefault = NULL; + } + lock_rw_unlock(&v->lock); + } + return 1; +} + +/** find a view by name */ +struct view* +views_find_view(struct views* vs, const char* name, int write) +{ + struct view* v; + struct view key; + key.node.key = &v; + key.name = (char *)name; + lock_rw_rdlock(&vs->lock); + if(!(v = (struct view*)rbtree_search(&vs->vtree, &key.node))) { + lock_rw_unlock(&vs->lock); + return 0; + } + if(write) { + lock_rw_wrlock(&v->lock); + } else { + lock_rw_rdlock(&v->lock); + } + lock_rw_unlock(&vs->lock); + return v; +} + +void views_print(struct views* v) +{ + /* TODO implement print */ + (void)v; +} --- contrib/unbound/services/view.h.orig +++ contrib/unbound/services/view.h @@ -0,0 +1,137 @@ +/* + * services/view.h - named views containing local zones authority service. + * + * 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 functions to enable named views that can hold local zone + * authority service. + */ + +#ifndef SERVICES_VIEW_H +#define SERVICES_VIEW_H +#include "util/rbtree.h" +#include "util/locks.h" +struct regional; +struct config_file; +struct config_view; +struct respip_set; + + +/** + * Views storage, shared. + */ +struct views { + /** lock on the view tree */ + lock_rw_type lock; + /** rbtree of struct view */ + rbtree_type vtree; +}; + +/** + * View. Named structure holding local authority zones. + */ +struct view { + /** rbtree node, key is name */ + rbnode_type node; + /** view name. + * Has to be right after rbnode_t due to pointer arithmetic in + * view_create's lock protect */ + char* name; + /** view specific local authority zones */ + struct local_zones* local_zones; + /** response-ip configuration data for this view */ + struct respip_set* respip_set; + /** Fallback to global local_zones when there is no match in the view + * specific tree. 1 for yes, 0 for no */ + int isfirst; + /** lock on the data in the structure + * For the node and name you need to also hold the views_tree lock to + * change them. */ + lock_rw_type lock; +}; + + +/** + * Create views storage + * @return new struct or NULL on error. + */ +struct views* views_create(void); + +/** + * Delete views storage + * @param v: views to delete. + */ +void views_delete(struct views* v); + +/** + * Apply config settings; + * Takes care of locking. + * @param v: view is set up. + * @param cfg: config data. + * @return false on error. + */ +int views_apply_cfg(struct views* v, struct config_file* cfg); + +/** + * Compare two view entries in rbtree. Sort canonical. + * @param v1: view 1 + * @param v2: view 2 + * @return: negative, positive or 0 comparison value. + */ +int view_cmp(const void* v1, const void* v2); + +/** + * Delete one view + * @param v: view to delete. + */ +void view_delete(struct view* v); + +/** + * Debug helper. Print all views + * Takes care of locking. + * @param v: the views tree + */ +void views_print(struct views* v); + +/* Find a view by name. + * @param vs: views + * @param name: name of the view we are looking for + * @param write: 1 for obtaining write lock on found view, 0 for read lock + * @return: locked view or NULL. + */ +struct view* views_find_view(struct views* vs, const char* name, int write); + +#endif /* SERVICES_VIEW_H */ --- contrib/unbound/sldns/keyraw.c.orig +++ contrib/unbound/sldns/keyraw.c @@ -90,6 +90,14 @@ case LDNS_ECDSAP384SHA384: return 384; #endif +#ifdef USE_ED25519 + case LDNS_ED25519: + return 256; +#endif +#ifdef USE_ED448 + case LDNS_ED448: + return 456; +#endif default: return 0; } @@ -388,6 +396,48 @@ } #endif /* USE_ECDSA */ +#ifdef USE_ED25519 +EVP_PKEY* +sldns_ed255192pkey_raw(const unsigned char* key, size_t keylen) +{ + /* ASN1 for ED25519 is 302a300506032b6570032100 <32byteskey> */ + uint8_t pre[] = {0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, + 0x70, 0x03, 0x21, 0x00}; + int pre_len = 12; + uint8_t buf[256]; + EVP_PKEY *evp_key; + /* pp gets modified by d2i() */ + const unsigned char* pp = (unsigned char*)buf; + if(keylen != 32 || keylen + pre_len > sizeof(buf)) + return NULL; /* wrong length */ + memmove(buf, pre, pre_len); + memmove(buf+pre_len, key, keylen); + evp_key = d2i_PUBKEY(NULL, &pp, (int)(pre_len+keylen)); + return evp_key; +} +#endif /* USE_ED25519 */ + +#ifdef USE_ED448 +EVP_PKEY* +sldns_ed4482pkey_raw(const unsigned char* key, size_t keylen) +{ + /* ASN1 for ED448 is 3043300506032b6571033a00 <57byteskey> */ + uint8_t pre[] = {0x30, 0x43, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, + 0x71, 0x03, 0x3a, 0x00}; + int pre_len = 12; + uint8_t buf[256]; + EVP_PKEY *evp_key; + /* pp gets modified by d2i() */ + const unsigned char* pp = (unsigned char*)buf; + if(keylen != 57 || keylen + pre_len > sizeof(buf)) + return NULL; /* wrong length */ + memmove(buf, pre, pre_len); + memmove(buf+pre_len, key, keylen); + evp_key = d2i_PUBKEY(NULL, &pp, (int)(pre_len+keylen)); + return evp_key; +} +#endif /* USE_ED448 */ + int sldns_digest_evp(unsigned char* data, unsigned int len, unsigned char* dest, const EVP_MD* md) --- contrib/unbound/sldns/keyraw.h.orig +++ contrib/unbound/sldns/keyraw.h @@ -93,6 +93,24 @@ RSA *sldns_key_buf2rsa_raw(unsigned char* key, size_t len); /** + * Converts a holding buffer with key material to EVP PKEY in openssl. + * Only available if ldns was compiled with ED25519. + * \param[in] key the uncompressed wireformat of the key. + * \param[in] len length of key data + * \return the key or NULL on error. + */ +EVP_PKEY* sldns_ed255192pkey_raw(const unsigned char* key, size_t len); + +/** + * Converts a holding buffer with key material to EVP PKEY in openssl. + * Only available if ldns was compiled with ED448. + * \param[in] key the uncompressed wireformat of the key. + * \param[in] len length of key data + * \return the key or NULL on error. + */ +EVP_PKEY* sldns_ed4482pkey_raw(const unsigned char* key, size_t len); + +/** * Utility function to calculate hash using generic EVP_MD pointer. * \param[in] data the data to hash. * \param[in] len length of data. --- contrib/unbound/sldns/parse.c.orig +++ contrib/unbound/sldns/parse.c @@ -33,7 +33,7 @@ sldns_fget_token_l(FILE *f, char *token, const char *delim, size_t limit, int *line_nr) { int c, prev_c; - int p; /* 0 -> no parenthese seen, >0 nr of ( seen */ + int p; /* 0 -> no parentheses seen, >0 nr of ( seen */ int com, quoted; char *t; size_t i; @@ -40,7 +40,7 @@ const char *d; const char *del; - /* standard delimeters */ + /* standard delimiters */ if (!delim) { /* from isspace(3) */ del = LDNS_PARSE_NORMAL; @@ -120,6 +120,10 @@ if (line_nr) { *line_nr = *line_nr + 1; } + if (limit > 0 && (i+1 >= limit || (size_t)(t-token)+1 >= limit)) { + *t = '\0'; + return -1; + } *t++ = ' '; prev_c = c; continue; @@ -137,7 +141,8 @@ if (c != '\0' && c != '\n') { i++; } - if (limit > 0 && (i >= limit || (size_t)(t-token) >= limit)) { + /* is there space for the character and the zero after it */ + if (limit > 0 && (i+1 >= limit || (size_t)(t-token)+1 >= limit)) { *t = '\0'; return -1; } @@ -240,7 +245,7 @@ size_t limit, int* par, const char* skipw) { int c, lc; - int p; /* 0 -> no parenthese seen, >0 nr of ( seen */ + int p; /* 0 -> no parentheses seen, >0 nr of ( seen */ int com, quoted; char *t; size_t i; @@ -321,8 +326,14 @@ if (c == '\n' && p != 0) { /* in parentheses */ /* do not write ' ' if we want to skip spaces */ - if(!(skipw && (strchr(skipw, c)||strchr(skipw, ' ')))) + if(!(skipw && (strchr(skipw, c)||strchr(skipw, ' ')))) { + /* check for space for the space character and a zero delimiter after that. */ + if (limit > 0 && (i+1 >= limit || (size_t)(t-token)+1 >= limit)) { + *t = '\0'; + return -1; + } *t++ = ' '; + } lc = c; continue; } @@ -344,7 +355,7 @@ } i++; - if (limit > 0 && (i >= limit || (size_t)(t-token) >= limit)) { + if (limit > 0 && (i+1 >= limit || (size_t)(t-token)+1 >= limit)) { *t = '\0'; return -1; } --- contrib/unbound/sldns/parse.h.orig +++ contrib/unbound/sldns/parse.h @@ -103,9 +103,9 @@ * after the keyword + k_del until we hit d_del * \param[in] f file pointer to read from * \param[in] keyword keyword to look for - * \param[in] k_del keyword delimeter + * \param[in] k_del keyword delimiter * \param[out] data the data found - * \param[in] d_del the data delimeter + * \param[in] d_del the data delimiter * \param[in] data_limit maximum size the the data buffer * \return the number of character read */ @@ -116,9 +116,9 @@ * after the keyword + k_del until we hit d_del * \param[in] f file pointer to read from * \param[in] keyword keyword to look for - * \param[in] k_del keyword delimeter + * \param[in] k_del keyword delimiter * \param[out] data the data found - * \param[in] d_del the data delimeter + * \param[in] d_del the data delimiter * \param[in] data_limit maximum size the the data buffer * \param[in] line_nr pointer to an integer containing the current line number (for debugging purposes) @@ -131,9 +131,9 @@ * after the keyword + k_del until we hit d_del * \param[in] b buffer pointer to read from * \param[in] keyword keyword to look for - * \param[in] k_del keyword delimeter + * \param[in] k_del keyword delimiter * \param[out] data the data found - * \param[in] d_del the data delimeter + * \param[in] d_del the data delimiter * \param[in] data_limit maximum size the the data buffer * \return the number of character read */ --- contrib/unbound/sldns/parseutil.c.orig +++ contrib/unbound/sldns/parseutil.c @@ -165,7 +165,7 @@ #endif /* SIZEOF_TIME_T <= 4 */ static int64_t -sldns_serial_arithmitics_time(int32_t time, time_t now) +sldns_serial_arithmetics_time(int32_t time, time_t now) { int32_t offset = time - (int32_t) now; return (int64_t) now + offset; @@ -172,13 +172,13 @@ } struct tm * -sldns_serial_arithmitics_gmtime_r(int32_t time, time_t now, struct tm *result) +sldns_serial_arithmetics_gmtime_r(int32_t time, time_t now, struct tm *result) { #if SIZEOF_TIME_T <= 4 - int64_t secs_since_epoch = sldns_serial_arithmitics_time(time, now); + int64_t secs_since_epoch = sldns_serial_arithmetics_time(time, now); return sldns_gmtime64_r(secs_since_epoch, result); #else - time_t secs_since_epoch = sldns_serial_arithmitics_time(time, now); + time_t secs_since_epoch = sldns_serial_arithmetics_time(time, now); return gmtime_r(&secs_since_epoch, result); #endif } @@ -402,10 +402,12 @@ /* ........ ........ ....4444 4....... ........ */ c = src[3] >> 7 ; + /* fallthrough */ case 3: dst[4] = b32[(src[2] & 0x0f) << 1 | c]; /* ........ .......3 3333.... ........ ........ */ c = src[2] >> 4 ; + /* fallthrough */ case 2: dst[3] = b32[(src[1] & 0x01) << 4 | c]; /* ........ ..22222. ........ ........ ........ */ @@ -413,6 +415,7 @@ /* .....111 11...... ........ ........ ........ */ c = src[1] >> 6 ; + /* fallthrough */ case 1: dst[1] = b32[(src[0] & 0x07) << 2 | c]; /* 00000... ........ ........ ........ ........ */ @@ -423,9 +426,12 @@ switch (src_sz) { case 1: dst[2] = '='; dst[3] = '='; + /* fallthrough */ case 2: dst[4] = '='; + /* fallthrough */ case 3: dst[5] = '='; dst[6] = '='; + /* fallthrough */ case 4: dst[7] = '='; } } @@ -537,15 +543,18 @@ /* ........ ........ ........ .55555.. ........ */ /* ........ ........ ....4444 4....... ........ */ dst[3] = buf[4] << 7 | buf[5] << 2 | buf[6] >> 3; + /* fallthrough */ case 5: /* ........ ........ ....4444 4....... ........ */ /* ........ .......3 3333.... ........ ........ */ dst[2] = buf[3] << 4 | buf[4] >> 1; + /* fallthrough */ case 4: /* ........ .......3 3333.... ........ ........ */ /* ........ ..22222. ........ ........ ........ */ /* .....111 11...... ........ ........ ........ */ dst[1] = buf[1] << 6 | buf[2] << 1 | buf[3] >> 4; + /* fallthrough */ case 2: /* .....111 11...... ........ ........ ........ */ /* 00000... ........ ........ ........ ........ */ --- contrib/unbound/sldns/parseutil.h.orig +++ contrib/unbound/sldns/parseutil.h @@ -62,13 +62,13 @@ * fields of RRSIG records. * * \param[in] time number of seconds since epoch (midnight, January 1st, 1970) - * to be intepreted as a serial arithmetics number relative to now. + * to be interpreted as a serial arithmetics number relative to now. * \param[in] now number of seconds since epoch (midnight, January 1st, 1970) * to which the time value is compared to determine the final value. * \param[out] result the struct with the broken-out time information * \return result on success or NULL on error */ -struct tm * sldns_serial_arithmitics_gmtime_r(int32_t time, time_t now, struct tm *result); +struct tm * sldns_serial_arithmetics_gmtime_r(int32_t time, time_t now, struct tm *result); /** * converts a ttl value (like 5d2h) to a long. --- contrib/unbound/sldns/rrdef.c.orig +++ contrib/unbound/sldns/rrdef.c @@ -175,7 +175,7 @@ LDNS_RDF_TYPE_TIME, LDNS_RDF_TYPE_TIME, LDNS_RDF_TYPE_INT16, - LDNS_RDF_TYPE_INT16, + LDNS_RDF_TYPE_TSIGERROR, LDNS_RDF_TYPE_INT16_DATA, LDNS_RDF_TYPE_INT16_DATA, }; @@ -185,7 +185,7 @@ LDNS_RDF_TYPE_INT16, LDNS_RDF_TYPE_INT16_DATA, LDNS_RDF_TYPE_INT16, - LDNS_RDF_TYPE_INT16, + LDNS_RDF_TYPE_TSIGERROR, LDNS_RDF_TYPE_INT16_DATA }; static const sldns_rdf_type type_tlsa_wireformat[] = { @@ -236,7 +236,7 @@ */ static sldns_rr_descriptor rdata_field_descriptors[] = { /* 0 */ - { 0, NULL, 0, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, + {(enum sldns_enum_rr_type)0, NULL, 0, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, /* 1 */ {LDNS_RR_TYPE_A, "A", 1, 1, type_a_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, /* 2 */ @@ -341,9 +341,10 @@ {LDNS_RR_TYPE_NSEC3PARAM, "NSEC3PARAM", 4, 4, type_nsec3param_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, /* 52 */ {LDNS_RR_TYPE_TLSA, "TLSA", 4, 4, type_tlsa_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, - -{LDNS_RR_TYPE_NULL, "TYPE53", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE54", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, + /* 53 */ + {LDNS_RR_TYPE_SMIMEA, "SMIMEA", 4, 4, type_tlsa_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, + /* 54 */ +{(enum sldns_enum_rr_type)0, "TYPE54", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, /* 55 * Hip ends with 0 or more Rendezvous Servers represented as dname's. * Hence the LDNS_RDF_TYPE_DNAME _variable field and the _maximum field @@ -357,8 +358,8 @@ /* 57 */ {LDNS_RR_TYPE_RKEY, "RKEY", 4, 4, type_key_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, #else -{LDNS_RR_TYPE_NULL, "TYPE56", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE57", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE56", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE57", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, #endif /* 58 */ {LDNS_RR_TYPE_TALINK, "TALINK", 2, 2, type_talink_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 2 }, @@ -371,54 +372,54 @@ {LDNS_RR_TYPE_OPENPGPKEY, "OPENPGPKEY", 1, 1, type_openpgpkey_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, /* 62 */ {LDNS_RR_TYPE_CSYNC, "CSYNC", 3, 3, type_csync_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE63", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE64", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE65", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE66", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE67", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE68", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE69", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE70", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE71", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE72", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE73", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE74", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE75", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE76", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE77", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE78", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE79", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE80", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE81", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE82", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE83", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE84", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE85", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE86", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE87", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE88", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE89", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE90", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE91", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE92", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE93", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE94", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE95", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE96", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE97", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE98", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE63", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE64", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE65", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE66", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE67", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE68", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE69", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE70", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE71", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE72", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE73", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE74", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE75", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE76", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE77", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE78", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE79", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE80", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE81", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE82", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE83", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE84", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE85", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE86", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE87", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE88", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE89", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE90", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE91", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE92", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE93", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE94", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE95", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE96", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE97", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE98", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, /* 99 */ {LDNS_RR_TYPE_SPF, "SPF", 1, 0, NULL, LDNS_RDF_TYPE_STR, LDNS_RR_NO_COMPRESS, 0 }, /* UINFO [IANA-Reserved] */ -{LDNS_RR_TYPE_NULL, "TYPE100", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE100", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, /* UID [IANA-Reserved] */ -{LDNS_RR_TYPE_NULL, "TYPE101", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE101", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, /* GID [IANA-Reserved] */ -{LDNS_RR_TYPE_NULL, "TYPE102", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE102", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, /* UNSPEC [IANA-Reserved] */ -{LDNS_RR_TYPE_NULL, "TYPE103", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE103", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, /* 104 */ {LDNS_RR_TYPE_NID, "NID", 2, 2, type_nid_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, @@ -434,145 +435,145 @@ /* 109 */ {LDNS_RR_TYPE_EUI64, "EUI64", 1, 1, type_eui64_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE110", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE111", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE112", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE113", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE114", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE115", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE116", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE117", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE118", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE119", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE120", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE121", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE122", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE123", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE124", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE125", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE126", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE127", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE128", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE129", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE130", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE131", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE132", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE133", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE134", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE135", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE136", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE137", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE138", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE139", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE140", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE141", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE142", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE143", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE144", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE145", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE146", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE147", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE148", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE149", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE150", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE151", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE152", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE153", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE154", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE155", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE156", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE157", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE158", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE159", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE160", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE161", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE162", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE163", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE164", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE165", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE166", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE167", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE168", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE169", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE170", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE171", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE172", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE173", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE174", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE175", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE176", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE177", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE178", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE179", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE180", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE181", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE182", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE183", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE184", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE185", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE186", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE187", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE188", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE189", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE190", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE191", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE192", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE193", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE194", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE195", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE196", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE197", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE198", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE199", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE200", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE201", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE202", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE203", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE204", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE205", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE206", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE207", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE208", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE209", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE210", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE211", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE212", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE213", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE214", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE215", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE216", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE217", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE218", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE219", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE220", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE221", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE222", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE223", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE224", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE225", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE226", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE227", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE228", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE229", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE230", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE231", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE232", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE233", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE234", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE235", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE236", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE237", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE238", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE239", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE240", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE241", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE242", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE243", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE244", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE245", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE246", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE247", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, -{LDNS_RR_TYPE_NULL, "TYPE248", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE110", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE111", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE112", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE113", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE114", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE115", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE116", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE117", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE118", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE119", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE120", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE121", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE122", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE123", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE124", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE125", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE126", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE127", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE128", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE129", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE130", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE131", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE132", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE133", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE134", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE135", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE136", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE137", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE138", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE139", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE140", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE141", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE142", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE143", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE144", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE145", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE146", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE147", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE148", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE149", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE150", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE151", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE152", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE153", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE154", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE155", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE156", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE157", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE158", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE159", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE160", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE161", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE162", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE163", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE164", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE165", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE166", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE167", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE168", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE169", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE170", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE171", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE172", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE173", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE174", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE175", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE176", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE177", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE178", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE179", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE180", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE181", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE182", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE183", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE184", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE185", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE186", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE187", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE188", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE189", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE190", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE191", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE192", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE193", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE194", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE195", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE196", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE197", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE198", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE199", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE200", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE201", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE202", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE203", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE204", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE205", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE206", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE207", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE208", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE209", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE210", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE211", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE212", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE213", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE214", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE215", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE216", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE217", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE218", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE219", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE220", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE221", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE222", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE223", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE224", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE225", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE226", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE227", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE228", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE229", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE230", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE231", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE232", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE233", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE234", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE235", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE236", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE237", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE238", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE239", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE240", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE241", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE242", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE243", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE244", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE245", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE246", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE247", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE248", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, /* LDNS_RDF_TYPE_INT16_DATA takes two fields (length and data) as one. * So, unlike RFC 2930 spec, we have 7 min/max rdf's i.s.o. 8/9. @@ -600,6 +601,12 @@ {LDNS_RR_TYPE_URI, "URI", 3, 3, type_uri_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, /* 257 */ {LDNS_RR_TYPE_CAA, "CAA", 3, 3, type_caa_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +#ifdef DRAFT_RRTYPES + /* 258 */ + {LDNS_RR_TYPE_AVC, "AVC", 1, 0, NULL, LDNS_RDF_TYPE_STR, LDNS_RR_NO_COMPRESS, 0 }, +#else +{(enum sldns_enum_rr_type)0, "TYPE258", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +#endif /* split in array, no longer contiguous */ @@ -607,7 +614,7 @@ /* 32768 */ {LDNS_RR_TYPE_TA, "TA", 4, 4, type_ds_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, #else -{LDNS_RR_TYPE_NULL, "TYPE32768", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, +{(enum sldns_enum_rr_type)0, "TYPE32768", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 }, #endif /* 32769 */ {LDNS_RR_TYPE_DLV, "DLV", 4, 4, type_ds_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 } @@ -703,18 +710,18 @@ /* special cases for query types */ if (strlen(name) == 4 && strncasecmp(name, "IXFR", 4) == 0) { - return 251; + return LDNS_RR_TYPE_IXFR; } else if (strlen(name) == 4 && strncasecmp(name, "AXFR", 4) == 0) { - return 252; + return LDNS_RR_TYPE_AXFR; } else if (strlen(name) == 5 && strncasecmp(name, "MAILB", 5) == 0) { - return 253; + return LDNS_RR_TYPE_MAILB; } else if (strlen(name) == 5 && strncasecmp(name, "MAILA", 5) == 0) { - return 254; + return LDNS_RR_TYPE_MAILA; } else if (strlen(name) == 3 && strncasecmp(name, "ANY", 3) == 0) { - return 255; + return LDNS_RR_TYPE_ANY; } - return 0; + return (enum sldns_enum_rr_type)0; } sldns_rr_class --- contrib/unbound/sldns/rrdef.h.orig +++ contrib/unbound/sldns/rrdef.h @@ -38,7 +38,7 @@ #define LDNS_KEY_REVOKE_KEY 0x0080 /* used to revoke KSK, rfc 5011 */ /* The first fields are contiguous and can be referenced instantly */ -#define LDNS_RDATA_FIELD_DESCRIPTORS_COMMON 258 +#define LDNS_RDATA_FIELD_DESCRIPTORS_COMMON 259 /** lookuptable for rr classes */ extern struct sldns_struct_lookup_table* sldns_rr_classes; @@ -182,9 +182,7 @@ LDNS_RR_TYPE_NSEC3PARAM = 51, /* RFC 5155 */ LDNS_RR_TYPE_NSEC3PARAMS = 51, LDNS_RR_TYPE_TLSA = 52, /* RFC 6698 */ - LDNS_RR_TYPE_SMIMEA = 53, /* draft-ietf-dane-smime, TLSA-like but may - be extended */ - + LDNS_RR_TYPE_SMIMEA = 53, /* RFC 8162 */ LDNS_RR_TYPE_HIP = 55, /* RFC 5205 */ /** draft-reid-dnsext-zs */ @@ -226,6 +224,7 @@ LDNS_RR_TYPE_ANY = 255, LDNS_RR_TYPE_URI = 256, /* RFC 7553 */ LDNS_RR_TYPE_CAA = 257, /* RFC 6844 */ + LDNS_RR_TYPE_AVC = 258, /** DNSSEC Trust Authorities */ LDNS_RR_TYPE_TA = 32768, @@ -330,13 +329,13 @@ LDNS_RDF_TYPE_NSEC3_NEXT_OWNER, /** 4 shorts represented as 4 * 16 bit hex numbers - * seperated by colons. For NID and L64. + * separated by colons. For NID and L64. */ LDNS_RDF_TYPE_ILNP64, - /** 6 * 8 bit hex numbers seperated by dashes. For EUI48. */ + /** 6 * 8 bit hex numbers separated by dashes. For EUI48. */ LDNS_RDF_TYPE_EUI48, - /** 8 * 8 bit hex numbers seperated by dashes. For EUI64. */ + /** 8 * 8 bit hex numbers separated by dashes. For EUI64. */ LDNS_RDF_TYPE_EUI64, /** A non-zero sequence of US-ASCII letters and numbers in lower case. @@ -350,6 +349,9 @@ */ LDNS_RDF_TYPE_LONG_STR, + /** TSIG extended 16bit error value */ + LDNS_RDF_TYPE_TSIGERROR, + /* Aliases */ LDNS_RDF_TYPE_BITMAP = LDNS_RDF_TYPE_NSEC }; @@ -372,6 +374,8 @@ LDNS_ECC_GOST = 12, /* RFC 5933 */ LDNS_ECDSAP256SHA256 = 13, /* RFC 6605 */ LDNS_ECDSAP384SHA384 = 14, /* RFC 6605 */ + LDNS_ED25519 = 15, /* RFC 8080 */ + LDNS_ED448 = 16, /* RFC 8080 */ LDNS_INDIRECT = 252, LDNS_PRIVATEDNS = 253, LDNS_PRIVATEOID = 254 @@ -420,7 +424,8 @@ LDNS_EDNS_DAU = 5, /* RFC6975 */ LDNS_EDNS_DHU = 6, /* RFC6975 */ LDNS_EDNS_N3U = 7, /* RFC6975 */ - LDNS_EDNS_CLIENT_SUBNET = 8, /* draft-vandergaast-edns-client-subnet */ + LDNS_EDNS_CLIENT_SUBNET = 8, /* RFC7871 */ + LDNS_EDNS_KEEPALIVE = 11, /* draft-ietf-dnsop-edns-tcp-keepalive*/ LDNS_EDNS_PADDING = 12 /* RFC7830 */ }; typedef enum sldns_enum_edns_option sldns_edns_option; @@ -427,6 +432,15 @@ #define LDNS_EDNS_MASK_DO_BIT 0x8000 +/** TSIG and TKEY extended rcodes (16bit), 0-15 are the normal rcodes. */ +#define LDNS_TSIG_ERROR_NOERROR 0 +#define LDNS_TSIG_ERROR_BADSIG 16 +#define LDNS_TSIG_ERROR_BADKEY 17 +#define LDNS_TSIG_ERROR_BADTIME 18 +#define LDNS_TSIG_ERROR_BADMODE 19 +#define LDNS_TSIG_ERROR_BADNAME 20 +#define LDNS_TSIG_ERROR_BADALG 21 + /** * Contains all information about resource record types. * --- contrib/unbound/sldns/sbuffer.c.orig +++ contrib/unbound/sldns/sbuffer.c @@ -48,6 +48,8 @@ buffer->_position = 0; buffer->_limit = buffer->_capacity = size; buffer->_fixed = 0; + if (!buffer->_fixed && buffer->_data) + free(buffer->_data); buffer->_data = malloc(size); if(!buffer->_data) { buffer->_status_err = 1; @@ -74,7 +76,7 @@ void *data; sldns_buffer_invariant(buffer); - assert(buffer->_position <= capacity); + assert(buffer->_position <= capacity && !buffer->_fixed); data = (uint8_t *) realloc(buffer->_data, capacity); if (!data) { @@ -126,19 +128,6 @@ if (written == -1) { buffer->_status_err = 1; return -1; - } else if ((size_t) written >= remaining) { - if (!sldns_buffer_reserve(buffer, (size_t) written + 1)) { - buffer->_status_err = 1; - return -1; - } - va_start(args, format); - written = vsnprintf((char *) sldns_buffer_current(buffer), - sldns_buffer_remaining(buffer), format, args); - va_end(args); - if (written == -1) { - buffer->_status_err = 1; - return -1; - } } buffer->_position += written; } @@ -158,13 +147,6 @@ free(buffer); } -void * -sldns_buffer_export(sldns_buffer *buffer) -{ - buffer->_fixed = 1; - return buffer->_data; -} - void sldns_buffer_copy(sldns_buffer* result, sldns_buffer* from) { --- contrib/unbound/sldns/sbuffer.h.orig +++ contrib/unbound/sldns/sbuffer.h @@ -87,6 +87,19 @@ } +INLINE void +sldns_write_uint48(void *dst, uint64_t data) +{ + uint8_t *p = (uint8_t *) dst; + p[0] = (uint8_t) ((data >> 40) & 0xff); + p[1] = (uint8_t) ((data >> 32) & 0xff); + p[2] = (uint8_t) ((data >> 24) & 0xff); + p[3] = (uint8_t) ((data >> 16) & 0xff); + p[4] = (uint8_t) ((data >> 8) & 0xff); + p[5] = (uint8_t) (data & 0xff); +} + + /** * \file sbuffer.h * @@ -368,7 +381,7 @@ { sldns_buffer_invariant(buffer); assert(at <= buffer->_limit); - return buffer->_limit - at; + return at < buffer->_limit ? buffer->_limit - at : 0; } /** @@ -425,10 +438,26 @@ } /** + * set the given byte to the buffer at the specified position + * \param[in] buffer the buffer + * \param[in] at the position (in number of bytes) to write the data at + * \param[in] c the byte to set to the buffer + * \param[in] count the number of bytes of bytes to write + */ + +INLINE void +sldns_buffer_set_at(sldns_buffer *buffer, size_t at, int c, size_t count) +{ + assert(sldns_buffer_available_at(buffer, at, count)); + memset(buffer->_data + at, c, count); +} + + +/** * writes count bytes of data to the current position of the buffer * \param[in] buffer the buffer * \param[in] data the data to write - * \param[in] count the lenght of the data to write + * \param[in] count the length of the data to write */ INLINE void sldns_buffer_write(sldns_buffer *buffer, const void *data, size_t count) @@ -524,6 +553,19 @@ } /** + * writes the given 6 byte integer at the given position in the buffer + * \param[in] buffer the buffer + * \param[in] at the position in the buffer + * \param[in] data the (lower) 48 bits to write + */ +INLINE void +sldns_buffer_write_u48_at(sldns_buffer *buffer, size_t at, uint64_t data) +{ + assert(sldns_buffer_available_at(buffer, at, 6)); + sldns_write_uint48(buffer->_data + at, data); +} + +/** * writes the given 4 byte integer at the current position in the buffer * \param[in] buffer the buffer * \param[in] data the 32 bits to write @@ -536,6 +578,18 @@ } /** + * writes the given 6 byte integer at the current position in the buffer + * \param[in] buffer the buffer + * \param[in] data the 48 bits to write + */ +INLINE void +sldns_buffer_write_u48(sldns_buffer *buffer, uint64_t data) +{ + sldns_buffer_write_u48_at(buffer, buffer->_position, data); + buffer->_position += 6; +} + +/** * copies count bytes of data at the given position to the given data-array * \param[in] buffer the buffer * \param[in] at the position in the buffer to start @@ -683,14 +737,6 @@ void sldns_buffer_free(sldns_buffer *buffer); /** - * Makes the buffer fixed and returns a pointer to the data. The - * caller is responsible for free'ing the result. - * \param[in] *buffer the buffer to be exported - * \return void - */ -void *sldns_buffer_export(sldns_buffer *buffer); - -/** * Copy contents of the from buffer to the result buffer and then flips * the result buffer. Data will be silently truncated if the result buffer is * too small. --- contrib/unbound/sldns/str2wire.c.orig +++ contrib/unbound/sldns/str2wire.c @@ -80,7 +80,7 @@ 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) + if (q >= buf + LDNS_MAX_DOMAINLEN) return RET_ERR(LDNS_WIREPARSE_ERR_DOMAINNAME_OVERFLOW, q-buf); switch (*s) { case '.': @@ -117,7 +117,7 @@ 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) { + if (q >= buf + LDNS_MAX_DOMAINLEN) { return RET_ERR(LDNS_WIREPARSE_ERR_DOMAINNAME_OVERFLOW, q-buf); } if (label_len > LDNS_MAX_LABELLEN) { @@ -150,6 +150,10 @@ 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); @@ -168,7 +172,9 @@ uint8_t dname[LDNS_MAX_DOMAINLEN+1]; *len = sizeof(dname); if(sldns_str2wire_dname_buf(str, dname, len) == 0) { - uint8_t* r = (uint8_t*)malloc(*len); + uint8_t* r; + if(*len > sizeof(dname)) return NULL; + r = (uint8_t*)malloc(*len); if(r) return memcpy(r, dname, *len); } *len = 0; @@ -187,7 +193,10 @@ sldns_buffer_position(strbuf)); } - if(strcmp(token, "@") == 0) { + 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; @@ -328,7 +337,7 @@ /** find delimiters for type */ static const char* -rrinternal_get_delims(sldns_rdf_type rdftype, uint16_t r_cnt, uint16_t r_max) +rrinternal_get_delims(sldns_rdf_type rdftype, size_t r_cnt, size_t r_max) { switch(rdftype) { case LDNS_RDF_TYPE_B64 : @@ -463,7 +472,7 @@ 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, uint16_t r_cnt, uint16_t r_max, size_t dname_len, + 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; @@ -613,7 +622,7 @@ uint8_t* origin, size_t origin_len) { const sldns_rr_descriptor *desc = sldns_rr_descript((uint16_t)rr_type); - uint16_t r_cnt, r_min, r_max; + 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; @@ -664,6 +673,14 @@ &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, "ed, &parens, + &pre_data_pos, delimiters, + rdftype, &token_strlen)) + break; } /* normal RR */ @@ -693,7 +710,7 @@ sldns_buffer_position(strbuf)); } /* write rdata length */ - sldns_write_uint16(rr+dname_len+8, rr_cur_len-dname_len-10); + sldns_write_uint16(rr+dname_len+8, (uint16_t)(rr_cur_len-dname_len-10)); *rr_len = rr_cur_len; return LDNS_WIREPARSE_ERR_OK; } @@ -828,7 +845,7 @@ } /* Strip whitespace from the start and the end of . */ -static char * +char * sldns_strip_ws(char *line) { char *s = line, *e; @@ -861,6 +878,8 @@ /* 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; @@ -868,6 +887,7 @@ 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; @@ -878,6 +898,7 @@ return s; } else if(strncmp(line, "$TTL", 4) == 0 && isspace((unsigned char)line[4])) { const char* end = NULL; + strlcpy((char*)rr, line, *len); *len = 0; *dname_len = 0; if(!parse_state) return LDNS_WIREPARSE_ERR_OK; @@ -884,11 +905,17 @@ parse_state->default_ttl = sldns_str2period( sldns_strip_ws(line+5), &end); } 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 { - return sldns_str2wire_rr_buf(line, rr, len, dname_len, + 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, @@ -896,6 +923,13 @@ (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); + } + return r; } return LDNS_WIREPARSE_ERR_OK; } @@ -940,6 +974,8 @@ 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: @@ -964,6 +1000,8 @@ 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: @@ -1065,7 +1103,7 @@ while(sldns_parse_char(&ch, &s)) { if(sl >= 255) return RET_ERR(LDNS_WIREPARSE_ERR_INVALID_STR, s-str); - if(*len < sl+1) + if(*len < sl+2) return RET_ERR(LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL, s-str); rd[++sl] = ch; @@ -1168,6 +1206,10 @@ { 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); @@ -1192,6 +1234,17 @@ 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; @@ -1201,6 +1254,10 @@ 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) @@ -1341,6 +1398,21 @@ 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 */ @@ -1369,7 +1441,7 @@ if (tm.tm_sec < 0 || tm.tm_sec > 59) return LDNS_WIREPARSE_ERR_SYNTAX_TIME; - sldns_write_uint32(rd, sldns_mktime_from_utc(&tm)); + sldns_write_uint32(rd, (uint32_t)sldns_mktime_from_utc(&tm)); } else { /* handle it as 32 bits timestamp */ char *end; @@ -1383,6 +1455,24 @@ 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; @@ -1478,7 +1568,7 @@ s = strtod(my_str, &my_str); } - /* skip blanks before norterness */ + /* skip blanks before northerness */ while (isblank((unsigned char) *my_str)) { my_str++; } @@ -1630,6 +1720,8 @@ 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 { @@ -1636,6 +1728,7 @@ int serv_port; struct servent *serv = getservbyname(token, proto_str); if(serv) serv_port=(int)ntohs((uint16_t)serv->s_port); + else if(strcasecmp(token, "domain")==0) serv_port=53; else { serv_port = atoi(token); if(serv_port == 0 && strcmp(token, "0") != 0) { @@ -1932,7 +2025,7 @@ if(!isalnum((unsigned char)*ptr)) return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_TAG, ptr-str); } - rd[0] = slen; + rd[0] = (uint8_t)slen; memmove(rd+1, str, slen); *len = slen+1; return LDNS_WIREPARSE_ERR_OK; @@ -2000,7 +2093,7 @@ 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, pklen); + sldns_write_uint16(rd+2, (uint16_t)pklen); *len = 4 + hitlen + pklen; return LDNS_WIREPARSE_ERR_OK; @@ -2008,16 +2101,31 @@ int sldns_str2wire_int16_data_buf(const char* str, uint8_t* rd, size_t* len) { - size_t sz = sldns_b64_pton_calculate_size(strlen(str)); + char* s; int n; - if(*len < sz+2) + 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(sz > 65535) + if(n > 65535) return LDNS_WIREPARSE_ERR_LABEL_OVERFLOW; - n = sldns_b64_pton(str, rd+2, (*len)-2); + + 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; + *len = ((size_t)n)+2; return LDNS_WIREPARSE_ERR_OK; } --- contrib/unbound/sldns/str2wire.h.orig +++ contrib/unbound/sldns/str2wire.h @@ -237,6 +237,8 @@ * @param rr: this is malloced by the user and the result is stored here, * if an RR is read. If no RR is read this is signalled with the * return len set to 0 (for ORIGIN, TTL directives). + * The read line is available in the rr_buf (zero terminated), for + * $DIRECTIVE style elements. * @param len: on input, the length of the rr buffer. on output the rr len. * Buffer size of 64k should be enough. * @param dname_len: returns the length of the dname initial part of the rr. @@ -418,6 +420,24 @@ int sldns_str2wire_period_buf(const char* str, uint8_t* rd, size_t* len); /** + * Convert rdf of type LDNS_RDF_TYPE_TSIGTIME from string to wireformat. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @return 0 on success, error on failure. + */ +int sldns_str2wire_tsigtime_buf(const char* str, uint8_t* rd, size_t* len); + +/** + * Convert rdf of type LDNS_RDF_TYPE_TSIGERROR from string to wireformat. + * @param str: the text to convert for this rdata element. + * @param rd: rdata buffer for the wireformat. + * @param len: length of rd buffer on input, used length on output. + * @return 0 on success, error on failure. + */ +int sldns_str2wire_tsigerror_buf(const char* str, uint8_t* rd, size_t* len); + +/** * Convert rdf of type LDNS_RDF_TYPE_LOC from string to wireformat. * @param str: the text to convert for this rdata element. * @param rd: rdata buffer for the wireformat. @@ -534,6 +554,12 @@ */ int sldns_str2wire_int16_data_buf(const char* str, uint8_t* rd, size_t* len); +/** + * Strip whitespace from the start and the end of line. + * @param line: modified with 0 to shorten it. + * @return new start with spaces skipped. + */ +char * sldns_strip_ws(char *line); #ifdef __cplusplus } #endif --- contrib/unbound/sldns/wire2str.c.orig +++ contrib/unbound/sldns/wire2str.c @@ -22,6 +22,7 @@ #include "sldns/parseutil.h" #include "sldns/sbuffer.h" #include "sldns/keyraw.h" +#include "util/data/dname.h" #ifdef HAVE_TIME_H #include #endif @@ -47,6 +48,8 @@ { 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" }, @@ -165,11 +168,34 @@ { 6, "DHU" }, { 7, "N3U" }, { 8, "edns-client-subnet" }, + { 11, "edns-tcp-keepalive"}, { 12, "Padding" }, { 0, NULL} }; sldns_lookup_table* sldns_edns_options = sldns_edns_options_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; + char* sldns_wire2str_pkt(uint8_t* data, size_t len) { size_t slen = (size_t)sldns_wire2str_pkt_buf(data, len, NULL, 0); @@ -227,21 +253,27 @@ 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); + 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); + 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); + 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, @@ -270,10 +302,16 @@ 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); + 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) @@ -328,7 +366,7 @@ int sldns_wire2str_pkt_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen) { - int w = 0; + int w = 0, comprloop = 0; unsigned qdcount, ancount, nscount, arcount, i; uint8_t* pkt = *d; size_t pktlen = *dlen; @@ -345,25 +383,25 @@ w += sldns_str_print(s, slen, ";; QUESTION SECTION:\n"); for(i=0; i 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)++; @@ -752,7 +799,7 @@ (*dlen)--; return sldns_str_print(s, slen, "."); } - while(*pos) { + while((!pkt || pos < pkt+pktlen) && *pos) { /* read label length */ uint8_t labellen = *pos++; if(in_buf) { (*d)++; (*dlen)--; } @@ -773,9 +820,12 @@ if(!pkt || target >= pktlen) return w + sldns_str_print(s, slen, "ErrorComprPtrOutOfBounds"); - if(counter++ > maxcompr) + if(counter++ > maxcompr) { + if(comprloop && *comprloop < 10) + (*comprloop)++; return w + sldns_str_print(s, slen, "ErrorComprPtrLooped"); + } in_buf = 0; pos = pkt+target; continue; @@ -788,8 +838,9 @@ } /* spool label characters, end with '.' */ - if(in_buf && *dlen < labellen) labellen = *dlen; - else if(!in_buf && pos+labellen > pkt+pktlen) + 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); for(i=0; i<(unsigned)labellen; i++) { w += dname_char_print(s, slen, *pos++); @@ -890,7 +941,7 @@ } 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 rdftype, uint8_t* pkt, size_t pktlen, int* comprloop) { if(*dlen == 0) return 0; switch(rdftype) { @@ -897,7 +948,7 @@ case LDNS_RDF_TYPE_NONE: return 0; case LDNS_RDF_TYPE_DNAME: - return sldns_wire2str_dname_scan(d, dlen, s, slen, pkt, pktlen); + 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: @@ -949,7 +1000,7 @@ 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); + pktlen, comprloop); case LDNS_RDF_TYPE_HIP: return sldns_wire2str_hip_scan(d, dlen, s, slen); case LDNS_RDF_TYPE_INT16_DATA: @@ -966,6 +1017,8 @@ 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_TSIGERROR: + return sldns_wire2str_tsigerror_scan(d, dlen, s, slen); } /* unknown rdf type */ return -1; @@ -1186,11 +1239,17 @@ 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); } @@ -1295,7 +1354,7 @@ if(*dl < 4) return -1; t = sldns_read_uint32(*d); date_buf[15]=0; - if(sldns_serial_arithmitics_gmtime_r(t, time(NULL), &tm) && + if(sldns_serial_arithmetics_gmtime_r(t, time(NULL), &tm) && strftime(date_buf, 15, "%Y%m%d%H%M%S", &tm)) { (*d) += 4; (*dl) -= 4; @@ -1431,6 +1490,10 @@ 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); } @@ -1479,7 +1542,7 @@ /* 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) + 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; @@ -1507,7 +1570,7 @@ w += sldns_wire2str_aaaa_scan(d, dl, s, sl); break; case 3: /* dname */ - w += sldns_wire2str_dname_scan(d, dl, s, sl, pkt, pktlen); + w += sldns_wire2str_dname_scan(d, dl, s, sl, pkt, pktlen, comprloop); break; default: /* unknown */ return -1; @@ -1521,12 +1584,12 @@ } int sldns_wire2str_ipseckey_scan(uint8_t** d, size_t* dl, char** s, size_t* sl, - uint8_t* pkt, size_t pktlen) + 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); + int w=sldns_wire2str_ipseckey_scan_internal(d, dl, s, sl, pkt, pktlen, comprloop); if(w == -1) { *d = od; *s = os; @@ -1564,6 +1627,7 @@ 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; @@ -1572,7 +1636,12 @@ return -1; (*d)+=2; (*dl)-=2; - return sldns_wire2str_b64_scan_num(d, dl, s, sl, n); + 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, @@ -1629,10 +1698,10 @@ if(*dl < 1+n) return -1; for(i=0; iname) + 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) { @@ -1837,6 +1921,25 @@ 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_option_print(char** s, size_t* sl, uint16_t option_code, uint8_t* optdata, size_t optlen) { @@ -1865,6 +1968,9 @@ 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; @@ -1960,10 +2066,10 @@ w += sldns_str_print(str, str_len, " ; udp: %u", (unsigned)udpsize); if(rdatalen) { - if(*data_len < rdatalen) { + if((size_t)*data_len < rdatalen) { w += sldns_str_print(str, str_len, " ; Error EDNS rdata too short; "); - rdatalen = *data_len; + rdatalen = (uint16_t)*data_len; } w += print_edns_opts(str, str_len, *data, rdatalen); (*data) += rdatalen; --- contrib/unbound/sldns/wire2str.h.orig +++ contrib/unbound/sldns/wire2str.h @@ -38,6 +38,8 @@ extern struct sldns_struct_lookup_table* sldns_edns_options; /** 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 @@ -154,10 +156,11 @@ * @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); + size_t* str_len, uint8_t* pkt, size_t pktlen, int* comprloop); /** * Scan wireformat question rr to string, with user buffers. @@ -168,10 +171,11 @@ * @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); + size_t* str_len, uint8_t* pkt, size_t pktlen, int* comprloop); /** * Scan wireformat RR to string in unknown RR format, with user buffers. @@ -182,10 +186,11 @@ * @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); + size_t* str_len, uint8_t* pkt, size_t pktlen, int* comprloop); /** * Print to string the RR-information comment in default format, @@ -226,10 +231,12 @@ * @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); + 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. @@ -252,10 +259,17 @@ * @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); + size_t* str_len, uint8_t* pkt, size_t pktlen, int* comprloop); /** * Scan wireformat rr type to string, with user buffers. @@ -357,6 +371,22 @@ 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) @@ -442,6 +472,17 @@ 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. @@ -463,11 +504,13 @@ * @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); + size_t* str_len, int rdftype, uint8_t* pkt, size_t pktlen, + int* comprloop); /** * Scan wireformat int8 field to string, with user buffers. @@ -764,11 +807,12 @@ * @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); + size_t* str_len, uint8_t* pkt, size_t pktlen, int* comprloop); /** * Scan wireformat HIP (algo, HIT, pubkey) field to string, with user buffers. @@ -797,6 +841,19 @@ 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. --- contrib/unbound/smallapp/unbound-anchor.c.orig +++ contrib/unbound/smallapp/unbound-anchor.c @@ -174,7 +174,7 @@ static void usage(void) { - printf("Usage: unbound-anchor [opts]\n"); + printf("Usage: local-unbound-anchor [opts]\n"); printf(" Setup or update root anchor. " "Most options have defaults.\n"); printf(" Run this program before you start the validator.\n"); @@ -190,11 +190,13 @@ printf("-x path pathname to xml in url, default %s\n", XMLNAME); printf("-s path pathname to p7s in url, default %s\n", P7SNAME); printf("-n name signer's subject emailAddress, default %s\n", P7SIGNER); + printf("-b address source address to bind to\n"); printf("-4 work using IPv4 only\n"); printf("-6 work using IPv6 only\n"); - printf("-f resolv.conf use given resolv.conf to resolve -u name\n"); - printf("-r root.hints use given root.hints to resolve -u name\n" + printf("-f resolv.conf use given resolv.conf\n"); + printf("-r root.hints use given root.hints\n" " builtin root hints are used by default\n"); + printf("-R fallback from -f to root query on error\n"); printf("-v more verbose\n"); printf("-C conf debug, read config\n"); printf("-P port use port for https connect, default 443\n"); @@ -241,7 +243,12 @@ get_builtin_ds(void) { return -". IN DS 19036 8 2 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5\n"; +/* The anchors must start on a new line with ". IN DS and end with \n"[;] + * because the makedist script greps on the source here */ +/* anchor 19036 is from 2010 */ +/* anchor 20326 is from 2017 */ +". IN DS 19036 8 2 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5\n" +". IN DS 20326 8 2 E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D\n"; } /** print hex data */ @@ -271,7 +278,7 @@ */ static struct ub_ctx* create_unbound_context(const char* res_conf, const char* root_hints, - const char* debugconf, int ip4only, int ip6only) + const char* debugconf, const char* srcaddr, int ip4only, int ip6only) { int r; struct ub_ctx* ctx = ub_ctx_create(); @@ -295,6 +302,10 @@ r = ub_ctx_set_option(ctx, "root-hints:", root_hints); if(r) ub_ctx_error_exit(ctx, root_hints, ub_strerror(r)); } + if(srcaddr) { + r = ub_ctx_set_option(ctx, "outgoing-interface:", srcaddr); + if(r) ub_ctx_error_exit(ctx, srcaddr, ub_strerror(r)); + } if(ip4only) { r = ub_ctx_set_option(ctx, "do-ip6:", "no"); if(r) ub_ctx_error_exit(ctx, "ip4only", ub_strerror(r)); @@ -344,7 +355,7 @@ exit(0); } while(!BIO_eof(bio)) { - X509* x = PEM_read_bio_X509(bio, NULL, 0, NULL); + X509* x = PEM_read_bio_X509(bio, NULL, NULL, NULL); if(x == NULL) { if(verb) { printf("failed to read X509\n"); @@ -385,7 +396,7 @@ return NULL; } while(!feof(in)) { - X509* x = PEM_read_X509(in, NULL, 0, NULL); + X509* x = PEM_read_X509(in, NULL, NULL, NULL); if(x == NULL) { if(verb) { printf("failed to read X509 file\n"); @@ -607,6 +618,7 @@ * @param res_conf: resolv.conf (if any). * @param root_hints: root hints (if any). * @param debugconf: unbound.conf for debugging options. + * @param srcaddr: source address option (if any). * @param ip4only: use only ip4 for resolve and only lookup A * @param ip6only: use only ip6 for resolve and only lookup AAAA * default is to lookup A and AAAA using ip4 and ip6. @@ -614,7 +626,8 @@ */ static struct ip_list* resolve_name(const char* host, int port, const char* res_conf, - const char* root_hints, const char* debugconf, int ip4only, int ip6only) + const char* root_hints, const char* debugconf, + const char* srcaddr, int ip4only, int ip6only) { struct ub_ctx* ctx; struct ip_list* list = NULL; @@ -625,7 +638,7 @@ /* create resolver context */ ctx = create_unbound_context(res_conf, root_hints, debugconf, - ip4only, ip6only); + srcaddr, ip4only, ip6only); /* try resolution of A */ if(!ip6only) { @@ -657,7 +670,7 @@ } } -/** cound unused IPs */ +/** count unused IPs */ static int count_unused(struct ip_list* p) { @@ -715,7 +728,7 @@ /** connect to IP address */ static int -connect_to_ip(struct ip_list* ip) +connect_to_ip(struct ip_list* ip, struct ip_list* src) { int fd; verb_addr("connect to", ip); @@ -725,6 +738,11 @@ print_sock_err("socket"); return -1; } + if(src && bind(fd, (struct sockaddr*)&src->addr, src->len) < 0) { + print_sock_err("bind"); + fd_close(fd); + return -1; + } if(connect(fd, (struct sockaddr*)&ip->addr, ip->len) < 0) { print_sock_err("connect"); fd_close(fd); @@ -757,7 +775,7 @@ return NULL; } SSL_set_connect_state(ssl); - (void)SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); + (void)SSL_set_mode(ssl, (long)SSL_MODE_AUTO_RETRY); if(!SSL_set_fd(ssl, fd)) { if(verb) printf("SSL_set_fd error\n"); SSL_free(ssl); @@ -918,7 +936,7 @@ size_t got = 0; int r; char* data; - if(len >= 0xfffffff0) + if((unsigned)len >= (unsigned)0xfffffff0) return NULL; /* to protect against integer overflow in malloc*/ data = malloc(len+1); if(!data) { @@ -1084,17 +1102,21 @@ } if(!data) return NULL; if(verb >= 4) print_data("read data", data, (int)len); - m = BIO_new_mem_buf(data, (int)len); + m = BIO_new(BIO_s_mem()); if(!m) { if(verb) printf("out of memory\n"); + free(data); exit(0); } + BIO_write(m, data, (int)len); + free(data); return m; } /** https to an IP addr, return BIO with pathname or NULL */ static BIO* -https_to_ip(struct ip_list* ip, const char* pathname, const char* urlname) +https_to_ip(struct ip_list* ip, const char* pathname, const char* urlname, + struct ip_list* src) { int fd; SSL* ssl; @@ -1103,7 +1125,7 @@ if(!sslctx) { return NULL; } - fd = connect_to_ip(ip); + fd = connect_to_ip(ip, src); if(fd == -1) { SSL_CTX_free(sslctx); return NULL; @@ -1131,10 +1153,12 @@ * @param ip_list: list of IP addresses to use to fetch from. * @param pathname: pathname of file on server to GET. * @param urlname: name to pass as the virtual host for this request. + * @param src: if nonNULL, source address to bind to. * @return a memory BIO with the file in it. */ static BIO* -https(struct ip_list* ip_list, const char* pathname, const char* urlname) +https(struct ip_list* ip_list, const char* pathname, const char* urlname, + struct ip_list* src) { struct ip_list* ip; BIO* bio = NULL; @@ -1142,7 +1166,7 @@ wipe_ip_usage(ip_list); while( (ip = pick_random_ip(ip_list)) ) { ip->used = 1; - bio = https_to_ip(ip, pathname, urlname); + bio = https_to_ip(ip, pathname, urlname, src); if(bio) break; } if(!bio) { @@ -1155,17 +1179,6 @@ return bio; } -/** free up a downloaded file BIO */ -static void -free_file_bio(BIO* bio) -{ - char* pp = NULL; - (void)BIO_reset(bio); - (void)BIO_get_mem_data(bio, &pp); - free(pp); - BIO_free(bio); -} - /** XML parse private data during the parse */ struct xml_data { /** the parser, reference */ @@ -1590,7 +1603,7 @@ xml_parse_setup(parser, &data, now); /* parse it */ - (void)BIO_reset(xml); + (void)BIO_seek(xml, 0); len = (int)BIO_get_mem_data(xml, &pp); if(!len || !pp) { if(verb) printf("out of memory\n"); @@ -1764,8 +1777,8 @@ X509_VERIFY_PARAM_free(param); #endif - (void)BIO_reset(p7s); - (void)BIO_reset(data); + (void)BIO_seek(p7s, 0); + (void)BIO_seek(data, 0); /* convert p7s to p7 (the signature) */ p7 = d2i_PKCS7_bio(p7s, NULL); @@ -1908,12 +1921,14 @@ do_certupdate(const char* root_anchor_file, const char* root_cert_file, const char* urlname, const char* xmlname, const char* p7sname, const char* p7signer, const char* res_conf, const char* root_hints, - const char* debugconf, int ip4only, int ip6only, int port, - struct ub_result* dnskey) + const char* debugconf, const char* srcaddr, int ip4only, int ip6only, + int port) + { STACK_OF(X509)* cert; BIO *xml, *p7s; struct ip_list* ip_list = NULL; + struct ip_list* src = NULL; /* read pem file or provide builtin */ cert = read_cert_or_builtin(root_cert_file); @@ -1920,8 +1935,13 @@ /* lookup A, AAAA for the urlname (or parse urlname if IP address) */ ip_list = resolve_name(urlname, port, res_conf, root_hints, debugconf, - ip4only, ip6only); + srcaddr, ip4only, ip6only); + if(srcaddr && !(src = parse_ip_addr(srcaddr, 0))) { + if(verb) printf("cannot parse source address: %s\n", srcaddr); + exit(0); + } + #ifdef USE_WINSOCK if(1) { /* libunbound finished, startup WSA for the https connection */ WSADATA wsa_data; @@ -1936,8 +1956,8 @@ #endif /* fetch the necessary files over HTTPS */ - xml = https(ip_list, xmlname, urlname); - p7s = https(ip_list, p7sname, urlname); + xml = https(ip_list, xmlname, urlname, src); + p7s = https(ip_list, p7sname, urlname, src); /* verify and update the root anchor */ verify_and_update_anchor(root_anchor_file, xml, p7s, cert, p7signer); @@ -1944,12 +1964,11 @@ if(verb) printf("success: the anchor has been updated " "using the cert\n"); - free_file_bio(xml); - free_file_bio(p7s); + BIO_free(xml); + BIO_free(p7s); #ifndef S_SPLINT_S sk_X509_pop_free(cert, X509_free); #endif - ub_resolve_free(dnskey); ip_list_free(ip_list); return 1; } @@ -2187,16 +2206,33 @@ return 0; } +static struct ub_result * +fetch_root_key(const char* root_anchor_file, const char* res_conf, + const char* root_hints, const char* debugconf, const char* srcaddr, + int ip4only, int ip6only) +{ + struct ub_ctx* ctx; + struct ub_result* dnskey; + + ctx = create_unbound_context(res_conf, root_hints, debugconf, + srcaddr, ip4only, ip6only); + add_5011_probe_root(ctx, root_anchor_file); + dnskey = prime_root_key(ctx); + ub_ctx_delete(ctx); + return dnskey; +} + /** perform the unbound-anchor work */ static int do_root_update_work(const char* root_anchor_file, const char* root_cert_file, const char* urlname, const char* xmlname, const char* p7sname, const char* p7signer, const char* res_conf, const char* root_hints, - const char* debugconf, int ip4only, int ip6only, int force, int port) + const char* debugconf, const char* srcaddr, int ip4only, int ip6only, + int force, int res_conf_fallback, int port) { - struct ub_ctx* ctx; struct ub_result* dnskey; int used_builtin = 0; + int rcode; /* see if builtin rootanchor needs to be provided, or if * rootanchor is 'revoked-trust-point' */ @@ -2205,12 +2241,22 @@ /* make unbound context with 5011-probe for root anchor, * and probe . DNSKEY */ - ctx = create_unbound_context(res_conf, root_hints, debugconf, - ip4only, ip6only); - add_5011_probe_root(ctx, root_anchor_file); - dnskey = prime_root_key(ctx); - ub_ctx_delete(ctx); - + dnskey = fetch_root_key(root_anchor_file, res_conf, + root_hints, debugconf, srcaddr, ip4only, ip6only); + rcode = dnskey->rcode; + + if (res_conf_fallback && res_conf && !dnskey->secure) { + if (verb) printf("%s failed, retrying direct\n", res_conf); + ub_resolve_free(dnskey); + /* try direct query without res_conf */ + dnskey = fetch_root_key(root_anchor_file, NULL, + root_hints, debugconf, srcaddr, ip4only, ip6only); + if (rcode != 0 && dnskey->rcode == 0) { + res_conf = NULL; + rcode = 0; + } + } + /* if secure: exit */ if(dnskey->secure && !force) { if(verb) printf("success: the anchor is ok\n"); @@ -2218,18 +2264,18 @@ return used_builtin; } if(force && verb) printf("debug cert update forced\n"); + ub_resolve_free(dnskey); /* if not (and NOERROR): check date and do certupdate */ - if((dnskey->rcode == 0 && + if((rcode == 0 && probe_date_allows_certupdate(root_anchor_file)) || force) { if(do_certupdate(root_anchor_file, root_cert_file, urlname, xmlname, p7sname, p7signer, res_conf, root_hints, - debugconf, ip4only, ip6only, port, dnskey)) + debugconf, srcaddr, ip4only, ip6only, port)) return 1; return used_builtin; } if(verb) printf("fail: the anchor is NOT ok and could not be fixed\n"); - ub_resolve_free(dnskey); return used_builtin; } @@ -2251,9 +2297,11 @@ const char* res_conf = NULL; const char* root_hints = NULL; const char* debugconf = NULL; + const char* srcaddr = NULL; int dolist=0, ip4only=0, ip6only=0, force=0, port = HTTPS_PORT; + int res_conf_fallback = 0; /* parse the options */ - while( (c=getopt(argc, argv, "46C:FP:a:c:f:hln:r:s:u:vx:")) != -1) { + while( (c=getopt(argc, argv, "46C:FRP:a:b:c:f:hln:r:s:u:vx:")) != -1) { switch(c) { case 'l': dolist = 1; @@ -2267,6 +2315,9 @@ case 'a': root_anchor_file = optarg; break; + case 'b': + srcaddr = optarg; + break; case 'c': root_cert_file = optarg; break; @@ -2288,6 +2339,9 @@ case 'r': root_hints = optarg; break; + case 'R': + res_conf_fallback = 1; + break; case 'C': debugconf = optarg; break; @@ -2307,7 +2361,7 @@ } } argc -= optind; - argv += optind; + /* argv += optind; not using further arguments */ if(argc != 0) usage(); @@ -2314,9 +2368,13 @@ #ifdef HAVE_ERR_LOAD_CRYPTO_STRINGS ERR_load_crypto_strings(); #endif +#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 @@ -2325,7 +2383,7 @@ #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL) (void)SSL_library_init(); #else - (void)OPENSSL_init_ssl(0, NULL); + (void)OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL); #endif if(dolist) do_list_builtin(); @@ -2332,5 +2390,5 @@ return do_root_update_work(root_anchor_file, root_cert_file, urlname, xmlname, p7sname, p7signer, res_conf, root_hints, debugconf, - ip4only, ip6only, force, port); + srcaddr, ip4only, ip6only, force, res_conf_fallback, port); } --- contrib/unbound/smallapp/unbound-checkconf.c.orig +++ contrib/unbound/smallapp/unbound-checkconf.c @@ -4,22 +4,22 @@ * 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 @@ -43,6 +43,7 @@ */ #include "config.h" +#include #include "util/log.h" #include "util/config_file.h" #include "util/module.h" @@ -53,6 +54,9 @@ #include "iterator/iter_hints.h" #include "validator/validator.h" #include "services/localzone.h" +#include "services/view.h" +#include "services/authzone.h" +#include "respip/respip.h" #include "sldns/sbuffer.h" #ifdef HAVE_GETOPT_H #include @@ -69,12 +73,15 @@ #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: unbound-checkconf [file]\n"); + 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"); @@ -86,10 +93,10 @@ exit(1); } -/** - * Print given option to stdout +/** + * Print given option to stdout * @param cfg: config - * @param opt: option name without trailing :. + * @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. */ @@ -97,9 +104,22 @@ print_option(struct config_file* cfg, const char* opt, int final) { if(strcmp(opt, "pidfile") == 0 && final) { - printf("%s\n", fname_after_chroot(cfg->pidfile, cfg, 1)); + 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); } @@ -115,6 +135,8 @@ 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); } @@ -121,8 +143,45 @@ (*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) @@ -135,6 +194,27 @@ local_zones_delete(zs); } +/** 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"); + views_delete(views); + respip_set_delete(respip); +} + /** emit warnings for IP in hosts */ static void warn_hosts(const char* typ, struct config_stub* list) @@ -149,7 +229,7 @@ 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", + "during use may not resolve.\n", s->name, typ, h->str, addr_is_ip6(&a, alen)?"6":"4"); } @@ -201,7 +281,7 @@ socklen_t alen; struct config_str2list* acl; for(acl=cfg->acls; acl; acl = acl->next) { - if(!netblockstrtoaddr(acl->str, UNBOUND_DNS_PORT, &a, &alen, + if(!netblockstrtoaddr(acl->str, UNBOUND_DNS_PORT, &a, &alen, &d)) { fatal_exit("cannot parse access control address %s %s", acl->str, acl->str2); @@ -209,9 +289,26 @@ } } +/** 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) +is_file(const char* fname) { struct stat buf; if(stat(fname, &buf) < 0) { @@ -231,7 +328,7 @@ /** true if fname is a directory */ static int -is_dir(const char* fname) +is_dir(const char* fname) { struct stat buf; if(stat(fname, &buf) < 0) { @@ -276,7 +373,7 @@ fatal_exit("%s: \"%s\" does not exist in " "chrootdir %s", desc, str, chrootdir); else - fatal_exit("%s: \"%s\" does not exist", + fatal_exit("%s: \"%s\" does not exist", desc, str); } /* put in a new full path for continued checking */ @@ -303,8 +400,8 @@ 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, '{') || + 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 */ @@ -316,14 +413,67 @@ } } +#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; jstubs); warn_hosts("forward-host", cfg->forwards); interfacechecks(cfg); aclchecks(cfg); + tcpconnlimitchecks(cfg); if(cfg->verbosity < 0) fatal_exit("verbosity value < 0"); @@ -341,27 +491,14 @@ #ifdef UB_ON_WINDOWS w_config_adjust_directory(cfg); #endif - if(cfg->chrootdir && cfg->chrootdir[0] && + 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] && + if(cfg->chrootdir && cfg->chrootdir[0] && !is_dir(cfg->chrootdir)) { fatal_exit("bad chroot directory"); } - if(cfg->chrootdir && cfg->chrootdir[0]) { - char buf[10240]; - buf[0] = 0; - if(fname[0] != '/') { - if(getcwd(buf, sizeof(buf)) == NULL) - fatal_exit("getcwd: %s", strerror(errno)); - (void)strlcat(buf, "/", sizeof(buf)); - } - (void)strlcat(buf, fname, sizeof(buf)); - if(strncmp(buf, cfg->chrootdir, strlen(cfg->chrootdir)) != 0) - fatal_exit("config file %s is not inside chroot %s", - buf, cfg->chrootdir); - } if(cfg->directory && cfg->directory[0]) { char* ad = fname_after_chroot(cfg->directory, cfg, 0); if(!ad) fatal_exit("out of memory"); @@ -387,49 +524,119 @@ } } - check_chroot_filelist("file with root-hints", + check_chroot_filelist("file with root-hints", cfg->root_hints, cfg->chrootdir, cfg); - check_chroot_filelist("trust-anchor-file", + check_chroot_filelist("trust-anchor-file", cfg->trust_anchor_file_list, cfg->chrootdir, cfg); - check_chroot_filelist("auto-trust-anchor-file", + check_chroot_filelist("auto-trust-anchor-file", cfg->auto_trust_anchor_file_list, cfg->chrootdir, cfg); - check_chroot_filelist_wild("trusted-keys-file", + check_chroot_filelist_wild("trusted-keys-file", cfg->trusted_keys_file_list, cfg->chrootdir, cfg); - check_chroot_string("dlv-anchor-file", &cfg->dlv_anchor_file, + check_chroot_string("dlv-anchor-file", &cfg->dlv_anchor_file, cfg->chrootdir, cfg); +#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; - - if(strcmp(cfg->module_conf, "iterator") != 0 + + /* 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 #ifdef WITH_PYTHONMODULE - && strcmp(cfg->module_conf, "python iterator") != 0 - && strcmp(cfg->module_conf, "python validator iterator") != 0 + && 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 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 + && strcmp(cfg->module_conf, "python dns64 iterator") != 0 + && strcmp(cfg->module_conf, "python dns64 validator 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 +#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 +#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); @@ -444,7 +651,8 @@ # endif } #endif - if(cfg->remote_control_enable && cfg->remote_control_use_cert) { + 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, @@ -457,7 +665,12 @@ cfg->control_cert_file); } + donotquerylocalhostcheck(cfg); localzonechecks(cfg); + view_and_respipchecks(cfg); +#ifdef CLIENT_SUBNET + ecs_conf_checks(cfg); +#endif } /** check forwards */ @@ -482,11 +695,23 @@ 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, 0i, &is_rpz)) { + 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[PATH_MAX]; + char oldwd[4096]; struct config_file* cfg = config_create(); if(!cfg) fatal_exit("out of memory"); @@ -507,7 +732,7 @@ config_delete(cfg); return; } - morechecks(cfg, cfgfile); + morechecks(cfg); check_mod(cfg, iter_get_funcblock()); check_mod(cfg, val_get_funcblock()); #ifdef WITH_PYTHONMODULE @@ -516,6 +741,7 @@ #endif check_fwd(cfg); check_hints(cfg); + check_auth(cfg); printf("unbound-checkconf: no errors in %s\n", cfgfile); config_delete(cfg); } --- contrib/unbound/smallapp/unbound-control-setup.sh.orig +++ contrib/unbound/smallapp/unbound-control-setup.sh @@ -148,8 +148,8 @@ # echo "empty password is used, simply click OK on the password dialog box." # openssl pkcs12 -export -in $CTL_BASE"_trust.pem" -inkey $CTL_BASE.key -name "unbound remote control client cert" -out $CTL_BASE"_browser.pfx" -password "pass:" || error "could not create browser certificate" -# remove unused permissions -chmod o-rw $SVR_BASE.pem $SVR_BASE.key $CTL_BASE.pem $CTL_BASE.key +# set desired permissions +chmod 0640 $SVR_BASE.pem $SVR_BASE.key $CTL_BASE.pem $CTL_BASE.key # remove crap rm -f request.cfg --- contrib/unbound/smallapp/unbound-control-setup.sh.in.orig +++ contrib/unbound/smallapp/unbound-control-setup.sh.in @@ -148,8 +148,8 @@ # echo "empty password is used, simply click OK on the password dialog box." # openssl pkcs12 -export -in $CTL_BASE"_trust.pem" -inkey $CTL_BASE.key -name "unbound remote control client cert" -out $CTL_BASE"_browser.pfx" -password "pass:" || error "could not create browser certificate" -# remove unused permissions -chmod o-rw $SVR_BASE.pem $SVR_BASE.key $CTL_BASE.pem $CTL_BASE.key +# set desired permissions +chmod 0640 $SVR_BASE.pem $SVR_BASE.key $CTL_BASE.pem $CTL_BASE.key # remove crap rm -f request.cfg --- contrib/unbound/smallapp/unbound-control.c.orig +++ contrib/unbound/smallapp/unbound-control.c @@ -58,16 +58,31 @@ #include "util/config_file.h" #include "util/locks.h" #include "util/net_help.h" +#include "util/shm_side/shm_main.h" +#include "daemon/stats.h" +#include "sldns/wire2str.h" +#include "sldns/pkthdr.h" +#include "services/rpz.h" +#ifdef HAVE_SYS_IPC_H +#include "sys/ipc.h" +#endif +#ifdef HAVE_SYS_SHM_H +#include "sys/shm.h" +#endif #ifdef HAVE_SYS_UN_H #include #endif +static void usage(void) ATTR_NORETURN; +static void ssl_err(const char* s) ATTR_NORETURN; +static void ssl_path_err(const char* s, const char *path) ATTR_NORETURN; + /** Give unbound-control usage, and exit (1). */ static void usage(void) { - printf("Usage: unbound-control [options] command\n"); + printf("Usage: local-unbound-control [options] command\n"); printf(" Remote control utility for unbound server.\n"); printf("Options:\n"); printf(" -c file config file, default is %s\n", CONFIGFILE); @@ -81,6 +96,9 @@ printf(" (this flushes data, stats, requestlist)\n"); printf(" stats print statistics\n"); printf(" stats_noreset peek at statistics\n"); +#ifdef HAVE_SHMGET + printf(" stats_shm print statistics using shm\n"); +#endif printf(" status display status of server\n"); printf(" verbosity change logging detail\n"); printf(" log_reopen close and open the logfile\n"); @@ -89,6 +107,9 @@ printf(" local_data add local data, for example\n"); printf(" local_data www.example.com A 192.0.2.1\n"); printf(" local_data_remove remove local RR data from name\n"); + printf(" local_zones, local_zones_remove, local_datas, local_datas_remove\n"); + printf(" same, but read list from stdin\n"); + printf(" (one entry per line).\n"); printf(" dump_cache print cache to stdout\n"); printf(" load_cache load cache from stdin\n"); printf(" lookup print nameservers for name\n"); @@ -124,7 +145,21 @@ printf(" or off to turn off root forwarding\n"); printf(" or give list of ip addresses\n"); printf(" ratelimit_list [+a] list ratelimited domains\n"); + printf(" ip_ratelimit_list [+a] list ratelimited ip addresses\n"); printf(" +a list all, also not ratelimited\n"); + printf(" list_auth_zones list auth zones\n"); + printf(" auth_zone_reload zone reload auth zone from zonefile\n"); + printf(" auth_zone_transfer zone transfer auth zone from master\n"); + printf(" view_list_local_zones view list local-zones in view\n"); + printf(" view_list_local_data view list local-data RRs in view\n"); + printf(" view_local_zone view name type add local-zone in view\n"); + printf(" view_local_zone_remove view name remove local-zone in view\n"); + printf(" view_local_data view RR... add local-data in view\n"); + printf(" view_local_datas view add list of local-data to view\n"); + printf(" one entry per line read from stdin\n"); + printf(" view_local_data_remove view name remove local-data in view\n"); + printf(" view_local_datas_remove view remove list of local-data from view\n"); + printf(" one entry per line read from stdin\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); @@ -131,6 +166,303 @@ exit(1); } +#ifdef HAVE_SHMGET +/** what to put on statistics lines between var and value, ": " or "=" */ +#define SQ "=" +/** if true, inhibits a lot of =0 lines from the stats output */ +static const int inhibit_zero = 1; +/** divide sum of timers to get average */ +static void +timeval_divide(struct timeval* avg, const struct timeval* sum, long long d) +{ +#ifndef S_SPLINT_S + size_t leftover; + if(d == 0) { + avg->tv_sec = 0; + avg->tv_usec = 0; + return; + } + avg->tv_sec = sum->tv_sec / d; + avg->tv_usec = sum->tv_usec / d; + /* handle fraction from seconds divide */ + leftover = sum->tv_sec - avg->tv_sec*d; + avg->tv_usec += (leftover*1000000)/d; +#endif +} + +/** print unsigned long stats value */ +#define PR_UL_NM(str, var) printf("%s."str SQ"%lu\n", nm, (unsigned long)(var)); +#define PR_UL(str, var) printf(str SQ"%lu\n", (unsigned long)(var)); +#define PR_UL_SUB(str, nm, var) printf(str".%s"SQ"%lu\n", nm, (unsigned long)(var)); +#define PR_TIMEVAL(str, var) printf(str SQ ARG_LL "d.%6.6d\n", \ + (long long)var.tv_sec, (int)var.tv_usec); +#define PR_STATSTIME(str, var) printf(str SQ ARG_LL "d.%6.6d\n", \ + (long long)var ## _sec, (int)var ## _usec); +#define PR_LL(str, var) printf(str SQ ARG_LL"d\n", (long long)(var)); + +/** print stat block */ +static void pr_stats(const char* nm, struct ub_stats_info* s) +{ + struct timeval sumwait, avg; + PR_UL_NM("num.queries", s->svr.num_queries); + PR_UL_NM("num.queries_ip_ratelimited", + s->svr.num_queries_ip_ratelimited); + PR_UL_NM("num.cachehits", + s->svr.num_queries - s->svr.num_queries_missed_cache); + PR_UL_NM("num.cachemiss", s->svr.num_queries_missed_cache); + PR_UL_NM("num.prefetch", s->svr.num_queries_prefetch); + PR_UL_NM("num.expired", s->svr.ans_expired); + PR_UL_NM("num.recursivereplies", s->mesh_replies_sent); +#ifdef USE_DNSCRYPT + PR_UL_NM("num.dnscrypt.crypted", s->svr.num_query_dnscrypt_crypted); + PR_UL_NM("num.dnscrypt.cert", s->svr.num_query_dnscrypt_cert); + PR_UL_NM("num.dnscrypt.cleartext", s->svr.num_query_dnscrypt_cleartext); + PR_UL_NM("num.dnscrypt.malformed", + s->svr.num_query_dnscrypt_crypted_malformed); +#endif /* USE_DNSCRYPT */ + printf("%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); + PR_UL_NM("requestlist.max", s->svr.max_query_list_size); + PR_UL_NM("requestlist.overwritten", s->mesh_jostled); + PR_UL_NM("requestlist.exceeded", s->mesh_dropped); + PR_UL_NM("requestlist.current.all", s->mesh_num_states); + PR_UL_NM("requestlist.current.user", s->mesh_num_reply_states); +#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); + printf("%s.", nm); + PR_TIMEVAL("recursion.time.avg", avg); + printf("%s.recursion.time.median"SQ"%g\n", nm, s->mesh_time_median); + PR_UL_NM("tcpusage", s->svr.tcp_accept_usage); +} + +/** print uptime */ +static void print_uptime(struct ub_shm_stat_info* shm_stat) +{ + PR_STATSTIME("time.now", shm_stat->time.now); + PR_STATSTIME("time.up", shm_stat->time.up); + PR_STATSTIME("time.elapsed", shm_stat->time.elapsed); +} + +/** print memory usage */ +static void print_mem(struct ub_shm_stat_info* shm_stat, + struct ub_stats_info* s) +{ + PR_LL("mem.cache.rrset", shm_stat->mem.rrset); + PR_LL("mem.cache.message", shm_stat->mem.msg); + PR_LL("mem.mod.iterator", shm_stat->mem.iter); + PR_LL("mem.mod.validator", shm_stat->mem.val); + PR_LL("mem.mod.respip", shm_stat->mem.respip); +#ifdef CLIENT_SUBNET + PR_LL("mem.mod.subnet", shm_stat->mem.subnet); +#endif +#ifdef USE_IPSECMOD + PR_LL("mem.mod.ipsecmod", shm_stat->mem.ipsecmod); +#endif +#ifdef USE_DNSCRYPT + PR_LL("mem.cache.dnscrypt_shared_secret", + shm_stat->mem.dnscrypt_shared_secret); + PR_LL("mem.cache.dnscrypt_nonce", + shm_stat->mem.dnscrypt_nonce); +#endif + PR_LL("mem.streamwait", s->svr.mem_stream_wait); +} + +/** print histogram */ +static void print_hist(struct ub_stats_info* s) +{ + struct timehist* hist; + size_t i; + hist = timehist_setup(); + if(!hist) + fatal_exit("out of memory"); + timehist_import(hist, s->svr.hist, NUM_BUCKETS_HIST); + for(i=0; inum; i++) { + printf("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); +} + +/** print extended */ +static void print_extended(struct ub_stats_info* s) +{ + int i; + char nm[16]; + + /* TYPE */ + for(i=0; isvr.qtype[i] == 0) + continue; + sldns_wire2str_type_buf((uint16_t)i, nm, sizeof(nm)); + PR_UL_SUB("num.query.type", nm, s->svr.qtype[i]); + } + if(!inhibit_zero || s->svr.qtype_big) { + PR_UL("num.query.type.other", s->svr.qtype_big); + } + + /* CLASS */ + for(i=0; isvr.qclass[i] == 0) + continue; + sldns_wire2str_class_buf((uint16_t)i, nm, sizeof(nm)); + PR_UL_SUB("num.query.class", nm, s->svr.qclass[i]); + } + if(!inhibit_zero || s->svr.qclass_big) { + PR_UL("num.query.class.other", s->svr.qclass_big); + } + + /* OPCODE */ + for(i=0; isvr.qopcode[i] == 0) + continue; + sldns_wire2str_opcode_buf(i, nm, sizeof(nm)); + PR_UL_SUB("num.query.opcode", nm, s->svr.qopcode[i]); + } + + /* transport */ + PR_UL("num.query.tcp", s->svr.qtcp); + PR_UL("num.query.tcpout", s->svr.qtcp_outgoing); + PR_UL("num.query.tls", s->svr.qtls); + PR_UL("num.query.tls_resume", s->svr.qtls_resume); + PR_UL("num.query.ipv6", s->svr.qipv6); + + /* flags */ + PR_UL("num.query.flags.QR", s->svr.qbit_QR); + PR_UL("num.query.flags.AA", s->svr.qbit_AA); + PR_UL("num.query.flags.TC", s->svr.qbit_TC); + PR_UL("num.query.flags.RD", s->svr.qbit_RD); + PR_UL("num.query.flags.RA", s->svr.qbit_RA); + PR_UL("num.query.flags.Z", s->svr.qbit_Z); + PR_UL("num.query.flags.AD", s->svr.qbit_AD); + PR_UL("num.query.flags.CD", s->svr.qbit_CD); + PR_UL("num.query.edns.present", s->svr.qEDNS); + PR_UL("num.query.edns.DO", s->svr.qEDNS_DO); + + /* RCODE */ + for(i=0; i LDNS_RCODE_REFUSED && s->svr.ans_rcode[i] == 0) + continue; + sldns_wire2str_rcode_buf(i, nm, sizeof(nm)); + PR_UL_SUB("num.answer.rcode", nm, s->svr.ans_rcode[i]); + } + if(!inhibit_zero || s->svr.ans_rcode_nodata) { + PR_UL("num.answer.rcode.nodata", s->svr.ans_rcode_nodata); + } + /* iteration */ + PR_UL("num.query.ratelimited", s->svr.queries_ratelimited); + /* validation */ + PR_UL("num.answer.secure", s->svr.ans_secure); + PR_UL("num.answer.bogus", s->svr.ans_bogus); + PR_UL("num.rrset.bogus", s->svr.rrset_bogus); + PR_UL("num.query.aggressive.NOERROR", s->svr.num_neg_cache_noerror); + PR_UL("num.query.aggressive.NXDOMAIN", s->svr.num_neg_cache_nxdomain); + /* threat detection */ + PR_UL("unwanted.queries", s->svr.unwanted_queries); + PR_UL("unwanted.replies", s->svr.unwanted_replies); + /* cache counts */ + PR_UL("msg.cache.count", s->svr.msg_cache_count); + PR_UL("rrset.cache.count", s->svr.rrset_cache_count); + PR_UL("infra.cache.count", s->svr.infra_cache_count); + PR_UL("key.cache.count", s->svr.key_cache_count); + /* applied RPZ actions */ + for(i=0; isvr.rpz_action[i] == 0) + continue; + PR_UL_SUB("num.rpz.action", rpz_action_to_string(i), s->svr.rpz_action[i]); + } +#ifdef USE_DNSCRYPT + PR_UL("dnscrypt_shared_secret.cache.count", + s->svr.shared_secret_cache_count); + PR_UL("num.query.dnscrypt.shared_secret.cachemiss", + s->svr.num_query_dnscrypt_secret_missed_cache); + PR_UL("dnscrypt_nonce.cache.count", s->svr.nonce_cache_count); + PR_UL("num.query.dnscrypt.replay", + s->svr.num_query_dnscrypt_replay); +#endif /* USE_DNSCRYPT */ + PR_UL("num.query.authzone.up", s->svr.num_query_authzone_up); + PR_UL("num.query.authzone.down", s->svr.num_query_authzone_down); +#ifdef CLIENT_SUBNET + PR_UL("num.query.subnet", s->svr.num_query_subnet); + PR_UL("num.query.subnet_cache", s->svr.num_query_subnet_cache); +#endif +} + +/** print statistics out of memory structures */ +static void do_stats_shm(struct config_file* cfg, struct ub_stats_info* stats, + struct ub_shm_stat_info* shm_stat) +{ + int i; + char nm[32]; + for(i=0; inum_threads; i++) { + snprintf(nm, sizeof(nm), "thread%d", i); + pr_stats(nm, &stats[i+1]); + } + pr_stats("total", &stats[0]); + print_uptime(shm_stat); + if(cfg->stat_extended) { + print_mem(shm_stat, &stats[0]); + print_hist(stats); + print_extended(stats); + } +} +#endif /* HAVE_SHMGET */ + +/** print statistics from shm memory segment */ +static void print_stats_shm(const char* cfgfile) +{ +#ifdef HAVE_SHMGET + struct config_file* cfg; + struct ub_stats_info* stats; + struct ub_shm_stat_info* shm_stat; + int id_ctl, id_arr; + /* read config */ + if(!(cfg = config_create())) + fatal_exit("out of memory"); + if(!config_read(cfg, cfgfile, NULL)) + fatal_exit("could not read config file"); + /* get shm segments */ + id_ctl = shmget(cfg->shm_key, sizeof(int), SHM_R); + if(id_ctl == -1) { + fatal_exit("shmget(%d): %s", cfg->shm_key, strerror(errno)); + } + id_arr = shmget(cfg->shm_key+1, sizeof(int), SHM_R); + if(id_arr == -1) { + fatal_exit("shmget(%d): %s", cfg->shm_key+1, strerror(errno)); + } + shm_stat = (struct ub_shm_stat_info*)shmat(id_ctl, NULL, SHM_RDONLY); + if(shm_stat == (void*)-1) { + fatal_exit("shmat(%d): %s", id_ctl, strerror(errno)); + } + stats = (struct ub_stats_info*)shmat(id_arr, NULL, SHM_RDONLY); + if(stats == (void*)-1) { + fatal_exit("shmat(%d): %s", id_arr, strerror(errno)); + } + + /* print the stats */ + do_stats_shm(cfg, stats, shm_stat); + + /* shutdown */ + shmdt(shm_stat); + shmdt(stats); + config_delete(cfg); +#else + (void)cfgfile; +#endif /* HAVE_SHMGET */ +} + /** exit with ssl error */ static void ssl_err(const char* s) { @@ -139,6 +471,22 @@ exit(1); } +/** exit with ssl error related to a file path */ +static void ssl_path_err(const char* s, const char *path) +{ + unsigned long err; + err = ERR_peek_error(); + if (ERR_GET_LIB(err) == ERR_LIB_SYS && + (ERR_GET_FUNC(err) == SYS_F_FOPEN || + ERR_GET_FUNC(err) == SYS_F_FREAD) ) { + fprintf(stderr, "error: %s\n%s: %s\n", + s, path, ERR_reason_error_string(err)); + exit(1); + } else { + ssl_err(s); + } +} + /** setup SSL context */ static SSL_CTX* setup_ctx(struct config_file* cfg) @@ -146,39 +494,44 @@ char* s_cert=NULL, *c_key=NULL, *c_cert=NULL; SSL_CTX* ctx; - if(cfg->remote_control_use_cert) { - s_cert = fname_after_chroot(cfg->server_cert_file, cfg, 1); - c_key = fname_after_chroot(cfg->control_key_file, cfg, 1); - c_cert = fname_after_chroot(cfg->control_cert_file, cfg, 1); - if(!s_cert || !c_key || !c_cert) - fatal_exit("out of memory"); - } - ctx = SSL_CTX_new(SSLv23_client_method()); + if(!(options_remote_is_address(cfg) && cfg->control_use_cert)) + return NULL; + s_cert = fname_after_chroot(cfg->server_cert_file, cfg, 1); + c_key = fname_after_chroot(cfg->control_key_file, cfg, 1); + c_cert = fname_after_chroot(cfg->control_cert_file, cfg, 1); + if(!s_cert || !c_key || !c_cert) + fatal_exit("out of memory"); + ctx = SSL_CTX_new(SSLv23_client_method()); if(!ctx) ssl_err("could not allocate SSL_CTX pointer"); - if((SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2) & SSL_OP_NO_SSLv2) +#if SSL_OP_NO_SSLv2 != 0 + if((SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2) & SSL_OP_NO_SSLv2) != SSL_OP_NO_SSLv2) ssl_err("could not set SSL_OP_NO_SSLv2"); - if(cfg->remote_control_use_cert) { - if((SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3) & SSL_OP_NO_SSLv3) - != SSL_OP_NO_SSLv3) - ssl_err("could not set SSL_OP_NO_SSLv3"); - if(!SSL_CTX_use_certificate_chain_file(ctx,c_cert) || - !SSL_CTX_use_PrivateKey_file(ctx,c_key,SSL_FILETYPE_PEM) - || !SSL_CTX_check_private_key(ctx)) - ssl_err("Error setting up SSL_CTX client key and cert"); - if (SSL_CTX_load_verify_locations(ctx, s_cert, NULL) != 1) - ssl_err("Error setting up SSL_CTX verify, server cert"); - SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); +#endif + if((SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3) & SSL_OP_NO_SSLv3) + != SSL_OP_NO_SSLv3) + ssl_err("could not set SSL_OP_NO_SSLv3"); +#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) + ssl_err("could not set SSL_OP_NO_RENEGOTIATION"); +#endif + if(!SSL_CTX_use_certificate_chain_file(ctx,c_cert)) + ssl_path_err("Error setting up SSL_CTX client cert", c_cert); + if (!SSL_CTX_use_PrivateKey_file(ctx,c_key,SSL_FILETYPE_PEM)) + ssl_path_err("Error setting up SSL_CTX client key", c_key); + if (!SSL_CTX_check_private_key(ctx)) + ssl_err("Error setting up SSL_CTX client key"); + if (SSL_CTX_load_verify_locations(ctx, s_cert, NULL) != 1) + ssl_path_err("Error setting up SSL_CTX verify, server cert", + s_cert); + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); - free(s_cert); - free(c_key); - free(c_cert); - } else { - /* Use ciphers that don't require authentication */ - if(!SSL_CTX_set_cipher_list(ctx, "aNULL")) - ssl_err("Error setting NULL cipher!"); - } + free(s_cert); + free(c_key); + free(c_cert); return ctx; } @@ -188,13 +541,17 @@ { struct sockaddr_storage addr; socklen_t addrlen; - int addrfamily = 0; - int fd; + int addrfamily = 0, proto = IPPROTO_TCP; + int fd, useport = 1; /* use svr or the first config entry */ if(!svr) { - if(cfg->control_ifs) - svr = cfg->control_ifs->str; - else svr = "127.0.0.1"; + if(cfg->control_ifs.first) { + svr = cfg->control_ifs.first->str; + } else if(cfg->do_ip4) { + svr = "127.0.0.1"; + } else { + svr = "::1"; + } /* config 0 addr (everything), means ask localhost */ if(strcmp(svr, "0.0.0.0") == 0) svr = "127.0.0.1"; @@ -217,6 +574,8 @@ (void)strlcpy(usock->sun_path, svr, sizeof(usock->sun_path)); addrlen = (socklen_t)sizeof(struct sockaddr_un); addrfamily = AF_LOCAL; + useport = 0; + proto = 0; #endif } else { if(!ipstrtoaddr(svr, cfg->control_port, &addr, &addrlen)) @@ -224,8 +583,8 @@ } if(addrfamily == 0) - addrfamily = addr_is_ip6(&addr, addrlen)?AF_INET6:AF_INET; - fd = socket(addrfamily, SOCK_STREAM, 0); + addrfamily = addr_is_ip6(&addr, addrlen)?PF_INET6:PF_INET; + fd = socket(addrfamily, SOCK_STREAM, proto); if(fd == -1) { #ifndef USE_WINSOCK fatal_exit("socket: %s", strerror(errno)); @@ -235,14 +594,18 @@ } if(connect(fd, (struct sockaddr*)&addr, addrlen) < 0) { #ifndef USE_WINSOCK - log_err_addr("connect", strerror(errno), &addr, addrlen); - if(errno == ECONNREFUSED && statuscmd) { + int err = errno; + if(!useport) log_err("connect: %s for %s", strerror(err), svr); + else log_err_addr("connect", strerror(err), &addr, addrlen); + if(err == ECONNREFUSED && statuscmd) { printf("unbound is stopped\n"); exit(3); } #else - log_err_addr("connect", wsa_strerror(WSAGetLastError()), &addr, addrlen); - if(WSAGetLastError() == WSAECONNREFUSED && statuscmd) { + int wsaerr = WSAGetLastError(); + if(!useport) log_err("connect: %s for %s", wsa_strerror(wsaerr), svr); + else log_err_addr("connect", wsa_strerror(wsaerr), &addr, addrlen); + if(wsaerr == WSAECONNREFUSED && statuscmd) { printf("unbound is stopped\n"); exit(3); } @@ -254,17 +617,18 @@ /** setup SSL on the connection */ static SSL* -setup_ssl(SSL_CTX* ctx, int fd, struct config_file* cfg) +setup_ssl(SSL_CTX* ctx, int fd) { SSL* ssl; X509* x; int r; + if(!ctx) return NULL; ssl = SSL_new(ctx); if(!ssl) ssl_err("could not SSL_new"); SSL_set_connect_state(ssl); - (void)SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); + (void)SSL_set_mode(ssl, (long)SSL_MODE_AUTO_RETRY); if(!SSL_set_fd(ssl, fd)) ssl_err("could not SSL_set_fd"); while(1) { @@ -280,62 +644,139 @@ /* check authenticity of server */ if(SSL_get_verify_result(ssl) != X509_V_OK) ssl_err("SSL verification failed"); - if(cfg->remote_control_use_cert) { - x = SSL_get_peer_certificate(ssl); - if(!x) - ssl_err("Server presented no peer certificate"); - X509_free(x); - } + x = SSL_get_peer_certificate(ssl); + if(!x) + ssl_err("Server presented no peer certificate"); + X509_free(x); return ssl; } +/** read from ssl or fd, fatalexit on error, 0 EOF, 1 success */ +static int +remote_read(SSL* ssl, int fd, char* buf, size_t len) +{ + if(ssl) { + int r; + ERR_clear_error(); + if((r = SSL_read(ssl, buf, (int)len-1)) <= 0) { + if(SSL_get_error(ssl, r) == SSL_ERROR_ZERO_RETURN) { + /* EOF */ + return 0; + } + ssl_err("could not SSL_read"); + } + buf[r] = 0; + } else { + ssize_t rr = recv(fd, buf, len-1, 0); + if(rr <= 0) { + if(rr == 0) { + /* EOF */ + return 0; + } +#ifndef USE_WINSOCK + fatal_exit("could not recv: %s", strerror(errno)); +#else + fatal_exit("could not recv: %s", wsa_strerror(WSAGetLastError())); +#endif + } + buf[rr] = 0; + } + return 1; +} + +/** write to ssl or fd, fatalexit on error */ +static void +remote_write(SSL* ssl, int fd, const char* buf, size_t len) +{ + if(ssl) { + if(SSL_write(ssl, buf, (int)len) <= 0) + ssl_err("could not SSL_write"); + } else { + if(send(fd, buf, len, 0) < (ssize_t)len) { +#ifndef USE_WINSOCK + fatal_exit("could not send: %s", strerror(errno)); +#else + fatal_exit("could not send: %s", wsa_strerror(WSAGetLastError())); +#endif + } + } +} + +/** check args, to see if too many args. Because when a file is sent it + * would wait for the terminal, and we can check for too many arguments, + * eg. user put arguments on the commandline. */ +static void +check_args_for_listcmd(int argc, char* argv[]) +{ + if(argc >= 1 && (strcmp(argv[0], "local_zones") == 0 || + strcmp(argv[0], "local_zones_remove") == 0 || + strcmp(argv[0], "local_datas") == 0 || + strcmp(argv[0], "local_datas_remove") == 0) && + argc >= 2) { + fatal_exit("too many arguments for command '%s', " + "content is piped in from stdin", argv[0]); + } + if(argc >= 1 && (strcmp(argv[0], "view_local_datas") == 0 || + strcmp(argv[0], "view_local_datas_remove") == 0) && + argc >= 3) { + fatal_exit("too many arguments for command '%s', " + "content is piped in from stdin", argv[0]); + } +} + /** send stdin to server */ static void -send_file(SSL* ssl, FILE* in, char* buf, size_t sz) +send_file(SSL* ssl, int fd, FILE* in, char* buf, size_t sz) { while(fgets(buf, (int)sz, in)) { - if(SSL_write(ssl, buf, (int)strlen(buf)) <= 0) - ssl_err("could not SSL_write contents"); + remote_write(ssl, fd, buf, strlen(buf)); } } +/** send end-of-file marker to server */ +static void +send_eof(SSL* ssl, int fd) +{ + char e[] = {0x04, 0x0a}; + remote_write(ssl, fd, e, sizeof(e)); +} + /** send command and display result */ static int -go_cmd(SSL* ssl, int quiet, int argc, char* argv[]) +go_cmd(SSL* ssl, int fd, int quiet, int argc, char* argv[]) { char pre[10]; const char* space=" "; const char* newline="\n"; int was_error = 0, first_line = 1; - int r, i; + int i; char buf[1024]; snprintf(pre, sizeof(pre), "UBCT%d ", UNBOUND_CONTROL_VERSION); - if(SSL_write(ssl, pre, (int)strlen(pre)) <= 0) - ssl_err("could not SSL_write"); + remote_write(ssl, fd, pre, strlen(pre)); for(i=0; i= 1 && (strcmp(argv[0], "local_zones") == 0 || + strcmp(argv[0], "local_zones_remove") == 0 || + strcmp(argv[0], "local_datas") == 0 || + strcmp(argv[0], "view_local_datas") == 0 || + strcmp(argv[0], "local_datas_remove") == 0 || + strcmp(argv[0], "view_local_datas_remove") == 0)) { + send_file(ssl, fd, stdin, buf, sizeof(buf)); + send_eof(ssl, fd); + } while(1) { - ERR_clear_error(); - if((r = SSL_read(ssl, buf, (int)sizeof(buf)-1)) <= 0) { - if(SSL_get_error(ssl, r) == SSL_ERROR_ZERO_RETURN) { - /* EOF */ - break; - } - ssl_err("could not SSL_read"); + if(remote_read(ssl, fd, buf, sizeof(buf)) == 0) { + break; /* EOF */ } - buf[r] = 0; if(first_line && strncmp(buf, "error", 5) == 0) { printf("%s", buf); was_error = 1; @@ -370,18 +811,18 @@ /* contact server */ fd = contact_server(svr, cfg, argc>0&&strcmp(argv[0],"status")==0); - ssl = setup_ssl(ctx, fd, cfg); + ssl = setup_ssl(ctx, fd); /* send command */ - ret = go_cmd(ssl, quiet, argc, argv); + ret = go_cmd(ssl, fd, quiet, argc, argv); - SSL_free(ssl); + if(ssl) SSL_free(ssl); #ifndef USE_WINSOCK close(fd); #else closesocket(fd); #endif - SSL_CTX_free(ctx); + if(ctx) SSL_CTX_free(ctx); config_delete(cfg); return ret; } @@ -403,7 +844,7 @@ WSADATA wsa_data; #endif #ifdef USE_THREAD_DEBUG - /* stop the file output from unbound-control, overwites the servers */ + /* stop the file output from unbound-control, overwrites the servers */ extern int check_locking_order; check_locking_order = 0; #endif /* USE_THREAD_DEBUG */ @@ -411,44 +852,10 @@ log_init(NULL, 0, NULL); checklock_start(); #ifdef USE_WINSOCK - if((r = WSAStartup(MAKEWORD(2,2), &wsa_data)) != 0) - fatal_exit("WSAStartup failed: %s", wsa_strerror(r)); /* use registry config file in preference to compiletime location */ if(!(cfgfile=w_lookup_reg_str("Software\\Unbound", "ConfigFile"))) cfgfile = CONFIGFILE; #endif - -#ifdef HAVE_ERR_LOAD_CRYPTO_STRINGS - ERR_load_crypto_strings(); -#endif - ERR_load_SSL_strings(); -#if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_CRYPTO) - OpenSSL_add_all_algorithms(); -#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(0, NULL); -#endif - - if(!RAND_status()) { - /* try to seed it */ - unsigned char buf[256]; - unsigned int seed=(unsigned)time(NULL) ^ (unsigned)getpid(); - unsigned int v = seed; - size_t i; - for(i=0; i<256/sizeof(v); i++) { - memmove(buf+i*sizeof(v), &v, sizeof(v)); - v = v*seed + (unsigned int)i; - } - RAND_seed(buf, 256); - log_warn("no entropy, seeding openssl PRNG with time\n"); - } - /* parse the options */ while( (c=getopt(argc, argv, "c:s:qh")) != -1) { switch(c) { @@ -478,11 +885,56 @@ strerror(errno)); } } + if(argc >= 1 && strcmp(argv[0], "stats_shm")==0) { + print_stats_shm(cfgfile); + return 0; + } + check_args_for_listcmd(argc, argv); +#ifdef USE_WINSOCK + if((r = WSAStartup(MAKEWORD(2,2), &wsa_data)) != 0) + fatal_exit("WSAStartup failed: %s", wsa_strerror(r)); +#endif + +#ifdef HAVE_ERR_LOAD_CRYPTO_STRINGS + ERR_load_crypto_strings(); +#endif +#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 + + if(!RAND_status()) { + /* try to seed it */ + unsigned char buf[256]; + unsigned int seed=(unsigned)time(NULL) ^ (unsigned)getpid(); + unsigned int v = seed; + size_t i; + for(i=0; i<256/sizeof(v); i++) { + memmove(buf+i*sizeof(v), &v, sizeof(v)); + v = v*seed + (unsigned int)i; + } + RAND_seed(buf, 256); + log_warn("no entropy, seeding openssl PRNG with time\n"); + } + ret = go(cfgfile, svr, quiet, argc, argv); #ifdef USE_WINSOCK - WSACleanup(); + WSACleanup(); #endif checklock_stop(); return ret; --- contrib/unbound/smallapp/unbound-host.c.orig +++ contrib/unbound/smallapp/unbound-host.c @@ -66,6 +66,14 @@ /* nss3 */ #include "nss.h" #endif +#ifdef HAVE_SSL +#ifdef HAVE_OPENSSL_SSL_H +#include +#endif +#ifdef HAVE_OPENSSL_ERR_H +#include +#endif +#endif /* HAVE_SSL */ /** verbosity for unbound-host app */ static int verb = 0; @@ -74,9 +82,8 @@ static void usage(void) { - printf("Usage: unbound-host [-vdhr46] [-c class] [-t type] hostname\n"); - printf(" [-y key] [-f keyfile] [-F namedkeyfile]\n"); - printf(" [-C configfile]\n"); + printf("Usage: unbound-host [-C configfile] [-vdhr46] [-c class] [-t type]\n"); + printf(" [-y key] [-f keyfile] [-F namedkeyfile] hostname\n"); printf(" Queries the DNS for information.\n"); printf(" The hostname is looked up for IP4, IP6 and mail.\n"); printf(" If an ip-address is given a reverse lookup is done.\n"); @@ -90,6 +97,8 @@ printf(" -f keyfile read trust anchors from file, with lines as -y.\n"); printf(" -F keyfile read named.conf-style trust anchors.\n"); printf(" -C config use the specified unbound.conf (none read by default)\n"); + printf(" pass as first argument if you want to override some\n"); + printf(" options with further arguments\n"); printf(" -r read forwarder information from /etc/resolv.conf\n"); printf(" breaks validation if the forwarder does not do DNSSEC.\n"); printf(" -v be more verbose, shows nodata and security.\n"); @@ -209,6 +218,7 @@ static const char* secure_str(struct ub_result* result) { + if(result->rcode != 0 && result->rcode != 3) return "(error)"; if(result->secure) return "(secure)"; if(result->bogus) return "(BOGUS (security failure))"; return "(insecure)"; @@ -330,6 +340,7 @@ exit(1); } printf("%s\n", s); + free(s); } else printf(" has no %s record", tstr); printf(" %s\n", secstatus); } @@ -415,6 +426,7 @@ int c; char* qclass = NULL; char* qtype = NULL; + char* use_syslog = NULL; struct ub_ctx* ctx = NULL; int debuglevel = 0; @@ -475,11 +487,11 @@ } if(debuglevel != 0) /* set after possible -C options */ check_ub_res(ub_ctx_debuglevel(ctx, debuglevel)); - if(ub_ctx_get_option(ctx, "use-syslog", &optarg) == 0) { - if(strcmp(optarg, "yes") == 0) /* disable use-syslog */ + if(ub_ctx_get_option(ctx, "use-syslog", &use_syslog) == 0) { + if(strcmp(use_syslog, "yes") == 0) /* disable use-syslog */ check_ub_res(ub_ctx_set_option(ctx, "use-syslog:", "no")); - free(optarg); + free(use_syslog); } argc -= optind; argv += optind; @@ -486,6 +498,28 @@ if(argc != 1) usage(); +#ifdef HAVE_SSL +#ifdef HAVE_ERR_LOAD_CRYPTO_STRINGS + ERR_load_crypto_strings(); +#endif +#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 */ #ifdef HAVE_NSS if(NSS_NoDB_Init(".") != SECSuccess) { fprintf(stderr, "could not init NSS\n"); --- contrib/unbound/smallapp/worker_cb.c.orig +++ contrib/unbound/smallapp/worker_cb.c @@ -99,14 +99,13 @@ log_assert(0); } -struct outbound_entry* worker_send_query(uint8_t* ATTR_UNUSED(qname), - size_t ATTR_UNUSED(qnamelen), uint16_t ATTR_UNUSED(qtype), - uint16_t ATTR_UNUSED(qclass), uint16_t ATTR_UNUSED(flags), +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), struct edns_option* ATTR_UNUSED(opt_list), - struct sockaddr_storage* ATTR_UNUSED(addr), + int ATTR_UNUSED(nocaps), struct sockaddr_storage* ATTR_UNUSED(addr), socklen_t ATTR_UNUSED(addrlen), uint8_t* ATTR_UNUSED(zone), - size_t ATTR_UNUSED(zonelen), struct module_qstate* ATTR_UNUSED(q)) + size_t ATTR_UNUSED(zonelen), int ATTR_UNUSED(ssl_upstream), + char* ATTR_UNUSED(tls_auth_name), struct module_qstate* ATTR_UNUSED(q)) { log_assert(0); return 0; @@ -132,14 +131,13 @@ log_assert(0); } -struct outbound_entry* libworker_send_query(uint8_t* ATTR_UNUSED(qname), - size_t ATTR_UNUSED(qnamelen), uint16_t ATTR_UNUSED(qtype), - uint16_t ATTR_UNUSED(qclass), uint16_t ATTR_UNUSED(flags), +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), struct edns_option* ATTR_UNUSED(opt_list), - struct sockaddr_storage* ATTR_UNUSED(addr), + int ATTR_UNUSED(nocaps), struct sockaddr_storage* ATTR_UNUSED(addr), socklen_t ATTR_UNUSED(addrlen), uint8_t* ATTR_UNUSED(zone), - size_t ATTR_UNUSED(zonelen), struct module_qstate* ATTR_UNUSED(q)) + size_t ATTR_UNUSED(zonelen), int ATTR_UNUSED(ssl_upstream), + char* ATTR_UNUSED(tls_auth_name), struct module_qstate* ATTR_UNUSED(q)) { log_assert(0); return 0; @@ -170,7 +168,7 @@ 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)) + char* ATTR_UNUSED(why_bogus), int ATTR_UNUSED(was_ratelimited)) { log_assert(0); } @@ -177,7 +175,7 @@ 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)) + char* ATTR_UNUSED(why_bogus), int ATTR_UNUSED(was_ratelimited)) { log_assert(0); } @@ -184,7 +182,7 @@ 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)) + char* ATTR_UNUSED(why_bogus), int ATTR_UNUSED(was_ratelimited)) { log_assert(0); } --- contrib/unbound/systemd.m4.orig +++ contrib/unbound/systemd.m4 @@ -0,0 +1,31 @@ +# macros for configuring systemd +# Copyright 2015, Sami Kerola, CloudFlare. +# BSD licensed. +AC_ARG_ENABLE([systemd], + [AS_HELP_STRING([--enable-systemd], [compile with systemd support])], + [], [enable_systemd=no]) +have_systemd=no +AS_IF([test "x$enable_systemd" != xno], [ + ifdef([PKG_CHECK_MODULES], [ + dnl systemd v209 or newer + PKG_CHECK_MODULES([SYSTEMD], [libsystemd], [have_systemd=yes], [have_systemd=no]) + dnl old systemd library + AS_IF([test "x$have_systemd" != "xyes"], [ + PKG_CHECK_MODULES([SYSTEMD_DAEMON], [libsystemd-daemon], + [have_systemd_daemon=yes], [have_systemd_daemon=no]) + AS_IF([test "x$have_systemd_daemon" = "xyes"], + [have_systemd=yes]) + ]) + AS_CASE([$enable_systemd:$have_systemd], + [yes:no], + [AC_MSG_ERROR([systemd enabled but libsystemd not found])], + [*:yes], + [AC_DEFINE([HAVE_SYSTEMD], [1], [Define to 1 if systemd should be used]) + LIBS="$LIBS $SYSTEMD_LIBS" + ] + ) + ], [ + AC_MSG_ERROR([systemd enabled but need pkg-config to configure for it]) + ]) +]) +AM_CONDITIONAL([USE_SYSTEMD], [test "x$have_systemd" = xyes]) --- contrib/unbound/util/alloc.c.orig +++ contrib/unbound/util/alloc.c @@ -52,7 +52,7 @@ /** setup new special type */ static void -alloc_setup_special(alloc_special_t* t) +alloc_setup_special(alloc_special_type* t) { memset(t, 0, sizeof(*t)); lock_rw_init(&t->entry.lock); @@ -66,10 +66,11 @@ static void prealloc_setup(struct alloc_cache* alloc) { - alloc_special_t* p; + alloc_special_type* p; int i; for(i=0; iquar; + while(p) { + np = alloc_special_next(p); + /* deinit special type */ + lock_rw_destroy(&p->entry.lock); + free(p); + p = np; + } +} + +void +alloc_clear_special(struct alloc_cache* alloc) +{ + if(!alloc->super) { + lock_quick_lock(&alloc->lock); + } + alloc_clear_special_list(alloc); + alloc->quar = 0; + alloc->num_quar = 0; + if(!alloc->super) { + lock_quick_unlock(&alloc->lock); + } +} + void alloc_clear(struct alloc_cache* alloc) { - alloc_special_t* p, *np; + alloc_special_type* p; struct regional* r, *nr; if(!alloc) return; @@ -146,15 +177,7 @@ alloc->super->num_quar += alloc->num_quar; lock_quick_unlock(&alloc->super->lock); } else { - /* free */ - p = alloc->quar; - while(p) { - np = alloc_special_next(p); - /* deinit special type */ - lock_rw_destroy(&p->entry.lock); - free(p); - p = np; - } + alloc_clear_special_list(alloc); } alloc->quar = 0; alloc->num_quar = 0; @@ -187,10 +210,10 @@ return id; } -alloc_special_t* +alloc_special_type* alloc_special_obtain(struct alloc_cache* alloc) { - alloc_special_t* p; + alloc_special_type* p; log_assert(alloc); /* see if in local cache */ if(alloc->quar) { @@ -217,7 +240,7 @@ } /* allocate new */ prealloc_setup(alloc); - if(!(p = (alloc_special_t*)malloc(sizeof(alloc_special_t)))) { + if(!(p = (alloc_special_type*)malloc(sizeof(alloc_special_type)))) { log_err("alloc_special_obtain: out of memory"); return NULL; } @@ -228,10 +251,10 @@ /** push mem and some more items to the super */ static void -pushintosuper(struct alloc_cache* alloc, alloc_special_t* mem) +pushintosuper(struct alloc_cache* alloc, alloc_special_type* mem) { int i; - alloc_special_t *p = alloc->quar; + alloc_special_type *p = alloc->quar; log_assert(p); log_assert(alloc && alloc->super && alloc->num_quar >= ALLOC_SPECIAL_MAX); @@ -253,7 +276,7 @@ } void -alloc_special_release(struct alloc_cache* alloc, alloc_special_t* mem) +alloc_special_release(struct alloc_cache* alloc, alloc_special_type* mem) { log_assert(alloc); if(!mem) @@ -286,12 +309,12 @@ size_t alloc_get_mem(struct alloc_cache* alloc) { - alloc_special_t* p; + alloc_special_type* p; size_t s = sizeof(*alloc); if(!alloc->super) { lock_quick_lock(&alloc->lock); /* superalloc needs locking */ } - s += sizeof(alloc_special_t) * alloc->num_quar; + s += sizeof(alloc_special_type) * alloc->num_quar; for(p = alloc->quar; p; p = alloc_special_next(p)) { s += lock_get_mem(&p->entry.lock); } @@ -353,6 +376,7 @@ { void* res; if(size == 0) size = 1; + log_assert(size <= SIZE_MAX-16); res = malloc(size+16); if(!res) return NULL; unbound_mem_alloc += size; @@ -375,6 +399,7 @@ if(nmemb != 0 && INT_MAX/nmemb < size) return NULL; /* integer overflow check */ s = (nmemb*size==0)?(size_t)1:nmemb*size; + log_assert(s <= SIZE_MAX-16); res = calloc(1, s+16); if(!res) return NULL; log_info("stat %p=calloc(%u, %u)", res+16, (unsigned)nmemb, (unsigned)size); @@ -424,6 +449,7 @@ /* nothing changes */ return ptr; } + log_assert(size <= SIZE_MAX-16); res = malloc(size+16); if(!res) return NULL; unbound_mem_alloc += size; @@ -498,7 +524,9 @@ const char* func) { /* [prefix .. len .. actual data .. suffix] */ - void* res = malloc(size+lite_pad*2+sizeof(size_t)); + void* res; + log_assert(size <= SIZE_MAX-(lite_pad*2+sizeof(size_t))); + res = malloc(size+lite_pad*2+sizeof(size_t)); if(!res) return NULL; memmove(res, lite_pre, lite_pad); memmove(res+lite_pad, &size, sizeof(size_t)); @@ -515,6 +543,7 @@ if(nmemb != 0 && INT_MAX/nmemb < size) return NULL; /* integer overflow check */ req = nmemb * size; + log_assert(req <= SIZE_MAX-(lite_pad*2+sizeof(size_t))); res = malloc(req+lite_pad*2+sizeof(size_t)); if(!res) return NULL; memmove(res, lite_pre, lite_pad); --- contrib/unbound/util/alloc.h.orig +++ contrib/unbound/util/alloc.h @@ -53,11 +53,11 @@ struct regional; /** The special type, packed rrset. Not allowed to be used for other memory */ -typedef struct ub_packed_rrset_key alloc_special_t; +typedef struct ub_packed_rrset_key alloc_special_type; /** clean the special type. Pass pointer. */ #define alloc_special_clean(x) (x)->id = 0; /** access next pointer. (in available spot). Pass pointer. */ -#define alloc_special_next(x) ((alloc_special_t*)((x)->entry.overflow_next)) +#define alloc_special_next(x) ((alloc_special_type*)((x)->entry.overflow_next)) /** set next pointer. (in available spot). Pass pointers. */ #define alloc_set_special_next(x, y) \ ((x)->entry.overflow_next) = (struct lruhash_entry*)(y); @@ -71,11 +71,11 @@ */ struct alloc_cache { /** lock, only used for the super. */ - lock_quick_t lock; + lock_quick_type lock; /** global allocator above this one. NULL for none (malloc/free) */ struct alloc_cache* super; /** singly linked lists of special type. These are free for use. */ - alloc_special_t* quar; + alloc_special_type* quar; /** number of items in quarantine. */ size_t num_quar; /** thread number for id creation */ @@ -116,20 +116,28 @@ void alloc_clear(struct alloc_cache* alloc); /** - * Get a new special_t element. + * Free the special alloced items. The rrset and message caches must be + * empty, there must be no more references to rrset pointers into the + * rrset cache. + * @param alloc: the special allocs are freed. + */ +void alloc_clear_special(struct alloc_cache* alloc); + +/** + * Get a new special_type element. * @param alloc: where to alloc it. * @return: memory block. Will not return NULL (instead fatal_exit). * The block is zeroed. */ -alloc_special_t* alloc_special_obtain(struct alloc_cache* alloc); +alloc_special_type* alloc_special_obtain(struct alloc_cache* alloc); /** - * Return special_t back to pool. + * Return special_type back to pool. * The block is cleaned up (zeroed) which also invalidates the ID inside. * @param alloc: where to alloc it. * @param mem: block to free. */ -void alloc_special_release(struct alloc_cache* alloc, alloc_special_t* mem); +void alloc_special_release(struct alloc_cache* alloc, alloc_special_type* mem); /** * Set ID number of special type to a fresh new ID number. --- contrib/unbound/util/config_file.c.orig +++ contrib/unbound/util/config_file.c @@ -59,19 +59,25 @@ #include "services/cache/infra.h" #include "sldns/wire2str.h" #include "sldns/parseutil.h" +#include "iterator/iterator.h" #ifdef HAVE_GLOB_H # include #endif +#ifdef CLIENT_SUBNET +#include "edns-subnet/edns-subnet.h" +#endif #ifdef HAVE_PWD_H #include #endif -/** from cfg username, after daemonise setup performed */ +/** from cfg username, after daemonize setup performed */ uid_t cfg_uid = (uid_t)-1; -/** from cfg username, after daemonise setup performed */ +/** from cfg username, after daemonize setup performed */ gid_t cfg_gid = (gid_t)-1; /** for debug allow small timeout values for fast rollovers */ int autr_permit_small_holddown = 0; +/** size (in bytes) of stream wait buffers max */ +size_t stream_wait_max = 4 * 1024 * 1024; /** global config during parsing */ struct config_parser_state* cfg_parser = 0; @@ -98,15 +104,26 @@ cfg->do_udp = 1; cfg->do_tcp = 1; cfg->tcp_upstream = 0; + cfg->udp_upstream_without_downstream = 0; cfg->tcp_mss = 0; cfg->outgoing_tcp_mss = 0; + cfg->tcp_idle_timeout = 30 * 1000; /* 30s in millisecs */ + cfg->do_tcp_keepalive = 0; + cfg->tcp_keepalive_timeout = 120 * 1000; /* 120s in millisecs */ cfg->ssl_service_key = NULL; cfg->ssl_service_pem = NULL; - cfg->ssl_port = 853; + cfg->ssl_port = UNBOUND_DNS_OVER_TLS_PORT; cfg->ssl_upstream = 0; + cfg->tls_cert_bundle = NULL; + cfg->tls_win_cert = 0; 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 */ @@ -125,6 +142,7 @@ 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 = 4096; /* 4k from rfc recommendation */ cfg->msg_buffer_size = 65552; /* 64 k + a small margin */ cfg->msg_cache_size = 4 * 1024 * 1024; @@ -139,6 +157,7 @@ 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; @@ -154,14 +173,17 @@ 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->so_rcvbuf = 0; cfg->so_sndbuf = 0; - cfg->so_reuseport = 0; + cfg->so_reuseport = REUSEPORT_DEFAULT; cfg->ip_transparent = 0; cfg->ip_freebind = 0; cfg->num_ifs = 0; @@ -170,12 +192,27 @@ 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 = 0; cfg->harden_large_queries = 0; cfg->harden_glue = 1; cfg->harden_dnssec_stripped = 1; - cfg->harden_below_nxdomain = 0; + cfg->harden_below_nxdomain = 1; cfg->harden_referral_path = 0; cfg->harden_algo_downgrade = 0; cfg->use_caps_bits_for_id = 0; @@ -185,6 +222,7 @@ cfg->unwanted_threshold = 0; cfg->hide_identity = 0; cfg->hide_version = 0; + cfg->hide_trustanchor = 0; cfg->identity = NULL; cfg->version = NULL; cfg->auto_trust_anchor_file_list = NULL; @@ -191,6 +229,8 @@ 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->dlv_anchor_file = NULL; cfg->dlv_anchor_list = NULL; cfg->domain_insecure = NULL; @@ -201,7 +241,13 @@ cfg->val_log_level = 0; cfg->val_log_squelch = 0; cfg->val_permissive_mode = 0; + cfg->aggressive_nsec = 0; cfg->ignore_cd = 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->add_holddown = 30*24*3600; cfg->del_holddown = 30*24*3600; cfg->keep_missing = 366*24*3600; /* one year plus a little leeway */ @@ -211,6 +257,10 @@ 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; @@ -217,11 +267,13 @@ cfg->insecure_lan_zones = 0; cfg->python_script = NULL; cfg->remote_control_enable = 0; - cfg->control_ifs = NULL; + cfg->control_ifs.first = NULL; + cfg->control_ifs.last = NULL; cfg->control_port = UNBOUND_CONTROL_PORT; - cfg->remote_control_use_cert = 1; - cfg->minimal_responses = 0; + cfg->control_use_cert = 1; + cfg->minimal_responses = 1; cfg->rrset_roundrobin = 0; + cfg->unknown_server_time_limit = 376; cfg->max_udp_size = 4096; if(!(cfg->server_key_file = strdup(RUN_DIR"/unbound_server.key"))) goto error_exit; @@ -232,7 +284,11 @@ 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 500 4096 2500"))) goto error_exit; #if defined(DNSTAP_SOCKET_PATH) @@ -240,16 +296,54 @@ goto error_exit; #endif cfg->disable_dnssec_lame_check = 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->qname_minimisation = 0; + cfg->qname_minimisation = 1; + cfg->qname_minimisation_strict = 0; + cfg->shm_enable = 0; + cfg->shm_key = 11777; + 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; +#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 +#ifdef USE_CACHEDB + if(!(cfg->cachedb_backend = strdup("testframe"))) goto error_exit; + if(!(cfg->cachedb_secret = strdup("default"))) goto error_exit; +#ifdef USE_REDIS + if(!(cfg->redis_server_host = strdup("127.0.0.1"))) goto error_exit; + cfg->redis_timeout = 100; + cfg->redis_server_port = 6379; +#endif /* USE_REDIS */ +#endif /* USE_CACHEDB */ +#ifdef USE_IPSET + cfg->ipset_name_v4 = NULL; + cfg->ipset_name_v6 = NULL; +#endif return cfg; error_exit: - config_delete(cfg); + config_delete(cfg); return NULL; } @@ -277,6 +371,7 @@ forward nameserver running on localhost */ cfg->val_log_level = 2; /* to fill why_bogus with */ cfg->val_log_squelch = 1; + cfg->minimal_responses = 0; return cfg; } @@ -322,10 +417,23 @@ /** 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)+2log_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-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("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-idle-timeout:", tcp_idle_timeout) + else S_YNO("edns-tcp-keepalive:", do_tcp_keepalive) + else S_NUMBER_NONZERO("edns-tcp-keepalive-timeout:", tcp_keepalive_timeout) else S_YNO("ssl-upstream:", ssl_upstream) else S_STR("ssl-service-key:", ssl_service_key) else S_STR("ssl-service-pem:", ssl_service_pem) else S_NUMBER_NONZERO("ssl-port:", ssl_port) + else S_STR("tls-cert-bundle:", tls_cert_bundle) + else S_YNO("tls-win-cert:", tls_win_cert) + 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("interface-automatic:", if_automatic) + 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) @@ -401,6 +527,7 @@ 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) @@ -421,6 +548,7 @@ 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_STR("identity:", identity) else S_STR("version:", version) else S_STRLIST("root-hints:", root_hints) @@ -432,7 +560,7 @@ 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("use-caps-for-id", use_caps_bits_for_id) + 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) @@ -443,6 +571,8 @@ 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_STR("dlv-anchor-file:", dlv_anchor_file) else S_STRLIST("dlv-anchor:", dlv_anchor_list) else S_STRLIST("domain-insecure:", domain_insecure) @@ -451,8 +581,22 @@ 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 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_STR("val-nsec3-keysize-iterations:", val_nsec3_key_iterations) else S_UNSIGNED_OR_ZERO("add-holddown:", add_holddown) else S_UNSIGNED_OR_ZERO("del-holddown:", del_holddown) @@ -465,11 +609,12 @@ 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("control-interface:", control_ifs) + 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) @@ -476,16 +621,75 @@ 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_STR("python-script:", python_script) + else S_STRLIST("python-script:", python_script) 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_STR("dnstap-socket-path:", dnstap_socket_path) + 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:") == 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_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) +#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 else if(strcmp(opt, "define-tag:") ==0) { return config_add_tag(cfg, val); /* val_sig_skew_min and max are copied into val_env during init, @@ -507,12 +711,18 @@ cfg->out_ifs = oi; } else { /* unknown or unsupported (from the set_option interface): - * interface, outgoing-interface, access-control, + * interface, outgoing-interface, access-control, * stub-zone, name, stub-addr, stub-host, stub-prime - * forward-first, stub-first, - * forward-zone, name, forward-addr, forward-host, + * 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 */ + * local-zone-tag, access-control-view, + * 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; @@ -667,14 +877,23 @@ config_get_option(struct config_file* cfg, const char* opt, void (*func)(char*,void*), void* arg) { - char buf[1024]; + char buf[1024], nopt[64]; size_t len = sizeof(buf); + if(!opt) return 0; + if(opt && opt[strlen(opt)-1] == ':' && strlen(opt)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); @@ -963,6 +1290,33 @@ } 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; @@ -983,6 +1337,30 @@ } } +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; + } +} /** delete string array */ static void config_del_strarray(char** array, int num) @@ -1021,12 +1399,27 @@ 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); + 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->module_conf); @@ -1042,27 +1435,49 @@ free(cfg->dlv_anchor_file); config_delstrlist(cfg->dlv_anchor_list); 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->acl_tags); + config_del_strbytelist(cfg->respip_tags); config_deltrplstrlist(cfg->acl_tag_actions); config_deltrplstrlist(cfg->acl_tag_datas); - config_delstrlist(cfg->control_ifs); + 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->dns64_prefix); + config_delstrlist(cfg->dns64_ignore_aaaa); free(cfg->dnstap_socket_path); 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); +#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); +#endif /* USE_REDIS */ +#endif /* USE_CACHEDB */ +#ifdef USE_IPSET + free(cfg->ipset_name_v4); + free(cfg->ipset_name_v6); +#endif free(cfg); } @@ -1194,11 +1609,15 @@ int cfg_strlist_append(struct config_strlist_head* list, char* item) { struct config_strlist *s; - if(!item || !list) + if(!item || !list) { + free(item); return 0; + } s = (struct config_strlist*)calloc(1, sizeof(struct config_strlist)); - if(!s) + if(!s) { + free(item); return 0; + } s->str = item; s->next = NULL; if(list->last) @@ -1226,15 +1645,35 @@ 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) + if(!item || !head) { + free(item); return 0; + } s = (struct config_strlist*)calloc(1, sizeof(struct config_strlist)); - if(!s) + if(!s) { + free(item); return 0; + } s->str = item; s->next = *head; *head = s; @@ -1241,15 +1680,46 @@ 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) + if(!item || !i2 || !head) { + free(item); + free(i2); return 0; + } s = (struct config_str2list*)calloc(1, sizeof(struct config_str2list)); - if(!s) + if(!s) { + free(item); + free(i2); return 0; + } s->str = item; s->str2 = i2; s->next = *head; @@ -1501,7 +1971,7 @@ return strdup(buf); } -int taglist_intersect(uint8_t* list1, size_t list1len, uint8_t* list2, +int taglist_intersect(uint8_t* list1, size_t list1len, const uint8_t* list2, size_t list2len) { size_t i; @@ -1519,13 +1989,19 @@ { 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; MAX_NEG_TTL = (time_t)config->max_negative_ttl; RTT_MIN_TIMEOUT = config->infra_cache_min_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; log_set_time_asc(config->log_time_ascii); autr_permit_small_holddown = config->permit_small_holddown; + stream_wait_max = config->stream_wait_size; } void config_lookup_uid(struct config_file* cfg) @@ -1708,6 +2184,11 @@ 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)); @@ -1857,7 +2338,7 @@ void errinf(struct module_qstate* qstate, const char* str) { struct config_strlist* p; - if(qstate->env->cfg->val_log_level < 2 || !str) + if((qstate->env->cfg->val_log_level < 2 && !qstate->env->cfg->log_servfail) || !str) return; p = (struct config_strlist*)regional_alloc(qstate->region, sizeof(*p)); if(!p) { @@ -1882,7 +2363,7 @@ void errinf_origin(struct module_qstate* qstate, struct sock_list *origin) { struct sock_list* p; - if(qstate->env->cfg->val_log_level < 2) + if(qstate->env->cfg->val_log_level < 2 && !qstate->env->cfg->log_servfail) return; for(p=origin; p; p=p->next) { char buf[256]; @@ -1899,7 +2380,7 @@ } } -char* errinf_to_str(struct module_qstate* qstate) +char* errinf_to_str_bogus(struct module_qstate* qstate) { char buf[20480]; char* p = buf; @@ -1924,12 +2405,37 @@ return p; } +char* errinf_to_str_servfail(struct module_qstate* qstate) +{ + char buf[20480]; + char* p = buf; + size_t left = sizeof(buf); + struct config_strlist* s; + char dname[LDNS_MAX_DOMAINLEN+1]; + char t[16], c[16]; + sldns_wire2str_type_buf(qstate->qinfo.qtype, t, sizeof(t)); + sldns_wire2str_class_buf(qstate->qinfo.qclass, c, sizeof(c)); + dname_str(qstate->qinfo.qname, dname); + snprintf(p, left, "SERVFAIL <%s %s %s>:", dname, t, c); + left -= strlen(p); p += strlen(p); + if(!qstate->errinf) + snprintf(p, left, " misc failure"); + else for(s=qstate->errinf; s; s=s->next) { + snprintf(p, left, " %s", s->str); + left -= strlen(p); p += strlen(p); + } + p = strdup(buf); + if(!p) + log_err("malloc failure in errinf_to_str"); + return p; +} + void errinf_rrset(struct module_qstate* qstate, struct ub_packed_rrset_key *rr) { char buf[1024]; char dname[LDNS_MAX_DOMAINLEN+1]; char t[16], c[16]; - if(qstate->env->cfg->val_log_level < 2 || !rr) + 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)); @@ -1942,9 +2448,19 @@ { char b[1024]; char buf[LDNS_MAX_DOMAINLEN+1]; - if(qstate->env->cfg->val_log_level < 2 || !str || !dname) + if((qstate->env->cfg->val_log_level < 2 && !qstate->env->cfg->log_servfail) || !str || !dname) return; dname_str(dname, buf); snprintf(b, sizeof(b), "%s %s", str, buf); errinf(qstate, b); } + +int options_remote_is_address(struct config_file* cfg) +{ + if(!cfg->remote_control_enable) return 0; + 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] != '/'); +} + --- contrib/unbound/util/config_file.h.orig +++ contrib/unbound/util/config_file.h @@ -42,6 +42,8 @@ #ifndef UTIL_CONFIG_FILE_H #define UTIL_CONFIG_FILE_H struct config_stub; +struct config_auth; +struct config_view; struct config_strlist; struct config_str2list; struct config_str3list; @@ -51,6 +53,14 @@ 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. @@ -83,10 +93,18 @@ int do_tcp; /** tcp upstream queries (no UDP upstream queries) */ int tcp_upstream; + /** udp upstream enabled when no UDP downstream is enabled (do_udp no)*/ + int udp_upstream_without_downstream; /** maximum segment size of tcp socket which queries are answered */ int tcp_mss; /** maximum segment size of tcp socket for outgoing queries */ 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; /** private key file for dnstcp-ssl service (enabled if not NULL) */ char* ssl_service_key; @@ -96,6 +114,18 @@ 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; /** outgoing port range number of ports (per thread) */ int outgoing_num_ports; @@ -108,6 +138,8 @@ /** 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 */ @@ -135,6 +167,11 @@ /** 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 */ @@ -167,13 +204,40 @@ 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 */ @@ -208,6 +272,8 @@ 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; @@ -226,11 +292,23 @@ 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; /** identity, hostname is returned if "". */ char* identity; /** version, package version returned if "". */ @@ -253,6 +331,10 @@ struct config_strlist* dlv_anchor_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; @@ -270,8 +352,21 @@ 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; + /** 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; /** nsec3 maximum iterations per key size, string */ char* val_nsec3_key_iterations; /** autotrust add holddown time, in seconds */ @@ -294,6 +389,12 @@ 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 */ @@ -310,6 +411,14 @@ 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 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 */ @@ -318,11 +427,11 @@ /** remote control section. enable toggle. */ int remote_control_enable; /** the interfaces the remote control should listen on */ - struct config_strlist* control_ifs; + 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; - /** use certificates for remote control */ - int remote_control_use_cert; /** private key file for server */ char* server_key_file; /** certificate file for server */ @@ -333,8 +442,11 @@ char* control_cert_file; /** Python script file */ - char* python_script; + struct config_strlist* python_script; + /** Use systemd socket activation. */ + int use_systemd; + /** daemonize, i.e. fork into the background. */ int do_daemonize; @@ -344,6 +456,9 @@ /* 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; @@ -352,6 +467,8 @@ /* 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; /** true to enable dnstap support */ int dnstap; @@ -382,7 +499,16 @@ /** true to disable DNSSEC lameness check in iterator */ int disable_dnssec_lame_check; - /** ratelimit 0 is off, otherwise qps (unless overridden) */ + /** ratelimit for ip addresses. 0 is off, otherwise qps (unless overridden) */ + int ip_ratelimit; + /** 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 for domains. 0 is off, otherwise qps (unless overridden) */ int ratelimit; /** number of slabs for ratelimit cache */ size_t ratelimit_slabs; @@ -396,14 +522,84 @@ int ratelimit_factor; /** 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; + + /** 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; + /** 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; +#ifdef USE_REDIS + /** redis server's IP address or host name */ + char* redis_server_host; + /** redis server's TCP port */ + int redis_server_port; + /** timeout (in ms) for communication with the redis server */ + int redis_timeout; +#endif +#endif + + /* ipset module */ +#ifdef USE_IPSET + char* ipset_name_v4; + char* ipset_name_v6; +#endif }; -/** from cfg username, after daemonise setup performed */ +/** from cfg username, after daemonize setup performed */ extern uid_t cfg_uid; -/** from cfg username, after daemonise setup performed */ +/** from cfg username, after daemonize setup performed */ extern gid_t cfg_gid; /** debug and enable small timeouts */ extern int autr_permit_small_holddown; +/** size (in bytes) of stream wait buffers max */ +extern size_t stream_wait_max; /** * Stub config options @@ -421,9 +617,80 @@ int isprime; /** if forward-first is set (failover to without if fails) */ int isfirst; + /** 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; +}; + +/** + * 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 { @@ -473,14 +740,6 @@ size_t str2len; }; -/** 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; -}; - /** * Create config file structure. Filled with default values. * @return: the new structure or NULL on memory error. @@ -591,14 +850,33 @@ * @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); @@ -612,6 +890,7 @@ * @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); @@ -682,6 +961,34 @@ 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. @@ -756,7 +1063,7 @@ * @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, uint8_t* list2, +int taglist_intersect(uint8_t* list1, size_t list1len, const uint8_t* list2, size_t list2len); /** @@ -848,14 +1155,22 @@ uint8_t* dname); /** - * Create error info in string + * 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(struct module_qstate* qstate); +char* errinf_to_str_bogus(struct module_qstate* qstate); /** + * Create error info in string. For other servfails. + * @param qstate: query state. + * @return string or NULL on malloc failure (already logged). + * This string is malloced and has to be freed by caller. + */ +char* errinf_to_str_servfail(struct module_qstate* qstate); + +/** * Used during options parsing */ struct config_parser_state { @@ -904,4 +1219,8 @@ void w_config_adjust_directory(struct config_file* cfg); #endif /* UB_ON_WINDOWS */ +/** debug option for unit tests. */ +extern int fake_dsa, fake_sha1; + #endif /* UTIL_CONFIG_FILE_H */ + --- contrib/unbound/util/configlexer.lex.orig +++ contrib/unbound/util/configlexer.lex @@ -10,10 +10,11 @@ #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 -#include #include #ifdef HAVE_GLOB_H # include @@ -112,8 +113,7 @@ /* check for wildcards */ #ifdef HAVE_GLOB glob_t g; - size_t i; - int r, flags; + int i, r, flags; if(!(!strchr(filename, '*') && !strchr(filename, '?') && !strchr(filename, '[') && !strchr(filename, '{') && !strchr(filename, '~'))) { flags = 0 @@ -120,9 +120,8 @@ #ifdef GLOB_ERR | GLOB_ERR #endif -#ifdef GLOB_NOSORT - | GLOB_NOSORT -#endif + /* do not set GLOB_NOSORT so the results are sorted + and in a predictable order. */ #ifdef GLOB_BRACE | GLOB_BRACE #endif @@ -145,7 +144,7 @@ return; } /* process files found, if any */ - for(i=0; i<(size_t)g.gl_pathc; i++) { + for(i=(int)g.gl_pathc-1; i>=0; i--) { config_start_include(g.gl_pathv[i]); } globfree(&g); @@ -211,6 +210,7 @@ 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) } @@ -227,10 +227,28 @@ 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) } +edns-tcp-keepalive{COLON} { YDVAR(1, VAR_EDNS_TCP_KEEPALIVE) } +edns-tcp-keepalive-timeout{COLON} { YDVAR(1, VAR_EDNS_TCP_KEEPALIVE_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) } +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) } +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) } @@ -247,6 +265,7 @@ 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) } @@ -280,6 +299,7 @@ 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) } @@ -286,15 +306,48 @@ 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) } 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) } +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) } +zonefile{COLON} { YDVAR(1, VAR_ZONEFILE) } +master{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) } +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) } identity{COLON} { YDVAR(1, VAR_IDENTITY) } version{COLON} { YDVAR(1, VAR_VERSION) } module-config{COLON} { YDVAR(1, VAR_MODULE_CONF) } @@ -304,6 +357,8 @@ 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) } @@ -310,7 +365,15 @@ 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) } +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) } +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) } @@ -322,8 +385,13 @@ 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) } @@ -332,6 +400,8 @@ statistics-interval{COLON} { YDVAR(1, VAR_STATISTICS_INTERVAL) } statistics-cumulative{COLON} { YDVAR(1, VAR_STATISTICS_CUMULATIVE) } extended-statistics{COLON} { YDVAR(1, VAR_EXTENDED_STATISTICS) } +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) } @@ -346,14 +416,17 @@ 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) } 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) } local-zone-override{COLON} { YDVAR(3, VAR_LOCAL_ZONE_OVERRIDE) } dnstap{COLON} { YDVAR(0, VAR_DNSTAP) } dnstap-enable{COLON} { YDVAR(1, VAR_DNSTAP_ENABLE) } @@ -375,12 +448,54 @@ 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) } 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) } +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) } +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-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) } +redis-server-host{COLON} { YDVAR(1, VAR_CACHEDB_REDISHOST) } +redis-server-port{COLON} { YDVAR(1, VAR_CACHEDB_REDISPORT) } +redis-timeout{COLON} { YDVAR(1, VAR_CACHEDB_REDISTIMEOUT) } +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) } {NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++; } /* Quoted strings. Strip leading and ending quotes */ --- contrib/unbound/util/configparser.y.orig +++ contrib/unbound/util/configparser.y @@ -51,6 +51,8 @@ int ub_c_lex(void); void ub_c_error(const char *message); +static void validate_respip_action(const char* action); + /* these need to be global, otherwise they cannot be used inside yacc */ extern struct config_parser_state* cfg_parser; @@ -70,7 +72,8 @@ %token VAR_SERVER VAR_VERBOSITY VAR_NUM_THREADS VAR_PORT %token VAR_OUTGOING_RANGE VAR_INTERFACE %token VAR_DO_IP4 VAR_DO_IP6 VAR_PREFER_IP6 VAR_DO_UDP VAR_DO_TCP -%token VAR_TCP_MSS VAR_OUTGOING_TCP_MSS +%token VAR_TCP_MSS VAR_OUTGOING_TCP_MSS VAR_TCP_IDLE_TIMEOUT +%token VAR_EDNS_TCP_KEEPALIVE VAR_EDNS_TCP_KEEPALIVE_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 @@ -104,13 +107,15 @@ %token VAR_AUTO_TRUST_ANCHOR_FILE VAR_KEEP_MISSING VAR_ADD_HOLDDOWN %token VAR_DEL_HOLDDOWN VAR_SO_RCVBUF VAR_EDNS_BUFFER_SIZE VAR_PREFETCH %token VAR_PREFETCH_KEY VAR_SO_SNDBUF VAR_SO_REUSEPORT VAR_HARDEN_BELOW_NXDOMAIN -%token VAR_IGNORE_CD_FLAG VAR_LOG_QUERIES VAR_TCP_UPSTREAM VAR_SSL_UPSTREAM +%token VAR_IGNORE_CD_FLAG VAR_LOG_QUERIES VAR_LOG_REPLIES VAR_LOG_LOCAL_ACTIONS +%token VAR_TCP_UPSTREAM VAR_SSL_UPSTREAM %token VAR_SSL_SERVICE_KEY 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_FIRST VAR_MINIMAL_RESPONSES VAR_RRSET_ROUNDROBIN %token VAR_MAX_UDP_SIZE VAR_DELAY_CLOSE %token VAR_UNBLOCK_LAN_ZONES VAR_INSECURE_LAN_ZONES %token VAR_INFRA_CACHE_MIN_RTT -%token VAR_DNS64_PREFIX VAR_DNS64_SYNTHALL +%token VAR_DNS64_PREFIX VAR_DNS64_SYNTHALL VAR_DNS64_IGNORE_AAAA %token VAR_DNSTAP VAR_DNSTAP_ENABLE VAR_DNSTAP_SOCKET_PATH %token VAR_DNSTAP_SEND_IDENTITY VAR_DNSTAP_SEND_VERSION %token VAR_DNSTAP_IDENTITY VAR_DNSTAP_VERSION @@ -120,20 +125,60 @@ %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_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_RATELIMIT_FOR_DOMAIN VAR_RATELIMIT_BELOW_DOMAIN VAR_RATELIMIT_FACTOR +%token VAR_RATELIMIT_FOR_DOMAIN VAR_RATELIMIT_BELOW_DOMAIN +%token VAR_IP_RATELIMIT_FACTOR VAR_RATELIMIT_FACTOR +%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_IP_FREEBIND VAR_DEFINE_TAG VAR_LOCAL_ZONE_TAG -%token VAR_ACCESS_CONTROL_TAG VAR_LOCAL_ZONE_OVERRIDE -%token VAR_ACCESS_CONTROL_TAG_ACTION VAR_ACCESS_CONTROL_TAG_DATA +%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_FAKE_DSA +%token VAR_FAKE_SHA1 VAR_LOG_IDENTITY VAR_HIDE_TRUSTANCHOR +%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_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_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_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 +%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 %% toplevelvars: /* empty */ | toplevelvars toplevelvar ; toplevelvar: serverstart contents_server | stubstart contents_stub | forwardstart contents_forward | pythonstart contents_py | - rcstart contents_rc | dtstart contents_dt + rcstart contents_rc | dtstart contents_dt | viewstart contents_view | + dnscstart contents_dnsc | cachedbstart contents_cachedb | + ipsetstart contents_ipset | authstart contents_auth | + rpzstart contents_rpz ; /* server: declaration */ @@ -148,7 +193,8 @@ server_outgoing_range | server_do_ip4 | server_do_ip6 | server_prefer_ip6 | server_do_udp | server_do_tcp | - server_tcp_mss | server_outgoing_tcp_mss | + server_tcp_mss | server_outgoing_tcp_mss | server_tcp_idle_timeout | + server_tcp_keepalive | server_tcp_keepalive_timeout | server_interface | server_chroot | server_username | server_directory | server_logfile | server_pidfile | server_msg_cache_size | server_msg_cache_slabs | @@ -184,22 +230,49 @@ 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_tcp_upstream | server_ssl_upstream | + 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_minimal_responses | server_rrset_roundrobin | server_max_udp_size | server_so_reuseport | server_delay_close | server_unblock_lan_zones | server_insecure_lan_zones | - server_dns64_prefix | server_dns64_synthall | + server_dns64_prefix | server_dns64_synthall | server_dns64_ignore_aaaa | server_infra_cache_min_rtt | server_harden_algo_downgrade | - server_ip_transparent | server_ratelimit | server_ratelimit_slabs | - server_ratelimit_size | server_ratelimit_for_domain | + server_ip_transparent | server_ip_ratelimit | server_ratelimit | + 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_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_tag_data | server_access_control_view | + server_qname_minimisation_strict | server_serve_expired | + server_serve_expired_ttl | server_serve_expired_ttl_reset | + server_serve_expired_reply_ttl | server_serve_expired_client_timeout | + 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 ; stubstart: VAR_STUB_ZONE { @@ -215,7 +288,8 @@ ; contents_stub: contents_stub content_stub | ; -content_stub: stub_name | stub_host | stub_addr | stub_prime | stub_first +content_stub: stub_name | stub_host | stub_addr | stub_prime | stub_first | + stub_no_cache | stub_ssl_upstream ; forwardstart: VAR_FORWARD_ZONE { @@ -231,8 +305,137 @@ ; contents_forward: contents_forward content_forward | ; -content_forward: forward_name | forward_host | forward_addr | forward_first +content_forward: forward_name | forward_host | forward_addr | forward_first | + forward_no_cache | forward_ssl_upstream ; +viewstart: VAR_VIEW + { + struct config_view* s; + OUTYY(("\nP(view:)\n")); + 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")); + 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->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 + ; + +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; + } + ; + +rpzstart: VAR_RPZ + { + struct config_auth* s; + OUTYY(("\nP(rpz:)\n")); + s = (struct config_auth*)calloc(1, sizeof(struct config_auth)); + if(s) { + s->next = cfg_parser->cfg->auths; + cfg_parser->cfg->auths = s; + /* defaults for 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 + ; server_num_threads: VAR_NUM_THREADS STRING_ARG { OUTYY(("P(server_num_threads:%s)\n", $2)); @@ -280,6 +483,26 @@ 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)); @@ -289,6 +512,156 @@ 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)); @@ -434,6 +807,41 @@ 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_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_tcp_upstream: VAR_TCP_UPSTREAM STRING_ARG { OUTYY(("P(server_tcp_upstream:%s)\n", $2)); @@ -443,6 +851,15 @@ 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)); @@ -475,6 +892,61 @@ 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_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)); @@ -516,6 +988,42 @@ 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)); @@ -548,9 +1056,11 @@ strncmp(d, cfg_parser->chroot, strlen( cfg_parser->chroot)) == 0) d += strlen(cfg_parser->chroot); - if(chdir(d)) + if(d[0]) { + if(chdir(d)) log_err("cannot chdir to directory: %s (%s)", d, strerror(errno)); + } } } ; @@ -621,6 +1131,28 @@ 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)); @@ -646,6 +1178,15 @@ 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_identity: VAR_IDENTITY STRING_ARG { OUTYY(("P(server_identity:%s)\n", $2)); @@ -706,6 +1247,14 @@ 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)); @@ -1001,6 +1550,15 @@ 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)); @@ -1033,11 +1591,14 @@ if(strcmp($3, "deny")!=0 && strcmp($3, "refuse")!=0 && strcmp($3, "deny_non_local")!=0 && strcmp($3, "refuse_non_local")!=0 && + strcmp($3, "allow_setrd")!=0 && strcmp($3, "allow")!=0 && strcmp($3, "allow_snoop")!=0) { yyerror("expected deny, refuse, deny_non_local, " - "refuse_non_local, allow or allow_snoop " - "in access control action"); + "refuse_non_local, allow, allow_setrd or " + "allow_snoop in access control action"); + free($2); + free($3); } else { if(!cfg_str2list_insert(&cfg_parser->cfg->acls, $2, $3)) fatal_exit("out of memory adding acl"); @@ -1151,6 +1712,17 @@ 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)); @@ -1160,6 +1732,77 @@ 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_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)); @@ -1251,17 +1894,30 @@ && strcmp($3, "always_transparent")!=0 && strcmp($3, "always_refuse")!=0 && strcmp($3, "always_nxdomain")!=0 - && strcmp($3, "inform")!=0 && strcmp($3, "inform_deny")!=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, " - "always_transparent, always_refuse, " - "always_nxdomain or nodefault"); - else if(strcmp($3, "nodefault")==0) { + "inform_redirect, always_transparent, " + "always_refuse, always_nxdomain, 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) { + 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)) @@ -1311,6 +1967,13 @@ 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)); @@ -1334,6 +1997,14 @@ 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_define_tag: VAR_DEFINE_TAG STRING_ARG { char* p, *s = $2; @@ -1355,8 +2026,10 @@ &len); free($3); OUTYY(("P(server_local_zone_tag:%s)\n", $2)); - if(!bitlist) + 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, @@ -1374,8 +2047,10 @@ &len); free($3); OUTYY(("P(server_access_control_tag:%s)\n", $2)); - if(!bitlist) + if(!bitlist) { yyerror("could not parse tags, (define-tag them first)"); + free($2); + } if(bitlist) { if(!cfg_strbytelist_insert( &cfg_parser->cfg->acl_tags, @@ -1422,6 +2097,46 @@ } } ; +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_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_ratelimit: VAR_RATELIMIT STRING_ARG { OUTYY(("P(server_ratelimit:%s)\n", $2)); @@ -1431,6 +2146,14 @@ 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)); @@ -1439,6 +2162,19 @@ 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)); @@ -1457,6 +2193,8 @@ 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)) @@ -1470,6 +2208,8 @@ 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)) @@ -1478,6 +2218,15 @@ } } ; +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)); @@ -1487,6 +2236,30 @@ 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)); @@ -1497,6 +2270,94 @@ 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_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 + } + ; stub_name: VAR_NAME STRING_ARG { OUTYY(("P(name:%s)\n", $2)); @@ -1530,6 +2391,25 @@ 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_prime: VAR_STUB_PRIME STRING_ARG { OUTYY(("P(stub-prime:%s)\n", $2)); @@ -1573,6 +2453,193 @@ 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); + } + ; +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_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, "noview")!=0 + && strcmp($3, "inform")!=0 && strcmp($3, "inform_deny")!=0) { + yyerror("local-zone type: expected static, deny, " + "refuse, redirect, transparent, " + "typetransparent, inform, inform_deny, " + "always_transparent, always_refuse, " + "always_nxdomain, noview or nodefault"); + 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) { + 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")); @@ -1606,7 +2673,7 @@ rc_control_interface: VAR_CONTROL_INTERFACE STRING_ARG { OUTYY(("P(control_interface:%s)\n", $2)); - if(!cfg_strlist_insert(&cfg_parser->cfg->control_ifs, $2)) + if(!cfg_strlist_append(&cfg_parser->cfg->control_ifs, $2)) yyerror("out of memory"); } ; @@ -1613,10 +2680,7 @@ rc_control_use_cert: VAR_CONTROL_USE_CERT STRING_ARG { OUTYY(("P(control_use_cert:%s)\n", $2)); - if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) - yyerror("expected yes or no."); - else cfg_parser->cfg->remote_control_use_cert = - (strcmp($2, "yes")==0); + cfg_parser->cfg->control_use_cert = (strcmp($2, "yes")==0); free($2); } ; @@ -1671,6 +2735,7 @@ 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_socket_path: VAR_DNSTAP_SOCKET_PATH STRING_ARG @@ -1686,6 +2751,7 @@ 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 @@ -1694,6 +2760,7 @@ 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 @@ -1717,6 +2784,7 @@ 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 @@ -1726,6 +2794,7 @@ 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 @@ -1735,6 +2804,7 @@ 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 @@ -1744,6 +2814,7 @@ 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 @@ -1753,6 +2824,7 @@ 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 @@ -1762,6 +2834,7 @@ yyerror("expected yes or no."); else cfg_parser->cfg->dnstap_log_forwarder_response_messages = (strcmp($2, "yes")==0); + free($2); } ; pythonstart: VAR_PYTHON @@ -1776,8 +2849,8 @@ py_script: VAR_PYTHON_SCRIPT STRING_ARG { OUTYY(("P(python-script:%s)\n", $2)); - free(cfg_parser->cfg->python_script); - cfg_parser->cfg->python_script = $2; + if(!cfg_strlist_append_ex(&cfg_parser->cfg->python_script, $2)) + yyerror("out of memory"); } server_disable_dnssec_lame_check: VAR_DISABLE_DNSSEC_LAME_CHECK STRING_ARG { @@ -1788,6 +2861,282 @@ (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")); + } + ; +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")); + } + ; +contents_cachedb: contents_cachedb content_cachedb + | ; +content_cachedb: cachedb_backend_name | cachedb_secret_seed | + redis_server_host | redis_server_port | redis_timeout + ; +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 + } + ; +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_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); + } + ; +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"); + } + } + ; + ipsetstart: VAR_IPSET + { + OUTYY(("\nP(ipset:)\n")); + } + ; + 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"); + } +} + + --- contrib/unbound/util/data/dname.c.orig +++ contrib/unbound/util/data/dname.c @@ -75,6 +75,8 @@ { size_t len = 0; size_t labellen; + if(maxlen == 0) + return 0; /* too short, shortest is '0' root label */ labellen = *dname++; while(labellen) { if(labellen&0xc0) @@ -231,6 +233,7 @@ dname_pkt_compare(sldns_buffer* pkt, uint8_t* d1, uint8_t* d2) { uint8_t len1, len2; + int count1 = 0, count2 = 0; log_assert(pkt && d1 && d2); len1 = *d1++; len2 = *d2++; @@ -237,11 +240,21 @@ while( len1 != 0 || len2 != 0 ) { /* resolve ptrs */ if(LABEL_IS_PTR(len1)) { + if((size_t)PTR_OFFSET(len1, *d1) + >= sldns_buffer_limit(pkt)) + return -1; + if(count1++ > MAX_COMPRESS_PTRS) + return -1; d1 = sldns_buffer_at(pkt, PTR_OFFSET(len1, *d1)); len1 = *d1++; continue; } if(LABEL_IS_PTR(len2)) { + if((size_t)PTR_OFFSET(len2, *d2) + >= sldns_buffer_limit(pkt)) + return 1; + if(count2++ > MAX_COMPRESS_PTRS) + return 1; d2 = sldns_buffer_at(pkt, PTR_OFFSET(len2, *d2)); len2 = *d2++; continue; @@ -270,8 +283,8 @@ return 0; } -hashvalue_t -dname_query_hash(uint8_t* dname, hashvalue_t h) +hashvalue_type +dname_query_hash(uint8_t* dname, hashvalue_type h) { uint8_t labuf[LDNS_MAX_LABELLEN+1]; uint8_t lablen; @@ -294,12 +307,13 @@ return h; } -hashvalue_t -dname_pkt_hash(sldns_buffer* pkt, uint8_t* dname, hashvalue_t h) +hashvalue_type +dname_pkt_hash(sldns_buffer* pkt, uint8_t* dname, hashvalue_type h) { uint8_t labuf[LDNS_MAX_LABELLEN+1]; uint8_t lablen; int i; + int count = 0; /* preserve case of query, make hash label by label */ lablen = *dname++; @@ -306,6 +320,11 @@ while(lablen) { if(LABEL_IS_PTR(lablen)) { /* follow pointer */ + if((size_t)PTR_OFFSET(lablen, *dname) + >= sldns_buffer_limit(pkt)) + return h; + if(count++ > MAX_COMPRESS_PTRS) + return h; dname = sldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname)); lablen = *dname++; continue; @@ -327,16 +346,29 @@ void dname_pkt_copy(sldns_buffer* pkt, uint8_t* to, uint8_t* dname) { /* copy over the dname and decompress it at the same time */ + size_t comprcount = 0; size_t len = 0; uint8_t lablen; lablen = *dname++; while(lablen) { if(LABEL_IS_PTR(lablen)) { + if(comprcount++ > MAX_COMPRESS_PTRS) { + /* too many compression pointers */ + *to = 0; /* end the result prematurely */ + return; + } /* follow pointer */ + if((size_t)PTR_OFFSET(lablen, *dname) + >= sldns_buffer_limit(pkt)) + return; dname = sldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname)); lablen = *dname++; continue; } + if(lablen > LDNS_MAX_LABELLEN) { + *to = 0; /* end the result prematurely */ + return; + } log_assert(lablen <= LDNS_MAX_LABELLEN); len += (size_t)lablen+1; if(len >= LDNS_MAX_DOMAINLEN) { @@ -357,6 +389,7 @@ void dname_print(FILE* out, struct sldns_buffer* pkt, uint8_t* dname) { uint8_t lablen; + int count = 0; if(!out) out = stdout; if(!dname) return; @@ -370,6 +403,15 @@ fputs("??compressionptr??", out); return; } + if((size_t)PTR_OFFSET(lablen, *dname) + >= sldns_buffer_limit(pkt)) { + fputs("??compressionptr??", out); + return; + } + if(count++ > MAX_COMPRESS_PTRS) { + fputs("??compressionptr??", out); + return; + } dname = sldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname)); lablen = *dname++; continue; @@ -523,6 +565,57 @@ return lastdiff; } +int +dname_lab_startswith(uint8_t* label, char* prefix, char** endptr) +{ + size_t plen = strlen(prefix); + size_t orig_plen = plen; + size_t lablen = (size_t)*label; + if(plen > lablen) + return 0; + label++; + while(plen--) { + if(*prefix != tolower((unsigned char)*label)) { + return 0; + } + prefix++; label++; + } + if(orig_plen < lablen) + *endptr = (char *)label; + else + /* prefix length == label length */ + *endptr = NULL; + return 1; +} + +int +dname_has_label(uint8_t* dname, size_t dnamelen, uint8_t* label) +{ + size_t len; + + /* 1 byte needed for the label length */ + if(dnamelen < 1) + return 0; + + len = *dname; + while(len <= dnamelen) { + if(!(*dname)) { + if(*dname == *label) + return 1; /* empty label match */ + /* termination label found, stop iterating */ + return 0; + } + if(*dname == *label && *label && + memlowercmp(dname+1, label+1, *dname) == 0) + return 1; + len += *dname; + dname += *dname; + dname++; + len++; + } + return 0; +} + int dname_buffer_write(sldns_buffer* pkt, uint8_t* dname) { --- contrib/unbound/util/data/dname.h.orig +++ contrib/unbound/util/data/dname.h @@ -127,7 +127,7 @@ * @param h: initial hash value. * @return: result hash value. */ -hashvalue_t dname_query_hash(uint8_t* dname, hashvalue_t h); +hashvalue_type dname_query_hash(uint8_t* dname, hashvalue_type h); /** * Hash dname, label by label, lowercasing, into hashvalue. @@ -139,7 +139,8 @@ * @return: result hash value. * Result is the same as dname_query_hash, even if compression is used. */ -hashvalue_t dname_pkt_hash(struct sldns_buffer* pkt, uint8_t* dname, hashvalue_t h); +hashvalue_type dname_pkt_hash(struct sldns_buffer* pkt, uint8_t* dname, + hashvalue_type h); /** * Copy over a valid dname and decompress it. @@ -185,6 +186,26 @@ int dname_lab_cmp(uint8_t* d1, int labs1, uint8_t* d2, int labs2, int* mlabs); /** + * Check if labels starts with given prefix + * @param label: dname label + * @param prefix: the string to match label with, null terminated. + * @param endptr: pointer to location in label after prefix, only if return + * value is 1. NULL if nothing in the label after the prefix, i.e. prefix + * and label are the same. + * @return: 1 if label starts with prefix, else 0 + */ +int dname_lab_startswith(uint8_t* label, char* prefix, char** endptr); + +/** + * Check if dname contains label + * @param dname: dname + * @param dnamelen: length of dname + * @param label: label to be checked for presence in dname + * @return: 1 if dname has this label, 0 otherwise + */ +int dname_has_label(uint8_t* dname, size_t dnamelen, uint8_t* label); + +/** * See if domain name d1 is a strict subdomain of d2. * That is a subdomain, but not equal. * @param d1: domain name, uncompressed wireformat --- contrib/unbound/util/data/msgencode.c.orig +++ contrib/unbound/util/data/msgencode.c @@ -48,7 +48,13 @@ #include "util/regional.h" #include "util/net_help.h" #include "sldns/sbuffer.h" +#include "services/localzone.h" +#ifdef HAVE_TIME_H +#include +#endif +#include + /** return code that means the function ran out of memory. negative so it does * not conflict with DNS rcodes. */ #define RETVAL_OUTMEM -2 @@ -458,6 +464,10 @@ owner_labs = dname_count_labels(key->rk.dname); owner_pos = sldns_buffer_position(pkt); + /* For an rrset with a fixed TTL, use the rrset's TTL as given */ + if((key->rk.flags & PACKED_RRSET_FIXEDTTL) != 0) + timenow = 0; + if(do_data) { const sldns_rr_descriptor* c = type_rdata_compressable(key); for(i=0; icount; i++) { @@ -470,7 +480,8 @@ sldns_buffer_write(pkt, &key->rk.type, 2); sldns_buffer_write(pkt, &key->rk.rrset_class, 2); if(data->rr_ttl[j] < timenow) - sldns_buffer_write_u32(pkt, 0); + sldns_buffer_write_u32(pkt, + SERVE_EXPIRED?SERVE_EXPIRED_REPLY_TTL:0); else sldns_buffer_write_u32(pkt, data->rr_ttl[j]-timenow); if(c) { @@ -507,7 +518,8 @@ sldns_buffer_write_u16(pkt, LDNS_RR_TYPE_RRSIG); sldns_buffer_write(pkt, &key->rk.rrset_class, 2); if(data->rr_ttl[i] < timenow) - sldns_buffer_write_u32(pkt, 0); + sldns_buffer_write_u32(pkt, + SERVE_EXPIRED?SERVE_EXPIRED_REPLY_TTL:0); else sldns_buffer_write_u32(pkt, data->rr_ttl[i]-timenow); /* rrsig rdata cannot be compressed, perform 100+ byte @@ -534,7 +546,11 @@ { int r; size_t i, setstart; - *num_rrs = 0; + /* 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 */ @@ -581,17 +597,20 @@ 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(qinfo->qname, - dname_count_labels(qinfo->qname), + if(!compress_tree_store(qname, dname_count_labels(qname), sldns_buffer_position(buffer), region, NULL, tree)) return RETVAL_OUTMEM; - if(sldns_buffer_current(buffer) == qinfo->qname) - sldns_buffer_skip(buffer, (ssize_t)qinfo->qname_len); - else sldns_buffer_write(buffer, qinfo->qname, qinfo->qname_len); + 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; @@ -622,15 +641,37 @@ return 0; } -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) +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; ian_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; + size_t rr_offset; sldns_buffer_clear(buffer); if(udpsize < sldns_buffer_limit(buffer)) @@ -646,7 +687,7 @@ /* insert query section */ if(rep->qdcount) { - if((r=insert_query(qinfo, &tree, buffer, region)) != + if((r=insert_query(qinfo, &tree, buffer, region)) != RETVAL_OK) { if(r == RETVAL_TRUNC) { /* create truncated message */ @@ -660,11 +701,38 @@ } /* roundrobin offset. using query id for random number. With ntohs * for different roundrobins for sequential id client senders. */ - rr_offset = RRSET_ROUNDROBIN?ntohs(id):0; + 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, + 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 */ @@ -678,9 +746,9 @@ sldns_buffer_write_u16_at(buffer, 6, ancount); /* if response is positive answer, auth/add sections are not required */ - if( ! (MINIMAL_RESPONSES && positive_answer(rep, qinfo->qtype)) ) { + if( ! (minimise && positive_answer(rep, qinfo->qtype)) ) { /* insert auth section */ - if((r=insert_section(rep, rep->ns_numrrsets, &nscount, buffer, + 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) { @@ -695,20 +763,22 @@ } sldns_buffer_write_u16_at(buffer, 8, nscount); - /* 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; + 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; } - return 0; + sldns_buffer_write_u16_at(buffer, 10, arcount); } - sldns_buffer_write_u16_at(buffer, 10, arcount); } sldns_buffer_flip(buffer); return 1; @@ -719,7 +789,7 @@ { size_t rdatalen = 0; struct edns_option* opt; - if(!edns || !edns->edns_present) + if(!edns || !edns->edns_present) return 0; for(opt = edns->opt_list; opt; opt = opt->next) { rdatalen += 4 + opt->opt_len; @@ -771,7 +841,7 @@ struct edns_data* edns, int dnssec, int secure) { uint16_t flags; - int attach_edns = 1; + unsigned int attach_edns = 0; if(!cached || rep->authoritative) { /* original flags, copy RD and CD bits from query. */ @@ -782,23 +852,36 @@ } 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; + if(sldns_buffer_capacity(pkt) < udpsize) + udpsize = sldns_buffer_capacity(pkt); if(udpsize < LDNS_HEADER_SIZE + calc_edns_field_size(edns)) { /* packet too small to contain edns, omit it. */ attach_edns = 0; } else { /* reserve space for edns record */ - udpsize -= calc_edns_field_size(edns); + attach_edns = (unsigned int)calc_edns_field_size(edns); + udpsize -= attach_edns; } if(!reply_info_encode(qinf, rep, id, flags, pkt, timenow, region, - udpsize, dnssec)) { + udpsize, dnssec, MINIMAL_RESPONSES)) { log_err("reply encode: out of memory"); return 0; } - if(attach_edns) + if(attach_edns && sldns_buffer_capacity(pkt) >= + sldns_buffer_limit(pkt)+attach_edns) attach_edns_record(pkt, edns); return 1; } @@ -807,6 +890,10 @@ 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 */ @@ -813,7 +900,7 @@ 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, qinfo->qname, qinfo->qname_len); + 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); @@ -838,9 +925,14 @@ sldns_buffer_write(buf, &flags, sizeof(uint16_t)); sldns_buffer_write(buf, &flags, sizeof(uint16_t)); if(qinfo) { - if(sldns_buffer_current(buf) == qinfo->qname) - sldns_buffer_skip(buf, (ssize_t)qinfo->qname_len); - else sldns_buffer_write(buf, qinfo->qname, qinfo->qname_len); + 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); } --- contrib/unbound/util/data/msgencode.h.orig +++ contrib/unbound/util/data/msgencode.h @@ -85,12 +85,14 @@ * @param region: to store temporary data in. * @param udpsize: size of the answer, 512, from EDNS, or 64k for TCP. * @param dnssec: if 0 DNSSEC records are omitted from the answer. + * @param minimise: if true, the answer is a minimal response, with + * authority and additional removed if possible. * @return: nonzero is success, or * 0 on error: malloc failure (no log_err has been done). */ int reply_info_encode(struct query_info* qinfo, struct reply_info* rep, uint16_t id, uint16_t flags, struct sldns_buffer* buffer, time_t timenow, - struct regional* region, uint16_t udpsize, int dnssec); + struct regional* region, uint16_t udpsize, int dnssec, int minimise); /** * Encode query packet. Assumes the buffer is large enough. --- contrib/unbound/util/data/msgparse.c.orig +++ contrib/unbound/util/data/msgparse.c @@ -55,7 +55,11 @@ { if(LABEL_IS_PTR(*dnow)) { /* ptr points to a previous dname */ - uint8_t* p = sldns_buffer_at(pkt, PTR_OFFSET(dnow[0], dnow[1])); + 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. */ @@ -71,7 +75,7 @@ */ static struct rrset_parse* new_rrset(struct msg_parse* msg, uint8_t* dname, size_t dnamelen, - uint16_t type, uint16_t dclass, hashvalue_t hash, + uint16_t type, uint16_t dclass, hashvalue_type hash, uint32_t rrset_flags, sldns_pkt_section section, struct regional* region) { @@ -159,13 +163,13 @@ return f; } -hashvalue_t +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_t h = 0xab; + 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 */ @@ -174,25 +178,25 @@ } /** create partial dname hash for rrset hash */ -static hashvalue_t +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_t h = 0xab; + hashvalue_type h = 0xab; h = dname_pkt_hash(pkt, dname, h); return h; } /** create a rrset hash from a partial dname hash */ -static hashvalue_t -pkt_hash_rrset_rest(hashvalue_t dname_h, uint16_t type, uint16_t dclass, +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_t h; + 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); @@ -201,7 +205,7 @@ /** compare rrset_parse with data */ static int -rrset_parse_equals(struct rrset_parse* p, sldns_buffer* pkt, hashvalue_t h, +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) { @@ -215,8 +219,8 @@ struct rrset_parse* msgparse_hashtable_lookup(struct msg_parse* msg, sldns_buffer* pkt, - hashvalue_t h, uint32_t rrset_flags, uint8_t* dname, size_t dnamelen, - uint16_t type, uint16_t dclass) + 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) { @@ -388,7 +392,7 @@ int hasother, sldns_pkt_section section, struct regional* region) { struct rrset_parse* dataset = sigset; - hashvalue_t hash = pkt_hash_rrset(pkt, sigset->dname, datatype, + 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 ); @@ -455,7 +459,7 @@ */ static int find_rrset(struct msg_parse* msg, sldns_buffer* pkt, uint8_t* dname, - size_t dnamelen, uint16_t type, uint16_t dclass, hashvalue_t* hash, + 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, @@ -462,7 +466,7 @@ uint16_t* prev_dclass, struct rrset_parse** rrset_prev, sldns_pkt_section section, struct regional* region) { - hashvalue_t dname_h = pkt_hash_rrset_first(pkt, dname); + hashvalue_type dname_h = pkt_hash_rrset_first(pkt, dname); uint16_t covtype; if(*rrset_prev) { /* check if equal to previous item */ @@ -824,7 +828,7 @@ uint16_t type, prev_type = 0; uint16_t dclass, prev_dclass = 0; uint32_t rrset_flags = 0; - hashvalue_t hash = 0; + hashvalue_type hash = 0; struct rrset_parse* rrset = NULL; int r; @@ -1018,7 +1022,7 @@ edns->opt_list = NULL; /* take the options */ - rdata_len = found->rr_first->size; + rdata_len = found->rr_first->size-2; rdata_ptr = found->rr_first->ttl_data+6; if(!parse_edns_options(rdata_ptr, rdata_len, edns, region)) return 0; @@ -1028,6 +1032,32 @@ return 0; } +/** 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 */ +static int +skip_pkt_rrs(sldns_buffer* pkt, int num) +{ + int i; + for(i=0; i 1) { return LDNS_RCODE_FORMERR; } if(LDNS_ARCOUNT(sldns_buffer_begin(pkt)) == 0) { - memset(edns, 0, sizeof(*edns)); edns->udp_size = 512; return 0; } @@ -1072,3 +1106,22 @@ return 0; } + +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; + } + } +} --- contrib/unbound/util/data/msgparse.h.orig +++ contrib/unbound/util/data/msgparse.h @@ -79,6 +79,12 @@ 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 */ @@ -138,7 +144,7 @@ /** next in list of all rrsets */ struct rrset_parse* rrset_all_next; /** hash value of rrset */ - hashvalue_t hash; + hashvalue_type hash; /** which section was it found in: one of * LDNS_SECTION_ANSWER, LDNS_SECTION_AUTHORITY, LDNS_SECTION_ADDITIONAL */ @@ -296,8 +302,8 @@ * @param rrset_flags: rrset flags (same as packed_rrset flags). * @return hash value */ -hashvalue_t pkt_hash_rrset(struct sldns_buffer* pkt, uint8_t* dname, uint16_t type, - uint16_t dclass, uint32_t rrset_flags); +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. @@ -312,7 +318,7 @@ * @return NULL or the rrset_parse if found. */ struct rrset_parse* msgparse_hashtable_lookup(struct msg_parse* msg, - struct sldns_buffer* pkt, hashvalue_t h, uint32_t rrset_flags, + struct sldns_buffer* pkt, hashvalue_type h, uint32_t rrset_flags, uint8_t* dname, size_t dnamelen, uint16_t type, uint16_t dclass); /** @@ -322,4 +328,13 @@ */ 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); + #endif /* UTIL_DATA_MSGPARSE_H */ --- contrib/unbound/util/data/msgreply.c.orig +++ contrib/unbound/util/data/msgreply.c @@ -52,6 +52,8 @@ #include "util/data/msgencode.h" #include "sldns/sbuffer.h" #include "sldns/wire2str.h" +#include "util/module.h" +#include "util/fptr_wlist.h" /** MAX TTL default for messages and rrsets */ time_t MAX_TTL = 3600 * 24 * 10; /* ten days */ @@ -59,6 +61,12 @@ time_t MIN_TTL = 0; /** MAX Negative TTL, for SOA records in authority section */ time_t MAX_NEG_TTL = 3600; /* one hour */ +/** If we serve expired entries and prefetch them */ +int SERVE_EXPIRED = 0; +/** Time to serve records after expiration */ +time_t SERVE_EXPIRED_TTL = 0; +/** TTL to use for expired records */ +time_t SERVE_EXPIRED_REPLY_TTL = 30; /** allocate qinfo, return 0 on error */ static int @@ -76,6 +84,7 @@ qinf->qname_len = msg->qname_len; qinf->qtype = msg->qtype; qinf->qclass = msg->qclass; + qinf->local_alias = NULL; return 1; } @@ -82,8 +91,8 @@ /** constructor for replyinfo */ struct reply_info* construct_reply_info_base(struct regional* region, uint16_t flags, size_t qd, - time_t ttl, time_t prettl, size_t an, size_t ns, size_t ar, - size_t total, enum sec_status sec) + time_t ttl, time_t prettl, time_t expttl, size_t an, size_t ns, + size_t ar, size_t total, enum sec_status sec) { struct reply_info* rep; /* rrset_count-1 because the first ref is part of the struct. */ @@ -100,6 +109,7 @@ rep->qdcount = qd; rep->ttl = ttl; rep->prefetch_ttl = prettl; + rep->serve_expired_ttl = expttl; rep->an_numrrsets = an; rep->ns_numrrsets = ns; rep->ar_numrrsets = ar; @@ -123,7 +133,7 @@ struct regional* region) { *rep = construct_reply_info_base(region, msg->flags, msg->qdcount, 0, - 0, msg->an_rrsets, msg->ns_rrsets, msg->ar_rrsets, + 0, 0, msg->an_rrsets, msg->ns_rrsets, msg->ar_rrsets, msg->rrset_count, sec_status_unchecked); if(!*rep) return 0; @@ -130,9 +140,8 @@ return 1; } -/** allocate (special) rrset keys, return 0 on error */ -static int -repinfo_alloc_rrset_keys(struct reply_info* rep, struct alloc_cache* alloc, +int +reply_info_alloc_rrset_keys(struct reply_info* rep, struct alloc_cache* alloc, struct regional* region) { size_t i; @@ -190,6 +199,8 @@ } if(*rr_ttl < MIN_TTL) *rr_ttl = MIN_TTL; + if(*rr_ttl > MAX_TTL) + *rr_ttl = MAX_TTL; if(*rr_ttl < data->ttl) data->ttl = *rr_ttl; @@ -236,10 +247,10 @@ break; } if(len) { + log_assert(len <= pkt_len); memmove(to, sldns_buffer_current(pkt), len); to += len; sldns_buffer_skip(pkt, (ssize_t)len); - log_assert(len <= pkt_len); pkt_len -= len; } rdf++; @@ -422,6 +433,7 @@ pset = pset->rrset_all_next; } rep->prefetch_ttl = PREFETCH_TTL_CALC(rep->ttl); + rep->serve_expired_ttl = rep->ttl + SERVE_EXPIRED_TTL; return 1; } @@ -435,10 +447,14 @@ return 0; if(!parse_create_repinfo(msg, rep, region)) return 0; - if(!repinfo_alloc_rrset_keys(*rep, alloc, region)) + if(!reply_info_alloc_rrset_keys(*rep, alloc, region)) { + if(!region) reply_info_parsedelete(*rep, alloc); return 0; - if(!parse_copy_decompress(pkt, msg, *rep, region)) + } + if(!parse_copy_decompress(pkt, msg, *rep, region)) { + if(!region) reply_info_parsedelete(*rep, alloc); return 0; + } return 1; } @@ -451,6 +467,7 @@ int ret; qinf->qname = NULL; + qinf->local_alias = NULL; *rep = NULL; if(!(msg = regional_alloc(region, sizeof(*msg)))) { return LDNS_RCODE_SERVFAIL; @@ -499,6 +516,7 @@ size_t i, j; rep->ttl += timenow; rep->prefetch_ttl += timenow; + rep->serve_expired_ttl += timenow; for(i=0; irrset_count; i++) { struct packed_rrset_data* data = (struct packed_rrset_data*) rep->ref[i].key->entry.data; @@ -531,8 +549,9 @@ /* minimum size: header + \0 + qtype + qclass */ if(sldns_buffer_limit(query) < LDNS_HEADER_SIZE + 5) return 0; - if(LDNS_OPCODE_WIRE(q) != LDNS_PACKET_QUERY || - LDNS_QDCOUNT(q) != 1 || sldns_buffer_position(query) != 0) + if((LDNS_OPCODE_WIRE(q) != LDNS_PACKET_QUERY && LDNS_OPCODE_WIRE(q) != + LDNS_PACKET_NOTIFY) || LDNS_QDCOUNT(q) != 1 || + sldns_buffer_position(query) != 0) return 0; sldns_buffer_skip(query, LDNS_HEADER_SIZE); m->qname = sldns_buffer_current(query); @@ -542,6 +561,7 @@ return 0; /* need qtype, qclass */ m->qtype = sldns_buffer_read_u16(query); m->qclass = sldns_buffer_read_u16(query); + m->local_alias = NULL; return 1; } @@ -603,10 +623,10 @@ free(r); } -hashvalue_t +hashvalue_type query_info_hash(struct query_info *q, uint16_t flags) { - hashvalue_t h = 0xab; + hashvalue_type h = 0xab; h = hashlittle(&q->qtype, sizeof(q->qtype), h); if(q->qtype == LDNS_RR_TYPE_AAAA && (flags&BIT_CD)) h++; @@ -617,7 +637,7 @@ struct msgreply_entry* query_info_entrysetup(struct query_info* q, struct reply_info* r, - hashvalue_t h) + hashvalue_type h) { struct msgreply_entry* e = (struct msgreply_entry*)malloc( sizeof(struct msgreply_entry)); @@ -627,9 +647,14 @@ e->entry.key = e; e->entry.data = r; lock_rw_init(&e->entry.lock); - lock_protect(&e->entry.lock, &e->key, sizeof(e->key)); - lock_protect(&e->entry.lock, &e->entry.hash, sizeof(e->entry.hash) + - sizeof(e->entry.key) + sizeof(e->entry.data)); + lock_protect(&e->entry.lock, &e->key.qname, sizeof(e->key.qname)); + lock_protect(&e->entry.lock, &e->key.qname_len, sizeof(e->key.qname_len)); + lock_protect(&e->entry.lock, &e->key.qtype, sizeof(e->key.qtype)); + lock_protect(&e->entry.lock, &e->key.qclass, sizeof(e->key.qclass)); + lock_protect(&e->entry.lock, &e->key.local_alias, sizeof(e->key.local_alias)); + lock_protect(&e->entry.lock, &e->entry.hash, sizeof(e->entry.hash)); + lock_protect(&e->entry.lock, &e->entry.key, sizeof(e->entry.key)); + lock_protect(&e->entry.lock, &e->entry.data, sizeof(e->entry.data)); lock_protect(&e->entry.lock, e->key.qname, e->key.qname_len); q->qname = NULL; return e; @@ -677,13 +702,13 @@ { struct reply_info* cp; cp = construct_reply_info_base(region, rep->flags, rep->qdcount, - rep->ttl, rep->prefetch_ttl, rep->an_numrrsets, - rep->ns_numrrsets, rep->ar_numrrsets, rep->rrset_count, - rep->security); + rep->ttl, rep->prefetch_ttl, rep->serve_expired_ttl, + rep->an_numrrsets, rep->ns_numrrsets, rep->ar_numrrsets, + rep->rrset_count, rep->security); if(!cp) return NULL; /* allocate ub_key structures special or not */ - if(!repinfo_alloc_rrset_keys(cp, alloc, region)) { + if(!reply_info_alloc_rrset_keys(cp, alloc, region)) { if(!region) reply_info_parsedelete(cp, alloc); return NULL; @@ -798,7 +823,7 @@ sldns_buffer* buf = sldns_buffer_new(65535); struct regional* region = regional_create(); if(!reply_info_encode(qinfo, rep, 0, rep->flags, buf, 0, - region, 65535, 1)) { + region, 65535, 1, 0)) { log_info("%s: log_dns_msg: out of memory", str); } else { char* s = sldns_wire2str_pkt(sldns_buffer_begin(buf), @@ -814,7 +839,47 @@ regional_destroy(region); } -void +void +log_reply_info(enum verbosity_value v, struct query_info *qinf, + struct sockaddr_storage *addr, socklen_t addrlen, struct timeval dur, + int cached, struct sldns_buffer *rmsg) +{ + char qname_buf[LDNS_MAX_DOMAINLEN+1]; + char clientip_buf[128]; + char rcode_buf[16]; + char type_buf[16]; + char class_buf[16]; + size_t pktlen; + uint16_t rcode = FLAGS_GET_RCODE(sldns_buffer_read_u16_at(rmsg, 2)); + + if(verbosity < v) + return; + + sldns_wire2str_rcode_buf((int)rcode, rcode_buf, sizeof(rcode_buf)); + addr_to_str(addr, addrlen, clientip_buf, sizeof(clientip_buf)); + if(rcode == LDNS_RCODE_FORMERR) + { + if(LOG_TAG_QUERYREPLY) + log_reply("%s - - - %s - - - ", clientip_buf, rcode_buf); + else log_info("%s - - - %s - - - ", clientip_buf, rcode_buf); + } else { + if(qinf->qname) + dname_str(qinf->qname, qname_buf); + else snprintf(qname_buf, sizeof(qname_buf), "null"); + pktlen = sldns_buffer_limit(rmsg); + sldns_wire2str_type_buf(qinf->qtype, type_buf, sizeof(type_buf)); + sldns_wire2str_class_buf(qinf->qclass, class_buf, sizeof(class_buf)); + if(LOG_TAG_QUERYREPLY) + log_reply("%s %s %s %s %s " ARG_LL "d.%6.6d %d %d", + clientip_buf, qname_buf, type_buf, class_buf, + rcode_buf, (long long)dur.tv_sec, (int)dur.tv_usec, cached, (int)pktlen); + else log_info("%s %s %s %s %s " ARG_LL "d.%6.6d %d %d", + clientip_buf, qname_buf, type_buf, class_buf, + rcode_buf, (long long)dur.tv_sec, (int)dur.tv_usec, cached, (int)pktlen); + } +} + +void log_query_info(enum verbosity_value v, const char* str, struct query_info* qinf) { @@ -858,6 +923,26 @@ return 1; } +struct reply_info* +parse_reply_in_temp_region(sldns_buffer* pkt, struct regional* region, + struct query_info* qi) +{ + struct reply_info* rep; + struct msg_parse* msg; + if(!(msg = regional_alloc(region, sizeof(*msg)))) { + return NULL; + } + memset(msg, 0, sizeof(*msg)); + sldns_buffer_set_position(pkt, 0); + if(parse_packet(pkt, msg, region) != 0){ + return 0; + } + if(!parse_create_msg(pkt, msg, NULL, qi, &rep, region)) { + return 0; + } + return rep; +} + int edns_opt_append(struct edns_data* edns, struct regional* region, uint16_t code, size_t len, uint8_t* data) { @@ -871,9 +956,12 @@ opt->next = NULL; opt->opt_code = code; opt->opt_len = len; - opt->opt_data = regional_alloc_init(region, data, len); - if(!opt->opt_data) - return 0; + opt->opt_data = NULL; + if(len > 0) { + opt->opt_data = regional_alloc_init(region, data, len); + if(!opt->opt_data) + return 0; + } /* append at end of list */ prevp = &edns->opt_list; @@ -883,16 +971,175 @@ return 1; } -int edns_opt_inplace_reply(struct edns_data* edns, struct regional* region) +int edns_opt_list_append(struct edns_option** list, uint16_t code, size_t len, + uint8_t* data, struct regional* region) { - (void)region; - /* remove all edns options from the reply, because only the - * options that we understand should be in the reply - * (sec 6.1.2 RFC 6891) */ - edns->opt_list = NULL; + struct edns_option** prevp; + struct edns_option* opt; + + /* allocate new element */ + opt = (struct edns_option*)regional_alloc(region, sizeof(*opt)); + if(!opt) + return 0; + opt->next = NULL; + opt->opt_code = code; + opt->opt_len = len; + opt->opt_data = NULL; + if(len > 0) { + opt->opt_data = regional_alloc_init(region, data, len); + if(!opt->opt_data) + return 0; + } + + /* append at end of list */ + prevp = list; + while(*prevp != NULL) { + prevp = &((*prevp)->next); + } + *prevp = opt; return 1; } +int edns_opt_list_remove(struct edns_option** list, uint16_t code) +{ + /* The list should already be allocated in a region. Freeing the + * allocated space in a region is not possible. We just unlink the + * required elements and they will be freed together with the region. */ + + struct edns_option* prev; + struct edns_option* curr; + if(!list || !(*list)) return 0; + + /* Unlink and repoint if the element(s) are first in list */ + while(list && *list && (*list)->opt_code == code) { + *list = (*list)->next; + } + + if(!list || !(*list)) return 1; + /* Unlink elements and reattach the list */ + prev = *list; + curr = (*list)->next; + while(curr != NULL) { + if(curr->opt_code == code) { + prev->next = curr->next; + curr = curr->next; + } else { + prev = curr; + curr = curr->next; + } + } + return 1; +} + +static int inplace_cb_reply_call_generic( + struct inplace_cb* callback_list, enum inplace_cb_list_type type, + struct query_info* qinfo, struct module_qstate* qstate, + struct reply_info* rep, int rcode, struct edns_data* edns, + struct comm_reply* repinfo, struct regional* region) +{ + struct inplace_cb* cb; + struct edns_option* opt_list_out = NULL; +#if defined(EXPORT_ALL_SYMBOLS) + (void)type; /* param not used when fptr_ok disabled */ +#endif + if(qstate) + opt_list_out = qstate->edns_opts_front_out; + for(cb=callback_list; cb; cb=cb->next) { + fptr_ok(fptr_whitelist_inplace_cb_reply_generic( + (inplace_cb_reply_func_type*)cb->cb, type)); + (void)(*(inplace_cb_reply_func_type*)cb->cb)(qinfo, qstate, rep, + rcode, edns, &opt_list_out, repinfo, region, cb->id, cb->cb_arg); + } + edns->opt_list = opt_list_out; + return 1; +} + +int inplace_cb_reply_call(struct module_env* env, struct query_info* qinfo, + struct module_qstate* qstate, struct reply_info* rep, int rcode, + struct edns_data* edns, struct comm_reply* repinfo, struct regional* region) +{ + return inplace_cb_reply_call_generic( + env->inplace_cb_lists[inplace_cb_reply], inplace_cb_reply, qinfo, + qstate, rep, rcode, edns, repinfo, region); +} + +int inplace_cb_reply_cache_call(struct module_env* env, + struct query_info* qinfo, struct module_qstate* qstate, + struct reply_info* rep, int rcode, struct edns_data* edns, + struct comm_reply* repinfo, struct regional* region) +{ + return inplace_cb_reply_call_generic( + env->inplace_cb_lists[inplace_cb_reply_cache], inplace_cb_reply_cache, + qinfo, qstate, rep, rcode, edns, repinfo, region); +} + +int inplace_cb_reply_local_call(struct module_env* env, + struct query_info* qinfo, struct module_qstate* qstate, + struct reply_info* rep, int rcode, struct edns_data* edns, + struct comm_reply* repinfo, struct regional* region) +{ + return inplace_cb_reply_call_generic( + env->inplace_cb_lists[inplace_cb_reply_local], inplace_cb_reply_local, + qinfo, qstate, rep, rcode, edns, repinfo, region); +} + +int inplace_cb_reply_servfail_call(struct module_env* env, + struct query_info* qinfo, struct module_qstate* qstate, + struct reply_info* rep, int rcode, struct edns_data* edns, + struct comm_reply* repinfo, struct regional* region) +{ + /* We are going to servfail. Remove any potential edns options. */ + if(qstate) + qstate->edns_opts_front_out = NULL; + return inplace_cb_reply_call_generic( + env->inplace_cb_lists[inplace_cb_reply_servfail], + inplace_cb_reply_servfail, qinfo, qstate, rep, rcode, edns, repinfo, + region); +} + +int inplace_cb_query_call(struct module_env* env, struct query_info* qinfo, + uint16_t flags, struct sockaddr_storage* addr, socklen_t addrlen, + uint8_t* zone, size_t zonelen, struct module_qstate* qstate, + struct regional* region) +{ + struct inplace_cb* cb = env->inplace_cb_lists[inplace_cb_query]; + for(; cb; cb=cb->next) { + fptr_ok(fptr_whitelist_inplace_cb_query( + (inplace_cb_query_func_type*)cb->cb)); + (void)(*(inplace_cb_query_func_type*)cb->cb)(qinfo, flags, + qstate, addr, addrlen, zone, zonelen, region, + cb->id, cb->cb_arg); + } + return 1; +} + +int inplace_cb_edns_back_parsed_call(struct module_env* env, + struct module_qstate* qstate) +{ + struct inplace_cb* cb = + env->inplace_cb_lists[inplace_cb_edns_back_parsed]; + for(; cb; cb=cb->next) { + fptr_ok(fptr_whitelist_inplace_cb_edns_back_parsed( + (inplace_cb_edns_back_parsed_func_type*)cb->cb)); + (void)(*(inplace_cb_edns_back_parsed_func_type*)cb->cb)(qstate, + cb->id, cb->cb_arg); + } + return 1; +} + +int inplace_cb_query_response_call(struct module_env* env, + struct module_qstate* qstate, struct dns_msg* response) { + struct inplace_cb* cb = + env->inplace_cb_lists[inplace_cb_query_response]; + for(; cb; cb=cb->next) { + fptr_ok(fptr_whitelist_inplace_cb_query_response( + (inplace_cb_query_response_func_type*)cb->cb)); + (void)(*(inplace_cb_query_response_func_type*)cb->cb)(qstate, + response, cb->id, cb->cb_arg); + } + return 1; +} + struct edns_option* edns_opt_copy_region(struct edns_option* list, struct regional* region) { @@ -983,6 +1230,7 @@ if(s->opt_data) { s->opt_data = memdup(s->opt_data, s->opt_len); if(!s->opt_data) { + free(s); edns_opt_list_free(result); return NULL; } @@ -1000,7 +1248,7 @@ return result; } -struct edns_option* edns_opt_find(struct edns_option* list, uint16_t code) +struct edns_option* edns_opt_list_find(struct edns_option* list, uint16_t code) { struct edns_option* p; for(p=list; p; p=p->next) { --- contrib/unbound/util/data/msgreply.h.orig +++ contrib/unbound/util/data/msgreply.h @@ -49,8 +49,14 @@ struct iovec; struct regional; struct edns_data; +struct edns_option; +struct inplace_cb; +struct module_qstate; +struct module_env; struct msg_parse; struct rrset_parse; +struct local_rrset; +struct dns_msg; /** calculate the prefetch TTL as 90% of original. Calculation * without numerical overflow (uin32_t) */ @@ -73,6 +79,23 @@ uint16_t qtype; /** qclass, host byte order */ uint16_t qclass; + /** + * Alias local answer(s) for the qname. If 'qname' is an alias defined + * in a local zone, this field will be set to the corresponding local + * RRset when the alias is determined. + * In the initial implementation this can only be a single CNAME RR + * (or NULL), but it could possibly be extended to be a DNAME or a + * chain of aliases. + * Users of this structure are responsible to initialize this field + * to be NULL; otherwise other part of query handling code may be + * confused. + * Users also have to be careful about the lifetime of data. On return + * from local zone lookup, it may point to data derived from + * configuration that may be dynamically invalidated or data allocated + * in an ephemeral regional allocator. A deep copy of the data may + * have to be generated if it has to be kept during iterative + * resolution. */ + struct local_rrset* local_alias; }; /** @@ -82,7 +105,7 @@ /** the key with lock, and ptr to packed data. */ struct ub_packed_rrset_key* key; /** id needed */ - rrset_id_t id; + rrset_id_type id; }; /** @@ -133,6 +156,12 @@ */ time_t prefetch_ttl; + /** + * Reply TTL extended with serve expired TTL, to limit time to serve + * expired message. + */ + time_t serve_expired_ttl; + /** * The security status from DNSSEC validation of this message. */ @@ -199,6 +228,7 @@ * @param qd: qd count * @param ttl: TTL of replyinfo * @param prettl: prefetch ttl + * @param expttl: serve expired ttl * @param an: an count * @param ns: ns count * @param ar: ar count @@ -209,8 +239,8 @@ */ struct reply_info* construct_reply_info_base(struct regional* region, uint16_t flags, size_t qd, - time_t ttl, time_t prettl, size_t an, size_t ns, size_t ar, - size_t total, enum sec_status sec); + time_t ttl, time_t prettl, time_t expttl, size_t an, size_t ns, + size_t ar, size_t total, enum sec_status sec); /** * Parse wire query into a queryinfo structure, return 0 on parse error. @@ -262,6 +292,10 @@ struct alloc_cache* alloc, struct query_info* qinf, struct reply_info** rep, struct regional* region); +/** get msg reply struct (in temp region) */ +struct reply_info* parse_reply_in_temp_region(struct sldns_buffer* pkt, + struct regional* region, struct query_info* qi); + /** * Sorts the ref array. * @param rep: reply info. rrsets must be filled in. @@ -307,7 +341,7 @@ /** calculate hash value of query_info, lowercases the qname, * uses CD flag for AAAA qtype */ -hashvalue_t query_info_hash(struct query_info *q, uint16_t flags); +hashvalue_type query_info_hash(struct query_info *q, uint16_t flags); /** * Setup query info entry @@ -317,7 +351,7 @@ * @return: newly allocated message reply cache item. */ struct msgreply_entry* query_info_entrysetup(struct query_info* q, - struct reply_info* r, hashvalue_t h); + struct reply_info* r, hashvalue_type h); /** * Copy reply_info and all rrsets in it and allocate. @@ -334,6 +368,21 @@ struct alloc_cache* alloc, struct regional* region); /** + * Allocate (special) rrset keys. + * @param rep: reply info in which the rrset keys to be allocated, rrset[] + * array should have bee allocated with NULL pointers. + * @param alloc: how to allocate rrset keys. + * Not used if region!=NULL, it can be NULL in that case. + * @param region: if this parameter is NULL then the alloc is used. + * otherwise, rrset keys are allocated in this region. + * In a region, no special rrset key structures are needed (not shared). + * and no rrset_ref array in the reply needs to be built up. + * @return 1 on success, 0 on error + */ +int reply_info_alloc_rrset_keys(struct reply_info* rep, + struct alloc_cache* alloc, struct regional* region); + +/** * Copy a parsed rrset into given key, decompressing and allocating rdata. * @param pkt: packet for decompression * @param msg: the parser message (for flags for trust). @@ -425,10 +474,27 @@ * @param qinfo: query section. * @param rep: rest of message. */ -void log_dns_msg(const char* str, struct query_info* qinfo, +void log_dns_msg(const char* str, struct query_info* qinfo, struct reply_info* rep); /** + * Print string with neat domain name, type, class, + * status code from, and size of a query response. + * + * @param v: at what verbosity level to print this. + * @param qinf: query section. + * @param addr: address of the client. + * @param addrlen: length of the client address. + * @param dur: how long it took to complete the query. + * @param cached: whether or not the reply is coming from + * the cache, or an outside network. + * @param rmsg: sldns buffer packet. + */ +void log_reply_info(enum verbosity_value v, struct query_info *qinf, + struct sockaddr_storage *addr, socklen_t addrlen, struct timeval dur, + int cached, struct sldns_buffer *rmsg); + +/** * Print string with neat domain name, type, class from query info. * @param v: at what verbosity level to print this. * @param str: string of message. @@ -439,31 +505,162 @@ /** * Append edns option to edns data structure + * @param edns: the edns data structure to append the edns option to. + * @param region: region to allocate the new edns option. + * @param code: the edns option's code. + * @param len: the edns option's length. + * @param data: the edns option's data. + * @return false on failure. */ int edns_opt_append(struct edns_data* edns, struct regional* region, uint16_t code, size_t len, uint8_t* data); /** + * Append edns option to edns option list + * @param list: the edns option list to append the edns option to. + * @param code: the edns option's code. + * @param len: the edns option's length. + * @param data: the edns option's data. + * @param region: region to allocate the new edns option. + * @return false on failure. + */ +int edns_opt_list_append(struct edns_option** list, uint16_t code, size_t len, + uint8_t* data, struct regional* region); + +/** + * Remove any option found on the edns option list that matches the code. + * @param list: the list of edns options. + * @param code: the opt code to remove. + * @return true when at least one edns option was removed, false otherwise. + */ +int edns_opt_list_remove(struct edns_option** list, uint16_t code); + +/** * Find edns option in edns list * @param list: list of edns options (eg. edns.opt_list) * @param code: opt code to find. * @return NULL or the edns_option element. */ -struct edns_option* edns_opt_find(struct edns_option* list, uint16_t code); +struct edns_option* edns_opt_list_find(struct edns_option* list, uint16_t code); /** - * Transform edns data structure from query structure into reply structure. - * In place transform, for errors and cache replies. - * @param edns: on input contains the edns from the query. On output contains - * the edns for the answer. Add new options to the opt_list to put them - * in the answer (allocated in the region, with edns_opt_append). - * @param region: to allocate stuff in. - * @return false on failure (servfail to client, or for some error encodings, - * no EDNS options in the answer). + * Call the registered functions in the inplace_cb_reply linked list. + * This function is going to get called while answering with a resolved query. + * @param env: module environment. + * @param qinfo: query info. + * @param qstate: module qstate. + * @param rep: Reply info. Could be NULL. + * @param rcode: return code. + * @param edns: edns data of the reply. + * @param repinfo: comm_reply. NULL. + * @param region: region to store data. + * @return false on failure (a callback function returned an error). */ -int edns_opt_inplace_reply(struct edns_data* edns, struct regional* region); +int inplace_cb_reply_call(struct module_env* env, struct query_info* qinfo, + struct module_qstate* qstate, struct reply_info* rep, int rcode, + struct edns_data* edns, struct comm_reply* repinfo, struct regional* region); /** + * Call the registered functions in the inplace_cb_reply_cache linked list. + * This function is going to get called while answering from cache. + * @param env: module environment. + * @param qinfo: query info. + * @param qstate: module qstate. NULL when replying from cache. + * @param rep: Reply info. + * @param rcode: return code. + * @param edns: edns data of the reply. Edns input can be found here. + * @param repinfo: comm_reply. Reply information for a communication point. + * @param region: region to store data. + * @return false on failure (a callback function returned an error). + */ +int inplace_cb_reply_cache_call(struct module_env* env, + struct query_info* qinfo, struct module_qstate* qstate, + struct reply_info* rep, int rcode, struct edns_data* edns, + struct comm_reply* repinfo, struct regional* region); + +/** + * Call the registered functions in the inplace_cb_reply_local linked list. + * This function is going to get called while answering with local data. + * @param env: module environment. + * @param qinfo: query info. + * @param qstate: module qstate. NULL when replying from cache. + * @param rep: Reply info. + * @param rcode: return code. + * @param edns: edns data of the reply. Edns input can be found here. + * @param repinfo: comm_reply. Reply information for a communication point. + * @param region: region to store data. + * @return false on failure (a callback function returned an error). + */ +int inplace_cb_reply_local_call(struct module_env* env, + struct query_info* qinfo, struct module_qstate* qstate, + struct reply_info* rep, int rcode, struct edns_data* edns, + struct comm_reply* repinfo, struct regional* region); + +/** + * Call the registered functions in the inplace_cb_reply linked list. + * This function is going to get called while answering with a servfail. + * @param env: module environment. + * @param qinfo: query info. + * @param qstate: module qstate. Contains the edns option lists. Could be NULL. + * @param rep: Reply info. NULL when servfail. + * @param rcode: return code. LDNS_RCODE_SERVFAIL. + * @param edns: edns data of the reply. Edns input can be found here if qstate + * is NULL. + * @param repinfo: comm_reply. Reply information for a communication point. + * @param region: region to store data. + * @return false on failure (a callback function returned an error). + */ +int inplace_cb_reply_servfail_call(struct module_env* env, + struct query_info* qinfo, struct module_qstate* qstate, + struct reply_info* rep, int rcode, struct edns_data* edns, + struct comm_reply* repinfo, struct regional* region); + +/** + * Call the registered functions in the inplace_cb_query linked list. + * This function is going to get called just before sending a query to a + * nameserver. + * @param env: module environment. + * @param qinfo: query info. + * @param flags: flags of the query. + * @param addr: to which server to send the query. + * @param addrlen: length of addr. + * @param zone: name of the zone of the delegation point. wireformat dname. + * This is the delegation point name for which the server is deemed + * authoritative. + * @param zonelen: length of zone. + * @param qstate: module qstate. + * @param region: region to store data. + * @return false on failure (a callback function returned an error). + */ +int inplace_cb_query_call(struct module_env* env, struct query_info* qinfo, + uint16_t flags, struct sockaddr_storage* addr, socklen_t addrlen, + uint8_t* zone, size_t zonelen, struct module_qstate* qstate, + struct regional* region); + +/** + * Call the registered functions in the inplace_cb_edns_back_parsed linked list. + * This function is going to get called after parsing the EDNS data on the + * reply from a nameserver. + * @param env: module environment. + * @param qstate: module qstate. + * @return false on failure (a callback function returned an error). + */ +int inplace_cb_edns_back_parsed_call(struct module_env* env, + struct module_qstate* qstate); + +/** + * Call the registered functions in the inplace_cb_query_response linked list. + * This function is going to get called after receiving a reply from a + * nameserver. + * @param env: module environment. + * @param qstate: module qstate. + * @param response: received response + * @return false on failure (a callback function returned an error). + */ +int inplace_cb_query_response_call(struct module_env* env, + struct module_qstate* qstate, struct dns_msg* response); + +/** * Copy edns option list allocated to the new region */ struct edns_option* edns_opt_copy_region(struct edns_option* list, --- contrib/unbound/util/data/packed_rrset.c.orig +++ contrib/unbound/util/data/packed_rrset.c @@ -40,6 +40,7 @@ */ #include "config.h" +#include "util/data/msgparse.h" #include "util/data/packed_rrset.h" #include "util/data/dname.h" #include "util/storage/lookup3.h" @@ -158,7 +159,7 @@ return 1; } -hashvalue_t +hashvalue_type rrset_key_hash(struct packed_rrset_key* key) { /* type is hashed in host order */ @@ -165,7 +166,7 @@ uint16_t t = ntohs(key->type); /* Note this MUST be identical to pkt_hash_rrset in msgparse.c */ /* this routine does not have a compressed name */ - hashvalue_t h = 0xab; + hashvalue_type h = 0xab; h = dname_query_hash(key->dname, h); h = hashlittle(&t, sizeof(t), h); h = hashlittle(&key->rrset_class, sizeof(uint16_t), h); @@ -253,6 +254,7 @@ case sec_status_bogus: return "sec_status_bogus"; case sec_status_indeterminate: return "sec_status_indeterminate"; case sec_status_insecure: return "sec_status_insecure"; + case sec_status_secure_sentinel_fail: return "sec_status_secure_sentinel_fail"; case sec_status_secure: return "sec_status_secure"; } return "unknown_sec_status_value"; @@ -350,11 +352,11 @@ /* make TTLs relative - once per rrset */ for(i=0; icount + d->rrsig_count; i++) { if(d->rr_ttl[i] < now) - d->rr_ttl[i] = 0; + d->rr_ttl[i] = SERVE_EXPIRED?SERVE_EXPIRED_REPLY_TTL:0; else d->rr_ttl[i] -= now; } if(d->ttl < now) - d->ttl = 0; + d->ttl = SERVE_EXPIRED?SERVE_EXPIRED_REPLY_TTL:0; else d->ttl -= now; return ck; } @@ -385,3 +387,19 @@ packed_rrset_ttl_add(dd, now); return dk; } + +int +packed_rrset_find_rr(struct packed_rrset_data* d, uint8_t* rdata, size_t len, + size_t* index) +{ + size_t i; + for(i=0; icount; i++) { + if(d->rr_len[i] != len) + continue; + if(memcmp(d->rr_data[i], rdata, len) == 0) { + *index = i; + return 1; + } + } + return 0; +} --- contrib/unbound/util/data/packed_rrset.h.orig +++ contrib/unbound/util/data/packed_rrset.h @@ -47,7 +47,7 @@ /** type used to uniquely identify rrsets. Cannot be reused without * clearing the cache. */ -typedef uint64_t rrset_id_t; +typedef uint64_t rrset_id_type; /** this rrset is NSEC and is at zone apex (at child side of zonecut) */ #define PACKED_RRSET_NSEC_AT_APEX 0x1 @@ -57,6 +57,10 @@ * this is set on SOA rrsets in the authority section, to keep its TTL separate * from the SOA in the answer section from a direct SOA query or ANY query. */ #define PACKED_RRSET_SOA_NEG 0x4 +/** This rrset is considered to have a fixed TTL; its TTL doesn't have to be + * updated on encoding in a reply. This flag is not expected to be set in + * cached data. */ +#define PACKED_RRSET_FIXEDTTL 0x80000000 /** number of rrs and rrsets for integer overflow protection. More than * this is not really possible (64K packet has much less RRs and RRsets) in @@ -83,6 +87,7 @@ * o PACKED_RRSET_NSEC_AT_APEX * o PACKED_RRSET_PARENT_SIDE * o PACKED_RRSET_SOA_NEG + * o PACKED_RRSET_FIXEDTTL (not supposed to be cached) */ uint32_t flags; /** the rrset type in network format */ @@ -114,7 +119,7 @@ * The other values in this struct may only be altered after changing * the id (which needs a writelock on entry.lock). */ - rrset_id_t id; + rrset_id_type id; /** key data: dname, type and class */ struct packed_rrset_key rk; }; @@ -182,6 +187,10 @@ * insecure. Generally this means that this RRset is below a trust * anchor, but also below a verified, insecure delegation. */ sec_status_insecure, + /** SECURE_SENTINEL_FAIL means that the object (RRset or message) + * validated according to local policy but did not succeed in the root + * KSK sentinel test (draft-ietf-dnsop-kskroll-sentinel). */ + sec_status_secure_sentinel_fail, /** SECURE means that the object (RRset or message) validated * according to local policy. */ sec_status_secure @@ -191,6 +200,12 @@ * RRset data. * * The data is packed, stored contiguously in memory. + * + * It is not always stored contiguously, in that case, an unpacked-packed + * rrset has the arrays separate. A bunch of routines work on that, but + * the packed rrset that is contiguous is for the rrset-cache and the + * cache-response routines in daemon/worker.c. + * * memory layout: * o base struct * o rr_len size_t array @@ -334,7 +349,7 @@ * @param key: the rrset key with name, type, class, flags. * @return hash value. */ -hashvalue_t rrset_key_hash(struct packed_rrset_key* key); +hashvalue_type rrset_key_hash(struct packed_rrset_key* key); /** * Fixup pointers in fixed data packed_rrset_data blob. @@ -431,4 +446,17 @@ struct ub_packed_rrset_key* key, struct alloc_cache* alloc, time_t now); +/** + * Find RR index in packed rrset + * Raw comparison, does not canonicalize RDATA + * @param d: packed rrset + * @param rdata: RDATA of RR to find + * @param len: length of rdata + * @param index: pointer to int to store index of found RR + * @return 1 if RR found, 0 otherwise + */ +int +packed_rrset_find_rr(struct packed_rrset_data* d, uint8_t* rdata, size_t len, + size_t* index); + #endif /* UTIL_DATA_PACKED_RRSET_H */ --- contrib/unbound/util/edns.c.orig +++ contrib/unbound/util/edns.c @@ -0,0 +1,85 @@ +/* + * util/edns.c - handle base EDNS options. + * + * 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 functions for base EDNS options. + */ + +#include "config.h" +#include "util/edns.h" +#include "util/config_file.h" +#include "util/netevent.h" +#include "util/regional.h" +#include "util/data/msgparse.h" +#include "util/data/msgreply.h" + +#include "edns.h" + +static int edns_keepalive(struct edns_data* edns_out, struct edns_data* edns_in, + struct comm_point* c, struct regional* region) +{ + if(c->type == comm_udp) + return 1; + + /* 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. + */ + if(c->tcp_keepalive || + edns_opt_list_find(edns_in->opt_list, LDNS_EDNS_KEEPALIVE)) { + int keepalive = c->tcp_timeout_msec / 100; + uint8_t data[2]; + data[0] = (uint8_t)((keepalive >> 8) & 0xff); + data[1] = (uint8_t)(keepalive & 0xff); + if(!edns_opt_list_append(&edns_out->opt_list, LDNS_EDNS_KEEPALIVE, + sizeof(data), data, region)) + return 0; + c->tcp_keepalive = 1; + } + return 1; +} + +int apply_edns_options(struct edns_data* edns_out, struct edns_data* edns_in, + struct config_file* cfg, struct comm_point* c, struct regional* region) +{ + if(cfg->do_tcp_keepalive && + !edns_keepalive(edns_out, edns_in, c, region)) + return 0; + + return 1; +} --- contrib/unbound/util/edns.h.orig +++ contrib/unbound/util/edns.h @@ -0,0 +1,62 @@ +/* + * util/edns.h - handle base EDNS options. + * + * 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 functions for base EDNS options. + */ + +#ifndef UTIL_EDNS_H +#define UTIL_EDNS_H + +struct edns_data; +struct config_file; +struct comm_point; +struct regional; + +/** + * Apply common EDNS options. + * + * @param edns_out: initialised edns information with outbound edns. + * @param edns_in: initialised edns information with received edns. + * @param cfg: configuration. + * @param c: comm channel. + * @param region: the region to allocate the edns options in. + */ +int apply_edns_options(struct edns_data* edns_out, struct edns_data* edns_in, + struct config_file* cfg, struct comm_point* c, struct regional* region); + +#endif --- contrib/unbound/util/fptr_wlist.c.orig +++ contrib/unbound/util/fptr_wlist.c @@ -49,8 +49,10 @@ #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" @@ -74,6 +76,7 @@ #ifdef UB_ON_WINDOWS #include "winrc/win_svc.h" #endif +#include "respip/respip.h" #ifdef WITH_PYTHONMODULE #include "pythonmod/pythonmod.h" @@ -81,19 +84,31 @@ #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 int -fptr_whitelist_comm_point(comm_point_callback_t *fptr) +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_t *fptr) +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; @@ -113,6 +128,10 @@ #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; return 0; } @@ -148,6 +167,7 @@ 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 UB_ON_WINDOWS else if(fptr == &worker_win_stop_cb) return 1; #endif @@ -155,7 +175,7 @@ } int -fptr_whitelist_pending_udp(comm_point_callback_t *fptr) +fptr_whitelist_pending_udp(comm_point_callback_type *fptr) { if(fptr == &serviced_udp_callback) return 1; else if(fptr == &worker_handle_reply) return 1; @@ -164,7 +184,7 @@ } int -fptr_whitelist_pending_tcp(comm_point_callback_t *fptr) +fptr_whitelist_pending_tcp(comm_point_callback_type *fptr) { if(fptr == &serviced_tcp_callback) return 1; else if(fptr == &worker_handle_reply) return 1; @@ -173,7 +193,7 @@ } int -fptr_whitelist_serviced_query(comm_point_callback_t *fptr) +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; @@ -203,11 +223,15 @@ 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_t fptr) +fptr_whitelist_hash_sizefunc(lruhash_sizefunc_type fptr) { if(fptr == &msgreply_sizefunc) return 1; else if(fptr == &ub_rrset_sizefunc) return 1; @@ -214,12 +238,20 @@ 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_t fptr) +fptr_whitelist_hash_compfunc(lruhash_compfunc_type fptr) { if(fptr == &query_info_compare) return 1; else if(fptr == &ub_rrset_compare) return 1; @@ -226,12 +258,17 @@ 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_t fptr) +fptr_whitelist_hash_delkeyfunc(lruhash_delkeyfunc_type fptr) { if(fptr == &query_entry_delete) return 1; else if(fptr == &ub_rrset_key_delete) return 1; @@ -238,12 +275,17 @@ 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_t fptr) +fptr_whitelist_hash_deldatafunc(lruhash_deldatafunc_type fptr) { if(fptr == &reply_info_delete) return 1; else if(fptr == &rrset_data_delete) return 1; @@ -251,14 +293,24 @@ 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_t fptr) +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; } @@ -265,10 +317,9 @@ /** whitelist env->send_query callbacks */ int fptr_whitelist_modenv_send_query(struct outbound_entry* (*fptr)( - uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, - uint16_t flags, int dnssec, int want_dnssec, int nocaps, - struct edns_option* opt_list, struct sockaddr_storage* addr, - socklen_t addrlen, uint8_t* zone, size_t zonelen, + struct query_info* qinfo, uint16_t flags, int dnssec, int want_dnssec, + int nocaps, struct sockaddr_storage* addr, socklen_t addrlen, + uint8_t* zone, size_t zonelen, int ssl_upstream, char* tls_auth_name, struct module_qstate* q)) { if(fptr == &worker_send_query) return 1; @@ -294,6 +345,16 @@ } 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; @@ -301,8 +362,8 @@ } int -fptr_whitelist_modenv_detect_cycle(int (*fptr)( - struct module_qstate* qstate, struct query_info* qinfo, +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; @@ -315,6 +376,7 @@ 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 @@ -321,6 +383,15 @@ #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; } @@ -330,6 +401,7 @@ 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 @@ -336,6 +408,15 @@ #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; } @@ -346,6 +427,7 @@ 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 @@ -352,6 +434,15 @@ #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; } @@ -362,6 +453,7 @@ 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 @@ -368,6 +460,15 @@ #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; } @@ -378,6 +479,7 @@ 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 @@ -384,6 +486,15 @@ #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; } @@ -393,6 +504,7 @@ 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 @@ -399,6 +511,15 @@ #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; } @@ -409,7 +530,7 @@ return 0; } -int fptr_whitelist_tube_listen(tube_callback_t* fptr) +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; @@ -416,12 +537,14 @@ return 0; } -int fptr_whitelist_mesh_cb(mesh_cb_func_t fptr) +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; return 0; } @@ -432,3 +555,74 @@ 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 + } else if(type == inplace_cb_reply_cache) { +#ifdef WITH_PYTHONMODULE + if(fptr == &python_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 + } else if(type == inplace_cb_reply_servfail) { +#ifdef WITH_PYTHONMODULE + if(fptr == &python_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 + (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 + 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 + return 0; +} + +int fptr_whitelist_serve_expired_lookup(serve_expired_lookup_func_type* fptr) +{ + if(fptr == &mesh_serve_expired_lookup) + return 1; + return 0; +} --- contrib/unbound/util/fptr_wlist.h.orig +++ contrib/unbound/util/fptr_wlist.h @@ -80,7 +80,7 @@ * @param fptr: function pointer to check. * @return false if not in whitelist. */ -int fptr_whitelist_comm_point(comm_point_callback_t *fptr); +int fptr_whitelist_comm_point(comm_point_callback_type *fptr); /** * Check function pointer whitelist for raw comm_point callback values. @@ -88,7 +88,7 @@ * @param fptr: function pointer to check. * @return false if not in whitelist. */ -int fptr_whitelist_comm_point_raw(comm_point_callback_t *fptr); +int fptr_whitelist_comm_point_raw(comm_point_callback_type *fptr); /** * Check function pointer whitelist for comm_timer callback values. @@ -137,7 +137,7 @@ * @param fptr: function pointer to check. * @return false if not in whitelist. */ -int fptr_whitelist_pending_udp(comm_point_callback_t *fptr); +int fptr_whitelist_pending_udp(comm_point_callback_type *fptr); /** * Check function pointer whitelist for pending tcp callback values. @@ -145,7 +145,7 @@ * @param fptr: function pointer to check. * @return false if not in whitelist. */ -int fptr_whitelist_pending_tcp(comm_point_callback_t *fptr); +int fptr_whitelist_pending_tcp(comm_point_callback_type *fptr); /** * Check function pointer whitelist for serviced query callback values. @@ -153,7 +153,7 @@ * @param fptr: function pointer to check. * @return false if not in whitelist. */ -int fptr_whitelist_serviced_query(comm_point_callback_t *fptr); +int fptr_whitelist_serviced_query(comm_point_callback_type *fptr); /** * Check function pointer whitelist for rbtree cmp callback values. @@ -169,7 +169,7 @@ * @param fptr: function pointer to check. * @return false if not in whitelist. */ -int fptr_whitelist_hash_sizefunc(lruhash_sizefunc_t fptr); +int fptr_whitelist_hash_sizefunc(lruhash_sizefunc_type fptr); /** * Check function pointer whitelist for lruhash compfunc callback values. @@ -177,7 +177,7 @@ * @param fptr: function pointer to check. * @return false if not in whitelist. */ -int fptr_whitelist_hash_compfunc(lruhash_compfunc_t fptr); +int fptr_whitelist_hash_compfunc(lruhash_compfunc_type fptr); /** * Check function pointer whitelist for lruhash delkeyfunc callback values. @@ -185,7 +185,7 @@ * @param fptr: function pointer to check. * @return false if not in whitelist. */ -int fptr_whitelist_hash_delkeyfunc(lruhash_delkeyfunc_t fptr); +int fptr_whitelist_hash_delkeyfunc(lruhash_delkeyfunc_type fptr); /** * Check function pointer whitelist for lruhash deldata callback values. @@ -193,7 +193,7 @@ * @param fptr: function pointer to check. * @return false if not in whitelist. */ -int fptr_whitelist_hash_deldatafunc(lruhash_deldatafunc_t fptr); +int fptr_whitelist_hash_deldatafunc(lruhash_deldatafunc_type fptr); /** * Check function pointer whitelist for lruhash markdel callback values. @@ -201,7 +201,7 @@ * @param fptr: function pointer to check. * @return false if not in whitelist. */ -int fptr_whitelist_hash_markdelfunc(lruhash_markdelfunc_t fptr); +int fptr_whitelist_hash_markdelfunc(lruhash_markdelfunc_type fptr); /** * Check function pointer whitelist for module_env send_query callback values. @@ -210,10 +210,9 @@ * @return false if not in whitelist. */ int fptr_whitelist_modenv_send_query(struct outbound_entry* (*fptr)( - uint8_t* qname, size_t qnamelen, uint16_t qtype, uint16_t qclass, - uint16_t flags, int dnssec, int want_dnssec, int nocaps, - struct edns_option*, struct sockaddr_storage* addr, socklen_t addrlen, - uint8_t* zone, size_t zonelen, + struct query_info* qinfo, uint16_t flags, int dnssec, int want_dnssec, + int nocaps, struct sockaddr_storage* addr, socklen_t addrlen, + uint8_t* zone, size_t zonelen, int ssl_upstream, char* tls_auth_name, struct module_qstate* q)); /** @@ -236,6 +235,15 @@ uint16_t qflags, int prime, int valrec, struct module_qstate** newq)); /** + * Check function pointer whitelist for module_env add_sub callback values. + * + * @param fptr: function pointer to check. + * @return false if not in whitelist. + */ +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)); +/** * Check function pointer whitelist for module_env kill_sub callback values. * * @param fptr: function pointer to check. @@ -318,7 +326,7 @@ * @param fptr: function pointer to check. * @return false if not in whitelist. */ -int fptr_whitelist_tube_listen(tube_callback_t* fptr); +int fptr_whitelist_tube_listen(tube_callback_type* fptr); /** * Check function pointer whitelist for mesh state callback values. @@ -326,7 +334,7 @@ * @param fptr: function pointer to check. * @return false if not in whitelist. */ -int fptr_whitelist_mesh_cb(mesh_cb_func_t fptr); +int fptr_whitelist_mesh_cb(mesh_cb_func_type fptr); /** * Check function pointer whitelist for config_get_option func values. @@ -335,6 +343,47 @@ */ int fptr_whitelist_print_func(void (*fptr)(char*,void*)); +/** + * Check function pointer whitelist for inplace_cb_reply, + * inplace_cb_reply_cache, inplace_cb_reply_local and inplace_cb_reply_servfail + * func values. + * @param fptr: function pointer to check. + * @param type: the type of the callback function. + * @return false if not in whitelist. + */ +int fptr_whitelist_inplace_cb_reply_generic(inplace_cb_reply_func_type* fptr, + enum inplace_cb_list_type type); + +/** + * Check function pointer whitelist for inplace_cb_query func values. + * @param fptr: function pointer to check. + * @return false if not in whitelist. + */ +int fptr_whitelist_inplace_cb_query(inplace_cb_query_func_type* fptr); + +/** + * Check function pointer whitelist for inplace_cb_edns_back_parsed func values. + * @param fptr: function pointer to check. + * @return false if not in whitelist. + */ +int fptr_whitelist_inplace_cb_edns_back_parsed( + inplace_cb_edns_back_parsed_func_type* fptr); + +/** + * Check function pointer whitelist for inplace_cb_query_response func values. + * @param fptr: function pointer to check. + * @return false if not in whitelist. + */ +int fptr_whitelist_inplace_cb_query_response( + inplace_cb_query_response_func_type* fptr); + +/** + * Check function pointer whitelist for serve_expired_lookup func values. + * @param fptr: function pointer to check. + * @return false if not in whitelist. + */ +int fptr_whitelist_serve_expired_lookup(serve_expired_lookup_func_type* fptr); + /** Due to module breakage by fptr wlist, these test app declarations * are presented here */ /** --- contrib/unbound/util/iana_ports.inc.orig +++ contrib/unbound/util/iana_ports.inc @@ -29,7 +29,6 @@ 44, 45, 46, -47, 48, 49, 50, @@ -41,7 +40,6 @@ 57, 58, 59, -61, 62, 63, 64, @@ -661,6 +659,7 @@ 847, 848, 853, +854, 860, 861, 862, @@ -961,8 +960,6 @@ 1298, 1299, 1300, -1301, -1302, 1303, 1304, 1305, @@ -1849,7 +1846,6 @@ 2197, 2198, 2199, -2200, 2201, 2202, 2203, @@ -2697,7 +2693,6 @@ 3068, 3069, 3070, -3071, 3072, 3073, 3074, @@ -3776,6 +3771,7 @@ 4188, 4191, 4192, +4197, 4199, 4300, 4301, @@ -3908,6 +3904,7 @@ 4600, 4601, 4621, +4646, 4658, 4659, 4660, @@ -3946,6 +3943,7 @@ 4700, 4701, 4702, +4711, 4725, 4726, 4727, @@ -3962,6 +3960,7 @@ 4743, 4744, 4745, +4746, 4747, 4749, 4750, @@ -3968,6 +3967,8 @@ 4751, 4752, 4753, +4754, +4755, 4784, 4785, 4789, @@ -4451,6 +4452,7 @@ 6446, 6455, 6456, +6464, 6471, 6480, 6481, @@ -4498,6 +4500,7 @@ 6626, 6627, 6628, +6629, 6633, 6634, 6635, @@ -4566,6 +4569,8 @@ 7013, 7014, 7015, +7016, +7017, 7019, 7020, 7021, @@ -4577,6 +4582,7 @@ 7040, 7070, 7071, +7072, 7080, 7088, 7095, @@ -4627,6 +4633,7 @@ 7402, 7410, 7411, +7420, 7421, 7426, 7427, @@ -4663,6 +4670,7 @@ 7629, 7633, 7648, +7663, 7674, 7675, 7676, @@ -4724,11 +4732,14 @@ 8002, 8003, 8005, +8006, +8007, 8008, 8019, 8020, 8021, 8022, +8023, 8025, 8026, 8032, @@ -4735,6 +4746,7 @@ 8033, 8034, 8040, +8041, 8052, 8053, 8054, @@ -4754,6 +4766,7 @@ 8088, 8097, 8100, +8111, 8115, 8116, 8118, @@ -4782,10 +4795,12 @@ 8206, 8207, 8208, +8211, 8230, 8231, 8232, 8243, +8266, 8276, 8280, 8282, @@ -4845,6 +4860,10 @@ 8793, 8800, 8804, +8805, +8807, +8808, +8809, 8873, 8880, 8883, @@ -4875,6 +4894,7 @@ 9006, 9007, 9009, +9011, 9020, 9021, 9022, @@ -4882,7 +4902,9 @@ 9024, 9025, 9026, +9060, 9080, +9081, 9084, 9085, 9086, @@ -4899,6 +4921,7 @@ 9104, 9105, 9106, +9111, 9119, 9131, 9160, @@ -5208,6 +5231,7 @@ 18463, 18634, 18635, +18668, 18769, 18881, 18888, @@ -5215,6 +5239,7 @@ 19007, 19191, 19194, +19220, 19283, 19315, 19398, @@ -5345,6 +5370,7 @@ 30260, 30832, 30999, +31016, 31029, 31416, 31457, @@ -5374,10 +5400,12 @@ 33331, 33334, 33434, +33435, 33656, 34249, 34378, 34379, +34567, 34962, 34963, 34964, @@ -5384,6 +5412,7 @@ 34980, 35001, 35004, +35100, 35355, 36001, 36411, @@ -5413,6 +5442,7 @@ 43189, 43190, 43210, +43438, 43439, 43440, 43441, @@ -5446,3 +5476,4 @@ 48556, 48619, 48653, +49001, --- contrib/unbound/util/locks.c.orig +++ contrib/unbound/util/locks.c @@ -110,15 +110,15 @@ * @param arg: user argument to func. */ void -ub_thr_fork_create(ub_thread_t* thr, void* (*func)(void*), void* arg) +ub_thr_fork_create(ub_thread_type* thr, void* (*func)(void*), void* arg) { pid_t pid = fork(); switch(pid) { default: /* main */ - *thr = (ub_thread_t)pid; + *thr = (ub_thread_type)pid; return; case 0: /* child */ - *thr = (ub_thread_t)getpid(); + *thr = (ub_thread_type)getpid(); (void)(*func)(arg); exit(0); case -1: /* error */ @@ -128,10 +128,10 @@ /** * There is no threading. Wait for a process to terminate. - * Note that ub_thread_t is defined as pid_t. + * Note that ub_thread_type is defined as pid_t. * @param thread: the process id to wait for. */ -void ub_thr_fork_wait(ub_thread_t thread) +void ub_thr_fork_wait(ub_thread_type thread) { int status = 0; if(waitpid((pid_t)thread, &status, 0) == -1) @@ -143,7 +143,7 @@ #endif /* !defined(HAVE_PTHREAD) && !defined(HAVE_SOLARIS_THREADS) && !defined(HAVE_WINDOWS_THREADS) */ #ifdef HAVE_SOLARIS_THREADS -void* ub_thread_key_get(ub_thread_key_t key) +void* ub_thread_key_get(ub_thread_key_type key) { void* ret=NULL; LOCKRET(thr_getspecific(key, &ret)); @@ -167,7 +167,7 @@ LocalFree(buf); } -void lock_basic_init(lock_basic_t* lock) +void lock_basic_init(lock_basic_type* lock) { /* implement own lock, because windows HANDLE as Mutex usage * uses too many handles and would bog down the whole system. */ @@ -174,12 +174,12 @@ (void)InterlockedExchange(lock, 0); } -void lock_basic_destroy(lock_basic_t* lock) +void lock_basic_destroy(lock_basic_type* lock) { (void)InterlockedExchange(lock, 0); } -void lock_basic_lock(lock_basic_t* lock) +void lock_basic_lock(lock_basic_type* lock) { LONG wait = 1; /* wait 1 msec at first */ @@ -191,13 +191,13 @@ /* the old value was 0, but we inserted 1, we locked it! */ } -void lock_basic_unlock(lock_basic_t* lock) +void lock_basic_unlock(lock_basic_type* lock) { /* unlock it by inserting the value of 0. xchg for cache coherency. */ (void)InterlockedExchange(lock, 0); } -void ub_thread_key_create(ub_thread_key_t* key, void* f) +void ub_thread_key_create(ub_thread_key_type* key, void* f) { *key = TlsAlloc(); if(*key == TLS_OUT_OF_INDEXES) { @@ -207,7 +207,7 @@ else ub_thread_key_set(*key, f); } -void ub_thread_key_set(ub_thread_key_t key, void* v) +void ub_thread_key_set(ub_thread_key_type key, void* v) { if(!TlsSetValue(key, v)) { log_win_err("TlsSetValue failed", GetLastError()); @@ -214,7 +214,7 @@ } } -void* ub_thread_key_get(ub_thread_key_t key) +void* ub_thread_key_get(ub_thread_key_type key) { void* ret = (void*)TlsGetValue(key); if(ret == NULL && GetLastError() != ERROR_SUCCESS) { @@ -223,7 +223,7 @@ return ret; } -void ub_thread_create(ub_thread_t* thr, void* (*func)(void*), void* arg) +void ub_thread_create(ub_thread_type* thr, void* (*func)(void*), void* arg) { #ifndef HAVE__BEGINTHREADEX *thr = CreateThread(NULL, /* default security (no inherit handle) */ @@ -233,7 +233,7 @@ NULL); /* do not store thread identifier anywhere */ #else /* the beginthreadex routine setups for the C lib; aligns stack */ - *thr=(ub_thread_t)_beginthreadex(NULL, 0, (void*)func, arg, 0, NULL); + *thr=(ub_thread_type)_beginthreadex(NULL, 0, (void*)func, arg, 0, NULL); #endif if(*thr == NULL) { log_win_err("CreateThread failed", GetLastError()); @@ -241,12 +241,12 @@ } } -ub_thread_t ub_thread_self(void) +ub_thread_type ub_thread_self(void) { return GetCurrentThread(); } -void ub_thread_join(ub_thread_t thr) +void ub_thread_join(ub_thread_type thr) { DWORD ret = WaitForSingleObject(thr, INFINITE); if(ret == WAIT_FAILED) { --- contrib/unbound/util/locks.h.orig +++ contrib/unbound/util/locks.h @@ -95,7 +95,7 @@ /******************* PTHREAD ************************/ /** use pthread mutex for basic lock */ -typedef pthread_mutex_t lock_basic_t; +typedef pthread_mutex_t lock_basic_type; /** small front for pthread init func, NULL is default attrs. */ #define lock_basic_init(lock) LOCKRET(pthread_mutex_init(lock, NULL)) #define lock_basic_destroy(lock) LOCKRET(pthread_mutex_destroy(lock)) @@ -104,7 +104,7 @@ #ifndef HAVE_PTHREAD_RWLOCK_T /** in case rwlocks are not supported, use a mutex. */ -typedef pthread_mutex_t lock_rw_t; +typedef pthread_mutex_t lock_rw_type; #define lock_rw_init(lock) LOCKRET(pthread_mutex_init(lock, NULL)) #define lock_rw_destroy(lock) LOCKRET(pthread_mutex_destroy(lock)) #define lock_rw_rdlock(lock) LOCKRET(pthread_mutex_lock(lock)) @@ -112,7 +112,7 @@ #define lock_rw_unlock(lock) LOCKRET(pthread_mutex_unlock(lock)) #else /* HAVE_PTHREAD_RWLOCK_T */ /** we use the pthread rwlock */ -typedef pthread_rwlock_t lock_rw_t; +typedef pthread_rwlock_t lock_rw_type; /** small front for pthread init func, NULL is default attrs. */ #define lock_rw_init(lock) LOCKRET(pthread_rwlock_init(lock, NULL)) #define lock_rw_destroy(lock) LOCKRET(pthread_rwlock_destroy(lock)) @@ -123,7 +123,7 @@ #ifndef HAVE_PTHREAD_SPINLOCK_T /** in case spinlocks are not supported, use a mutex. */ -typedef pthread_mutex_t lock_quick_t; +typedef pthread_mutex_t lock_quick_type; /** small front for pthread init func, NULL is default attrs. */ #define lock_quick_init(lock) LOCKRET(pthread_mutex_init(lock, NULL)) #define lock_quick_destroy(lock) LOCKRET(pthread_mutex_destroy(lock)) @@ -132,7 +132,7 @@ #else /* HAVE_PTHREAD_SPINLOCK_T */ /** use pthread spinlock for the quick lock */ -typedef pthread_spinlock_t lock_quick_t; +typedef pthread_spinlock_t lock_quick_type; /** * allocate process private since this is available whether * Thread Process-Shared Synchronization is supported or not. @@ -148,14 +148,31 @@ #endif /* HAVE SPINLOCK */ /** Thread creation */ -typedef pthread_t ub_thread_t; -/** Pass where to store tread_t in thr. Use default NULL attributes. */ -#define ub_thread_create(thr, func, arg) LOCKRET(pthread_create(thr, NULL, func, arg)) +typedef pthread_t ub_thread_type; +/** On alpine linux default thread stack size is 80 Kb. See +http://wiki.musl-libc.org/wiki/Functional_differences_from_glibc#Thread_stack_size +This is not enough and cause segfault. Other linux distros have 2 Mb at least. +Wrapper for set up thread stack size */ +#define PTHREADSTACKSIZE 2*1024*1024 +#define PTHREADCREATE(thr, stackrequired, func, arg) do {\ + pthread_attr_t attr; \ + size_t stacksize; \ + LOCKRET(pthread_attr_init(&attr)); \ + LOCKRET(pthread_attr_getstacksize(&attr, &stacksize)); \ + if (stacksize < stackrequired) { \ + LOCKRET(pthread_attr_setstacksize(&attr, stackrequired)); \ + LOCKRET(pthread_create(thr, &attr, func, arg)); \ + LOCKRET(pthread_attr_getstacksize(&attr, &stacksize)); \ + verbose(VERB_ALGO, "Thread stack size set to %u", (unsigned)stacksize); \ + } else {LOCKRET(pthread_create(thr, NULL, func, arg));} \ + } while(0) +/** Use wrapper for set thread stack size on attributes. */ +#define ub_thread_create(thr, func, arg) PTHREADCREATE(thr, PTHREADSTACKSIZE, func, arg) /** get self id. */ #define ub_thread_self() pthread_self() /** wait for another thread to terminate */ #define ub_thread_join(thread) LOCKRET(pthread_join(thread, NULL)) -typedef pthread_key_t ub_thread_key_t; +typedef pthread_key_t ub_thread_key_type; #define ub_thread_key_create(key, f) LOCKRET(pthread_key_create(key, f)) #define ub_thread_key_set(key, v) LOCKRET(pthread_setspecific(key, v)) #define ub_thread_key_get(key) pthread_getspecific(key) @@ -167,7 +184,7 @@ #include #include -typedef rwlock_t lock_rw_t; +typedef rwlock_t lock_rw_type; #define lock_rw_init(lock) LOCKRET(rwlock_init(lock, USYNC_THREAD, NULL)) #define lock_rw_destroy(lock) LOCKRET(rwlock_destroy(lock)) #define lock_rw_rdlock(lock) LOCKRET(rw_rdlock(lock)) @@ -175,7 +192,7 @@ #define lock_rw_unlock(lock) LOCKRET(rw_unlock(lock)) /** use basic mutex */ -typedef mutex_t lock_basic_t; +typedef mutex_t lock_basic_type; #define lock_basic_init(lock) LOCKRET(mutex_init(lock, USYNC_THREAD, NULL)) #define lock_basic_destroy(lock) LOCKRET(mutex_destroy(lock)) #define lock_basic_lock(lock) LOCKRET(mutex_lock(lock)) @@ -182,7 +199,7 @@ #define lock_basic_unlock(lock) LOCKRET(mutex_unlock(lock)) /** No spinlocks in solaris threads API. Use a mutex. */ -typedef mutex_t lock_quick_t; +typedef mutex_t lock_quick_type; #define lock_quick_init(lock) LOCKRET(mutex_init(lock, USYNC_THREAD, NULL)) #define lock_quick_destroy(lock) LOCKRET(mutex_destroy(lock)) #define lock_quick_lock(lock) LOCKRET(mutex_lock(lock)) @@ -189,14 +206,14 @@ #define lock_quick_unlock(lock) LOCKRET(mutex_unlock(lock)) /** Thread creation, create a default thread. */ -typedef thread_t ub_thread_t; +typedef thread_t ub_thread_type; #define ub_thread_create(thr, func, arg) LOCKRET(thr_create(NULL, NULL, func, arg, NULL, thr)) #define ub_thread_self() thr_self() #define ub_thread_join(thread) LOCKRET(thr_join(thread, NULL, NULL)) -typedef thread_key_t ub_thread_key_t; +typedef thread_key_t ub_thread_key_type; #define ub_thread_key_create(key, f) LOCKRET(thr_keycreate(key, f)) #define ub_thread_key_set(key, v) LOCKRET(thr_setspecific(key, v)) -void* ub_thread_key_get(ub_thread_key_t key); +void* ub_thread_key_get(ub_thread_key_type key); #else /* we do not HAVE_SOLARIS_THREADS and no PTHREADS */ @@ -205,7 +222,7 @@ #include /* Use a mutex */ -typedef LONG lock_rw_t; +typedef LONG lock_rw_type; #define lock_rw_init(lock) lock_basic_init(lock) #define lock_rw_destroy(lock) lock_basic_destroy(lock) #define lock_rw_rdlock(lock) lock_basic_lock(lock) @@ -213,14 +230,14 @@ #define lock_rw_unlock(lock) lock_basic_unlock(lock) /** the basic lock is a mutex, implemented opaquely, for error handling. */ -typedef LONG lock_basic_t; -void lock_basic_init(lock_basic_t* lock); -void lock_basic_destroy(lock_basic_t* lock); -void lock_basic_lock(lock_basic_t* lock); -void lock_basic_unlock(lock_basic_t* lock); +typedef LONG lock_basic_type; +void lock_basic_init(lock_basic_type* lock); +void lock_basic_destroy(lock_basic_type* lock); +void lock_basic_lock(lock_basic_type* lock); +void lock_basic_unlock(lock_basic_type* lock); /** on windows no spinlock, use mutex too. */ -typedef LONG lock_quick_t; +typedef LONG lock_quick_type; #define lock_quick_init(lock) lock_basic_init(lock) #define lock_quick_destroy(lock) lock_basic_destroy(lock) #define lock_quick_lock(lock) lock_basic_lock(lock) @@ -227,14 +244,14 @@ #define lock_quick_unlock(lock) lock_basic_unlock(lock) /** Thread creation, create a default thread. */ -typedef HANDLE ub_thread_t; -void ub_thread_create(ub_thread_t* thr, void* (*func)(void*), void* arg); -ub_thread_t ub_thread_self(void); -void ub_thread_join(ub_thread_t thr); -typedef DWORD ub_thread_key_t; -void ub_thread_key_create(ub_thread_key_t* key, void* f); -void ub_thread_key_set(ub_thread_key_t key, void* v); -void* ub_thread_key_get(ub_thread_key_t key); +typedef HANDLE ub_thread_type; +void ub_thread_create(ub_thread_type* thr, void* (*func)(void*), void* arg); +ub_thread_type ub_thread_self(void); +void ub_thread_join(ub_thread_type thr); +typedef DWORD ub_thread_key_type; +void ub_thread_key_create(ub_thread_key_type* key, void* f); +void ub_thread_key_set(ub_thread_key_type key, void* v); +void* ub_thread_key_get(ub_thread_key_type key); #else /* we do not HAVE_SOLARIS_THREADS, PTHREADS or WINDOWS_THREADS */ @@ -241,7 +258,7 @@ /******************* NO THREADS ************************/ #define THREADS_DISABLED 1 /** In case there is no thread support, define locks to do nothing */ -typedef int lock_rw_t; +typedef int lock_rw_type; #define lock_rw_init(lock) /* nop */ #define lock_rw_destroy(lock) /* nop */ #define lock_rw_rdlock(lock) /* nop */ @@ -249,7 +266,7 @@ #define lock_rw_unlock(lock) /* nop */ /** define locks to do nothing */ -typedef int lock_basic_t; +typedef int lock_basic_type; #define lock_basic_init(lock) /* nop */ #define lock_basic_destroy(lock) /* nop */ #define lock_basic_lock(lock) /* nop */ @@ -256,7 +273,7 @@ #define lock_basic_unlock(lock) /* nop */ /** define locks to do nothing */ -typedef int lock_quick_t; +typedef int lock_quick_type; #define lock_quick_init(lock) /* nop */ #define lock_quick_destroy(lock) /* nop */ #define lock_quick_lock(lock) /* nop */ @@ -263,7 +280,7 @@ #define lock_quick_unlock(lock) /* nop */ /** Thread creation, threads do not exist */ -typedef pid_t ub_thread_t; +typedef pid_t ub_thread_type; /** ub_thread_create is simulated with fork (extremely heavy threads, * with no shared memory). */ #define ub_thread_create(thr, func, arg) \ @@ -270,9 +287,9 @@ ub_thr_fork_create(thr, func, arg) #define ub_thread_self() getpid() #define ub_thread_join(thread) ub_thr_fork_wait(thread) -void ub_thr_fork_wait(ub_thread_t thread); -void ub_thr_fork_create(ub_thread_t* thr, void* (*func)(void*), void* arg); -typedef void* ub_thread_key_t; +void ub_thr_fork_wait(ub_thread_type thread); +void ub_thr_fork_create(ub_thread_type* thr, void* (*func)(void*), void* arg); +typedef void* ub_thread_key_type; #define ub_thread_key_create(key, f) (*(key)) = NULL #define ub_thread_key_set(key, v) (key) = (v) #define ub_thread_key_get(key) (key) --- contrib/unbound/util/log.c.orig +++ contrib/unbound/util/log.c @@ -61,25 +61,24 @@ #endif /* default verbosity */ -enum verbosity_value verbosity = 0; +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_t logkey; +static ub_thread_key_type logkey; #ifndef THREADS_DISABLED /** pthread mutex to protect FILE* */ -static lock_quick_t log_lock; +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 */ -/** time to print in log, if NULL, use time(2) */ -static time_t* log_now = NULL; /** print time in UTC or in secondsfrom1970 */ static int log_time_asc = 0; @@ -90,21 +89,25 @@ if(!key_created) { key_created = 1; ub_thread_key_create(&logkey, NULL); - lock_quick_init(&log_lock); + lock_basic_init(&log_lock); } - lock_quick_lock(&log_lock); + lock_basic_lock(&log_lock); if(logfile #if defined(HAVE_SYSLOG_H) || defined(UB_ON_WINDOWS) || logging_to_syslog #endif ) { - lock_quick_unlock(&log_lock); /* verbose() needs the lock */ + 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_quick_lock(&log_lock); + lock_basic_lock(&log_lock); } - if(logfile && logfile != stderr) - fclose(logfile); + 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(); @@ -113,9 +116,11 @@ 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 */ - openlog(ident, LOG_NDELAY, LOG_DAEMON); + /* 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_quick_unlock(&log_lock); + lock_basic_unlock(&log_lock); return; } #elif defined(UB_ON_WINDOWS) @@ -124,13 +129,13 @@ } if(use_syslog) { logging_to_syslog = 1; - lock_quick_unlock(&log_lock); + lock_basic_unlock(&log_lock); return; } #endif /* HAVE_SYSLOG_H */ if(!filename || !filename[0]) { logfile = stderr; - lock_quick_unlock(&log_lock); + lock_basic_unlock(&log_lock); return; } /* open the file for logging */ @@ -139,7 +144,7 @@ filename += strlen(chrootdir); f = fopen(filename, "a"); if(!f) { - lock_quick_unlock(&log_lock); + lock_basic_unlock(&log_lock); log_err("Could not open logfile %s: %s", filename, strerror(errno)); return; @@ -149,14 +154,14 @@ setvbuf(f, NULL, (int)_IOLBF, 0); #endif logfile = f; - lock_quick_unlock(&log_lock); + lock_basic_unlock(&log_lock); } void log_file(FILE *f) { - lock_quick_lock(&log_lock); + lock_basic_lock(&log_lock); logfile = f; - lock_quick_unlock(&log_lock); + lock_basic_unlock(&log_lock); } void log_thread_set(int* num) @@ -177,16 +182,40 @@ ident = id; } -void log_set_time(time_t* t) +void log_ident_set_default(const char* id) { - log_now = t; + default_ident = id; } +void log_ident_revert_to_default() +{ + 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) @@ -235,14 +264,12 @@ return; } #endif /* HAVE_SYSLOG_H */ - lock_quick_lock(&log_lock); + lock_basic_lock(&log_lock); if(!logfile) { - lock_quick_unlock(&log_lock); + lock_basic_unlock(&log_lock); return; } - if(log_now) - now = (time_t)*log_now; - else now = (time_t)time(NULL); + 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) { @@ -264,7 +291,7 @@ /* line buffering does not work on windows */ fflush(logfile); #endif - lock_quick_unlock(&log_lock); + lock_basic_unlock(&log_lock); } /** @@ -376,6 +403,24 @@ 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) --- contrib/unbound/util/log.h.orig +++ contrib/unbound/util/log.h @@ -107,19 +107,30 @@ int log_thread_get(void); /** - * Set identity to print, default is 'unbound'. + * Set identity to print, default is 'unbound'. * @param id: string to print. Name of executable. */ void log_ident_set(const char* id); /** - * Set the time value to print in log entries. - * @param t: the point is copied and used to find the time. - * if NULL, time(2) is used. + * Set default identity to print, default is 'unbound'. + * @param id: string to print. Name of executable. */ -void log_set_time(time_t* t); +void log_ident_set_default(const char* id); /** + * Revert identity to print, back to the recorded default value. + */ +void log_ident_revert_to_default(void); + +/** + * Set identity to print if there is an identity, otherwise + * set the default. + * @param identity: the identity to set. + */ +void log_ident_set_or_default(const char* identity); + +/** * Set if the time value is printed ascii or decimal in log entries. * @param use_asc: if true, ascii is printed, otherwise decimal. * If the conversion fails or you have no time functions, @@ -127,6 +138,9 @@ */ void log_set_time_asc(int use_asc); +/** get log lock */ +void* log_get_lock(void); + /** * Log informational message. * Pass printf formatted arguments. No trailing newline is needed. @@ -158,6 +172,20 @@ void log_hex(const char* msg, void* data, size_t length); /** + * Log query. + * Pass printf formatted arguments. No trailing newline is needed. + * @param format: printf-style format string. Arguments follow. + */ +void log_query(const char* format, ...) ATTR_FORMAT(printf, 1, 2); + +/** + * Log reply. + * Pass printf formatted arguments. No trailing newline is needed. + * @param format: printf-style format string. Arguments follow. + */ +void log_reply(const char* format, ...) ATTR_FORMAT(printf, 1, 2); + +/** * Easy alternative for log_hex, takes a sldns_buffer. * @param level: verbosity level for this message, compared to global * verbosity setting. @@ -171,7 +199,7 @@ * Pass printf formatted arguments. No trailing newline is needed. * @param format: printf-style format string. Arguments follow. */ -void fatal_exit(const char* format, ...) ATTR_FORMAT(printf, 1, 2); +void fatal_exit(const char* format, ...) ATTR_FORMAT(printf, 1, 2) ATTR_NORETURN; /** * va_list argument version of log_info. @@ -186,11 +214,17 @@ * an assertion that is thrown to the logfile. */ #ifdef UNBOUND_DEBUG +#ifdef __clang_analyzer__ +/* clang analyzer needs to know that log_assert is an assertion, otherwise + * it could complain about the nullptr the assert is guarding against. */ +#define log_assert(x) assert(x) +#else # define log_assert(x) \ do { if(!(x)) \ fatal_exit("%s:%d: %s: assertion %s failed", \ __FILE__, __LINE__, __func__, #x); \ } while(0); +#endif #else # define log_assert(x) /*nothing*/ #endif --- contrib/unbound/util/mini_event.c.orig +++ contrib/unbound/util/mini_event.c @@ -41,6 +41,7 @@ */ #include "config.h" +#include "util/mini_event.h" #ifdef HAVE_TIME_H #include #endif @@ -48,7 +49,6 @@ #if defined(USE_MINI_EVENT) && !defined(USE_WINSOCK) #include -#include "util/mini_event.h" #include "util/fptr_wlist.h" /** compare events in tree, based on timevalue, ptr for uniqueness */ @@ -147,7 +147,7 @@ wait->tv_sec = (time_t)-1; #endif - while((rbnode_t*)(p = (struct event*)rbtree_first(base->times)) + while((rbnode_type*)(p = (struct event*)rbtree_first(base->times)) !=RBTREE_NULL) { #ifndef S_SPLINT_S if(p->ev_timeout.tv_sec > now->tv_sec || @@ -313,7 +313,7 @@ struct timeval *now = ev->ev_base->time_tv; ev->ev_timeout.tv_sec = tv->tv_sec + now->tv_sec; ev->ev_timeout.tv_usec = tv->tv_usec + now->tv_usec; - while(ev->ev_timeout.tv_usec > 1000000) { + while(ev->ev_timeout.tv_usec >= 1000000) { ev->ev_timeout.tv_usec -= 1000000; ev->ev_timeout.tv_sec++; } --- contrib/unbound/util/mini_event.h.orig +++ contrib/unbound/util/mini_event.h @@ -96,7 +96,7 @@ struct event_base { /** sorted by timeout (absolute), ptr */ - rbtree_t* times; + rbtree_type* times; /** array of 0 - maxfd of ptr to event for it */ struct event** fds; /** max fd in use */ @@ -128,7 +128,7 @@ */ struct event { /** node in timeout rbtree */ - rbnode_t node; + rbnode_type node; /** is event already added */ int added; --- contrib/unbound/util/module.c.orig +++ contrib/unbound/util/module.c @@ -39,6 +39,7 @@ #include "config.h" #include "util/module.h" +#include "sldns/wire2str.h" const char* strextstate(enum module_ext_state s) @@ -69,3 +70,179 @@ } return "bad_event_value"; } + +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; iedns_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; iedns_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; iedns_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; iedns_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; iedns_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; + } +} --- contrib/unbound/util/module.h.orig +++ contrib/unbound/util/module.h @@ -166,6 +166,9 @@ 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; @@ -174,11 +177,147 @@ 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 5 +#define MAX_MODULE 16 +/** Maximum number of known edns options */ +#define MAX_KNOWN_EDNS_OPTS 256 + +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, 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. */ @@ -202,10 +341,7 @@ * 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 qname: query name. (host order) - * @param qnamelen: length in bytes of qname, including trailing 0. - * @param qtype: query type. (host order) - * @param qclass: query class. (host order) + * @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. @@ -214,12 +350,13 @@ * 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 opt_list: set these EDNS options on the outgoing packet. - * or NULL if none (the list is deep-copied). * @param addr: where to. * @param addrlen: length of addr. * @param zone: delegation point name. * @param zonelen: length of zone name. + * @param ssl_upstream: use SSL for upstream queries. + * @param tls_auth_name: if ssl_upstream, use this name with TLS + * authentication. * @param q: wich query state to reactivate upon return. * @return: false on failure (memory or socket related). no query was * sent. Or returns an outbound entry with qsent and qstate set. @@ -226,11 +363,11 @@ * This outbound_entry will be used on later module invocations * that involve this query (timeout, error or reply). */ - struct outbound_entry* (*send_query)(uint8_t* qname, size_t qnamelen, - uint16_t qtype, uint16_t qclass, uint16_t flags, int dnssec, - int want_dnssec, int nocaps, struct edns_option* opt_list, + struct outbound_entry* (*send_query)(struct query_info* qinfo, + uint16_t flags, int dnssec, int want_dnssec, int nocaps, struct sockaddr_storage* addr, socklen_t addrlen, - uint8_t* zone, size_t zonelen, struct module_qstate* q); + uint8_t* zone, size_t zonelen, int ssl_upstream, + char* tls_auth_name, struct module_qstate* q); /** * Detach-subqueries. @@ -268,9 +405,40 @@ 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 fresly created sub. + * delete the freshly created sub. * @param newq: the new subquery that is no longer needed. * It is removed. */ @@ -299,6 +467,10 @@ 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 */ @@ -322,6 +494,8 @@ 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; @@ -335,6 +509,20 @@ 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; + + /* Make every mesh state unique, do not aggregate mesh states. */ + int unique_mesh; }; /** @@ -392,7 +580,17 @@ 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 { @@ -433,6 +631,38 @@ 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; + + /** + * 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; + + /** whether the reply should be dropped */ + int is_drop; }; /** @@ -522,4 +752,110 @@ */ const char* strmodulevent(enum module_ev e); +/** + * 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 */ --- contrib/unbound/util/net_help.c.orig +++ contrib/unbound/util/net_help.c @@ -43,15 +43,21 @@ #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 #ifdef HAVE_OPENSSL_SSL_H #include +#include +#include #endif #ifdef HAVE_OPENSSL_ERR_H #include #endif +#ifdef USE_WINSOCK +#include +#endif /** max length of an IP address (the address portion) that we allow */ #define MAX_ADDR_STRLEN 128 /* characters */ @@ -64,6 +70,15 @@ /** rrset order roundrobin: default is no */ int RRSET_ROUNDROBIN = 0; +/** 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; + /* returns true is string addr is an ip6 specced address */ int str_is_ip6(const char* str) @@ -114,8 +129,9 @@ #elif defined(HAVE_IOCTLSOCKET) unsigned long off = 0; if(ioctlsocket(s, FIONBIO, &off) != 0) { - log_err("can't ioctlsocket FIONBIO off: %s", - wsa_strerror(WSAGetLastError())); + if(WSAGetLastError() != WSAEINVAL || verbosity >= 4) + log_err("can't ioctlsocket FIONBIO off: %s", + wsa_strerror(WSAGetLastError())); } #endif return 1; @@ -240,7 +256,8 @@ int netblockstrtoaddr(const char* str, int port, struct sockaddr_storage* addr, socklen_t* addrlen, int* net) { - char* s = NULL; + char buf[64]; + char* s; *net = (str_is_ip6(str)?128:32); if((s=strchr(str, '/'))) { if(atoi(s+1) > *net) { @@ -252,25 +269,186 @@ log_err("cannot parse netblock: '%s'", str); return 0; } - if(!(s = strdup(str))) { - log_err("out of memory"); - return 0; - } - *strchr(s, '/') = '\0'; + strlcpy(buf, str, sizeof(buf)); + s = strchr(buf, '/'); + if(s) *s = 0; + s = buf; } if(!ipstrtoaddr(s?s:str, port, addr, addrlen)) { - free(s); log_err("cannot parse ip address: '%s'", str); return 0; } if(s) { - free(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; + size_t 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); +} + +/** 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) { @@ -302,6 +480,37 @@ 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) { @@ -351,7 +560,7 @@ 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", str, err, dest); + else log_err("%s: %s for %s port %d", str, err, dest, (int)port); } int @@ -596,10 +805,19 @@ 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_get_error(), buf, sizeof(buf)); + 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)); @@ -607,9 +825,102 @@ } #else (void)str; + (void)err; #endif /* HAVE_SSL */ } +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 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((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 +#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 @@ -618,19 +929,20 @@ log_crypto_err("could not SSL_CTX_new"); return NULL; } - /* no SSLv2, SSLv3 because has defects */ - 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"); + if(!key || key[0] == 0) { + log_err("error: no tls-service-key file specified"); SSL_CTX_free(ctx); return NULL; } - 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"); + 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"); @@ -649,24 +961,7 @@ SSL_CTX_free(ctx); return NULL; } -#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 - + 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"); @@ -684,8 +979,98 @@ #endif } -void* connect_sslctx_create(char* key, char* pem, char* verifypem) +#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) { + /* return error if a cert fails */ + verbose(VERB_ALGO, "%s %d:%s", + "Unable to parse certificate in memory", + (int)ERR_get_error(), ERR_error_string(ERR_get_error(), NULL)); + 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) { + verbose(VERB_ALGO, "%s %d:%s\n", + "Error adding certificate", (int)ERR_get_error(), + ERR_error_string(ERR_get_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) { @@ -692,6 +1077,7 @@ 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"); @@ -698,6 +1084,7 @@ 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"); @@ -704,6 +1091,14 @@ 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"); + return 0; + } +#endif if(key && key[0]) { if(!SSL_CTX_use_certificate_chain_file(ctx, pem)) { log_err("error in client certificate %s", pem); @@ -724,17 +1119,30 @@ return NULL; } } - 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; + 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 + (void)wincert; +#endif SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); } return ctx; #else - (void)key; (void)pem; (void)verifypem; + (void)key; (void)pem; (void)verifypem; (void)wincert; return NULL; #endif } @@ -748,7 +1156,7 @@ return NULL; } SSL_set_accept_state(ssl); - (void)SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); + (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); @@ -770,7 +1178,7 @@ return NULL; } SSL_set_connect_state(ssl); - (void)SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); + (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); @@ -785,14 +1193,22 @@ #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_t *ub_openssl_locks = NULL; +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), @@ -810,14 +1226,18 @@ { #if defined(HAVE_SSL) && defined(OPENSSL_THREADS) && !defined(THREADS_DISABLED) && defined(CRYPTO_LOCK) && OPENSSL_VERSION_NUMBER < 0x10100000L int i; - ub_openssl_locks = (lock_basic_t*)reallocarray( - NULL, (size_t)CRYPTO_num_locks(), sizeof(lock_basic_t)); + 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; inext) { + 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, "r"); + 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; + 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; + } + return 1; +#else + (void)sslctx; + (void)tls_session_ticket_keys; + return 0; +#endif + +} + +int tls_session_ticket_key_cb(void *ATTR_UNUSED(sslctx), unsigned char* key_name, unsigned char* iv, void *evp_sctx, void *hmac_ctx, int enc) +{ +#ifdef HAVE_SSL + const EVP_MD *digest; + const EVP_CIPHER *cipher; + int evp_cipher_length; + digest = EVP_sha256(); + 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; + } +#ifndef 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; + } + +#ifndef 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 +} + +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; +} --- contrib/unbound/util/net_help.h.orig +++ contrib/unbound/util/net_help.h @@ -44,6 +44,7 @@ #include "util/log.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 @@ -73,10 +74,10 @@ /** set RCODE bits in uint16 flags */ #define FLAGS_SET_RCODE(f, r) (f = (((f) & 0xfff0) | (r))) -/** timeout in seconds for UDP queries to auth servers. */ -#define UDP_AUTH_QUERY_TIMEOUT 4 -/** timeout in seconds for TCP queries to auth servers. */ -#define TCP_AUTH_QUERY_TIMEOUT 30 +/** timeout in milliseconds for UDP queries to auth servers. */ +#define UDP_AUTH_QUERY_TIMEOUT 3000 +/** timeout in milliseconds for TCP queries to auth servers. */ +#define TCP_AUTH_QUERY_TIMEOUT 3000 /** Advertised version of EDNS capabilities */ #define EDNS_ADVERTISED_VERSION 0 /** Advertised size of EDNS capabilities */ @@ -99,6 +100,9 @@ /** 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. @@ -190,7 +194,7 @@ /** * Convert ip netblock (ip/netsize) string and port to sockaddr. - * *SLOW*, does a malloc internally to avoid writing over 'ip' string. + * 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. @@ -202,6 +206,29 @@ 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 tls-auth-name string is a pointer into the input string. + * Uses DNS port by default. + * @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); + +/** + * 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. @@ -213,6 +240,12 @@ 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. @@ -345,6 +378,26 @@ */ 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); + +/** + * 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. @@ -359,9 +412,11 @@ * @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); +void* connect_sslctx_create(char* key, char* pem, char* verifypem, int wincert); /** * accept a new fd and wrap it in a BIO in SSL @@ -390,4 +445,45 @@ */ 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); + +/** + * callback TLS session ticket encrypt and decrypt + * For use with SSL_CTX_set_tlsext_ticket_key_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 hmax context, function sets this. + * @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(void *s, unsigned char* key_name,unsigned char* iv, void *evp_ctx, void *hmac_ctx, int enc); + +/** 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); #endif /* NET_HELP_H */ --- contrib/unbound/util/netevent.c.orig +++ contrib/unbound/util/netevent.c @@ -43,10 +43,14 @@ #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 "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_OPENSSL_SSL_H #include #endif @@ -80,10 +84,11 @@ # endif #endif -/** The TCP reading or writing query timeout in milliseconds */ +/** The TCP writing query timeout in milliseconds */ #define TCP_QUERY_TIMEOUT 120000 -/** The TCP timeout in msec for fast queries, above half are used */ -#define TCP_QUERY_TIMEOUT_FAST 200 +/** 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 */ @@ -146,7 +151,8 @@ /** 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, - comm_point_callback_t* callback, void* callback_arg); + struct sldns_buffer* spoolbuf, comm_point_callback_type* callback, + void* callback_arg); /* -------- End of local definitions -------- */ @@ -172,7 +178,7 @@ } ub_comm_base_now(b); ub_get_event_sys(b->eb->base, &evnm, &evsys, &evmethod); - verbose(VERB_ALGO, "%s %s user %s method.", evnm, evsys, evmethod); + verbose(VERB_ALGO, "%s %s uses %s method.", evnm, evsys, evmethod); return b; } @@ -298,6 +304,12 @@ # endif ) && verbosity < VERB_DETAIL) 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( @@ -647,7 +659,7 @@ (void)comm_point_send_udp_msg_if(rep.c, rep.c->buffer, (struct sockaddr*)&rep.addr, rep.addrlen, &rep); } - if(rep.c->fd == -1) /* commpoint closed */ + if(!rep.c || rep.c->fd == -1) /* commpoint closed */ break; } #else @@ -654,7 +666,7 @@ (void)fd; (void)event; (void)arg; - fatal_exit("recvmsg: No support for IPV6_PKTINFO. " + fatal_exit("recvmsg: No support for IPV6_PKTINFO; IP_PKTINFO or IP_RECVDSTADDR. " "Please disable interface-automatic"); #endif /* AF_INET6 && IPV6_PKTINFO && HAVE_RECVMSG */ } @@ -665,6 +677,7 @@ 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); @@ -701,10 +714,15 @@ 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(rep.c, rep.c->buffer, +#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.addr, rep.addrlen); } - if(rep.c->fd != fd) /* commpoint closed to -1 or reused for + if(!rep.c || rep.c->fd != fd) /* commpoint closed to -1 or reused for another UDP port. Note rep.c cannot be reused with TCP fd. */ break; } @@ -714,18 +732,39 @@ static void setup_tcp_handler(struct comm_point* c, int fd, int cur, int max) { + int handler_usage; log_assert(c->type == comm_tcp); 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_timeout_msec = TCP_QUERY_TIMEOUT; /* 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(cur > max/2) - c->tcp_timeout_msec = TCP_QUERY_TIMEOUT_FAST; - comm_point_start_listening(c, fd, c->tcp_timeout_msec); + /* 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, + c->tcp_timeout_msec < TCP_QUERY_TIMEOUT_MINIMUM + ? TCP_QUERY_TIMEOUT_MINIMUM + : c->tcp_timeout_msec); } void comm_base_handle_slow_accept(int ATTR_UNUSED(fd), @@ -746,7 +785,12 @@ { 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. */ @@ -809,7 +853,19 @@ #endif 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; } @@ -817,20 +873,21 @@ static long win_bio_cb(BIO *b, int oper, const char* ATTR_UNUSED(argp), int ATTR_UNUSED(argi), long argl, long retvalue) { + 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"), - WSAGetLastError()==WSAEWOULDBLOCK?"wsawb":""); + 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(WSAGetLastError() == WSAEWOULDBLOCK) + 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(WSAGetLastError() == WSAEWOULDBLOCK) + if(wsa_err == WSAEWOULDBLOCK) ub_winsock_tcp_wouldblock((struct ub_event*) BIO_get_callback_arg(b), UB_EV_WRITE); } @@ -869,6 +926,14 @@ } /* 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 = 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.addr, @@ -932,8 +997,12 @@ 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); + if(c->tcp_req_info) { + tcp_req_info_handle_writedone(c->tcp_req_info); + } else { + comm_point_stop_listening(c); + comm_point_start_listening(c, -1, c->tcp_timeout_msec); + } } /** do the callback when reading is done */ @@ -945,14 +1014,73 @@ if(c->tcp_do_toggle_rw) c->tcp_is_reading = 0; c->tcp_byte_count = 0; - 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, c->tcp_timeout_msec); + 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, c->tcp_timeout_msec); + } } } +#ifdef HAVE_SSL +/** log certificate details */ +static void +log_cert(unsigned level, const char* str, X509* 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, 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) { + verbose(level, "%s: \n%s", str, pp); + } + BIO_free(bio); +} +#endif /* HAVE_SSL */ + +#ifdef HAVE_SSL +/** true if the ssl handshake error has to be squelched from the logs */ +static int +squelch_err_ssl_handshake(unsigned long err) +{ + if(verbosity >= VERB_QUERY) + return 0; /* only squelch on low verbosity */ + /* this is very specific, we could filter on ERR_GET_REASON() + * (the third element in ERR_PACK) */ + if(err == ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_GET_RECORD, SSL_R_HTTPS_PROXY_REQUEST) || + err == ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_GET_RECORD, SSL_R_HTTP_REQUEST) || + err == ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_GET_RECORD, SSL_R_WRONG_VERSION_NUMBER) || + err == ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_READ_BYTES, SSL_R_SSLV3_ALERT_BAD_CERTIFICATE) +#ifdef SSL_F_TLS_POST_PROCESS_CLIENT_HELLO + || err == ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_POST_PROCESS_CLIENT_HELLO, SSL_R_NO_SHARED_CIPHER) +#endif +#ifdef SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO + || err == ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO, SSL_R_UNKNOWN_PROTOCOL) + || err == ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO, SSL_R_UNSUPPORTED_PROTOCOL) +# ifdef SSL_R_VERSION_TOO_LOW + || err == ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO, SSL_R_VERSION_TOO_LOW) +# endif +#endif + ) + return 1; + return 0; +} +#endif /* HAVE_SSL */ + /** continue ssl handshake */ #ifdef HAVE_SSL static int @@ -992,20 +1120,74 @@ 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(errno != 0) log_err("SSL_handshake syscall: %s", strerror(errno)); return 0; } else { - log_crypto_err("ssl handshake failed"); - log_addr(1, "ssl handshake failed", &c->repinfo.addr, - c->repinfo.addrlen); + unsigned long err = ERR_get_error(); + if(!squelch_err_ssl_handshake(err)) { + log_crypto_err_code("ssl handshake failed", err); + log_addr(VERB_OPS, "ssl handshake failed", &c->repinfo.addr, + c->repinfo.addrlen); + } return 0; } } /* this is where peer verification could take place */ - log_addr(VERB_ALGO, "SSL DNS connection", &c->repinfo.addr, - c->repinfo.addrlen); + if((SSL_get_verify_mode(c->ssl)&SSL_VERIFY_PEER)) { + /* verification */ + if(SSL_get_verify_result(c->ssl) == X509_V_OK) { + X509* x = SSL_get_peer_certificate(c->ssl); + if(!x) { + log_addr(VERB_ALGO, "SSL connection failed: " + "no certificate", + &c->repinfo.addr, c->repinfo.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.addr, + c->repinfo.addrlen); + } else { +#endif + log_addr(VERB_ALGO, "SSL connection " + "authenticated", &c->repinfo.addr, + c->repinfo.addrlen); +#ifdef HAVE_SSL_GET0_PEERNAME + } +#endif + X509_free(x); + } else { + X509* x = SSL_get_peer_certificate(c->ssl); + if(x) { + log_cert(VERB_ALGO, "peer certificate", x); + X509_free(x); + } + log_addr(VERB_ALGO, "SSL connection failed: " + "failed to authenticate", + &c->repinfo.addr, c->repinfo.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.addr, + c->repinfo.addrlen); + } /* setup listen rw correctly */ if(c->tcp_is_reading) { @@ -1039,8 +1221,11 @@ 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) { + ub_winsock_tcp_wouldblock(c->ev->ev, UB_EV_READ); return 1; /* read more later */ } else if(want == SSL_ERROR_WANT_WRITE) { c->ssl_shake_state = comm_ssl_shake_hs_write; @@ -1047,6 +1232,10 @@ 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)); @@ -1056,7 +1245,7 @@ return 0; } c->tcp_byte_count += r; - if(c->tcp_byte_count != sizeof(uint16_t)) + if(c->tcp_byte_count < sizeof(uint16_t)) return 1; if(sldns_buffer_read_u16_at(c->buffer, 0) > sldns_buffer_capacity(c->buffer)) { @@ -1069,33 +1258,42 @@ 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)); } - 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) { - if(errno != 0) - log_err("SSL_read syscall: %s", - strerror(errno)); + 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) { + ub_winsock_tcp_wouldblock(c->ev->ev, UB_EV_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"); return 0; } - log_crypto_err("could not SSL_read"); - return 0; + sldns_buffer_skip(c->buffer, (ssize_t)r); } - sldns_buffer_skip(c->buffer, (ssize_t)r); if(sldns_buffer_remaining(c->buffer) <= 0) { tcp_callback_reader(c); } @@ -1119,24 +1317,44 @@ return 1; } /* ignore return, if fails we may simply block */ - (void)SSL_set_mode(c->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE); + (void)SSL_set_mode(c->ssl, (long)SSL_MODE_ENABLE_PARTIAL_WRITE); if(c->tcp_byte_count < sizeof(uint16_t)) { uint16_t len = htons(sldns_buffer_limit(c->buffer)); ERR_clear_error(); - r = SSL_write(c->ssl, - (void*)(((uint8_t*)&len)+c->tcp_byte_count), - (int)(sizeof(uint16_t)-c->tcp_byte_count)); + 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_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) { + ub_winsock_tcp_wouldblock(c->ev->ev, UB_EV_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)); @@ -1164,12 +1382,17 @@ if(want == SSL_ERROR_ZERO_RETURN) { return 0; /* closed */ } else if(want == SSL_ERROR_WANT_READ) { - c->ssl_shake_state = comm_ssl_shake_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) { + ub_winsock_tcp_wouldblock(c->ev->ev, UB_EV_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)); @@ -1220,9 +1443,11 @@ /* read length bytes */ r = recv(fd,(void*)sldns_buffer_at(c->buffer,c->tcp_byte_count), sizeof(uint16_t)-c->tcp_byte_count, 0); - if(r == 0) + 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) { + } else if(r == -1) { #ifndef USE_WINSOCK if(errno == EINTR || errno == EAGAIN) return 1; @@ -1271,6 +1496,8 @@ r = recv(fd, (void*)sldns_buffer_current(c->buffer), sldns_buffer_remaining(c->buffer), 0); 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) { #ifndef USE_WINSOCK @@ -1310,7 +1537,13 @@ 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) return 0; log_assert(fd != -1); @@ -1364,7 +1597,7 @@ 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(sldns_buffer_limit(c->buffer)); + uint16_t len = htons(sldns_buffer_limit(buffer)); struct msghdr msg; struct iovec iov[2]; c->tcp_do_fastopen = 0; @@ -1371,10 +1604,9 @@ memset(&msg, 0, sizeof(msg)); 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(c->buffer); - iov[1].iov_len = sldns_buffer_limit(c->buffer); + iov[1].iov_base = sldns_buffer_begin(buffer); + iov[1].iov_len = sldns_buffer_limit(buffer); log_assert(iov[0].iov_len > 0); - log_assert(iov[1].iov_len > 0); msg.msg_name = &c->repinfo.addr; msg.msg_namelen = c->repinfo.addrlen; msg.msg_iov = iov; @@ -1383,7 +1615,7 @@ if (r == -1) { #if defined(EINPROGRESS) && defined(EWOULDBLOCK) /* Handshake is underway, maybe because no TFO cookie available. - Come back to write the messsage*/ + Come back to write the message*/ if(errno == EINPROGRESS || errno == EWOULDBLOCK) return 1; #endif @@ -1390,19 +1622,41 @@ if(errno == EINTR || errno == EAGAIN) return 1; /* Not handling EISCONN here as shouldn't ever hit that case.*/ - if(errno != 0 && verbosity < 2) + if(errno != EPIPE && errno != 0 && verbosity < 2) return 0; /* silence lots of chatter in the logs */ - else if(errno != 0) + if(errno != EPIPE && errno != 0) { log_err_addr("tcp sendmsg", strerror(errno), &c->repinfo.addr, c->repinfo.addrlen); - return 0; + return 0; + } + /* fallthrough to nonFASTOPEN + * (MSG_FASTOPEN on Linux 3 produces EPIPE) + * we need to perform connect() */ + if(connect(fd, (struct sockaddr *)&c->repinfo.addr, c->repinfo.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.addr, c->repinfo.addrlen)) { + log_err_addr("outgoing tcp: connect after EPIPE for fastopen", + strerror(errno), &c->repinfo.addr, c->repinfo.addrlen); + } + return 0; + } + } 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 - + sldns_buffer_set_position(buffer, c->tcp_byte_count - sizeof(uint16_t)); - if(sldns_buffer_remaining(c->buffer) == 0) { + if(sldns_buffer_remaining(buffer) == 0) { tcp_callback_writer(c); return 1; } @@ -1411,15 +1665,14 @@ #endif /* USE_MSG_FASTOPEN */ if(c->tcp_byte_count < sizeof(uint16_t)) { - uint16_t len = htons(sldns_buffer_limit(c->buffer)); + uint16_t len = htons(sldns_buffer_limit(buffer)); #ifdef HAVE_WRITEV struct iovec iov[2]; 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(c->buffer); - iov[1].iov_len = sldns_buffer_limit(c->buffer); + iov[1].iov_base = sldns_buffer_begin(buffer); + iov[1].iov_len = sldns_buffer_limit(buffer); log_assert(iov[0].iov_len > 0); - log_assert(iov[1].iov_len > 0); r = writev(fd, iov, 2); #else /* HAVE_WRITEV */ r = send(fd, (void*)(((uint8_t*)&len)+c->tcp_byte_count), @@ -1433,6 +1686,10 @@ #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.addr, c->repinfo.addrlen); @@ -1450,6 +1707,8 @@ 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.addr, c->repinfo.addrlen); @@ -1459,20 +1718,24 @@ 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 - + sldns_buffer_set_position(buffer, c->tcp_byte_count - sizeof(uint16_t)); - if(sldns_buffer_remaining(c->buffer) == 0) { + if(sldns_buffer_remaining(buffer) == 0) { tcp_callback_writer(c); return 1; } } - log_assert(sldns_buffer_remaining(c->buffer) > 0); - r = send(fd, (void*)sldns_buffer_current(c->buffer), - sldns_buffer_remaining(c->buffer), 0); + log_assert(sldns_buffer_remaining(buffer) > 0); + 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 log_err_addr("tcp send r", strerror(errno), &c->repinfo.addr, c->repinfo.addrlen); #else @@ -1482,14 +1745,16 @@ 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 r", wsa_strerror(WSAGetLastError()), &c->repinfo.addr, c->repinfo.addrlen); #endif return 0; } - sldns_buffer_skip(c->buffer, r); + sldns_buffer_skip(buffer, r); - if(sldns_buffer_remaining(c->buffer) == 0) { + if(sldns_buffer_remaining(buffer) == 0) { tcp_callback_writer(c); } @@ -1496,6 +1761,29 @@ return 1; } +/** read again to drain buffers when there could be more to read */ +static void +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; + } + } +} + void comm_point_tcp_handle_callback(int fd, short event, void* arg) { @@ -1503,7 +1791,39 @@ log_assert(c->type == comm_tcp); ub_comm_base_now(c->ev->base); +#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) { + int has_tcpq = (c->tcp_req_info != NULL); if(!comm_point_tcp_handle_read(fd, c, 0)) { reclaim_tcp_handler(c); if(!c->tcp_do_close) { @@ -1513,9 +1833,12 @@ NETEVENT_CLOSED, NULL); } } + if(has_tcpq && c->tcp_req_info && c->tcp_req_info->read_again) + tcp_req_info_read_again(fd, c); return; } if(event&UB_EV_WRITE) { + int has_tcpq = (c->tcp_req_info != NULL); if(!comm_point_tcp_handle_write(fd, c)) { reclaim_tcp_handler(c); if(!c->tcp_do_close) { @@ -1525,11 +1848,625 @@ NETEVENT_CLOSED, NULL); } } + if(has_tcpq && c->tcp_req_info && c->tcp_req_info->read_again) + tcp_req_info_read_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) { + 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"); + return 0; + } + 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), 0); + if(r == 0) { + return 0; + } else if(r == -1) { +#ifndef USE_WINSOCK + if(errno == EINTR || errno == EAGAIN) + return 1; + log_err_addr("read (in http r)", strerror(errno), + &c->repinfo.addr, c->repinfo.addrlen); +#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; + } + log_err_addr("read (in http r)", + wsa_strerror(WSAGetLastError()), + &c->repinfo.addr, c->repinfo.addrlen); +#endif + return 0; + } + 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); ibuffer); + 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 */ +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) - c->http_stored; + 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; + } + 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; + } + /* call callback with this data amount, then + * wait for more */ + 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 nonchunked 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; + 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; +} + +/** + * 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; + /* read more data */ + if(c->ssl) { + if(!ssl_http_read_more(c)) + return 0; + } else { + if(!http_read_more(fd, c)) + return 0; + } + + sldns_buffer_flip(c->buffer); + while(sldns_buffer_remaining(c->buffer) > 0) { + /* 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.addr, c->repinfo.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.addr, c->repinfo.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"); + 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; + log_err_addr("http send r", strerror(errno), + &c->repinfo.addr, c->repinfo.addrlen); +#else + if(WSAGetLastError() == WSAEINPROGRESS) + return 1; + if(WSAGetLastError() == WSAEWOULDBLOCK) { + ub_winsock_tcp_wouldblock(c->ev->ev, UB_EV_WRITE); + return 1; + } + log_err_addr("http send r", wsa_strerror(WSAGetLastError()), + &c->repinfo.addr, c->repinfo.addrlen); +#endif + return 0; + } + sldns_buffer_skip(c->buffer, r); + return 1; +} + +/** + * 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 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, "tcp took too long, dropped"); - reclaim_tcp_handler(c); + 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, @@ -1537,7 +2474,31 @@ } return; } - log_err("Ignored event %d for tcphdl.", event); + 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) @@ -1573,7 +2534,7 @@ struct comm_point* comm_point_create_udp(struct comm_base *base, int fd, sldns_buffer* buffer, - comm_point_callback_t* callback, void* callback_arg) + comm_point_callback_type* callback, void* callback_arg) { struct comm_point* c = (struct comm_point*)calloc(1, sizeof(struct comm_point)); @@ -1605,6 +2566,10 @@ #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; @@ -1628,7 +2593,7 @@ struct comm_point* comm_point_create_udp_ancil(struct comm_base *base, int fd, sldns_buffer* buffer, - comm_point_callback_t* callback, void* callback_arg) + comm_point_callback_type* callback, void* callback_arg) { struct comm_point* c = (struct comm_point*)calloc(1, sizeof(struct comm_point)); @@ -1655,6 +2620,10 @@ 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; @@ -1683,7 +2652,8 @@ static struct comm_point* comm_point_create_tcp_handler(struct comm_base *base, struct comm_point* parent, size_t bufsize, - comm_point_callback_t* callback, void* callback_arg) + struct sldns_buffer* spoolbuf, comm_point_callback_type* callback, + void* callback_arg) { struct comm_point* c = (struct comm_point*)calloc(1, sizeof(struct comm_point)); @@ -1714,6 +2684,10 @@ 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; @@ -1726,9 +2700,29 @@ #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; + 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; @@ -1740,6 +2734,9 @@ { 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; @@ -1748,8 +2745,10 @@ } struct comm_point* -comm_point_create_tcp(struct comm_base *base, int fd, int num, size_t bufsize, - comm_point_callback_t* callback, void* callback_arg) +comm_point_create_tcp(struct comm_base *base, int fd, int num, + int idle_timeout, struct tcl_list* tcp_conn_limit, size_t bufsize, + struct sldns_buffer* spoolbuf, comm_point_callback_type* callback, + void* callback_arg) { struct comm_point* c = (struct comm_point*)calloc(1, sizeof(struct comm_point)); @@ -1770,6 +2769,10 @@ 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; @@ -1789,6 +2792,10 @@ #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; evbits = UB_EV_READ | UB_EV_PERSIST; @@ -1808,7 +2815,7 @@ /* now prealloc the tcp handlers */ for(i=0; itcp_handlers[i] = comm_point_create_tcp_handler(base, - c, bufsize, callback, callback_arg); + c, bufsize, spoolbuf, callback, callback_arg); if(!c->tcp_handlers[i]) { comm_point_delete(c); return NULL; @@ -1820,7 +2827,7 @@ struct comm_point* comm_point_create_tcp_out(struct comm_base *base, size_t bufsize, - comm_point_callback_t* callback, void* callback_arg) + comm_point_callback_type* callback, void* callback_arg) { struct comm_point* c = (struct comm_point*)calloc(1, sizeof(struct comm_point)); @@ -1844,6 +2851,10 @@ 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; @@ -1857,6 +2868,10 @@ #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; @@ -1876,8 +2891,77 @@ } 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; + 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_t* callback, void* callback_arg) + comm_point_callback_type* callback, void* callback_arg) { struct comm_point* c = (struct comm_point*)calloc(1, sizeof(struct comm_point)); @@ -1914,6 +2998,10 @@ #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; /* ub_event stuff */ @@ -1938,7 +3026,7 @@ struct comm_point* comm_point_create_raw(struct comm_base* base, int fd, int writing, - comm_point_callback_t* callback, void* callback_arg) + comm_point_callback_type* callback, void* callback_arg) { struct comm_point* c = (struct comm_point*)calloc(1, sizeof(struct comm_point)); @@ -1970,6 +3058,10 @@ #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; /* ub_event stuff */ @@ -1999,12 +3091,21 @@ { if(!c) return; - if(c->fd != -1) + if(c->fd != -1) { if(ub_event_del(c->ev->ev) != 0) { log_err("could not event_del on close"); } + } + tcl_close_connection(c->tcl_addr); + if(c->tcp_req_info) + tcp_req_info_clear(c->tcp_req_info); /* close fd after removing from event lists, or epoll.. is messed up */ if(c->fd != -1 && !c->do_not_close) { + 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); + } verbose(VERB_ALGO, "close fd %d", c->fd); #ifndef USE_WINSOCK close(c->fd); @@ -2020,7 +3121,7 @@ { if(!c) return; - if(c->type == comm_tcp && c->ssl) { + if((c->type == comm_tcp || c->type == comm_http) && c->ssl) { #ifdef HAVE_SSL SSL_shutdown(c->ssl); SSL_free(c->ssl); @@ -2034,8 +3135,17 @@ free(c->tcp_handlers); } free(c->timeout); - if(c->type == comm_tcp || c->type == comm_local) + 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); + } + } ub_event_free(c->ev->ev); free(c->ev); free(c); @@ -2044,14 +3154,23 @@ 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, - repinfo->c->buffer, (struct sockaddr*)&repinfo->addr, + buffer, (struct sockaddr*)&repinfo->addr, repinfo->addrlen, repinfo); else - comm_point_send_udp_msg(repinfo->c, repinfo->c->buffer, + comm_point_send_udp_msg(repinfo->c, buffer, (struct sockaddr*)&repinfo->addr, repinfo->addrlen); #ifdef USE_DNSTAP if(repinfo->c->dtenv != NULL && @@ -2066,8 +3185,12 @@ dt_msg_send_client_response(repinfo->c->tcp_parent->dtenv, &repinfo->addr, repinfo->c->type, repinfo->c->buffer); #endif - comm_point_start_listening(repinfo->c, -1, - repinfo->c->tcp_timeout_msec); + if(repinfo->c->tcp_req_info) { + tcp_req_info_send_reply(repinfo->c->tcp_req_info); + } else { + comm_point_start_listening(repinfo->c, -1, + repinfo->c->tcp_timeout_msec); + } } } @@ -2076,10 +3199,12 @@ { if(!repinfo) return; - log_assert(repinfo && repinfo->c); + 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; reclaim_tcp_handler(repinfo->c); } @@ -2095,8 +3220,8 @@ void comm_point_start_listening(struct comm_point* c, int newfd, int msec) { - verbose(VERB_ALGO, "comm point start listening %d", - c->fd==-1?newfd:c->fd); + 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; @@ -2116,7 +3241,7 @@ c->timeout->tv_usec = (msec%1000)*1000; #endif /* S_SPLINT_S */ } - if(c->type == comm_tcp) { + 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_is_reading) ub_event_add_bits(c->ev->ev, UB_EV_READ); @@ -2160,8 +3285,15 @@ s = sizeof(*c) + sizeof(*c->ev); if(c->timeout) s += sizeof(*c->timeout); - if(c->type == comm_tcp || c->type == comm_local) + 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; imax_tcp_count; i++) --- contrib/unbound/util/netevent.h.orig +++ contrib/unbound/util/netevent.h @@ -60,9 +60,12 @@ #ifndef NET_EVENT_H #define NET_EVENT_H +#include "dnscrypt/dnscrypt.h" + struct sldns_buffer; struct comm_point; struct comm_reply; +struct tcl_list; struct ub_event_base; /* internal event notification data storage structure. */ @@ -71,7 +74,7 @@ struct internal_timer; /* A sub struct of the comm_timer super struct */ /** callback from communication point function type */ -typedef int comm_point_callback_t(struct comm_point*, void*, int, +typedef int comm_point_callback_type(struct comm_point*, void*, int, struct comm_reply*); /** to pass no_error to callback function */ @@ -82,6 +85,8 @@ #define NETEVENT_TIMEOUT -2 /** to pass fallback from capsforID to callback function; 0x20 failed */ #define NETEVENT_CAPSFAIL -3 +/** to pass done transfer to callback function; http file is complete */ +#define NETEVENT_DONE -4 /** timeout to slow accept calls when not possible, in msec. */ #define NETEVENT_SLOW_ACCEPT_TIME 2000 @@ -114,6 +119,13 @@ socklen_t addrlen; /** return type 0 (none), 4(IP4), 6(IP6) */ int srctype; + /* DnsCrypt context */ +#ifdef USE_DNSCRYPT + uint8_t client_nonce[crypto_box_HALF_NONCEBYTES]; + uint8_t nmkey[crypto_box_BEFORENMBYTES]; + const dnsccert *dnsc_cert; + int is_dnscrypted; +#endif /** the return source interface data */ union { #ifdef IPV6_PKTINFO @@ -124,9 +136,11 @@ #elif defined(IP_RECVDSTADDR) struct in_addr v4addr; #endif - } + } /** variable with return source data */ pktinfo; + /** max udp size for udp packets */ + size_t max_udp_size; }; /** @@ -190,6 +204,19 @@ comm_ssl_shake_hs_write } ssl_shake_state; + /* -------- HTTP ------- */ + /** Currently reading in http headers */ + int http_in_headers; + /** Currently reading in chunk headers, 0=not, 1=firstline, 2=unused + * (more lines), 3=trailer headers after chunk */ + int http_in_chunk_headers; + /** chunked transfer */ + int http_is_chunked; + /** http temp buffer (shared buffer for temporary work) */ + struct sldns_buffer* http_temp; + /** http stored content in buffer */ + size_t http_stored; + /* -------- dnstap ------- */ /** the dnstap environment */ struct dt_env* dtenv; @@ -202,6 +229,8 @@ comm_tcp_accept, /** TCP handler socket - handle byteperbyte readwrite. */ comm_tcp, + /** HTTP handler socket */ + comm_http, /** AF_UNIX socket - for internal commands. */ comm_local, /** raw - not DNS format - for pipe readers and writers */ @@ -228,14 +257,31 @@ /** timeout in msec for TCP wait times for this connection */ int tcp_timeout_msec; + /** if set, tcp keepalive is enabled on this connection */ + int tcp_keepalive; + /** if set, checks for pending error from nonblocking connect() call.*/ int tcp_check_nb_connect; + /** if set, check for connection limit on tcp accept. */ + struct tcl_list* tcp_conn_limit; + /** the entry for the connection. */ + struct tcl_addr* tcl_addr; + + /** the structure to keep track of open requests on this channel */ + struct tcp_req_info* tcp_req_info; + #ifdef USE_MSG_FASTOPEN /** used to track if the sendto() call should be done when using TFO. */ int tcp_do_fastopen; #endif +#ifdef USE_DNSCRYPT + /** Is this a dnscrypt channel */ + int dnscrypt; + /** encrypted buffer pointer. Either to perthread, or own buffer or NULL */ + struct sldns_buffer* dnscrypt_buffer; +#endif /** number of queries outstanding on this socket, used by * outside network for udp ports */ int inuse; @@ -264,7 +310,7 @@ For UDP this is done without changing the commpoint. In TCP it sets write state. */ - comm_point_callback_t* callback; + comm_point_callback_type* callback; /** argument to pass to callback. */ void *cb_arg; }; @@ -382,7 +428,7 @@ */ struct comm_point* comm_point_create_udp(struct comm_base* base, int fd, struct sldns_buffer* buffer, - comm_point_callback_t* callback, void* callback_arg); + comm_point_callback_type* callback, void* callback_arg); /** * Create an UDP with ancillary data comm point. Calls malloc. @@ -398,7 +444,7 @@ */ struct comm_point* comm_point_create_udp_ancil(struct comm_base* base, int fd, struct sldns_buffer* buffer, - comm_point_callback_t* callback, void* callback_arg); + comm_point_callback_type* callback, void* callback_arg); /** * Create a TCP listener comm point. Calls malloc. @@ -409,7 +455,11 @@ * @param fd: file descriptor of open TCP socket set to listen nonblocking. * @param num: becomes max_tcp_count, the routine allocates that * many tcp handler commpoints. + * @param idle_timeout: TCP idle timeout in ms. + * @param tcp_conn_limit: TCP connection limit info. * @param bufsize: size of buffer to create for handlers. + * @param spoolbuf: shared spool buffer for tcp_req_info structures. + * or NULL to not create those structures in the tcp handlers. * @param callback: callback function pointer for TCP handlers. * @param callback_arg: will be passed to your callback function. * @return: returns the TCP listener commpoint. You can find the @@ -418,8 +468,9 @@ * Inits timeout to NULL. All handlers are on the free list. */ struct comm_point* comm_point_create_tcp(struct comm_base* base, - int fd, int num, size_t bufsize, - comm_point_callback_t* callback, void* callback_arg); + int fd, int num, int idle_timeout, struct tcl_list* tcp_conn_limit, + size_t bufsize, struct sldns_buffer* spoolbuf, + comm_point_callback_type* callback, void* callback_arg); /** * Create an outgoing TCP commpoint. No file descriptor is opened, left at -1. @@ -430,9 +481,23 @@ * @return: the commpoint or NULL on error. */ struct comm_point* comm_point_create_tcp_out(struct comm_base* base, - size_t bufsize, comm_point_callback_t* callback, void* callback_arg); + size_t bufsize, comm_point_callback_type* callback, void* callback_arg); /** + * Create an outgoing HTTP commpoint. No file descriptor is opened, left at -1. + * @param base: in which base to alloc the commpoint. + * @param bufsize: size of buffer to create for handlers. + * @param callback: callback function pointer for the handler. + * @param callback_arg: will be passed to your callback function. + * @param temp: sldns buffer, shared between other http_out commpoints, for + * temporary data when performing callbacks. + * @return: the commpoint or NULL on error. + */ +struct comm_point* comm_point_create_http_out(struct comm_base* base, + size_t bufsize, comm_point_callback_type* callback, + void* callback_arg, struct sldns_buffer* temp); + +/** * Create commpoint to listen to a local domain file descriptor. * @param base: in which base to alloc the commpoint. * @param fd: file descriptor of open AF_UNIX socket set to listen nonblocking. @@ -443,7 +508,7 @@ */ struct comm_point* comm_point_create_local(struct comm_base* base, int fd, size_t bufsize, - comm_point_callback_t* callback, void* callback_arg); + comm_point_callback_type* callback, void* callback_arg); /** * Create commpoint to listen to a local domain pipe descriptor. @@ -456,7 +521,7 @@ */ struct comm_point* comm_point_create_raw(struct comm_base* base, int fd, int writing, - comm_point_callback_t* callback, void* callback_arg); + comm_point_callback_type* callback, void* callback_arg); /** * Close a comm point fd. @@ -650,6 +715,16 @@ /** * This routine is published for checks and tests, and is only used internally. + * handle libevent callback for tcp data comm point + * @param fd: file descriptor. + * @param event: event bits from libevent: + * EV_READ, EV_WRITE, EV_SIGNAL, EV_TIMEOUT. + * @param arg: the comm_point structure. + */ +void comm_point_http_handle_callback(int fd, short event, void* arg); + +/** + * This routine is published for checks and tests, and is only used internally. * handle libevent callback for timer comm. * @param fd: file descriptor (always -1). * @param event: event bits from libevent: --- contrib/unbound/util/random.c.orig +++ contrib/unbound/util/random.c @@ -78,16 +78,9 @@ */ #define MAX_VALUE 0x7fffffff -#if defined(HAVE_SSL) -void -ub_systemseed(unsigned int ATTR_UNUSED(seed)) -{ - /* arc4random_uniform does not need seeds, it gets kernel entropy */ -} - +#if defined(HAVE_SSL) || defined(HAVE_LIBBSD) struct ub_randstate* -ub_initstate(unsigned int ATTR_UNUSED(seed), - struct ub_randstate* ATTR_UNUSED(from)) +ub_initstate(struct ub_randstate* ATTR_UNUSED(from)) { struct ub_randstate* s = (struct ub_randstate*)malloc(1); if(!s) { @@ -119,13 +112,8 @@ int ready; }; -void ub_systemseed(unsigned int ATTR_UNUSED(seed)) +struct ub_randstate* ub_initstate(struct ub_randstate* ATTR_UNUSED(from)) { -} - -struct ub_randstate* ub_initstate(unsigned int ATTR_UNUSED(seed), - struct ub_randstate* ATTR_UNUSED(from)) -{ struct ub_randstate* s = (struct ub_randstate*)calloc(1, sizeof(*s)); if(!s) { log_err("malloc failure in random init"); @@ -140,7 +128,9 @@ /* random 31 bit value. */ SECStatus s = PK11_GenerateRandom((unsigned char*)&x, (int)sizeof(x)); if(s != SECSuccess) { - log_err("PK11_GenerateRandom error: %s", + /* unbound needs secure randomness for randomized + * ID bits and port numbers in packets to upstream servers */ + fatal_exit("PK11_GenerateRandom error: %s", PORT_ErrorToString(PORT_GetError())); } return x & MAX_VALUE; @@ -157,18 +147,8 @@ int seeded; }; -void ub_systemseed(unsigned int ATTR_UNUSED(seed)) +struct ub_randstate* ub_initstate(struct ub_randstate* ATTR_UNUSED(from)) { -/** - * We seed on init and not here, as we need the ctx to re-seed. - * This also means that re-seeding is not supported. - */ - log_err("Re-seeding not supported, generator untouched"); -} - -struct ub_randstate* ub_initstate(unsigned int seed, - struct ub_randstate* ATTR_UNUSED(from)) -{ struct ub_randstate* s = (struct ub_randstate*)calloc(1, sizeof(*s)); uint8_t buf[YARROW256_SEED_FILE_SIZE]; if(!s) { @@ -183,15 +163,10 @@ yarrow256_seed(&s->ctx, YARROW256_SEED_FILE_SIZE, buf); s->seeded = yarrow256_is_seeded(&s->ctx); } else { - /* Stretch the uint32 input seed and feed it to Yarrow */ - uint32_t v = seed; - size_t i; - for(i=0; i < (YARROW256_SEED_FILE_SIZE/sizeof(seed)); i++) { - memmove(buf+i*sizeof(seed), &v, sizeof(seed)); - v = v*seed + (uint32_t)i; - } - yarrow256_seed(&s->ctx, YARROW256_SEED_FILE_SIZE, buf); - s->seeded = yarrow256_is_seeded(&s->ctx); + log_err("nettle random(yarrow) cannot initialize, " + "getentropy failed: %s", strerror(errno)); + free(s); + return NULL; } return s; @@ -208,10 +183,10 @@ } return x & MAX_VALUE; } -#endif /* HAVE_SSL or HAVE_NSS or HAVE_NETTLE */ +#endif /* HAVE_SSL or HAVE_LIBBSD or HAVE_NSS or HAVE_NETTLE */ -#if defined(HAVE_NSS) || defined(HAVE_NETTLE) +#if defined(HAVE_NSS) || defined(HAVE_NETTLE) && !defined(HAVE_LIBBSD) long int ub_random_max(struct ub_randstate* state, long int x) { @@ -223,7 +198,7 @@ v = ub_random(state); return (v % x); } -#endif /* HAVE_NSS or HAVE_NETTLE */ +#endif /* HAVE_NSS or HAVE_NETTLE and !HAVE_LIBBSD */ void ub_randfree(struct ub_randstate* s) --- contrib/unbound/util/random.h.orig +++ contrib/unbound/util/random.h @@ -48,24 +48,13 @@ struct ub_randstate; /** - * Initialize the system randomness. Obtains entropy from the system - * before a chroot or privilege makes it unavailable. - * You do not have to call this, otherwise ub_initstate does so. - * @param seed: seed value to create state (if no good entropy is found). - */ -void ub_systemseed(unsigned int seed); - -/** * Initialize a random generator state for use - * @param seed: seed value to create state contents. - * (ignored for arc4random). * @param from: if not NULL, the seed is taken from this random structure. * can be used to seed random states via a parent-random-state that * is itself seeded with entropy. * @return new state or NULL alloc failure. */ -struct ub_randstate* ub_initstate(unsigned int seed, - struct ub_randstate* from); +struct ub_randstate* ub_initstate(struct ub_randstate* from); /** * Generate next random number from the state passed along. --- contrib/unbound/util/rbtree.c.orig +++ contrib/unbound/util/rbtree.c @@ -50,7 +50,7 @@ #define RED 1 /** the NULL node, global alloc */ -rbnode_t rbtree_null_node = { +rbnode_type rbtree_null_node = { RBTREE_NULL, /* Parent. */ RBTREE_NULL, /* Left. */ RBTREE_NULL, /* Right. */ @@ -59,13 +59,14 @@ }; /** rotate subtree left (to preserve redblack property) */ -static void rbtree_rotate_left(rbtree_t *rbtree, rbnode_t *node); +static void rbtree_rotate_left(rbtree_type *rbtree, rbnode_type *node); /** rotate subtree right (to preserve redblack property) */ -static void rbtree_rotate_right(rbtree_t *rbtree, rbnode_t *node); +static void rbtree_rotate_right(rbtree_type *rbtree, rbnode_type *node); /** Fixup node colours when insert happened */ -static void rbtree_insert_fixup(rbtree_t *rbtree, rbnode_t *node); +static void rbtree_insert_fixup(rbtree_type *rbtree, rbnode_type *node); /** Fixup node colours when delete happened */ -static void rbtree_delete_fixup(rbtree_t* rbtree, rbnode_t* child, rbnode_t* child_parent); +static void rbtree_delete_fixup(rbtree_type* rbtree, rbnode_type* child, + rbnode_type* child_parent); /* * Creates a new red black tree, initializes and returns a pointer to it. @@ -73,13 +74,13 @@ * Return NULL on failure. * */ -rbtree_t * +rbtree_type * rbtree_create (int (*cmpf)(const void *, const void *)) { - rbtree_t *rbtree; + rbtree_type *rbtree; /* Allocate memory for it */ - rbtree = (rbtree_t *) malloc(sizeof(rbtree_t)); + rbtree = (rbtree_type *) malloc(sizeof(rbtree_type)); if (!rbtree) { return NULL; } @@ -91,7 +92,7 @@ } void -rbtree_init(rbtree_t *rbtree, int (*cmpf)(const void *, const void *)) +rbtree_init(rbtree_type *rbtree, int (*cmpf)(const void *, const void *)) { /* Initialize it */ rbtree->root = RBTREE_NULL; @@ -104,9 +105,9 @@ * */ static void -rbtree_rotate_left(rbtree_t *rbtree, rbnode_t *node) +rbtree_rotate_left(rbtree_type *rbtree, rbnode_type *node) { - rbnode_t *right = node->right; + rbnode_type *right = node->right; node->right = right->left; if (right->left != RBTREE_NULL) right->left->parent = node; @@ -131,9 +132,9 @@ * */ static void -rbtree_rotate_right(rbtree_t *rbtree, rbnode_t *node) +rbtree_rotate_right(rbtree_type *rbtree, rbnode_type *node) { - rbnode_t *left = node->left; + rbnode_type *left = node->left; node->left = left->right; if (left->right != RBTREE_NULL) left->right->parent = node; @@ -154,9 +155,9 @@ } static void -rbtree_insert_fixup(rbtree_t *rbtree, rbnode_t *node) +rbtree_insert_fixup(rbtree_type *rbtree, rbnode_type *node) { - rbnode_t *uncle; + rbnode_type *uncle; /* While not at the root and need fixing... */ while (node != rbtree->root && node->parent->color == RED) { @@ -223,15 +224,15 @@ * Returns NULL on failure or the pointer to the newly added node * otherwise. */ -rbnode_t * -rbtree_insert (rbtree_t *rbtree, rbnode_t *data) +rbnode_type * +rbtree_insert (rbtree_type *rbtree, rbnode_type *data) { /* XXX Not necessary, but keeps compiler quiet... */ int r = 0; /* We start at the root of the tree */ - rbnode_t *node = rbtree->root; - rbnode_t *parent = RBTREE_NULL; + rbnode_type *node = rbtree->root; + rbnode_type *parent = RBTREE_NULL; fptr_ok(fptr_whitelist_rbtree_cmp(rbtree->cmp)); /* Lets find the new parent... */ @@ -276,10 +277,10 @@ * Searches the red black tree, returns the data if key is found or NULL otherwise. * */ -rbnode_t * -rbtree_search (rbtree_t *rbtree, const void *key) +rbnode_type * +rbtree_search (rbtree_type *rbtree, const void *key) { - rbnode_t *node; + rbnode_type *node; if (rbtree_find_less_equal(rbtree, key, &node)) { return node; @@ -295,13 +296,14 @@ } /** helpers for delete: swap node pointers */ -static void swap_np(rbnode_t** x, rbnode_t** y) +static void swap_np(rbnode_type** x, rbnode_type** y) { - rbnode_t* t = *x; *x = *y; *y = t; + rbnode_type* t = *x; *x = *y; *y = t; } /** Update parent pointers of child trees of 'parent' */ -static void change_parent_ptr(rbtree_t* rbtree, rbnode_t* parent, rbnode_t* old, rbnode_t* new) +static void change_parent_ptr(rbtree_type* rbtree, rbnode_type* parent, + rbnode_type* old, rbnode_type* new) { if(parent == RBTREE_NULL) { @@ -315,7 +317,8 @@ if(parent->right == old) parent->right = new; } /** Update parent pointer of a node 'child' */ -static void change_child_ptr(rbnode_t* child, rbnode_t* old, rbnode_t* new) +static void change_child_ptr(rbnode_type* child, rbnode_type* old, + rbnode_type* new) { if(child == RBTREE_NULL) return; log_assert(child->parent == old || child->parent == new); @@ -322,11 +325,11 @@ if(child->parent == old) child->parent = new; } -rbnode_t* -rbtree_delete(rbtree_t *rbtree, const void *key) +rbnode_type* +rbtree_delete(rbtree_type *rbtree, const void *key) { - rbnode_t *to_delete; - rbnode_t *child; + rbnode_type *to_delete; + rbnode_type *child; if((to_delete = rbtree_search(rbtree, key)) == 0) return 0; rbtree->count--; @@ -334,11 +337,11 @@ if(to_delete->left != RBTREE_NULL && to_delete->right != RBTREE_NULL) { /* swap with smallest from right subtree (or largest from left) */ - rbnode_t *smright = to_delete->right; + rbnode_type *smright = to_delete->right; while(smright->left != RBTREE_NULL) smright = smright->left; /* swap the smright and to_delete elements in the tree, - * but the rbnode_t is first part of user data struct + * but the rbnode_type is first part of user data struct * so cannot just swap the keys and data pointers. Instead * readjust the pointers left,right,parent */ @@ -400,9 +403,10 @@ return to_delete; } -static void rbtree_delete_fixup(rbtree_t* rbtree, rbnode_t* child, rbnode_t* child_parent) +static void rbtree_delete_fixup(rbtree_type* rbtree, rbnode_type* child, + rbnode_type* child_parent) { - rbnode_t* sibling; + rbnode_type* sibling; int go_up = 1; /* determine sibling to the node that is one-black short */ @@ -504,10 +508,11 @@ } int -rbtree_find_less_equal(rbtree_t *rbtree, const void *key, rbnode_t **result) +rbtree_find_less_equal(rbtree_type *rbtree, const void *key, + rbnode_type **result) { int r; - rbnode_t *node; + rbnode_type *node; log_assert(result); @@ -540,19 +545,19 @@ * Finds the first element in the red black tree * */ -rbnode_t * -rbtree_first (rbtree_t *rbtree) +rbnode_type * +rbtree_first (rbtree_type *rbtree) { - rbnode_t *node; + rbnode_type *node; for (node = rbtree->root; node->left != RBTREE_NULL; node = node->left); return node; } -rbnode_t * -rbtree_last (rbtree_t *rbtree) +rbnode_type * +rbtree_last (rbtree_type *rbtree) { - rbnode_t *node; + rbnode_type *node; for (node = rbtree->root; node->right != RBTREE_NULL; node = node->right); return node; @@ -562,10 +567,10 @@ * Returns the next node... * */ -rbnode_t * -rbtree_next (rbnode_t *node) +rbnode_type * +rbtree_next (rbnode_type *node) { - rbnode_t *parent; + rbnode_type *parent; if (node->right != RBTREE_NULL) { /* One right, then keep on going left... */ @@ -581,10 +586,10 @@ return node; } -rbnode_t * -rbtree_previous(rbnode_t *node) +rbnode_type * +rbtree_previous(rbnode_type *node) { - rbnode_t *parent; + rbnode_type *parent; if (node->left != RBTREE_NULL) { /* One left, then keep on going right... */ @@ -602,7 +607,7 @@ /** recursive descent traverse */ static void -traverse_post(void (*func)(rbnode_t*, void*), void* arg, rbnode_t* node) +traverse_post(void (*func)(rbnode_type*, void*), void* arg, rbnode_type* node) { if(!node || node == RBTREE_NULL) return; @@ -614,7 +619,8 @@ } void -traverse_postorder(rbtree_t* tree, void (*func)(rbnode_t*, void*), void* arg) +traverse_postorder(rbtree_type* tree, void (*func)(rbnode_type*, void*), + void* arg) { traverse_post(func, arg, tree->root); } --- contrib/unbound/util/rbtree.h.orig +++ contrib/unbound/util/rbtree.h @@ -45,40 +45,40 @@ /** * This structure must be the first member of the data structure in - * the rbtree. This allows easy casting between an rbnode_t and the + * the rbtree. This allows easy casting between an rbnode_type and the * user data (poor man's inheritance). */ -typedef struct rbnode_t rbnode_t; +typedef struct rbnode_type rbnode_type; /** - * The rbnode_t struct definition. + * The rbnode_type struct definition. */ -struct rbnode_t { +struct rbnode_type { /** parent in rbtree, RBTREE_NULL for root */ - rbnode_t *parent; + rbnode_type *parent; /** left node (smaller items) */ - rbnode_t *left; + rbnode_type *left; /** right node (larger items) */ - rbnode_t *right; + rbnode_type *right; /** pointer to sorting key */ - const void *key; + const void *key; /** colour of this node */ - uint8_t color; + uint8_t color; }; /** The nullpointer, points to empty node */ #define RBTREE_NULL &rbtree_null_node /** the global empty node */ -extern rbnode_t rbtree_null_node; +extern rbnode_type rbtree_null_node; /** An entire red black tree */ -typedef struct rbtree_t rbtree_t; +typedef struct rbtree_type rbtree_type; /** definition for tree struct */ -struct rbtree_t { +struct rbtree_type { /** The root of the red-black tree */ - rbnode_t *root; + rbnode_type *root; /** The number of the nodes in the tree */ - size_t count; + size_t count; /** * Key compare function. <0,0,>0 like strcmp. @@ -92,7 +92,7 @@ * @param cmpf: compare function (like strcmp) takes pointers to two keys. * @return: new tree, empty. */ -rbtree_t *rbtree_create(int (*cmpf)(const void *, const void *)); +rbtree_type *rbtree_create(int (*cmpf)(const void *, const void *)); /** * Init a new tree (malloced by caller) with given key compare function. @@ -99,7 +99,7 @@ * @param rbtree: uninitialised memory for new tree, returned empty. * @param cmpf: compare function (like strcmp) takes pointers to two keys. */ -void rbtree_init(rbtree_t *rbtree, int (*cmpf)(const void *, const void *)); +void rbtree_init(rbtree_type *rbtree, int (*cmpf)(const void *, const void *)); /** * Insert data into the tree. @@ -107,7 +107,7 @@ * @param data: element to insert. * @return: data ptr or NULL if key already present. */ -rbnode_t *rbtree_insert(rbtree_t *rbtree, rbnode_t *data); +rbnode_type *rbtree_insert(rbtree_type *rbtree, rbnode_type *data); /** * Delete element from tree. @@ -116,7 +116,7 @@ * @return: node that is now unlinked from the tree. User to delete it. * returns 0 if node not present */ -rbnode_t *rbtree_delete(rbtree_t *rbtree, const void *key); +rbnode_type *rbtree_delete(rbtree_type *rbtree, const void *key); /** * Find key in tree. Returns NULL if not found. @@ -124,7 +124,7 @@ * @param key: key that must match. * @return: node that fits or NULL. */ -rbnode_t *rbtree_search(rbtree_t *rbtree, const void *key); +rbnode_type *rbtree_search(rbtree_type *rbtree, const void *key); /** * Find, but match does not have to be exact. @@ -135,8 +135,8 @@ * @return: true if exact match in result. Else result points to <= element, * or NULL if key is smaller than the smallest key. */ -int rbtree_find_less_equal(rbtree_t *rbtree, const void *key, - rbnode_t **result); +int rbtree_find_less_equal(rbtree_type *rbtree, const void *key, + rbnode_type **result); /** * Returns first (smallest) node in the tree @@ -143,7 +143,7 @@ * @param rbtree: tree * @return: smallest element or NULL if tree empty. */ -rbnode_t *rbtree_first(rbtree_t *rbtree); +rbnode_type *rbtree_first(rbtree_type *rbtree); /** * Returns last (largest) node in the tree @@ -150,7 +150,7 @@ * @param rbtree: tree * @return: largest element or NULL if tree empty. */ -rbnode_t *rbtree_last(rbtree_t *rbtree); +rbnode_type *rbtree_last(rbtree_type *rbtree); /** * Returns next larger node in the tree @@ -157,7 +157,7 @@ * @param rbtree: tree * @return: next larger element or NULL if no larger in tree. */ -rbnode_t *rbtree_next(rbnode_t *rbtree); +rbnode_type *rbtree_next(rbnode_type *rbtree); /** * Returns previous smaller node in the tree @@ -164,16 +164,16 @@ * @param rbtree: tree * @return: previous smaller element or NULL if no previous in tree. */ -rbnode_t *rbtree_previous(rbnode_t *rbtree); +rbnode_type *rbtree_previous(rbnode_type *rbtree); /** - * Call with node=variable of struct* with rbnode_t as first element. + * Call with node=variable of struct* with rbnode_type as first element. * with type is the type of a pointer to that struct. */ #define RBTREE_FOR(node, type, rbtree) \ for(node=(type)rbtree_first(rbtree); \ - (rbnode_t*)node != RBTREE_NULL; \ - node = (type)rbtree_next((rbnode_t*)node)) + (rbnode_type*)node != RBTREE_NULL; \ + node = (type)rbtree_next((rbnode_type*)node)) /** * Call function for all elements in the redblack tree, such that @@ -186,7 +186,7 @@ * The function must not alter the rbtree. * @param arg: user argument. */ -void traverse_postorder(rbtree_t* tree, void (*func)(rbnode_t*, void*), +void traverse_postorder(rbtree_type* tree, void (*func)(rbnode_type*, void*), void* arg); #endif /* UTIL_RBTREE_H_ */ --- contrib/unbound/util/regional.c.orig +++ contrib/unbound/util/regional.c @@ -84,6 +84,7 @@ regional_create_custom(size_t size) { struct regional* r = (struct regional*)malloc(size); + size = ALIGN_UP(size, ALIGNMENT); log_assert(sizeof(struct regional) <= size); if(!r) return NULL; r->first_size = size; @@ -120,8 +121,18 @@ void * regional_alloc(struct regional *r, size_t size) { - size_t a = ALIGN_UP(size, ALIGNMENT); + size_t a; void *s; + if( +#if SIZEOF_SIZE_T == 8 + (unsigned long long)size >= 0xffffffffffffff00ULL +#else + (unsigned)size >= (unsigned)0xffffff00UL +#endif + ) + return NULL; /* protect against integer overflow in + malloc and ALIGN_UP */ + a = ALIGN_UP(size, ALIGNMENT); /* large objects */ if(a > REGIONAL_LARGE_OBJECT_SIZE) { s = malloc(ALIGNMENT + size); --- contrib/unbound/util/rtt.c.orig +++ contrib/unbound/util/rtt.c @@ -41,6 +41,7 @@ */ #include "config.h" #include "util/rtt.h" +#include "iterator/iterator.h" /* overwritten by config: infra_cache_min_rtt: */ int RTT_MIN_TIMEOUT = 50; @@ -61,7 +62,7 @@ rtt_init(struct rtt_info* rtt) { rtt->srtt = 0; - rtt->rttvar = 94; + rtt->rttvar = UNKNOWN_SERVER_NICENESS/4; rtt->rto = calc_rto(rtt); /* default value from the book is 0 + 4*0.75 = 3 seconds */ /* first RTO is 0 + 4*0.094 = 0.376 seconds */ --- contrib/unbound/util/shm_side/shm_main.c.orig +++ contrib/unbound/util/shm_side/shm_main.c @@ -0,0 +1,298 @@ +/* + * util/shm_side/shm_main.c - SHM for statistics transport + * + * 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 functions for the SHM implementation. + */ + +#include "config.h" +#include +#include +#ifdef HAVE_SYS_IPC_H +#include +#endif +#ifdef HAVE_SYS_SHM_H +#include +#endif +#include +#include +#include "shm_main.h" +#include "daemon/daemon.h" +#include "daemon/worker.h" +#include "daemon/stats.h" +#include "services/mesh.h" +#include "services/cache/rrset.h" +#include "services/cache/infra.h" +#include "validator/validator.h" +#include "util/config_file.h" +#include "util/fptr_wlist.h" +#include "util/log.h" + +#ifdef HAVE_SHMGET +/** subtract timers and the values do not overflow or become negative */ +static void +stat_timeval_subtract(long long *d_sec, long long *d_usec, const struct timeval* end, + const struct timeval* start) +{ +#ifndef S_SPLINT_S + time_t end_usec = end->tv_usec; + *d_sec = end->tv_sec - start->tv_sec; + if(end_usec < start->tv_usec) { + end_usec += 1000000; + (*d_sec)--; + } + *d_usec = end_usec - start->tv_usec; +#endif +} +#endif /* HAVE_SHMGET */ + +int shm_main_init(struct daemon* daemon) +{ +#ifdef HAVE_SHMGET + struct ub_shm_stat_info *shm_stat; + size_t shm_size; + + /* sanitize */ + if(!daemon) + return 0; + if(!daemon->cfg->shm_enable) + return 1; + if(daemon->cfg->stat_interval == 0) + log_warn("shm-enable is yes but statistics-interval is 0"); + + /* Statistics to maintain the number of thread + total */ + shm_size = (sizeof(struct ub_stats_info) * (daemon->num + 1)); + + /* Allocation of needed memory */ + daemon->shm_info = (struct shm_main_info*)calloc(1, shm_size); + + /* Sanitize */ + if(!daemon->shm_info) { + log_err("shm fail: malloc failure"); + return 0; + } + + daemon->shm_info->key = daemon->cfg->shm_key; + + /* Check for previous create SHM */ + daemon->shm_info->id_ctl = shmget(daemon->shm_info->key, sizeof(int), SHM_R); + daemon->shm_info->id_arr = shmget(daemon->shm_info->key + 1, sizeof(int), SHM_R); + + /* Destroy previous SHM */ + if (daemon->shm_info->id_ctl >= 0) + shmctl(daemon->shm_info->id_ctl, IPC_RMID, NULL); + + /* Destroy previous SHM */ + if (daemon->shm_info->id_arr >= 0) + shmctl(daemon->shm_info->id_arr, IPC_RMID, NULL); + + /* SHM: Create the segment */ + daemon->shm_info->id_ctl = shmget(daemon->shm_info->key, sizeof(struct ub_shm_stat_info), IPC_CREAT | 0644); + + if (daemon->shm_info->id_ctl < 0) + { + log_err("SHM failed(id_ctl) cannot shmget(key %d) %s", + daemon->shm_info->key, strerror(errno)); + + /* Just release memory unused */ + free(daemon->shm_info); + + return 0; + } + + daemon->shm_info->id_arr = shmget(daemon->shm_info->key + 1, shm_size, IPC_CREAT | 0644); + + if (daemon->shm_info->id_arr < 0) + { + log_err("SHM failed(id_arr) cannot shmget(key %d + 1) %s", + daemon->shm_info->key, strerror(errno)); + + /* Just release memory unused */ + free(daemon->shm_info); + + return 0; + } + + /* SHM: attach the segment */ + daemon->shm_info->ptr_ctl = (struct ub_shm_stat_info*) + shmat(daemon->shm_info->id_ctl, NULL, 0); + if(daemon->shm_info->ptr_ctl == (void *) -1) { + log_err("SHM failed(ctl) cannot shmat(%d) %s", + daemon->shm_info->id_ctl, strerror(errno)); + + /* Just release memory unused */ + free(daemon->shm_info); + + return 0; + } + + daemon->shm_info->ptr_arr = (struct ub_stats_info*) + shmat(daemon->shm_info->id_arr, NULL, 0); + + if (daemon->shm_info->ptr_arr == (void *) -1) + { + log_err("SHM failed(arr) cannot shmat(%d) %s", + daemon->shm_info->id_arr, strerror(errno)); + + /* Just release memory unused */ + free(daemon->shm_info); + + return 0; + } + + /* Zero fill SHM to stand clean while is not filled by other events */ + memset(daemon->shm_info->ptr_ctl, 0, sizeof(struct ub_shm_stat_info)); + memset(daemon->shm_info->ptr_arr, 0, shm_size); + + shm_stat = daemon->shm_info->ptr_ctl; + shm_stat->num_threads = daemon->num; + +#else + (void)daemon; +#endif /* HAVE_SHMGET */ + return 1; +} + +void shm_main_shutdown(struct daemon* daemon) +{ +#ifdef HAVE_SHMGET + /* web are OK, just disabled */ + if(!daemon->cfg->shm_enable) + return; + + verbose(VERB_DETAIL, "SHM shutdown - KEY [%d] - ID CTL [%d] ARR [%d] - PTR CTL [%p] ARR [%p]", + daemon->shm_info->key, daemon->shm_info->id_ctl, daemon->shm_info->id_arr, daemon->shm_info->ptr_ctl, daemon->shm_info->ptr_arr); + + /* Destroy previous SHM */ + if (daemon->shm_info->id_ctl >= 0) + shmctl(daemon->shm_info->id_ctl, IPC_RMID, NULL); + + if (daemon->shm_info->id_arr >= 0) + shmctl(daemon->shm_info->id_arr, IPC_RMID, NULL); + + if (daemon->shm_info->ptr_ctl) + shmdt(daemon->shm_info->ptr_ctl); + + if (daemon->shm_info->ptr_arr) + shmdt(daemon->shm_info->ptr_arr); + +#else + (void)daemon; +#endif /* HAVE_SHMGET */ +} + +void shm_main_run(struct worker *worker) +{ +#ifdef HAVE_SHMGET + struct ub_shm_stat_info *shm_stat; + struct ub_stats_info *stat_total; + struct ub_stats_info *stat_info; + int offset; + +#ifndef S_SPLINT_S + verbose(VERB_DETAIL, "SHM run - worker [%d] - daemon [%p] - timenow(%u) - timeboot(%u)", + worker->thread_num, worker->daemon, (unsigned)worker->env.now_tv->tv_sec, (unsigned)worker->daemon->time_boot.tv_sec); +#endif + + offset = worker->thread_num + 1; + stat_total = worker->daemon->shm_info->ptr_arr; + stat_info = worker->daemon->shm_info->ptr_arr + offset; + + /* Copy data to the current position */ + server_stats_compile(worker, stat_info, 0); + + /* First thread, zero fill total, and copy general info */ + if (worker->thread_num == 0) { + + /* Copy data to the current position */ + memset(stat_total, 0, sizeof(struct ub_stats_info)); + + /* Point to data into SHM */ +#ifndef S_SPLINT_S + shm_stat = worker->daemon->shm_info->ptr_ctl; + shm_stat->time.now_sec = (long long)worker->env.now_tv->tv_sec; + shm_stat->time.now_usec = (long long)worker->env.now_tv->tv_usec; +#endif + + stat_timeval_subtract(&shm_stat->time.up_sec, &shm_stat->time.up_usec, worker->env.now_tv, &worker->daemon->time_boot); + stat_timeval_subtract(&shm_stat->time.elapsed_sec, &shm_stat->time.elapsed_usec, worker->env.now_tv, &worker->daemon->time_last_stat); + + shm_stat->mem.msg = (long long)slabhash_get_mem(worker->env.msg_cache); + shm_stat->mem.rrset = (long long)slabhash_get_mem(&worker->env.rrset_cache->table); + shm_stat->mem.dnscrypt_shared_secret = 0; +#ifdef USE_DNSCRYPT + if(worker->daemon->dnscenv) { + shm_stat->mem.dnscrypt_shared_secret = (long long)slabhash_get_mem( + worker->daemon->dnscenv->shared_secrets_cache); + shm_stat->mem.dnscrypt_nonce = (long long)slabhash_get_mem( + worker->daemon->dnscenv->nonces_cache); + } +#endif + shm_stat->mem.val = (long long)mod_get_mem(&worker->env, + "validator"); + shm_stat->mem.iter = (long long)mod_get_mem(&worker->env, + "iterator"); + shm_stat->mem.respip = (long long)mod_get_mem(&worker->env, + "respip"); + + /* 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 */ + shm_stat->mem.subnet = 0; +#ifdef CLIENT_SUBNET + shm_stat->mem.subnet = (long long)mod_get_mem(&worker->env, + "subnet"); +#endif + /* ipsecmod 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 */ + shm_stat->mem.ipsecmod = 0; +#ifdef USE_IPSECMOD + shm_stat->mem.ipsecmod = (long long)mod_get_mem(&worker->env, + "ipsecmod"); +#endif + } + + server_stats_add(stat_total, stat_info); + + /* print the thread statistics */ + stat_total->mesh_time_median /= (double)worker->daemon->num; + +#else + (void)worker; +#endif /* HAVE_SHMGET */ +} --- contrib/unbound/util/shm_side/shm_main.h.orig +++ contrib/unbound/util/shm_side/shm_main.h @@ -0,0 +1,68 @@ +/* + * util/shm_side/shm_main.h - control the shared memory 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 SHM side. + */ + +#ifndef UTIL_SHM_SIDE_MAIN_H +#define UTIL_SHM_SIDE_MAIN_H +struct daemon; +struct worker; + +/* get struct ub_shm_stat_info */ +#include "libunbound/unbound.h" + +/** + * The SHM info. + */ +struct shm_main_info { + /** stats_info array, shared memory segment. + * [0] is totals, [1..thread_num] are per-thread stats */ + struct ub_stats_info* ptr_arr; + /** the global stats block, shared memory segment */ + struct ub_shm_stat_info* ptr_ctl; + int key; + int id_ctl; + int id_arr; +}; + +int shm_main_init(struct daemon* daemon); +void shm_main_shutdown(struct daemon* daemon); +void shm_main_run(struct worker *worker); + +#endif /* UTIL_SHM_SIDE_MAIN_H */ --- contrib/unbound/util/storage/dnstree.c.orig +++ contrib/unbound/util/storage/dnstree.c @@ -71,17 +71,17 @@ return 0; } -void name_tree_init(rbtree_t* tree) +void name_tree_init(rbtree_type* tree) { rbtree_init(tree, &name_tree_compare); } -void addr_tree_init(rbtree_t* tree) +void addr_tree_init(rbtree_type* tree) { rbtree_init(tree, &addr_tree_compare); } -int name_tree_insert(rbtree_t* tree, struct name_tree_node* node, +int name_tree_insert(rbtree_type* tree, struct name_tree_node* node, uint8_t* name, size_t len, int labs, uint16_t dclass) { node->node.key = node; @@ -93,7 +93,7 @@ return rbtree_insert(tree, &node->node) != NULL; } -int addr_tree_insert(rbtree_t* tree, struct addr_tree_node* node, +int addr_tree_insert(rbtree_type* tree, struct addr_tree_node* node, struct sockaddr_storage* addr, socklen_t addrlen, int net) { node->node.key = node; @@ -104,11 +104,12 @@ return rbtree_insert(tree, &node->node) != NULL; } -void addr_tree_init_parents(rbtree_t* tree) +void addr_tree_init_parents_node(struct addr_tree_node* node) { - struct addr_tree_node* node, *prev = NULL, *p; + struct addr_tree_node* prev = NULL, *p; int m; - RBTREE_FOR(node, struct addr_tree_node*, tree) { + for(; (rbnode_type*)node != RBTREE_NULL; + node = (struct addr_tree_node*)rbtree_next((rbnode_type*)node)) { node->parent = NULL; if(!prev || prev->addrlen != node->addrlen) { prev = node; @@ -130,8 +131,14 @@ } } -void name_tree_init_parents(rbtree_t* tree) +void addr_tree_init_parents(rbtree_type* tree) { + addr_tree_init_parents_node( + (struct addr_tree_node*)rbtree_first(tree)); +} + +void name_tree_init_parents(rbtree_type* tree) +{ struct name_tree_node* node, *prev = NULL, *p; int m; RBTREE_FOR(node, struct name_tree_node*, tree) { @@ -156,7 +163,7 @@ } } -struct name_tree_node* name_tree_find(rbtree_t* tree, uint8_t* name, +struct name_tree_node* name_tree_find(rbtree_type* tree, uint8_t* name, size_t len, int labs, uint16_t dclass) { struct name_tree_node key; @@ -168,10 +175,10 @@ return (struct name_tree_node*)rbtree_search(tree, &key); } -struct name_tree_node* name_tree_lookup(rbtree_t* tree, uint8_t* name, +struct name_tree_node* name_tree_lookup(rbtree_type* tree, uint8_t* name, size_t len, int labs, uint16_t dclass) { - rbnode_t* res = NULL; + rbnode_type* res = NULL; struct name_tree_node *result; struct name_tree_node key; key.node.key = &key; @@ -200,10 +207,10 @@ return result; } -struct addr_tree_node* addr_tree_lookup(rbtree_t* tree, +struct addr_tree_node* addr_tree_lookup(rbtree_type* tree, struct sockaddr_storage* addr, socklen_t addrlen) { - rbnode_t* res = NULL; + rbnode_type* res = NULL; struct addr_tree_node* result; struct addr_tree_node key; key.node.key = &key; @@ -231,10 +238,10 @@ return result; } -struct addr_tree_node* addr_tree_find(rbtree_t* tree, +struct addr_tree_node* addr_tree_find(rbtree_type* tree, struct sockaddr_storage* addr, socklen_t addrlen, int net) { - rbnode_t* res = NULL; + rbnode_type* res = NULL; struct addr_tree_node key; key.node.key = &key; memcpy(&key.addr, addr, addrlen); @@ -245,10 +252,10 @@ } int -name_tree_next_root(rbtree_t* tree, uint16_t* dclass) +name_tree_next_root(rbtree_type* tree, uint16_t* dclass) { struct name_tree_node key; - rbnode_t* n; + rbnode_type* n; struct name_tree_node* p; if(*dclass == 0) { /* first root item is first item in tree */ --- contrib/unbound/util/storage/dnstree.h.orig +++ contrib/unbound/util/storage/dnstree.h @@ -49,12 +49,12 @@ * This is not sorted canonically, but fast. * This can be looked up to obtain a closest encloser parent name. * - * The tree itself is a rbtree_t. + * The tree itself is a rbtree_type. * This is the element node put as first entry in the client structure. */ struct name_tree_node { /** rbtree node, key is this struct : dclass and name */ - rbnode_t node; + rbnode_type node; /** parent in tree */ struct name_tree_node* parent; /** name in uncompressed wireformat */ @@ -71,12 +71,12 @@ * Tree of IP addresses. Sorted first by protocol, then by bits. * This can be looked up to obtain the enclosing subnet. * - * The tree itself is a rbtree_t. + * The tree itself is a rbtree_type. * This is the element node put as first entry in the client structure. */ struct addr_tree_node { /** rbtree node, key is this struct : proto and subnet */ - rbnode_t node; + rbnode_type node; /** parent in tree */ struct addr_tree_node* parent; /** address */ @@ -91,7 +91,7 @@ * Init a name tree to be empty * @param tree: to init. */ -void name_tree_init(rbtree_t* tree); +void name_tree_init(rbtree_type* tree); /** * insert element into name tree. @@ -105,7 +105,7 @@ * @param dclass: class of name * @return false on error (duplicate element). */ -int name_tree_insert(rbtree_t* tree, struct name_tree_node* node, +int name_tree_insert(rbtree_type* tree, struct name_tree_node* node, uint8_t* name, size_t len, int labs, uint16_t dclass); /** @@ -113,7 +113,7 @@ * Should be performed after insertions are done, before lookups * @param tree: name tree */ -void name_tree_init_parents(rbtree_t* tree); +void name_tree_init_parents(rbtree_type* tree); /** * Lookup exact match in name tree @@ -124,7 +124,7 @@ * @param dclass: class of name * @return node or NULL if not found. */ -struct name_tree_node* name_tree_find(rbtree_t* tree, uint8_t* name, +struct name_tree_node* name_tree_find(rbtree_type* tree, uint8_t* name, size_t len, int labs, uint16_t dclass); /** @@ -136,7 +136,7 @@ * @param dclass: class of name * @return closest enclosing node (could be equal) or NULL if not found. */ -struct name_tree_node* name_tree_lookup(rbtree_t* tree, uint8_t* name, +struct name_tree_node* name_tree_lookup(rbtree_type* tree, uint8_t* name, size_t len, int labs, uint16_t dclass); /** @@ -145,13 +145,13 @@ * @param dclass: the class to look for next (or higher). * @return false if no classes found, true means class put into c. */ -int name_tree_next_root(rbtree_t* tree, uint16_t* dclass); +int name_tree_next_root(rbtree_type* tree, uint16_t* dclass); /** * Init addr tree to be empty. * @param tree: to init. */ -void addr_tree_init(rbtree_t* tree); +void addr_tree_init(rbtree_type* tree); /** * insert element into addr tree. @@ -163,7 +163,7 @@ * @param net: size of subnet. * @return false on error (duplicate element). */ -int addr_tree_insert(rbtree_t* tree, struct addr_tree_node* node, +int addr_tree_insert(rbtree_type* tree, struct addr_tree_node* node, struct sockaddr_storage* addr, socklen_t addrlen, int net); /** @@ -171,9 +171,16 @@ * Should be performed after insertions are done, before lookups * @param tree: addr tree */ -void addr_tree_init_parents(rbtree_t* tree); +void addr_tree_init_parents(rbtree_type* tree); /** + * Initialize parent pointers in partial addr tree. + * Reinitialize pointer for part of tree, used after node deletion + * @param node: node to start parent pointer initialization for. + */ +void addr_tree_init_parents_node(struct addr_tree_node* node); + +/** * Lookup closest encloser in addr tree. * @param tree: addr tree * @param addr: to lookup. @@ -180,7 +187,7 @@ * @param addrlen: length of addr * @return closest enclosing node (could be equal) or NULL if not found. */ -struct addr_tree_node* addr_tree_lookup(rbtree_t* tree, +struct addr_tree_node* addr_tree_lookup(rbtree_type* tree, struct sockaddr_storage* addr, socklen_t addrlen); /** @@ -191,7 +198,7 @@ * @param net: size of subnet * @return addr tree element, or NULL if not found. */ -struct addr_tree_node* addr_tree_find(rbtree_t* tree, +struct addr_tree_node* addr_tree_find(rbtree_type* tree, struct sockaddr_storage* addr, socklen_t addrlen, int net); /** compare name tree nodes */ --- contrib/unbound/util/storage/lookup3.c.orig +++ contrib/unbound/util/storage/lookup3.c @@ -1,4 +1,7 @@ /* + May 2019(Wouter) patch to enable the valgrind clean implementation all the + time. This enables better security audit and checks, which is better + than the speedup. Git issue #30. Renamed the define ARRAY_CLEAN_ACCESS. February 2013(Wouter) patch defines for BSD endianness, from Brad Smith. January 2012(Wouter) added randomised initial value, fallout from 28c3. March 2007(Wouter) adapted from lookup3.c original, add config.h include. @@ -5,6 +8,7 @@ added #ifdef VALGRIND to remove 298,384,660 'unused variable k8' warnings. added include of lookup3.h to check definitions match declarations. removed include of stdint - config.h takes care of platform independence. + added fallthrough comments for new gcc warning suppression. url http://burtleburtle.net/bob/hash/index.html. */ /* @@ -43,6 +47,7 @@ ------------------------------------------------------------------------------- */ /*#define SELF_TEST 1*/ +#define ARRAY_CLEAN_ACCESS 1 #include "config.h" #include "util/storage/lookup3.h" @@ -235,7 +240,9 @@ switch(length) /* all the case statements fall through */ { case 3 : c+=k[2]; + /* fallthrough */ case 2 : b+=k[1]; + /* fallthrough */ case 1 : a+=k[0]; final(a,b,c); case 0: /* case 0: nothing left to add */ @@ -333,7 +340,7 @@ u.ptr = key; if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) { const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ -#ifdef VALGRIND +#ifdef ARRAY_CLEAN_ACCESS const uint8_t *k8; #endif @@ -358,7 +365,7 @@ * still catch it and complain. The masking trick does make the hash * noticeably faster for short strings (like English words). */ -#ifndef VALGRIND +#ifndef ARRAY_CLEAN_ACCESS switch(length) { @@ -473,16 +480,27 @@ switch(length) /* all the case statements fall through */ { case 12: c+=((uint32_t)k[11])<<24; + /* fallthrough */ case 11: c+=((uint32_t)k[10])<<16; + /* fallthrough */ case 10: c+=((uint32_t)k[9])<<8; + /* fallthrough */ case 9 : c+=k[8]; + /* fallthrough */ case 8 : b+=((uint32_t)k[7])<<24; + /* fallthrough */ case 7 : b+=((uint32_t)k[6])<<16; + /* fallthrough */ case 6 : b+=((uint32_t)k[5])<<8; + /* fallthrough */ case 5 : b+=k[4]; + /* fallthrough */ case 4 : a+=((uint32_t)k[3])<<24; + /* fallthrough */ case 3 : a+=((uint32_t)k[2])<<16; + /* fallthrough */ case 2 : a+=((uint32_t)k[1])<<8; + /* fallthrough */ case 1 : a+=k[0]; break; case 0 : return c; --- contrib/unbound/util/storage/lruhash.c.orig +++ contrib/unbound/util/storage/lruhash.c @@ -59,9 +59,10 @@ } struct lruhash* -lruhash_create(size_t start_size, size_t maxmem, lruhash_sizefunc_t sizefunc, - lruhash_compfunc_t compfunc, lruhash_delkeyfunc_t delkeyfunc, - lruhash_deldatafunc_t deldatafunc, void* arg) +lruhash_create(size_t start_size, size_t maxmem, + lruhash_sizefunc_type sizefunc, lruhash_compfunc_type compfunc, + lruhash_delkeyfunc_type delkeyfunc, + lruhash_deldatafunc_type deldatafunc, void* arg) { struct lruhash* table = (struct lruhash*)calloc(1, sizeof(struct lruhash)); @@ -215,7 +216,7 @@ struct lruhash_entry* bin_find_entry(struct lruhash* table, - struct lruhash_bin* bin, hashvalue_t hash, void* key) + struct lruhash_bin* bin, hashvalue_type hash, void* key) { struct lruhash_entry* p = bin->overflow_list; while(p) { @@ -296,7 +297,7 @@ } void -lruhash_insert(struct lruhash* table, hashvalue_t hash, +lruhash_insert(struct lruhash* table, hashvalue_type hash, struct lruhash_entry* entry, void* data, void* cb_arg) { struct lruhash_bin* bin; @@ -352,7 +353,7 @@ } struct lruhash_entry* -lruhash_lookup(struct lruhash* table, hashvalue_t hash, void* key, int wr) +lruhash_lookup(struct lruhash* table, hashvalue_type hash, void* key, int wr) { struct lruhash_entry* entry; struct lruhash_bin* bin; @@ -374,7 +375,7 @@ } void -lruhash_remove(struct lruhash* table, hashvalue_t hash, void* key) +lruhash_remove(struct lruhash* table, hashvalue_type hash, void* key) { struct lruhash_entry* entry; struct lruhash_bin* bin; @@ -512,7 +513,7 @@ } void -lruhash_setmarkdel(struct lruhash* table, lruhash_markdelfunc_t md) +lruhash_setmarkdel(struct lruhash* table, lruhash_markdelfunc_type md) { lock_quick_lock(&table->lock); table->markdelfunc = md; @@ -542,3 +543,89 @@ } lock_quick_unlock(&h->lock); } + +/* + * Demote: the opposite of touch, move an entry to the bottom + * of the LRU pile. + */ + +void +lru_demote(struct lruhash* table, struct lruhash_entry* entry) +{ + log_assert(table && entry); + if (entry == table->lru_end) + return; /* nothing to do */ + /* remove from current lru position */ + lru_remove(table, entry); + /* add at end */ + entry->lru_next = NULL; + entry->lru_prev = table->lru_end; + + if (table->lru_end == NULL) + { + table->lru_start = entry; + } + else + { + table->lru_end->lru_next = entry; + } + table->lru_end = entry; +} + +struct lruhash_entry* +lruhash_insert_or_retrieve(struct lruhash* table, hashvalue_type hash, + struct lruhash_entry* entry, void* data, void* cb_arg) +{ + struct lruhash_bin* bin; + struct lruhash_entry* found, *reclaimlist = NULL; + size_t need_size; + fptr_ok(fptr_whitelist_hash_sizefunc(table->sizefunc)); + fptr_ok(fptr_whitelist_hash_delkeyfunc(table->delkeyfunc)); + fptr_ok(fptr_whitelist_hash_deldatafunc(table->deldatafunc)); + fptr_ok(fptr_whitelist_hash_compfunc(table->compfunc)); + fptr_ok(fptr_whitelist_hash_markdelfunc(table->markdelfunc)); + need_size = table->sizefunc(entry->key, data); + if (cb_arg == NULL) cb_arg = table->cb_arg; + + /* find bin */ + lock_quick_lock(&table->lock); + bin = &table->array[hash & table->size_mask]; + lock_quick_lock(&bin->lock); + + /* see if entry exists already */ + if ((found = bin_find_entry(table, bin, hash, entry->key)) != NULL) { + /* if so: keep the existing data - acquire a writelock */ + lock_rw_wrlock(&found->lock); + } + else + { + /* if not: add to bin */ + entry->overflow_next = bin->overflow_list; + bin->overflow_list = entry; + lru_front(table, entry); + table->num++; + table->space_used += need_size; + /* return the entry that was presented, and lock it */ + found = entry; + lock_rw_wrlock(&found->lock); + } + lock_quick_unlock(&bin->lock); + if (table->space_used > table->space_max) + reclaim_space(table, &reclaimlist); + if (table->num >= table->size) + table_grow(table); + lock_quick_unlock(&table->lock); + + /* finish reclaim if any (outside of critical region) */ + while (reclaimlist) { + struct lruhash_entry* n = reclaimlist->overflow_next; + void* d = reclaimlist->data; + (*table->delkeyfunc)(reclaimlist->key, cb_arg); + (*table->deldatafunc)(d, cb_arg); + reclaimlist = n; + } + + /* return the entry that was selected */ + return found; +} + --- contrib/unbound/util/storage/lruhash.h.orig +++ contrib/unbound/util/storage/lruhash.h @@ -116,7 +116,7 @@ #define HASH_DEFAULT_MAXMEM 4*1024*1024 /* bytes */ /** the type of a hash value */ -typedef uint32_t hashvalue_t; +typedef uint32_t hashvalue_type; /** * Type of function that calculates the size of an entry. @@ -124,22 +124,22 @@ * Keys that are identical must also calculate to the same size. * size = func(key, data). */ -typedef size_t (*lruhash_sizefunc_t)(void*, void*); +typedef size_t (*lruhash_sizefunc_type)(void*, void*); /** type of function that compares two keys. return 0 if equal. */ -typedef int (*lruhash_compfunc_t)(void*, void*); +typedef int (*lruhash_compfunc_type)(void*, void*); /** old keys are deleted. * The RRset type has to revoke its ID number, markdel() is used first. * This function is called: func(key, userarg) */ -typedef void (*lruhash_delkeyfunc_t)(void*, void*); +typedef void (*lruhash_delkeyfunc_type)(void*, void*); /** old data is deleted. This function is called: func(data, userarg). */ -typedef void (*lruhash_deldatafunc_t)(void*, void*); +typedef void (*lruhash_deldatafunc_type)(void*, void*); /** mark a key as pending to be deleted (and not to be used by anyone). * called: func(key) */ -typedef void (*lruhash_markdelfunc_t)(void*); +typedef void (*lruhash_markdelfunc_type)(void*); /** * Hash table that keeps LRU list of entries. @@ -146,17 +146,17 @@ */ struct lruhash { /** lock for exclusive access, to the lookup array */ - lock_quick_t lock; + lock_quick_type lock; /** the size function for entries in this table */ - lruhash_sizefunc_t sizefunc; + lruhash_sizefunc_type sizefunc; /** the compare function for entries in this table. */ - lruhash_compfunc_t compfunc; + lruhash_compfunc_type compfunc; /** how to delete keys. */ - lruhash_delkeyfunc_t delkeyfunc; + lruhash_delkeyfunc_type delkeyfunc; /** how to delete data. */ - lruhash_deldatafunc_t deldatafunc; + lruhash_deldatafunc_type deldatafunc; /** how to mark a key pending deletion */ - lruhash_markdelfunc_t markdelfunc; + lruhash_markdelfunc_type markdelfunc; /** user argument for user functions */ void* cb_arg; @@ -188,7 +188,7 @@ * Lock for exclusive access to the linked list * This lock makes deletion of items safe in this overflow list. */ - lock_quick_t lock; + lock_quick_type lock; /** linked list of overflow entries */ struct lruhash_entry* overflow_list; }; @@ -207,7 +207,7 @@ * Even with a writelock, you cannot change hash and key. * You need to delete it to change hash or key. */ - lock_rw_t lock; + lock_rw_type lock; /** next entry in overflow chain. Covered by hashlock and binlock. */ struct lruhash_entry* overflow_next; /** next entry in lru chain. covered by hashlock. */ @@ -215,7 +215,7 @@ /** prev entry in lru chain. covered by hashlock. */ struct lruhash_entry* lru_prev; /** hash value of the key. It may not change, until entry deleted. */ - hashvalue_t hash; + hashvalue_type hash; /** key */ void* key; /** data */ @@ -236,9 +236,9 @@ * @return: new hash table or NULL on malloc failure. */ struct lruhash* lruhash_create(size_t start_size, size_t maxmem, - lruhash_sizefunc_t sizefunc, lruhash_compfunc_t compfunc, - lruhash_delkeyfunc_t delkeyfunc, lruhash_deldatafunc_t deldatafunc, - void* arg); + lruhash_sizefunc_type sizefunc, lruhash_compfunc_type compfunc, + lruhash_delkeyfunc_type delkeyfunc, + lruhash_deldatafunc_type deldatafunc, void* arg); /** * Delete hash table. Entries are all deleted. @@ -269,7 +269,7 @@ * @param data: the data. * @param cb_override: if not null overrides the cb_arg for the deletefunc. */ -void lruhash_insert(struct lruhash* table, hashvalue_t hash, +void lruhash_insert(struct lruhash* table, hashvalue_type hash, struct lruhash_entry* entry, void* data, void* cb_override); /** @@ -285,8 +285,8 @@ * @return: pointer to the entry or NULL. The entry is locked. * The user must unlock the entry when done. */ -struct lruhash_entry* lruhash_lookup(struct lruhash* table, hashvalue_t hash, - void* key, int wr); +struct lruhash_entry* lruhash_lookup(struct lruhash* table, + hashvalue_type hash, void* key, int wr); /** * Touch entry, so it becomes the most recently used in the LRU list. @@ -299,8 +299,40 @@ /** * Set the markdelfunction (or NULL) */ -void lruhash_setmarkdel(struct lruhash* table, lruhash_markdelfunc_t md); +void lruhash_setmarkdel(struct lruhash* table, lruhash_markdelfunc_type md); +/************************* getdns functions ************************/ +/*** these are used by getdns only and not by unbound. ***/ + +/** + * Demote entry, so it becomes the least recently used in the LRU list. + * Caller must hold hash table lock. The entry must be inserted already. + * @param table: hash table. + * @param entry: entry to make last in LRU. + */ +void lru_demote(struct lruhash* table, struct lruhash_entry* entry); + +/** + * Insert a new element into the hashtable, or retrieve the corresponding + * element of it exits. + * + * If key is already present data pointer in that entry is kept. + * If it is not present, a new entry is created. In that case, + * the space calculation function is called with the key, data. + * If necessary the least recently used entries are deleted to make space. + * If necessary the hash array is grown up. + * + * @param table: hash table. + * @param hash: hash value. User calculates the hash. + * @param entry: identifies the entry. + * @param data: the data. + * @param cb_arg: if not null overrides the cb_arg for the deletefunc. + * @return: pointer to the existing entry if the key was already present, + * or to the entry argument if it was not. + */ +struct lruhash_entry* lruhash_insert_or_retrieve(struct lruhash* table, hashvalue_type hash, + struct lruhash_entry* entry, void* data, void* cb_arg); + /************************* Internal functions ************************/ /*** these are only exposed for unit tests. ***/ @@ -311,7 +343,7 @@ * @param hash: hash of key. * @param key: what to look for. */ -void lruhash_remove(struct lruhash* table, hashvalue_t hash, void* key); +void lruhash_remove(struct lruhash* table, hashvalue_type hash, void* key); /** init the hash bins for the table */ void bin_init(struct lruhash_bin* array, size_t size); @@ -328,7 +360,7 @@ * @return: the entry or NULL if not found. */ struct lruhash_entry* bin_find_entry(struct lruhash* table, - struct lruhash_bin* bin, hashvalue_t hash, void* key); + struct lruhash_bin* bin, hashvalue_type hash, void* key); /** * Remove entry from bin overflow chain. --- contrib/unbound/util/storage/slabhash.c.orig +++ contrib/unbound/util/storage/slabhash.c @@ -46,9 +46,9 @@ #include "util/storage/slabhash.h" struct slabhash* slabhash_create(size_t numtables, size_t start_size, - size_t maxmem, lruhash_sizefunc_t sizefunc, - lruhash_compfunc_t compfunc, lruhash_delkeyfunc_t delkeyfunc, - lruhash_deldatafunc_t deldatafunc, void* arg) + size_t maxmem, lruhash_sizefunc_type sizefunc, + lruhash_compfunc_type compfunc, lruhash_delkeyfunc_type delkeyfunc, + lruhash_deldatafunc_type deldatafunc, void* arg) { size_t i; struct slabhash* sl = (struct slabhash*)calloc(1, @@ -108,12 +108,12 @@ /** helper routine to calculate the slabhash index */ static unsigned int -slab_idx(struct slabhash* sl, hashvalue_t hash) +slab_idx(struct slabhash* sl, hashvalue_type hash) { return ((hash & sl->mask) >> sl->shift); } -void slabhash_insert(struct slabhash* sl, hashvalue_t hash, +void slabhash_insert(struct slabhash* sl, hashvalue_type hash, struct lruhash_entry* entry, void* data, void* arg) { lruhash_insert(sl->array[slab_idx(sl, hash)], hash, entry, data, arg); @@ -120,12 +120,12 @@ } struct lruhash_entry* slabhash_lookup(struct slabhash* sl, - hashvalue_t hash, void* key, int wr) + hashvalue_type hash, void* key, int wr) { return lruhash_lookup(sl->array[slab_idx(sl, hash)], hash, key, wr); } -void slabhash_remove(struct slabhash* sl, hashvalue_t hash, void* key) +void slabhash_remove(struct slabhash* sl, hashvalue_type hash, void* key) { lruhash_remove(sl->array[slab_idx(sl, hash)], hash, key); } @@ -153,6 +153,19 @@ return total; } +int slabhash_is_size(struct slabhash* sl, size_t size, size_t slabs) +{ + /* divide by slabs and then multiply by the number of slabs, + * because if the size is not an even multiple of slabs, the + * uneven amount needs to be removed for comparison */ + if(!sl) return 0; + if(sl->size != slabs) return 0; + if(slabs == 0) return 0; + if( (size/slabs)*slabs == slabhash_get_size(sl)) + return 1; + return 0; +} + size_t slabhash_get_mem(struct slabhash* sl) { size_t i, total = sizeof(*sl); @@ -163,7 +176,7 @@ return total; } -struct lruhash* slabhash_gettable(struct slabhash* sl, hashvalue_t hash) +struct lruhash* slabhash_gettable(struct slabhash* sl, hashvalue_type hash) { return sl->array[slab_idx(sl, hash)]; } @@ -202,7 +215,7 @@ deldata((struct slabhash_testdata*)data); } -void slabhash_setmarkdel(struct slabhash* sl, lruhash_markdelfunc_t md) +void slabhash_setmarkdel(struct slabhash* sl, lruhash_markdelfunc_type md) { size_t i; for(i=0; isize; i++) { --- contrib/unbound/util/storage/slabhash.h.orig +++ contrib/unbound/util/storage/slabhash.h @@ -80,9 +80,9 @@ * @return: new hash table or NULL on malloc failure. */ struct slabhash* slabhash_create(size_t numtables, size_t start_size, - size_t maxmem, lruhash_sizefunc_t sizefunc, - lruhash_compfunc_t compfunc, lruhash_delkeyfunc_t delkeyfunc, - lruhash_deldatafunc_t deldatafunc, void* arg); + size_t maxmem, lruhash_sizefunc_type sizefunc, + lruhash_compfunc_type compfunc, lruhash_delkeyfunc_type delkeyfunc, + lruhash_deldatafunc_type deldatafunc, void* arg); /** * Delete hash table. Entries are all deleted. @@ -107,9 +107,9 @@ * But entry->data is set to NULL before deletion, and put into * the existing entry. The data is then freed. * @param data: the data. - * @param cb_override: if not NULL overrides the cb_arg for deletfunc. + * @param cb_override: if not NULL overrides the cb_arg for deletefunc. */ -void slabhash_insert(struct slabhash* table, hashvalue_t hash, +void slabhash_insert(struct slabhash* table, hashvalue_type hash, struct lruhash_entry* entry, void* data, void* cb_override); /** @@ -126,7 +126,7 @@ * The user must unlock the entry when done. */ struct lruhash_entry* slabhash_lookup(struct slabhash* table, - hashvalue_t hash, void* key, int wr); + hashvalue_type hash, void* key, int wr); /** * Remove entry from hashtable. Does nothing if not found in hashtable. @@ -135,7 +135,7 @@ * @param hash: hash of key. * @param key: what to look for. */ -void slabhash_remove(struct slabhash* table, hashvalue_t hash, void* key); +void slabhash_remove(struct slabhash* table, hashvalue_type hash, void* key); /** * Output debug info to the log as to state of the hash table. @@ -153,6 +153,15 @@ size_t slabhash_get_size(struct slabhash* table); /** + * See if slabhash is of given (size, slabs) configuration. + * @param table: hash table + * @param size: max size to test for + * @param slabs: slab count to test for. + * @return true if equal + */ +int slabhash_is_size(struct slabhash* table, size_t size, size_t slabs); + +/** * Retrieve slab hash current memory use. * @param table: hash table. * @return memory in use. @@ -165,7 +174,7 @@ * @param hash: hash value. * @return the lru hash table. */ -struct lruhash* slabhash_gettable(struct slabhash* table, hashvalue_t hash); +struct lruhash* slabhash_gettable(struct slabhash* table, hashvalue_type hash); /** * Set markdel function @@ -172,7 +181,7 @@ * @param table: slabbed hash table. * @param md: markdel function ptr. */ -void slabhash_setmarkdel(struct slabhash* table, lruhash_markdelfunc_t md); +void slabhash_setmarkdel(struct slabhash* table, lruhash_markdelfunc_type md); /** * Traverse a slabhash. --- contrib/unbound/util/tcp_conn_limit.c.orig +++ contrib/unbound/util/tcp_conn_limit.c @@ -0,0 +1,194 @@ +/* + * daemon/tcp_conn_limit.c - client TCP connection limit storage for the server. + * + * 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 helps the server discard excess TCP connections. + */ +#include "config.h" +#include "util/regional.h" +#include "util/log.h" +#include "util/config_file.h" +#include "util/net_help.h" +#include "util/tcp_conn_limit.h" +#include "services/localzone.h" +#include "sldns/str2wire.h" + +struct tcl_list* +tcl_list_create(void) +{ + struct tcl_list* tcl = (struct tcl_list*)calloc(1, + sizeof(struct tcl_list)); + if(!tcl) + return NULL; + tcl->region = regional_create(); + if(!tcl->region) { + tcl_list_delete(tcl); + return NULL; + } + return tcl; +} + +static void +tcl_list_free_node(rbnode_type* node, void* ATTR_UNUSED(arg)) +{ + struct tcl_addr* n = (struct tcl_addr*) node; + lock_quick_destroy(&n->lock); +#ifdef THREADS_DISABLED + (void)n; +#endif +} + +void +tcl_list_delete(struct tcl_list* tcl) +{ + if(!tcl) + return; + traverse_postorder(&tcl->tree, tcl_list_free_node, NULL); + regional_destroy(tcl->region); + free(tcl); +} + +/** insert new address into tcl_list structure */ +static struct tcl_addr* +tcl_list_insert(struct tcl_list* tcl, struct sockaddr_storage* addr, + socklen_t addrlen, int net, uint32_t limit, + int complain_duplicates) +{ + struct tcl_addr* node = regional_alloc_zero(tcl->region, + sizeof(struct tcl_addr)); + if(!node) + return NULL; + lock_quick_init(&node->lock); + node->limit = limit; + if(!addr_tree_insert(&tcl->tree, &node->node, addr, addrlen, net)) { + if(complain_duplicates) + verbose(VERB_QUERY, "duplicate tcl address ignored."); + } + return node; +} + +/** apply tcl_list string */ +static int +tcl_list_str_cfg(struct tcl_list* tcl, const char* str, const char* s2, + int complain_duplicates) +{ + struct sockaddr_storage addr; + int net; + socklen_t addrlen; + uint32_t limit; + if(atoi(s2) < 0) { + log_err("bad connection limit %s", s2); + return 0; + } + limit = (uint32_t)atoi(s2); + if(!netblockstrtoaddr(str, UNBOUND_DNS_PORT, &addr, &addrlen, &net)) { + log_err("cannot parse connection limit netblock: %s", str); + return 0; + } + if(!tcl_list_insert(tcl, &addr, addrlen, net, limit, + complain_duplicates)) { + log_err("out of memory"); + return 0; + } + return 1; +} + +/** read tcl_list config */ +static int +read_tcl_list(struct tcl_list* tcl, struct config_file* cfg) +{ + struct config_str2list* p; + for(p = cfg->tcp_connection_limits; p; p = p->next) { + log_assert(p->str && p->str2); + if(!tcl_list_str_cfg(tcl, p->str, p->str2, 1)) + return 0; + } + return 1; +} + +int +tcl_list_apply_cfg(struct tcl_list* tcl, struct config_file* cfg) +{ + regional_free_all(tcl->region); + addr_tree_init(&tcl->tree); + if(!read_tcl_list(tcl, cfg)) + return 0; + addr_tree_init_parents(&tcl->tree); + return 1; +} + +int +tcl_new_connection(struct tcl_addr* tcl) +{ + if(tcl) { + int res = 1; + lock_quick_lock(&tcl->lock); + if(tcl->count >= tcl->limit) + res = 0; + else + tcl->count++; + lock_quick_unlock(&tcl->lock); + return res; + } + return 1; +} + +void +tcl_close_connection(struct tcl_addr* tcl) +{ + if(tcl) { + lock_quick_lock(&tcl->lock); + log_assert(tcl->count > 0); + tcl->count--; + lock_quick_unlock(&tcl->lock); + } +} + +struct tcl_addr* +tcl_addr_lookup(struct tcl_list* tcl, struct sockaddr_storage* addr, + socklen_t addrlen) +{ + return (struct tcl_addr*)addr_tree_lookup(&tcl->tree, + addr, addrlen); +} + +size_t +tcl_list_get_mem(struct tcl_list* tcl) +{ + if(!tcl) return 0; + return sizeof(*tcl) + regional_get_mem(tcl->region); +} --- contrib/unbound/util/tcp_conn_limit.h.orig +++ contrib/unbound/util/tcp_conn_limit.h @@ -0,0 +1,130 @@ +/* + * daemon/tcp_conn_limit.h - client TCP connection limit storage for the server. + * + * 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 keeps track of the limit on the number of TCP connections + * each client makes the server. + */ + +#ifndef DAEMON_TCP_CONN_LIMIT_H +#define DAEMON_TCP_CONN_LIMIT_H +#include "util/storage/dnstree.h" +#include "util/locks.h" +struct config_file; +struct regional; + +/** + * TCP connection limit storage structure + */ +struct tcl_list { + /** regional for allocation */ + struct regional* region; + /** + * Tree of the addresses that are TCP connection limited. + * contents of type tcl_addr. + */ + rbtree_type tree; +}; + +/** + * + * An address span with connection limit information + */ +struct tcl_addr { + /** node in address tree */ + struct addr_tree_node node; + /** lock on structure data */ + lock_quick_type lock; + /** connection limit on this netblock */ + uint32_t limit; + /** current connection count on this netblock */ + uint32_t count; +}; + +/** + * Create TCP connection limit structure + * @return new structure or NULL on error. + */ +struct tcl_list* tcl_list_create(void); + +/** + * Delete TCP connection limit structure. + * @param tcl: to delete. + */ +void tcl_list_delete(struct tcl_list* tcl); + +/** + * Process TCP connection limit config. + * @param tcl: where to store. + * @param cfg: config options. + * @return 0 on error. + */ +int tcl_list_apply_cfg(struct tcl_list* tcl, struct config_file* cfg); + +/** + * Increment TCP connection count if found, provided the + * count was below the limit. + * @param tcl: structure for tcl storage, or NULL. + * @return: 0 if limit reached, 1 if tcl was NULL or limit not reached. + */ +int tcl_new_connection(struct tcl_addr* tcl); + +/** + * Decrement TCP connection count if found. + * @param tcl: structure for tcl storage, or NULL. + */ +void tcl_close_connection(struct tcl_addr* tcl); + +/** + * Lookup address to see its TCP connection limit structure + * @param tcl: structure for address storage. + * @param addr: address to check + * @param addrlen: length of addr. + * @return: tcl structure from this address. + */ +struct tcl_addr* +tcl_addr_lookup(struct tcl_list* tcl, struct sockaddr_storage* addr, + socklen_t addrlen); + +/** + * Get memory used by TCP connection limit structure. + * @param tcl: structure for address storage. + * @return bytes in use. + */ +size_t tcl_list_get_mem(struct tcl_list* tcl); + +#endif /* DAEMON_TCP_CONN_LIMIT_H */ --- contrib/unbound/util/timehist.c.orig +++ contrib/unbound/util/timehist.c @@ -225,7 +225,7 @@ } void -timehist_export(struct timehist* hist, size_t* array, size_t sz) +timehist_export(struct timehist* hist, long long* array, size_t sz) { size_t i; if(!hist) return; @@ -232,11 +232,11 @@ if(sz > hist->num) sz = hist->num; for(i=0; ibuckets[i].count; + array[i] = (long long)hist->buckets[i].count; } void -timehist_import(struct timehist* hist, size_t* array, size_t sz) +timehist_import(struct timehist* hist, long long* array, size_t sz) { size_t i; if(!hist) return; @@ -243,5 +243,5 @@ if(sz > hist->num) sz = hist->num; for(i=0; ibuckets[i].count = array[i]; + hist->buckets[i].count = (size_t)array[i]; } --- contrib/unbound/util/timehist.h.orig +++ contrib/unbound/util/timehist.h @@ -121,7 +121,7 @@ * @param array: the array to export to. * @param sz: number of items in array. */ -void timehist_export(struct timehist* hist, size_t* array, size_t sz); +void timehist_export(struct timehist* hist, long long* array, size_t sz); /** * Import histogram from an array. @@ -129,6 +129,6 @@ * @param array: the array to import from. * @param sz: number of items in array. */ -void timehist_import(struct timehist* hist, size_t* array, size_t sz); +void timehist_import(struct timehist* hist, long long* array, size_t sz); #endif /* UTIL_TIMEHIST_H */ --- contrib/unbound/util/tube.c.orig +++ contrib/unbound/util/tube.c @@ -426,7 +426,7 @@ } int tube_setup_bg_listen(struct tube* tube, struct comm_base* base, - tube_callback_t* cb, void* arg) + tube_callback_type* cb, void* arg) { tube->listen_cb = cb; tube->listen_arg = arg; @@ -454,8 +454,9 @@ int tube_queue_item(struct tube* tube, uint8_t* msg, size_t len) { - struct tube_res_list* item = - (struct tube_res_list*)malloc(sizeof(*item)); + struct tube_res_list* item; + if(!tube || !tube->res_com) return 0; + item = (struct tube_res_list*)malloc(sizeof(*item)); if(!item) { free(msg); log_err("out of memory for async answer"); @@ -667,7 +668,7 @@ } int tube_setup_bg_listen(struct tube* tube, struct comm_base* base, - tube_callback_t* cb, void* arg) + tube_callback_type* cb, void* arg) { tube->listen_cb = cb; tube->listen_arg = arg; @@ -687,8 +688,9 @@ int tube_queue_item(struct tube* tube, uint8_t* msg, size_t len) { - struct tube_res_list* item = - (struct tube_res_list*)malloc(sizeof(*item)); + struct tube_res_list* item; + if(!tube) return 0; + item = (struct tube_res_list*)malloc(sizeof(*item)); verbose(VERB_ALGO, "tube queue_item len %d", (int)len); if(!item) { free(msg); --- contrib/unbound/util/tube.h.orig +++ contrib/unbound/util/tube.h @@ -55,7 +55,7 @@ * void mycallback(tube, msg, len, error, user_argument); * if error is true (NETEVENT_*), msg is probably NULL. */ -typedef void tube_callback_t(struct tube*, uint8_t*, size_t, int, void*); +typedef void tube_callback_type(struct tube*, uint8_t*, size_t, int, void*); /** * A pipe @@ -70,7 +70,7 @@ /** listen commpoint */ struct comm_point* listen_com; /** listen callback */ - tube_callback_t* listen_cb; + tube_callback_type* listen_cb; /** listen callback user arg */ void* listen_arg; /** are we currently reading a command, 0 if not, else bytecount */ @@ -92,7 +92,7 @@ #else /* USE_WINSOCK */ /** listen callback */ - tube_callback_t* listen_cb; + tube_callback_type* listen_cb; /** listen callback user arg */ void* listen_arg; /** the windows sockets event (signaled if items in pipe) */ @@ -101,7 +101,7 @@ struct ub_event* ev_listen; /** lock on the list of outstanding items */ - lock_basic_t res_lock; + lock_basic_type res_lock; /** list of outstanding results on pipe */ struct tube_res_list* res_list; /** last in list */ @@ -222,7 +222,7 @@ * @return true if successful, false on error. */ int tube_setup_bg_listen(struct tube* tube, struct comm_base* base, - tube_callback_t* cb, void* arg); + tube_callback_type* cb, void* arg); /** * Remove bg listen setup from event base. --- contrib/unbound/util/ub_event.c.orig +++ contrib/unbound/util/ub_event.c @@ -95,6 +95,7 @@ UB_EV_BITS_CB(comm_signal_callback) UB_EV_BITS_CB(comm_point_local_handle_callback) UB_EV_BITS_CB(comm_point_raw_handle_callback) +UB_EV_BITS_CB(comm_point_http_handle_callback) UB_EV_BITS_CB(tube_handle_signal) UB_EV_BITS_CB(comm_base_handle_slow_accept) @@ -116,12 +117,17 @@ return my_comm_point_local_handle_callback; else if(cb == comm_point_raw_handle_callback) return my_comm_point_raw_handle_callback; + else if(cb == comm_point_http_handle_callback) + return my_comm_point_http_handle_callback; else if(cb == tube_handle_signal) return my_tube_handle_signal; else if(cb == comm_base_handle_slow_accept) return my_comm_base_handle_slow_accept; - else + else { + log_assert(0); /* this NULL callback pointer should not happen, + we should have the necessary routine listed above */ return NULL; + } } #else # define NATIVE_BITS(b) (b) @@ -289,11 +295,18 @@ if (!ev) return NULL; +#ifndef HAVE_EVENT_ASSIGN event_set(ev, fd, NATIVE_BITS(bits), NATIVE_BITS_CB(cb), arg); if (event_base_set(AS_EVENT_BASE(base), ev) != 0) { free(ev); return NULL; } +#else + if (event_assign(ev, AS_EVENT_BASE(base), fd, bits, cb, arg) != 0) { + free(ev); + return NULL; + } +#endif return AS_UB_EVENT(ev); } @@ -306,11 +319,18 @@ if (!ev) return NULL; +#if !HAVE_DECL_EVSIGNAL_ASSIGN signal_set(ev, fd, NATIVE_BITS_CB(cb), arg); if (event_base_set(AS_EVENT_BASE(base), ev) != 0) { free(ev); return NULL; } +#else + if (evsignal_assign(ev, AS_EVENT_BASE(base), fd, cb, arg) != 0) { + free(ev); + return NULL; + } +#endif return AS_UB_EVENT(ev); } @@ -427,7 +447,7 @@ void ub_comm_base_now(struct comm_base* cb) { - #ifdef USE_MINI_EVENT +#ifdef USE_MINI_EVENT /** minievent updates the time when it blocks. */ (void)cb; /* nothing to do */ #else /* !USE_MINI_EVENT */ @@ -438,7 +458,9 @@ if(gettimeofday(tv, NULL) < 0) { log_err("gettimeofday: %s", strerror(errno)); } +#ifndef S_SPLINT_S *tt = tv->tv_sec; +#endif #endif /* USE_MINI_EVENT */ } --- contrib/unbound/util/ub_event.h.orig +++ contrib/unbound/util/ub_event.h @@ -67,7 +67,7 @@ /** Return the name, system and method for the pluggable event base */ void ub_get_event_sys(struct ub_event_base*, const char** n, const char** s, const char** m); -/** Return a default event base. In the deamon thess will be the only event +/** Return a default event base. In the daemon this will be the only event * bases used. */ struct ub_event_base* ub_default_event_base(int, time_t*, struct timeval*); --- contrib/unbound/util/ub_event_pluggable.c.orig +++ contrib/unbound/util/ub_event_pluggable.c @@ -453,7 +453,7 @@ * ub_base is guaranteed to exist and to be the default * event base. */ - assert(b); + assert(b != NULL); *n = "pluggable-event"; *s = event_get_version(); # if defined(HAVE_EV_LOOP) || defined(HAVE_EV_DEFAULT_LOOP) @@ -687,6 +687,8 @@ if(gettimeofday(tv, NULL) < 0) { log_err("gettimeofday: %s", strerror(errno)); } +#ifndef S_SPLINT_S *tt = tv->tv_sec; +#endif } --- contrib/unbound/util/winsock_event.c.orig +++ contrib/unbound/util/winsock_event.c @@ -169,7 +169,7 @@ #endif verbose(VERB_CLIENT, "winsock_event handle_timeouts"); - while((rbnode_t*)(p = (struct event*)rbtree_first(base->times)) + while((rbnode_type*)(p = (struct event*)rbtree_first(base->times)) !=RBTREE_NULL) { #ifndef S_SPLINT_S if(p->ev_timeout.tv_sec > now->tv_sec || @@ -558,7 +558,7 @@ struct timeval *now = ev->ev_base->time_tv; ev->ev_timeout.tv_sec = tv->tv_sec + now->tv_sec; ev->ev_timeout.tv_usec = tv->tv_usec + now->tv_usec; - while(ev->ev_timeout.tv_usec > 1000000) { + while(ev->ev_timeout.tv_usec >= 1000000) { ev->ev_timeout.tv_usec -= 1000000; ev->ev_timeout.tv_sec++; } --- contrib/unbound/util/winsock_event.h.orig +++ contrib/unbound/util/winsock_event.h @@ -132,7 +132,7 @@ struct event_base { /** sorted by timeout (absolute), ptr */ - rbtree_t* times; + rbtree_type* times; /** array (first part in use) of handles to work on */ struct event** items; /** number of items in use in array */ @@ -169,7 +169,7 @@ */ struct event { /** node in timeout rbtree */ - rbnode_t node; + rbnode_type node; /** is event already added */ int added; --- contrib/unbound/validator/autotrust.c.orig +++ contrib/unbound/validator/autotrust.c @@ -86,7 +86,6 @@ if(!global) return; /* elements deleted by parent */ - memset(global, 0, sizeof(*global)); free(global); } @@ -370,10 +369,10 @@ free(tp); return NULL; } - lock_basic_unlock(&anchors->lock); lock_basic_init(&tp->lock); lock_protect(&tp->lock, tp, sizeof(*tp)); lock_protect(&tp->lock, tp->autr, sizeof(*tp->autr)); + lock_basic_unlock(&anchors->lock); return tp; } @@ -718,6 +717,7 @@ list_i = list; i = 0; while(iter(&list_i, &rr, &rr_len, &dname_len)) { + log_assert(data->rr_data[i]); memmove(data->rr_data[i], sldns_wirerr_get_rdatawl(rr, rr_len, dname_len), data->rr_len[i]); @@ -1064,7 +1064,7 @@ /** string for a trustanchor state */ static const char* -trustanchor_state2str(autr_state_t s) +trustanchor_state2str(autr_state_type s) { switch (s) { case AUTR_STATE_START: return " START "; @@ -1174,6 +1174,9 @@ { FILE* out; char* fname = tp->autr->file; +#ifndef S_SPLINT_S + long long llvalue; +#endif char tempf[2048]; log_assert(tp->autr); if(!env) { @@ -1180,9 +1183,23 @@ log_err("autr_write_file: Module environment is NULL."); return; } - /* unique name with pid number and thread number */ - snprintf(tempf, sizeof(tempf), "%s.%d-%d", fname, (int)getpid(), - env->worker?*(int*)env->worker:0); + /* unique name with pid number, thread number, and struct pointer + * (the pointer uniquifies for multiple libunbound contexts) */ +#ifndef S_SPLINT_S +#if defined(SIZE_MAX) && defined(UINT32_MAX) && (UINT32_MAX == SIZE_MAX || INT32_MAX == SIZE_MAX) + /* avoid warning about upcast on 32bit systems */ + llvalue = (unsigned long)tp; +#else + llvalue = (unsigned long long)tp; +#endif +#ifndef USE_WINSOCK + snprintf(tempf, sizeof(tempf), "%s.%d-%d-%llx", fname, (int)getpid(), + env->worker?*(int*)env->worker:0, llvalue); +#else + snprintf(tempf, sizeof(tempf), "%s.%d-%d-%I64x", fname, (int)getpid(), + env->worker?*(int*)env->worker:0, llvalue); +#endif +#endif /* S_SPLINT_S */ verbose(VERB_ALGO, "autotrust: write to disk: %s", tempf); out = fopen(tempf, "w"); if(!out) { @@ -1227,17 +1244,20 @@ * @param ve: validator environment (with options) for verification. * @param tp: trust point to verify with * @param rrset: DNSKEY rrset to verify. + * @param qstate: qstate with region. * @return false on failure, true if verification successful. */ static int verify_dnskey(struct module_env* env, struct val_env* ve, - struct trust_anchor* tp, struct ub_packed_rrset_key* rrset) + struct trust_anchor* tp, struct ub_packed_rrset_key* rrset, + struct module_qstate* qstate) { char* reason = NULL; uint8_t sigalg[ALGO_NEEDS_MAX+1]; int downprot = env->cfg->harden_algo_downgrade; enum sec_status sec = val_verify_DNSKEY_with_TA(env, ve, rrset, - tp->ds_rrset, tp->dnskey_rrset, downprot?sigalg:NULL, &reason); + tp->ds_rrset, tp->dnskey_rrset, downprot?sigalg:NULL, &reason, + qstate); /* sigalg is ignored, it returns algorithms signalled to exist, but * in 5011 there are no other rrsets to check. if downprot is * enabled, then it checks that the DNSKEY is signed with all @@ -1276,7 +1296,8 @@ /** Is rr self-signed revoked key */ static int rr_is_selfsigned_revoked(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key* dnskey_rrset, size_t i) + struct ub_packed_rrset_key* dnskey_rrset, size_t i, + struct module_qstate* qstate) { enum sec_status sec; char* reason = NULL; @@ -1285,7 +1306,7 @@ /* no algorithm downgrade protection necessary, if it is selfsigned * revoked it can be removed. */ sec = dnskey_verify_rrset(env, ve, dnskey_rrset, dnskey_rrset, i, - &reason); + &reason, LDNS_SECTION_ANSWER, qstate); return (sec == sec_status_secure); } @@ -1501,7 +1522,7 @@ static void check_contains_revoked(struct module_env* env, struct val_env* ve, struct trust_anchor* tp, struct ub_packed_rrset_key* dnskey_rrset, - int* changed) + int* changed, struct module_qstate* qstate) { struct packed_rrset_data* dd = (struct packed_rrset_data*) dnskey_rrset->entry.data; @@ -1521,7 +1542,7 @@ } if(!ta) continue; /* key not found */ - if(rr_is_selfsigned_revoked(env, ve, dnskey_rrset, i)) { + if(rr_is_selfsigned_revoked(env, ve, dnskey_rrset, i, qstate)) { /* checked if there is an rrsig signed by this key. */ /* same keytag, but stored can be revoked already, so * compare keytags, with +0 or +128(REVOKE flag) */ @@ -1571,6 +1592,11 @@ verbose(VERB_ALGO, "DS match attempt failed"); continue; } + /* match of hash is sufficient for bootstrap of trust point */ + (void)reason; + (void)ve; + return 1; + /* no need to check RRSIG, DS hash already matched with source if(dnskey_verify_rrset(env, ve, dnskey_rrset, dnskey_rrset, key_idx, &reason) == sec_status_secure) { return 1; @@ -1578,6 +1604,7 @@ verbose(VERB_ALGO, "DS match failed because the key " "does not verify the keyset: %s", reason); } + */ } return 0; } @@ -1679,7 +1706,7 @@ /** Set the state for this trust anchor */ static void set_trustanchor_state(struct module_env* env, struct autr_ta* ta, int* changed, - autr_state_t s) + autr_state_type s) { verbose_key(ta, VERB_ALGO, "update: %s to %s", trustanchor_state2str(ta->s), trustanchor_state2str(s)); @@ -1989,7 +2016,7 @@ static time_t wait_probe_time(struct val_anchors* anchors) { - rbnode_t* t = rbtree_first(&anchors->autr->probe); + rbnode_type* t = rbtree_first(&anchors->autr->probe); if(t != RBTREE_NULL) return ((struct trust_anchor*)t->key)->autr->next_probe_time; return 0; @@ -2112,7 +2139,8 @@ } int autr_process_prime(struct module_env* env, struct val_env* ve, - struct trust_anchor* tp, struct ub_packed_rrset_key* dnskey_rrset) + struct trust_anchor* tp, struct ub_packed_rrset_key* dnskey_rrset, + struct module_qstate* qstate) { int changed = 0; log_assert(tp && tp->autr); @@ -2148,12 +2176,12 @@ verbose(VERB_ALGO, "autotrust: no dnskey rrset"); /* no update of query_failed, because then we would have * to write to disk. But we cannot because we maybe are - * still 'initialising' with DS records, that we cannot write + * still 'initializing' with DS records, that we cannot write * in the full format (which only contains KSKs). */ return 1; /* trust point exists */ } /* check for revoked keys to remove immediately */ - check_contains_revoked(env, ve, tp, dnskey_rrset, &changed); + check_contains_revoked(env, ve, tp, dnskey_rrset, &changed, qstate); if(changed) { verbose(VERB_ALGO, "autotrust: revokedkeys, reassemble"); if(!autr_assemble(tp)) { @@ -2169,7 +2197,7 @@ } } /* verify the dnskey rrset and see if it is valid. */ - if(!verify_dnskey(env, ve, tp, dnskey_rrset)) { + if(!verify_dnskey(env, ve, tp, dnskey_rrset, qstate)) { verbose(VERB_ALGO, "autotrust: dnskey did not verify."); /* only increase failure count if this is not the first prime, * this means there was a previous successful probe */ @@ -2233,7 +2261,7 @@ log_info("out of memory in debug_print_ta"); return; } - if(str && str[0]) str[strlen(str)-1]=0; /* remove newline */ + if(str[0]) str[strlen(str)-1]=0; /* remove newline */ ctime_r(&ta->last_change, buf); if(buf[0]) buf[strlen(buf)-1]=0; /* remove newline */ log_info("[%s] %s ;;state:%d ;;pending_count:%d%s%s last:%s", @@ -2255,10 +2283,10 @@ log_info("assembled %d DS and %d DNSKEYs", (int)tp->numDS, (int)tp->numDNSKEY); if(tp->ds_rrset) { - log_packed_rrset(0, "DS:", tp->ds_rrset); + log_packed_rrset(NO_VERBOSE, "DS:", tp->ds_rrset); } if(tp->dnskey_rrset) { - log_packed_rrset(0, "DNSKEY:", tp->dnskey_rrset); + log_packed_rrset(NO_VERBOSE, "DNSKEY:", tp->dnskey_rrset); } log_info("file %s", tp->autr->file); ctime_r(&tp->autr->last_queried, buf); @@ -2295,7 +2323,7 @@ void probe_answer_cb(void* arg, int ATTR_UNUSED(rcode), sldns_buffer* ATTR_UNUSED(buf), enum sec_status ATTR_UNUSED(sec), - char* ATTR_UNUSED(why_bogus)) + char* ATTR_UNUSED(why_bogus), int ATTR_UNUSED(was_ratelimited)) { /* retry was set before the query was done, * re-querytime is set when query succeeded, but that may not @@ -2328,6 +2356,7 @@ qinfo.qname_len = tp->namelen; qinfo.qtype = LDNS_RR_TYPE_DNSKEY; qinfo.qclass = tp->dclass; + qinfo.local_alias = NULL; log_query_info(VERB_ALGO, "autotrust probe", &qinfo); verbose(VERB_ALGO, "retry probe set in %d seconds", (int)tp->autr->next_probe_time - (int)*env->now); @@ -2362,7 +2391,7 @@ todo_probe(struct module_env* env, time_t* next) { struct trust_anchor* tp; - rbnode_t* el; + rbnode_type* el; /* get first one */ lock_basic_lock(&env->anchors->lock); if( (el=rbtree_first(&env->anchors->autr->probe)) == RBTREE_NULL) { --- contrib/unbound/validator/autotrust.h.orig +++ contrib/unbound/validator/autotrust.h @@ -47,6 +47,7 @@ struct trust_anchor; struct ub_packed_rrset_key; struct module_env; +struct module_qstate; struct val_env; struct sldns_buffer; @@ -58,7 +59,7 @@ AUTR_STATE_MISSING = 3, AUTR_STATE_REVOKED = 4, AUTR_STATE_REMOVED = 5 -} autr_state_t; +} autr_state_type; /** * Autotrust metadata for one trust anchor key. @@ -73,7 +74,7 @@ /** last update of key state (new pending count keeps date the same) */ time_t last_change; /** 5011 state */ - autr_state_t s; + autr_state_type s; /** pending count */ uint8_t pending_count; /** fresh TA was seen */ @@ -90,7 +91,7 @@ /** file to store the trust point in. chrootdir already applied. */ char* file; /** rbtree node for probe sort, key is struct trust_anchor */ - rbnode_t pnode; + rbnode_type pnode; /** the keys */ struct autr_ta* keys; @@ -126,7 +127,7 @@ struct autr_global_data { /** rbtree of autotrust anchors sorted by next probe time. * When time is equal, sorted by anchor class, name. */ - rbtree_t probe; + rbtree_type probe; }; /** @@ -188,12 +189,14 @@ * @param tp: trust anchor to process. * @param dnskey_rrset: DNSKEY rrset probed (can be NULL if bad prime result). * allocated in a region. Has not been validated yet. + * @param qstate: qstate with region. * @return false if trust anchor was revoked completely. * Otherwise logs errors to log, does not change return value. * On errors, likely the trust point has been unchanged. */ int autr_process_prime(struct module_env* env, struct val_env* ve, - struct trust_anchor* tp, struct ub_packed_rrset_key* dnskey_rrset); + struct trust_anchor* tp, struct ub_packed_rrset_key* dnskey_rrset, + struct module_qstate* qstate); /** * Debug printout of rfc5011 tracked anchors @@ -203,6 +206,6 @@ /** callback for query answer to 5011 probe */ void probe_answer_cb(void* arg, int rcode, struct sldns_buffer* buf, - enum sec_status sec, char* errinf); + enum sec_status sec, char* errinf, int was_ratelimited); #endif /* VALIDATOR_AUTOTRUST_H */ --- contrib/unbound/validator/val_anchor.c.orig +++ contrib/unbound/validator/val_anchor.c @@ -113,7 +113,7 @@ /** destroy locks in tree and delete autotrust anchors */ static void -anchors_delfunc(rbnode_t* elem, void* ATTR_UNUSED(arg)) +anchors_delfunc(rbnode_type* elem, void* ATTR_UNUSED(arg)) { struct trust_anchor* ta = (struct trust_anchor*)elem; if(!ta) return; @@ -198,7 +198,7 @@ size_t namelen, uint16_t dclass) { struct trust_anchor key; - rbnode_t* n; + rbnode_type* n; if(!name) return NULL; key.node.key = &key; key.name = name; @@ -222,7 +222,7 @@ size_t namelen, uint16_t dclass, int lockit) { #ifdef UNBOUND_DEBUG - rbnode_t* r; + rbnode_type* r; #endif struct trust_anchor* ta = (struct trust_anchor*)malloc( sizeof(struct trust_anchor)); @@ -990,7 +990,7 @@ size_t nods, nokey; lock_basic_lock(&anchors->lock); ta=(struct trust_anchor*)rbtree_first(anchors->tree); - while((rbnode_t*)ta != RBTREE_NULL) { + 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)) { @@ -1007,12 +1007,12 @@ nods = anchors_ds_unsupported(ta); nokey = anchors_dnskey_unsupported(ta); if(nods) { - log_nametypeclass(0, "warning: unsupported " + log_nametypeclass(NO_VERBOSE, "warning: unsupported " "algorithm for trust anchor", ta->name, LDNS_RR_TYPE_DS, ta->dclass); } if(nokey) { - log_nametypeclass(0, "warning: unsupported " + log_nametypeclass(NO_VERBOSE, "warning: unsupported " "algorithm for trust anchor", ta->name, LDNS_RR_TYPE_DNSKEY, ta->dclass); } @@ -1164,7 +1164,7 @@ { struct trust_anchor key; struct trust_anchor* result; - rbnode_t* res = NULL; + rbnode_type* res = NULL; key.node.key = &key; key.name = qname; key.namelabs = dname_count_labels(qname); @@ -1273,3 +1273,80 @@ 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; icount; 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; icount; 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; ierrinf && qstate->env->cfg->val_log_level >= 2) { /* on malloc failure there is simply no reason string */ - key_entry_set_reason(k, errinf_to_str(qstate)); + key_entry_set_reason(k, errinf_to_str_bogus(qstate)); } key_entry_hash(k); slabhash_insert(kcache->slab, k->entry.hash, &k->entry, --- contrib/unbound/validator/val_neg.c.orig +++ contrib/unbound/validator/val_neg.c @@ -111,7 +111,7 @@ /** clear datas on cache deletion */ static void -neg_clear_datas(rbnode_t* n, void* ATTR_UNUSED(arg)) +neg_clear_datas(rbnode_type* n, void* ATTR_UNUSED(arg)) { struct val_neg_data* d = (struct val_neg_data*)n; free(d->name); @@ -120,7 +120,7 @@ /** clear zones on cache deletion */ static void -neg_clear_zones(rbnode_t* n, void* ATTR_UNUSED(arg)) +neg_clear_zones(rbnode_type* n, void* ATTR_UNUSED(arg)) { struct val_neg_zone* z = (struct val_neg_zone*)n; /* delete all the rrset entries in the tree */ @@ -235,6 +235,7 @@ /* remove it from the lru list */ neg_lru_remove(neg, el); + log_assert(neg->first != el && neg->last != el); /* go up the tree and reduce counts */ p = el; @@ -371,7 +372,7 @@ { struct val_neg_zone key; struct val_neg_zone* result; - rbnode_t* res = NULL; + rbnode_type* res = NULL; key.node.key = &key; key.name = nm; key.len = nm_len; @@ -411,7 +412,7 @@ { struct val_neg_data key; struct val_neg_data* result; - rbnode_t* res = NULL; + rbnode_type* res = NULL; key.node.key = &key; key.name = nm; key.len = nm_len; @@ -677,7 +678,7 @@ uint8_t* end; size_t end_len; int end_labs, m; - rbnode_t* walk, *next; + rbnode_type* walk, *next; struct val_neg_data* cur; uint8_t buf[257]; /* get endpoint */ @@ -847,34 +848,71 @@ wipeout(neg, zone, el, nsec); } +/** see if the reply has signed NSEC records and return the signer */ +static uint8_t* reply_nsec_signer(struct reply_info* rep, size_t* signer_len, + uint16_t* dclass) +{ + size_t i; + struct packed_rrset_data* d; + uint8_t* s; + 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) { + d = (struct packed_rrset_data*)rep->rrsets[i]-> + entry.data; + /* return first signer name of first NSEC */ + if(d->rrsig_count != 0) { + val_find_rrset_signer(rep->rrsets[i], + &s, signer_len); + if(s && *signer_len) { + *dclass = ntohs(rep->rrsets[i]-> + rk.rrset_class); + return s; + } + } + } + } + return 0; +} + void val_neg_addreply(struct val_neg_cache* neg, struct reply_info* rep) { size_t i, need; struct ub_packed_rrset_key* soa; + uint8_t* dname = NULL; + size_t dname_len; + uint16_t rrset_class; struct val_neg_zone* zone; /* see if secure nsecs inside */ if(!reply_has_nsec(rep)) return; /* find the zone name in message */ - soa = reply_find_soa(rep); - if(!soa) - return; + if((soa = reply_find_soa(rep))) { + dname = soa->rk.dname; + dname_len = soa->rk.dname_len; + rrset_class = ntohs(soa->rk.rrset_class); + } + else { + /* No SOA in positive (wildcard) answer. Use signer from the + * validated answer RRsets' signature. */ + if(!(dname = reply_nsec_signer(rep, &dname_len, &rrset_class))) + return; + } log_nametypeclass(VERB_ALGO, "negcache insert for zone", - soa->rk.dname, LDNS_RR_TYPE_SOA, ntohs(soa->rk.rrset_class)); + dname, LDNS_RR_TYPE_SOA, rrset_class); /* ask for enough space to store all of it */ need = calc_data_need(rep) + - calc_zone_need(soa->rk.dname, soa->rk.dname_len); + calc_zone_need(dname, dname_len); lock_basic_lock(&neg->lock); neg_make_space(neg, need); /* find or create the zone entry */ - zone = neg_find_zone(neg, soa->rk.dname, soa->rk.dname_len, - ntohs(soa->rk.rrset_class)); + zone = neg_find_zone(neg, dname, dname_len, rrset_class); if(!zone) { - if(!(zone = neg_create_zone(neg, soa->rk.dname, - soa->rk.dname_len, ntohs(soa->rk.rrset_class)))) { + if(!(zone = neg_create_zone(neg, dname, dname_len, + rrset_class))) { lock_basic_unlock(&neg->lock); log_err("out of memory adding negative zone"); return; @@ -911,7 +949,7 @@ uint8_t* qname, size_t len, int labs, struct val_neg_data** data) { struct val_neg_data key; - rbnode_t* r; + rbnode_type* r; key.node.key = &key; key.name = qname; key.len = len; @@ -1007,6 +1045,7 @@ qinfo.qname = qname; qinfo.qtype = LDNS_RR_TYPE_DLV; qinfo.qclass = qclass; + qinfo.local_alias = NULL; if(!nsec_proves_nodata(nsec, &qinfo, &wc) && !val_nsec_proves_name_error(nsec, qname)) { /* the NSEC is not a denial for the DLV */ @@ -1028,33 +1067,6 @@ return 1; } -/** see if the reply has signed NSEC records and return the signer */ -static uint8_t* reply_nsec_signer(struct reply_info* rep, size_t* signer_len, - uint16_t* dclass) -{ - size_t i; - struct packed_rrset_data* d; - uint8_t* s; - 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) { - d = (struct packed_rrset_data*)rep->rrsets[i]-> - entry.data; - /* return first signer name of first NSEC */ - if(d->rrsig_count != 0) { - val_find_rrset_signer(rep->rrsets[i], - &s, signer_len); - if(s && *signer_len) { - *dclass = ntohs(rep->rrsets[i]-> - rk.rrset_class); - return s; - } - } - } - } - return 0; -} - void val_neg_addreferral(struct val_neg_cache* neg, struct reply_info* rep, uint8_t* zone_name) { @@ -1182,6 +1194,73 @@ return r; } +/** + * Get best NSEC record for qname. Might be matching, covering or totally + * useless. + * @param neg_cache: neg cache + * @param qname: to lookup rrset name + * @param qname_len: length of qname. + * @param qclass: class of rrset to lookup, host order + * @param rrset_cache: rrset cache + * @param now: to check ttl against + * @param region: where to alloc result + * @return rrset or NULL + */ +static struct ub_packed_rrset_key* +neg_find_nsec(struct val_neg_cache* neg_cache, uint8_t* qname, size_t qname_len, + uint16_t qclass, struct rrset_cache* rrset_cache, time_t now, + struct regional* region) +{ + int labs; + uint32_t flags; + struct val_neg_zone* zone; + struct val_neg_data* data; + struct ub_packed_rrset_key* nsec; + + labs = dname_count_labels(qname); + lock_basic_lock(&neg_cache->lock); + zone = neg_closest_zone_parent(neg_cache, qname, qname_len, labs, + qclass); + while(zone && !zone->in_use) + zone = zone->parent; + if(!zone) { + lock_basic_unlock(&neg_cache->lock); + return NULL; + } + + /* NSEC only for now */ + if(zone->nsec3_hash) { + lock_basic_unlock(&neg_cache->lock); + return NULL; + } + + /* ignore return value, don't care if it is an exact or smaller match */ + (void)neg_closest_data(zone, qname, qname_len, labs, &data); + if(!data) { + lock_basic_unlock(&neg_cache->lock); + return NULL; + } + + /* ENT nodes are not in use, try the previous node. If the previous node + * is not in use, we don't have an useful NSEC and give up. */ + if(!data->in_use) { + data = (struct val_neg_data*)rbtree_previous((rbnode_type*)data); + if((rbnode_type*)data == RBTREE_NULL || !data->in_use) { + lock_basic_unlock(&neg_cache->lock); + return NULL; + } + } + + flags = 0; + if(query_dname_compare(data->name, zone->name) == 0) + flags = PACKED_RRSET_NSEC_AT_APEX; + + nsec = grab_nsec(rrset_cache, data->name, data->len, LDNS_RR_TYPE_NSEC, + zone->dclass, flags, region, 0, 0, now); + lock_basic_unlock(&neg_cache->lock); + return nsec; +} + /** find nsec3 closest encloser in neg cache */ static struct val_neg_data* neg_find_nsec3_ce(struct val_neg_zone* zone, uint8_t* qname, size_t qname_len, @@ -1399,41 +1478,144 @@ struct dns_msg* val_neg_getmsg(struct val_neg_cache* neg, struct query_info* qinfo, struct regional* region, struct rrset_cache* rrset_cache, - sldns_buffer* buf, time_t now, int addsoa, uint8_t* topname) + sldns_buffer* buf, time_t now, int addsoa, uint8_t* topname, + struct config_file* cfg) { struct dns_msg* msg; - struct ub_packed_rrset_key* rrset; + struct ub_packed_rrset_key* nsec; /* qname matching/covering nsec */ + struct ub_packed_rrset_key* wcrr; /* wildcard record or nsec */ + uint8_t* nodata_wc = NULL; + uint8_t* ce = NULL; + size_t ce_len; + uint8_t wc_ce[LDNS_MAX_DOMAINLEN+3]; + struct query_info wc_qinfo; + struct ub_packed_rrset_key* cache_wc; + struct packed_rrset_data* wcrr_data; + int rcode = LDNS_RCODE_NOERROR; uint8_t* zname; size_t zname_len; int zname_labs; struct val_neg_zone* zone; - /* only for DS queries */ - if(qinfo->qtype != LDNS_RR_TYPE_DS) + /* only for DS queries when aggressive use of NSEC is disabled */ + if(qinfo->qtype != LDNS_RR_TYPE_DS && !cfg->aggressive_nsec) return NULL; log_assert(!topname || dname_subdomain_c(qinfo->qname, topname)); - /* see if info from neg cache is available - * For NSECs, because there is no optout; a DS next to a delegation - * always has exactly an NSEC for it itself; check its DS bit. - * flags=0 (not the zone apex). - */ - rrset = grab_nsec(rrset_cache, qinfo->qname, qinfo->qname_len, - LDNS_RR_TYPE_NSEC, qinfo->qclass, 0, region, 1, - qinfo->qtype, now); - if(rrset) { - /* return msg with that rrset */ + /* Get best available NSEC for qname */ + nsec = neg_find_nsec(neg, qinfo->qname, qinfo->qname_len, qinfo->qclass, + rrset_cache, now, region); + + /* Matching NSEC, use to generate No Data answer. Not creating answers + * yet for No Data proven using wildcard. */ + if(nsec && nsec_proves_nodata(nsec, qinfo, &nodata_wc) && !nodata_wc) { if(!(msg = dns_msg_create(qinfo->qname, qinfo->qname_len, qinfo->qtype, qinfo->qclass, region, 2))) return NULL; - /* TTL already subtracted in grab_nsec */ - if(!dns_msg_authadd(msg, region, rrset, 0)) + if(!dns_msg_authadd(msg, region, nsec, 0)) return NULL; if(addsoa && !add_soa(rrset_cache, now, region, msg, NULL)) return NULL; + + lock_basic_lock(&neg->lock); + neg->num_neg_cache_noerror++; + lock_basic_unlock(&neg->lock); return msg; + } else if(nsec && val_nsec_proves_name_error(nsec, qinfo->qname)) { + if(!(msg = dns_msg_create(qinfo->qname, qinfo->qname_len, + qinfo->qtype, qinfo->qclass, region, 3))) + return NULL; + if(!(ce = nsec_closest_encloser(qinfo->qname, nsec))) + return NULL; + dname_count_size_labels(ce, &ce_len); + + /* No extra extra NSEC required if both nameerror qname and + * nodata *.ce. are proven already. */ + if(!nodata_wc || query_dname_compare(nodata_wc, ce) != 0) { + /* Qname proven non existing, get wildcard record for + * QTYPE or NSEC covering or matching wildcard. */ + + /* Num labels in ce is always smaller than in qname, + * therefore adding the wildcard label cannot overflow + * buffer. */ + wc_ce[0] = 1; + wc_ce[1] = (uint8_t)'*'; + memmove(wc_ce+2, ce, ce_len); + wc_qinfo.qname = wc_ce; + wc_qinfo.qname_len = ce_len + 2; + wc_qinfo.qtype = qinfo->qtype; + + + if((cache_wc = rrset_cache_lookup(rrset_cache, wc_qinfo.qname, + wc_qinfo.qname_len, wc_qinfo.qtype, + qinfo->qclass, 0/*flags*/, now, 0/*read only*/))) { + /* Synthesize wildcard answer */ + wcrr_data = (struct packed_rrset_data*)cache_wc->entry.data; + if(!(wcrr_data->security == sec_status_secure || + (wcrr_data->security == sec_status_unchecked && + wcrr_data->rrsig_count > 0))) { + lock_rw_unlock(&cache_wc->entry.lock); + return NULL; + } + if(!(wcrr = packed_rrset_copy_region(cache_wc, + region, now))) { + lock_rw_unlock(&cache_wc->entry.lock); + return NULL; + }; + lock_rw_unlock(&cache_wc->entry.lock); + wcrr->rk.dname = qinfo->qname; + wcrr->rk.dname_len = qinfo->qname_len; + if(!dns_msg_ansadd(msg, region, wcrr, 0)) + return NULL; + /* No SOA needed for wildcard synthesised + * answer. */ + addsoa = 0; + } else { + /* Get wildcard NSEC for possible non existence + * proof */ + if(!(wcrr = neg_find_nsec(neg, wc_qinfo.qname, + wc_qinfo.qname_len, qinfo->qclass, + rrset_cache, now, region))) + return NULL; + + nodata_wc = NULL; + if(val_nsec_proves_name_error(wcrr, wc_ce)) + rcode = LDNS_RCODE_NXDOMAIN; + else if(!nsec_proves_nodata(wcrr, &wc_qinfo, + &nodata_wc) || nodata_wc) + /* &nodata_wc shouldn't be set, wc_qinfo + * already contains wildcard domain. */ + /* NSEC doesn't prove anything for + * wildcard. */ + return NULL; + if(query_dname_compare(wcrr->rk.dname, + nsec->rk.dname) != 0) + if(!dns_msg_authadd(msg, region, wcrr, 0)) + return NULL; + } + } + + if(!dns_msg_authadd(msg, region, nsec, 0)) + return NULL; + if(addsoa && !add_soa(rrset_cache, now, region, msg, NULL)) + return NULL; + + /* Increment statistic counters */ + lock_basic_lock(&neg->lock); + if(rcode == LDNS_RCODE_NOERROR) + neg->num_neg_cache_noerror++; + else if(rcode == LDNS_RCODE_NXDOMAIN) + neg->num_neg_cache_nxdomain++; + lock_basic_unlock(&neg->lock); + + FLAGS_SET_RCODE(msg->rep->flags, rcode); + return msg; } + /* No aggressive use of NSEC3 for now, only proceed for DS types. */ + if(qinfo->qtype != LDNS_RR_TYPE_DS){ + return NULL; + } /* check NSEC3 neg cache for type DS */ /* need to look one zone higher for DS type */ zname = qinfo->qname; --- contrib/unbound/validator/val_neg.h.orig +++ contrib/unbound/validator/val_neg.h @@ -67,9 +67,9 @@ struct val_neg_cache { /** the big lock on the negative cache. Because we use a rbtree * for the data (quick lookup), we need a big lock */ - lock_basic_t lock; + lock_basic_type lock; /** The zone rbtree. contents sorted canonical, type val_neg_zone */ - rbtree_t tree; + rbtree_type tree; /** the first in linked list of LRU of val_neg_data */ struct val_neg_data* first; /** last in lru (least recently used element) */ @@ -80,6 +80,12 @@ size_t max; /** max nsec3 iterations allowed */ size_t nsec3_max_iter; + /** number of times neg cache records were used to generate NOERROR + * responses. */ + size_t num_neg_cache_noerror; + /** number of times neg cache records were used to generate NXDOMAIN + * responses. */ + size_t num_neg_cache_nxdomain; }; /** @@ -87,7 +93,7 @@ */ struct val_neg_zone { /** rbtree node element, key is this struct: the name, class */ - rbnode_t node; + rbnode_type node; /** name; the key */ uint8_t* name; /** length of name */ @@ -114,7 +120,7 @@ /** tree of NSEC data for this zone, sorted canonical * by NSEC owner name */ - rbtree_t tree; + rbtree_type tree; /** class of node; host order */ uint16_t dclass; @@ -135,7 +141,7 @@ */ struct val_neg_data { /** rbtree node element, key is this struct: the name */ - rbnode_t node; + rbnode_type node; /** name; the key */ uint8_t* name; /** length of name */ @@ -250,6 +256,7 @@ * more conservative, especially for opt-out zones, since the receiver * may have a trust-anchor below the optout and thus the optout cannot * be used to create a proof from the negative cache. + * @param cfg: config options. * @return a reply message if something was found. * This reply may still need validation. * NULL if nothing found (or out of memory). @@ -257,7 +264,7 @@ struct dns_msg* val_neg_getmsg(struct val_neg_cache* neg, struct query_info* qinfo, struct regional* region, struct rrset_cache* rrset_cache, struct sldns_buffer* buf, time_t now, - int addsoa, uint8_t* topname); + int addsoa, uint8_t* topname, struct config_file* cfg); /**** functions exposed for unit test ****/ --- contrib/unbound/validator/val_nsec.c.orig +++ contrib/unbound/validator/val_nsec.c @@ -176,7 +176,7 @@ static int nsec_verify_rrset(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* nsec, struct key_entry_key* kkey, - char** reason) + char** reason, struct module_qstate* qstate) { struct packed_rrset_data* d = (struct packed_rrset_data*) nsec->entry.data; @@ -185,7 +185,8 @@ rrset_check_sec_status(env->rrset_cache, nsec, *env->now); if(d->security == sec_status_secure) return 1; - d->security = val_verify_rrset_entry(env, ve, nsec, kkey, reason); + d->security = val_verify_rrset_entry(env, ve, nsec, kkey, reason, + LDNS_SECTION_AUTHORITY, qstate); if(d->security == sec_status_secure) { rrset_update_sec_status(env->rrset_cache, nsec, *env->now); return 1; @@ -196,7 +197,8 @@ enum sec_status val_nsec_prove_nodata_dsreply(struct module_env* env, struct val_env* ve, struct query_info* qinfo, struct reply_info* rep, - struct key_entry_key* kkey, time_t* proof_ttl, char** reason) + struct key_entry_key* kkey, time_t* proof_ttl, char** reason, + struct module_qstate* qstate) { struct ub_packed_rrset_key* nsec = reply_find_rrset_section_ns( rep, qinfo->qname, qinfo->qname_len, LDNS_RR_TYPE_NSEC, @@ -213,7 +215,7 @@ * 1) this is a delegation point and there is no DS * 2) this is not a delegation point */ if(nsec) { - if(!nsec_verify_rrset(env, ve, nsec, kkey, reason)) { + if(!nsec_verify_rrset(env, ve, nsec, kkey, reason, qstate)) { verbose(VERB_ALGO, "NSEC RRset for the " "referral did not verify."); return sec_status_bogus; @@ -242,7 +244,8 @@ i++) { if(rep->rrsets[i]->rk.type != htons(LDNS_RR_TYPE_NSEC)) continue; - if(!nsec_verify_rrset(env, ve, rep->rrsets[i], kkey, reason)) { + if(!nsec_verify_rrset(env, ve, rep->rrsets[i], kkey, reason, + qstate)) { verbose(VERB_ALGO, "NSEC for empty non-terminal " "did not verify."); return sec_status_bogus; @@ -343,7 +346,7 @@ } else { /* See if the next owner name covers a wildcard * empty non-terminal. */ - while (dname_strict_subdomain_c(nm, nsec->rk.dname)) { + while (dname_canonical_compare(nsec->rk.dname, nm) < 0) { /* wildcard does not apply if qname below * the name that exists under the '*' */ if (dname_subdomain_c(qinfo->qname, nm)) @@ -510,7 +513,6 @@ /* Determine if a NSEC record proves the non-existence of a * wildcard that could have produced qname. */ int labs; - int i; uint8_t* ce = nsec_closest_encloser(qname, nsec); uint8_t* strip; size_t striplen; @@ -523,13 +525,13 @@ * and next names. */ labs = dname_count_labels(qname) - dname_count_labels(ce); - for(i=labs; i>0; i--) { + if(labs > 0) { /* i is number of labels to strip off qname, prepend * wild */ strip = qname; striplen = qnamelen; - dname_remove_labels(&strip, &striplen, i); + dname_remove_labels(&strip, &striplen, labs); if(striplen > LDNS_MAX_DOMAINLEN-2) - continue; /* too long to prepend wildcard */ + return 0; /* too long to prepend wildcard */ buf[0] = 1; buf[1] = (uint8_t)'*'; memmove(buf+2, strip, striplen); --- contrib/unbound/validator/val_nsec.h.orig +++ contrib/unbound/validator/val_nsec.h @@ -46,6 +46,7 @@ #include "util/data/packed_rrset.h" struct val_env; struct module_env; +struct module_qstate; struct ub_packed_rrset_key; struct reply_info; struct query_info; @@ -64,6 +65,7 @@ * @param kkey: key entry to use for verification of signatures. * @param proof_ttl: if secure, the TTL of how long this proof lasts. * @param reason: string explaining why bogus. + * @param qstate: qstate with region. * @return security status. * SECURE: proved absence of DS. * INSECURE: proved that this was not a delegation point. @@ -73,7 +75,7 @@ enum sec_status val_nsec_prove_nodata_dsreply(struct module_env* env, struct val_env* ve, struct query_info* qinfo, struct reply_info* rep, struct key_entry_key* kkey, - time_t* proof_ttl, char** reason); + time_t* proof_ttl, char** reason, struct module_qstate* qstate); /** * nsec typemap check, takes an NSEC-type bitmap as argument, checks for type. --- contrib/unbound/validator/val_nsec3.c.orig +++ contrib/unbound/validator/val_nsec3.c @@ -520,6 +520,10 @@ } (void)nsec3_get_salt(h1->nsec3, h1->rr, &s1, &s1len); (void)nsec3_get_salt(h2->nsec3, h2->rr, &s2, &s2len); + if(s1len == 0 && s2len == 0) + return 0; + if(!s1) return -1; + if(!s2) return 1; if(s1len != s2len) { if(s1len < s2len) return -1; @@ -623,7 +627,7 @@ } int -nsec3_hash_name(rbtree_t* table, struct regional* region, sldns_buffer* buf, +nsec3_hash_name(rbtree_type* table, struct regional* region, sldns_buffer* buf, struct ub_packed_rrset_key* nsec3, int rr, uint8_t* dname, size_t dname_len, struct nsec3_cached_hash** hash) { @@ -630,7 +634,7 @@ struct nsec3_cached_hash* c; struct nsec3_cached_hash looki; #ifdef UNBOUND_DEBUG - rbnode_t* n; + rbnode_type* n; #endif int r; looki.node.key = &looki; @@ -730,13 +734,13 @@ */ static int find_matching_nsec3(struct module_env* env, struct nsec3_filter* flt, - rbtree_t* ct, uint8_t* nm, size_t nmlen, + rbtree_type* ct, uint8_t* nm, size_t nmlen, struct ub_packed_rrset_key** rrset, int* rr) { size_t i_rs; int i_rr; struct ub_packed_rrset_key* s; - struct nsec3_cached_hash* hash; + struct nsec3_cached_hash* hash = NULL; int r; /* this loop skips other-zone and unknown NSEC3s, also non-NSEC3 RRs */ @@ -748,7 +752,7 @@ if(r == 0) { log_err("nsec3: malloc failure"); break; /* alloc failure */ - } else if(r < 0) + } else if(r != 1) continue; /* malformed NSEC3 */ else if(nsec3_hash_matches_owner(flt, hash, s)) { *rrset = s; /* rrset with this name */ @@ -823,13 +827,13 @@ */ static int find_covering_nsec3(struct module_env* env, struct nsec3_filter* flt, - rbtree_t* ct, uint8_t* nm, size_t nmlen, + rbtree_type* ct, uint8_t* nm, size_t nmlen, struct ub_packed_rrset_key** rrset, int* rr) { size_t i_rs; int i_rr; struct ub_packed_rrset_key* s; - struct nsec3_cached_hash* hash; + struct nsec3_cached_hash* hash = NULL; int r; /* this loop skips other-zone and unknown NSEC3s, also non-NSEC3 RRs */ @@ -841,7 +845,7 @@ if(r == 0) { log_err("nsec3: malloc failure"); break; /* alloc failure */ - } else if(r < 0) + } else if(r != 1) continue; /* malformed NSEC3 */ else if(nsec3_covers(flt->zone, hash, s, i_rr, env->scratch_buffer)) { @@ -869,7 +873,7 @@ */ static int nsec3_find_closest_encloser(struct module_env* env, struct nsec3_filter* flt, - rbtree_t* ct, struct query_info* qinfo, struct ce_response* ce) + rbtree_type* ct, struct query_info* qinfo, struct ce_response* ce) { uint8_t* nm = qinfo->qname; size_t nmlen = qinfo->qname_len; @@ -936,7 +940,7 @@ */ static enum sec_status nsec3_prove_closest_encloser(struct module_env* env, struct nsec3_filter* flt, - rbtree_t* ct, struct query_info* qinfo, int prove_does_not_exist, + rbtree_type* ct, struct query_info* qinfo, int prove_does_not_exist, struct ce_response* ce) { uint8_t* nc; @@ -1016,7 +1020,7 @@ /** Do the name error proof */ static enum sec_status nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt, - rbtree_t* ct, struct query_info* qinfo) + rbtree_type* ct, struct query_info* qinfo) { struct ce_response ce; uint8_t* wc; @@ -1037,7 +1041,7 @@ "nsec3 is an insecure delegation"); return sec; } - log_nametypeclass(VERB_ALGO, "nsec3 namerror: proven ce=", ce.ce,0,0); + log_nametypeclass(VERB_ALGO, "nsec3 nameerror: proven ce=", ce.ce,0,0); /* At this point, we know that qname does not exist. Now we need * to prove that the wildcard does not exist. */ @@ -1062,7 +1066,7 @@ struct ub_packed_rrset_key** list, size_t num, struct query_info* qinfo, struct key_entry_key* kkey) { - rbtree_t ct; + rbtree_type ct; struct nsec3_filter flt; if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) @@ -1086,7 +1090,7 @@ /** Do the nodata proof */ static enum sec_status nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, - rbtree_t* ct, struct query_info* qinfo) + rbtree_type* ct, struct query_info* qinfo) { struct ce_response ce; uint8_t* wc; @@ -1180,7 +1184,7 @@ nsec3_has_type(rrset, rr, LDNS_RR_TYPE_NS) && !nsec3_has_type(rrset, rr, LDNS_RR_TYPE_SOA)) { verbose(VERB_ALGO, "nsec3 nodata proof: matching " - "wilcard is a delegation, bogus"); + "wildcard is a delegation, bogus"); return sec_status_bogus; } /* everything is peachy keen, except for optout spans */ @@ -1221,7 +1225,7 @@ struct ub_packed_rrset_key** list, size_t num, struct query_info* qinfo, struct key_entry_key* kkey) { - rbtree_t ct; + rbtree_type ct; struct nsec3_filter flt; if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) @@ -1240,7 +1244,7 @@ struct ub_packed_rrset_key** list, size_t num, struct query_info* qinfo, struct key_entry_key* kkey, uint8_t* wc) { - rbtree_t ct; + rbtree_type ct; struct nsec3_filter flt; struct ce_response ce; uint8_t* nc; @@ -1285,7 +1289,7 @@ static int list_is_secure(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key** list, size_t num, - struct key_entry_key* kkey, char** reason) + struct key_entry_key* kkey, char** reason, struct module_qstate* qstate) { struct packed_rrset_data* d; size_t i; @@ -1299,7 +1303,7 @@ if(d->security == sec_status_secure) continue; d->security = val_verify_rrset_entry(env, ve, list[i], kkey, - reason); + reason, LDNS_SECTION_AUTHORITY, qstate); if(d->security != sec_status_secure) { verbose(VERB_ALGO, "NSEC3 did not verify"); return 0; @@ -1312,9 +1316,10 @@ enum sec_status nsec3_prove_nods(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey, char** reason) + struct query_info* qinfo, struct key_entry_key* kkey, char** reason, + struct module_qstate* qstate) { - rbtree_t ct; + rbtree_type ct; struct nsec3_filter flt; struct ce_response ce; struct ub_packed_rrset_key* rrset; @@ -1325,7 +1330,7 @@ *reason = "no valid NSEC3s"; return sec_status_bogus; /* no valid NSEC3s, bogus */ } - if(!list_is_secure(env, ve, list, num, kkey, reason)) + if(!list_is_secure(env, ve, list, num, kkey, reason, qstate)) return sec_status_bogus; /* not all NSEC3 records secure */ rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */ filter_init(&flt, list, num, qinfo); /* init RR iterator */ @@ -1403,7 +1408,7 @@ struct query_info* qinfo, struct key_entry_key* kkey, int* nodata) { enum sec_status sec, secnx; - rbtree_t ct; + rbtree_type ct; struct nsec3_filter flt; *nodata = 0; --- contrib/unbound/validator/val_nsec3.h.orig +++ contrib/unbound/validator/val_nsec3.h @@ -71,6 +71,7 @@ struct val_env; struct regional; struct module_env; +struct module_qstate; struct ub_packed_rrset_key; struct reply_info; struct query_info; @@ -185,6 +186,7 @@ * @param qinfo: query that is verified for. * @param kkey: key entry that signed the NSEC3s. * @param reason: string for bogus result. + * @param qstate: qstate with region. * @return: * sec_status SECURE of the proposition is proven by the NSEC3 RRs, * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored. @@ -194,7 +196,8 @@ enum sec_status nsec3_prove_nods(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey, char** reason); + struct query_info* qinfo, struct key_entry_key* kkey, char** reason, + struct module_qstate* qstate); /** * Prove NXDOMAIN or NODATA. @@ -224,7 +227,7 @@ */ struct nsec3_cached_hash { /** rbtree node, key is this structure */ - rbnode_t node; + rbnode_type node; /** where are the parameters for conversion, in this rrset data */ struct ub_packed_rrset_key* nsec3; /** where are the parameters for conversion, this RR number in data */ @@ -271,7 +274,7 @@ * 0 on a malloc failure. * -1 if the NSEC3 rr was badly formatted (i.e. formerr). */ -int nsec3_hash_name(rbtree_t* table, struct regional* region, +int nsec3_hash_name(rbtree_type* table, struct regional* region, struct sldns_buffer* buf, struct ub_packed_rrset_key* nsec3, int rr, uint8_t* dname, size_t dname_len, struct nsec3_cached_hash** hash); --- contrib/unbound/validator/val_secalgo.c.orig +++ contrib/unbound/validator/val_secalgo.c @@ -54,6 +54,11 @@ #error "Need crypto library to do digital signature cryptography" #endif +/** fake DSA support for unit tests */ +int fake_dsa = 0; +/** fake SHA1 support for unit tests */ +int fake_sha1 = 0; + /* OpenSSL implementation */ #ifdef HAVE_SSL #ifdef HAVE_OPENSSL_ERR_H @@ -72,6 +77,22 @@ #include #endif +/** + * Output a libcrypto openssl error to the logfile. + * @param str: string to add to it. + * @param e: the error to output, error number from ERR_get_error(). + */ +static void +log_crypto_error(const char* str, unsigned long e) +{ + char buf[128]; + /* or use ERR_error_string if ERR_error_string_n is not avail TODO */ + ERR_error_string_n(e, buf, sizeof(buf)); + /* buf now contains */ + /* error:[error code]:[library name]:[function name]:[reason string] */ + log_err("%s crypto %s", str, buf); +} + /* return size of digest if supported, or 0 otherwise */ size_t nsec3_hash_algo_size_supported(int id) @@ -91,7 +112,13 @@ { switch(algo) { case NSEC3_HASH_SHA1: +#ifdef OPENSSL_FIPS + if(!sldns_digest_evp(buf, len, res, EVP_sha1())) + log_crypto_error("could not digest with EVP_sha1", + ERR_get_error()); +#else (void)SHA1(buf, len, res); +#endif return 1; default: return 0; @@ -101,7 +128,13 @@ void secalgo_hash_sha256(unsigned char* buf, size_t len, unsigned char* res) { +#ifdef OPENSSL_FIPS + if(!sldns_digest_evp(buf, len, res, EVP_sha256())) + log_crypto_error("could not digest with EVP_sha256", + ERR_get_error()); +#else (void)SHA256(buf, len, res); +#endif } /** @@ -113,9 +146,12 @@ ds_digest_size_supported(int algo) { switch(algo) { -#ifdef HAVE_EVP_SHA1 case LDNS_SHA1: +#if defined(HAVE_EVP_SHA1) && defined(USE_SHA1) return SHA_DIGEST_LENGTH; +#else + if(fake_sha1) return 20; + return 0; #endif #ifdef HAVE_EVP_SHA256 case LDNS_SHA256: @@ -155,14 +191,26 @@ unsigned char* res) { switch(algo) { -#ifdef HAVE_EVP_SHA1 +#if defined(HAVE_EVP_SHA1) && defined(USE_SHA1) case LDNS_SHA1: +#ifdef OPENSSL_FIPS + if(!sldns_digest_evp(buf, len, res, EVP_sha1())) + log_crypto_error("could not digest with EVP_sha1", + ERR_get_error()); +#else (void)SHA1(buf, len, res); +#endif return 1; #endif #ifdef HAVE_EVP_SHA256 case LDNS_SHA256: +#ifdef OPENSSL_FIPS + if(!sldns_digest_evp(buf, len, res, EVP_sha256())) + log_crypto_error("could not digest with EVP_sha256", + ERR_get_error()); +#else (void)SHA256(buf, len, res); +#endif return 1; #endif #ifdef USE_GOST @@ -173,7 +221,13 @@ #endif #ifdef USE_ECDSA case LDNS_SHA384: +#ifdef OPENSSL_FIPS + if(!sldns_digest_evp(buf, len, res, EVP_sha384())) + log_crypto_error("could not digest with EVP_sha384", + ERR_get_error()); +#else (void)SHA384(buf, len, res); +#endif return 1; #endif default: @@ -192,12 +246,24 @@ case LDNS_RSAMD5: /* RFC 6725 deprecates RSAMD5 */ return 0; -#ifdef USE_DSA case LDNS_DSA: case LDNS_DSA_NSEC3: +#if defined(USE_DSA) && defined(USE_SHA1) + return 1; +#else + if(fake_dsa || fake_sha1) return 1; + return 0; #endif + case LDNS_RSASHA1: case LDNS_RSASHA1_NSEC3: +#ifdef USE_SHA1 + return 1; +#else + if(fake_sha1) return 1; + return 0; +#endif + #if defined(HAVE_EVP_SHA256) && defined(USE_SHA2) case LDNS_RSASHA256: #endif @@ -208,7 +274,16 @@ case LDNS_ECDSAP256SHA256: case LDNS_ECDSAP384SHA384: #endif +#ifdef USE_ED25519 + case LDNS_ED25519: +#endif +#ifdef USE_ED448 + case LDNS_ED448: +#endif +#if (defined(HAVE_EVP_SHA256) && defined(USE_SHA2)) || (defined(HAVE_EVP_SHA512) && defined(USE_SHA2)) || defined(USE_ECDSA) || defined(USE_ED25519) || defined(USE_ED448) return 1; +#endif + #ifdef USE_GOST case LDNS_ECC_GOST: /* we support GOST if it can be loaded */ @@ -219,22 +294,6 @@ } } -/** - * Output a libcrypto openssl error to the logfile. - * @param str: string to add to it. - * @param e: the error to output, error number from ERR_get_error(). - */ -static void -log_crypto_error(const char* str, unsigned long e) -{ - char buf[128]; - /* or use ERR_error_string if ERR_error_string_n is not avail TODO */ - ERR_error_string_n(e, buf, sizeof(buf)); - /* buf now contains */ - /* error:[error code]:[library name]:[function name]:[reason string] */ - log_err("%s crypto %s", str, buf); -} - #ifdef USE_DSA /** * Setup DSA key digest in DER encoding ... @@ -264,8 +323,14 @@ dsasig = DSA_SIG_new(); if(!dsasig) return 0; +#ifdef HAVE_DSA_SIG_SET0 + if(!DSA_SIG_set0(dsasig, R, S)) return 0; +#else +# ifndef S_SPLINT_S dsasig->r = R; dsasig->s = S; +# endif /* S_SPLINT_S */ +#endif *sig = NULL; newlen = i2d_DSA_SIG(dsasig, sig); if(newlen < 0) { @@ -292,7 +357,7 @@ setup_ecdsa_sig(unsigned char** sig, unsigned int* len) { /* convert from two BIGNUMs in the rdata buffer, to ASN notation. - * ASN preable: 30440220 0220 + * ASN preamble: 30440220 0220 * the '20' is the length of that field (=bnsize). i * the '44' is the total remaining length. * if negative, start with leading zero. @@ -381,13 +446,13 @@ setup_key_digest(int algo, EVP_PKEY** evp_key, const EVP_MD** digest_type, unsigned char* key, size_t keylen) { -#ifdef USE_DSA +#if defined(USE_DSA) && defined(USE_SHA1) DSA* dsa; #endif RSA* rsa; switch(algo) { -#ifdef USE_DSA +#if defined(USE_DSA) && defined(USE_SHA1) case LDNS_DSA: case LDNS_DSA_NSEC3: *evp_key = EVP_PKEY_new(); @@ -406,12 +471,20 @@ "EVP_PKEY_assign_DSA failed"); return 0; } +#ifdef HAVE_EVP_DSS1 *digest_type = EVP_dss1(); +#else + *digest_type = EVP_sha1(); +#endif break; -#endif /* USE_DSA */ +#endif /* USE_DSA && USE_SHA1 */ + +#if defined(USE_SHA1) || (defined(HAVE_EVP_SHA256) && defined(USE_SHA2)) || (defined(HAVE_EVP_SHA512) && defined(USE_SHA2)) +#ifdef USE_SHA1 case LDNS_RSASHA1: case LDNS_RSASHA1_NSEC3: +#endif #if defined(HAVE_EVP_SHA256) && defined(USE_SHA2) case LDNS_RSASHA256: #endif @@ -446,9 +519,14 @@ *digest_type = EVP_sha512(); else #endif +#ifdef USE_SHA1 *digest_type = EVP_sha1(); +#else + { verbose(VERB_QUERY, "no digest available"); return 0; } +#endif + break; +#endif /* defined(USE_SHA1) || (defined(HAVE_EVP_SHA256) && defined(USE_SHA2)) || (defined(HAVE_EVP_SHA512) && defined(USE_SHA2)) */ - break; case LDNS_RSAMD5: *evp_key = EVP_PKEY_new(); if(!*evp_key) { @@ -515,6 +593,28 @@ #endif break; #endif /* USE_ECDSA */ +#ifdef USE_ED25519 + case LDNS_ED25519: + *evp_key = sldns_ed255192pkey_raw(key, keylen); + if(!*evp_key) { + verbose(VERB_QUERY, "verify: " + "sldns_ed255192pkey_raw failed"); + return 0; + } + *digest_type = NULL; + break; +#endif /* USE_ED25519 */ +#ifdef USE_ED448 + case LDNS_ED448: + *evp_key = sldns_ed4482pkey_raw(key, keylen); + if(!*evp_key) { + verbose(VERB_QUERY, "verify: " + "sldns_ed4482pkey_raw failed"); + return 0; + } + *digest_type = NULL; + break; +#endif /* USE_ED448 */ default: verbose(VERB_QUERY, "verify: unknown algorithm %d", algo); @@ -545,6 +645,15 @@ EVP_MD_CTX* ctx; int res, dofree = 0, docrypto_free = 0; EVP_PKEY *evp_key = NULL; + +#ifndef USE_DSA + if((algo == LDNS_DSA || algo == LDNS_DSA_NSEC3) &&(fake_dsa||fake_sha1)) + return sec_status_secure; +#endif +#ifndef USE_SHA1 + if(fake_sha1 && (algo == LDNS_DSA || algo == LDNS_DSA_NSEC3 || algo == LDNS_RSASHA1 || algo == LDNS_RSASHA1_NSEC3)) + return sec_status_secure; +#endif if(!setup_key_digest(algo, &evp_key, &digest_type, key, keylen)) { verbose(VERB_QUERY, "verify: failed to setup key"); @@ -595,18 +704,29 @@ else if(docrypto_free) OPENSSL_free(sigblock); return sec_status_unchecked; } - if(EVP_VerifyInit(ctx, digest_type) == 0) { - verbose(VERB_QUERY, "verify: EVP_VerifyInit failed"); +#ifndef HAVE_EVP_DIGESTVERIFY + if(EVP_DigestInit(ctx, digest_type) == 0) { + verbose(VERB_QUERY, "verify: EVP_DigestInit failed"); +#ifdef HAVE_EVP_MD_CTX_NEW EVP_MD_CTX_destroy(ctx); +#else + EVP_MD_CTX_cleanup(ctx); + free(ctx); +#endif EVP_PKEY_free(evp_key); if(dofree) free(sigblock); else if(docrypto_free) OPENSSL_free(sigblock); return sec_status_unchecked; } - if(EVP_VerifyUpdate(ctx, (unsigned char*)sldns_buffer_begin(buf), + if(EVP_DigestUpdate(ctx, (unsigned char*)sldns_buffer_begin(buf), (unsigned int)sldns_buffer_limit(buf)) == 0) { - verbose(VERB_QUERY, "verify: EVP_VerifyUpdate failed"); + verbose(VERB_QUERY, "verify: EVP_DigestUpdate failed"); +#ifdef HAVE_EVP_MD_CTX_NEW EVP_MD_CTX_destroy(ctx); +#else + EVP_MD_CTX_cleanup(ctx); + free(ctx); +#endif EVP_PKEY_free(evp_key); if(dofree) free(sigblock); else if(docrypto_free) OPENSSL_free(sigblock); @@ -614,7 +734,25 @@ } res = EVP_VerifyFinal(ctx, sigblock, sigblock_len, evp_key); +#else /* HAVE_EVP_DIGESTVERIFY */ + if(EVP_DigestVerifyInit(ctx, NULL, digest_type, NULL, evp_key) == 0) { + verbose(VERB_QUERY, "verify: EVP_DigestVerifyInit failed"); #ifdef HAVE_EVP_MD_CTX_NEW + EVP_MD_CTX_destroy(ctx); +#else + EVP_MD_CTX_cleanup(ctx); + free(ctx); +#endif + EVP_PKEY_free(evp_key); + if(dofree) free(sigblock); + else if(docrypto_free) OPENSSL_free(sigblock); + return sec_status_unchecked; + } + res = EVP_DigestVerify(ctx, sigblock, sigblock_len, + (unsigned char*)sldns_buffer_begin(buf), + sldns_buffer_limit(buf)); +#endif +#ifdef HAVE_EVP_MD_CTX_NEW EVP_MD_CTX_destroy(ctx); #else EVP_MD_CTX_cleanup(ctx); @@ -686,8 +824,10 @@ { /* uses libNSS */ switch(algo) { +#ifdef USE_SHA1 case LDNS_SHA1: return SHA1_LENGTH; +#endif #ifdef USE_SHA2 case LDNS_SHA256: return SHA256_LENGTH; @@ -709,9 +849,11 @@ { /* uses libNSS */ switch(algo) { +#ifdef USE_SHA1 case LDNS_SHA1: return HASH_HashBuf(HASH_AlgSHA1, res, buf, len) == SECSuccess; +#endif #if defined(USE_SHA2) case LDNS_SHA256: return HASH_HashBuf(HASH_AlgSHA256, res, buf, len) @@ -739,12 +881,15 @@ case LDNS_RSAMD5: /* RFC 6725 deprecates RSAMD5 */ return 0; -#ifdef USE_DSA +#if defined(USE_SHA1) || defined(USE_SHA2) +#if defined(USE_DSA) && defined(USE_SHA1) case LDNS_DSA: case LDNS_DSA_NSEC3: #endif +#ifdef USE_SHA1 case LDNS_RSASHA1: case LDNS_RSASHA1_NSEC3: +#endif #ifdef USE_SHA2 case LDNS_RSASHA256: #endif @@ -752,6 +897,8 @@ case LDNS_RSASHA512: #endif return 1; +#endif /* SHA1 or SHA2 */ + #ifdef USE_ECDSA case LDNS_ECDSAP256SHA256: case LDNS_ECDSAP384SHA384: @@ -983,7 +1130,9 @@ */ switch(algo) { -#ifdef USE_DSA + +#if defined(USE_SHA1) || defined(USE_SHA2) +#if defined(USE_DSA) && defined(USE_SHA1) case LDNS_DSA: case LDNS_DSA_NSEC3: *pubkey = nss_buf2dsa(key, keylen); @@ -995,8 +1144,10 @@ /* no prefix for DSA verification */ break; #endif +#ifdef USE_SHA1 case LDNS_RSASHA1: case LDNS_RSASHA1_NSEC3: +#endif #ifdef USE_SHA2 case LDNS_RSASHA256: #endif @@ -1023,13 +1174,22 @@ *prefixlen = sizeof(p_sha512); } else #endif +#ifdef USE_SHA1 { *htype = HASH_AlgSHA1; *prefix = p_sha1; *prefixlen = sizeof(p_sha1); } +#else + { + verbose(VERB_QUERY, "verify: no digest algo"); + return 0; + } +#endif break; +#endif /* SHA1 or SHA2 */ + case LDNS_RSAMD5: *pubkey = nss_buf2rsa(key, keylen); if(!*pubkey) { @@ -1111,7 +1271,7 @@ return sec_status_bogus; } -#ifdef USE_DSA +#if defined(USE_DSA) && defined(USE_SHA1) /* need to convert DSA, ECDSA signatures? */ if((algo == LDNS_DSA || algo == LDNS_DSA_NSEC3)) { if(sigblock_len == 1+2*SHA1_LENGTH) { @@ -1206,6 +1366,9 @@ #include "ecdsa.h" #include "ecc-curve.h" #endif +#ifdef HAVE_NETTLE_EDDSA_H +#include "eddsa.h" +#endif static int _digest_nettle(int algo, uint8_t* buf, size_t len, @@ -1292,7 +1455,12 @@ { switch(algo) { case LDNS_SHA1: +#ifdef USE_SHA1 return SHA1_DIGEST_SIZE; +#else + if(fake_sha1) return 20; + return 0; +#endif #ifdef USE_SHA2 case LDNS_SHA256: return SHA256_DIGEST_SIZE; @@ -1314,8 +1482,10 @@ unsigned char* res) { switch(algo) { +#ifdef USE_SHA1 case LDNS_SHA1: return _digest_nettle(SHA1_DIGEST_SIZE, buf, len, res); +#endif #if defined(USE_SHA2) case LDNS_SHA256: return _digest_nettle(SHA256_DIGEST_SIZE, buf, len, res); @@ -1339,12 +1509,22 @@ { /* uses libnettle */ switch(id) { -#ifdef USE_DSA case LDNS_DSA: case LDNS_DSA_NSEC3: +#if defined(USE_DSA) && defined(USE_SHA1) + return 1; +#else + if(fake_dsa || fake_sha1) return 1; + return 0; #endif case LDNS_RSASHA1: case LDNS_RSASHA1_NSEC3: +#ifdef USE_SHA1 + return 1; +#else + if(fake_sha1) return 1; + return 0; +#endif #ifdef USE_SHA2 case LDNS_RSASHA256: case LDNS_RSASHA512: @@ -1354,6 +1534,10 @@ case LDNS_ECDSAP384SHA384: #endif return 1; +#ifdef USE_ED25519 + case LDNS_ED25519: + return 1; +#endif case LDNS_RSAMD5: /* RFC 6725 deprecates RSAMD5 */ case LDNS_ECC_GOST: default: @@ -1361,13 +1545,13 @@ } } -#ifdef USE_DSA +#if defined(USE_DSA) && defined(USE_SHA1) static char * _verify_nettle_dsa(sldns_buffer* buf, unsigned char* sigblock, unsigned int sigblock_len, unsigned char* key, unsigned int keylen) { uint8_t digest[SHA1_DIGEST_SIZE]; - uint8_t key_t; + uint8_t key_t_value; int res = 0; size_t offset; struct dsa_public_key pubkey; @@ -1406,8 +1590,8 @@ } /* Validate T values constraints - RFC 2536 sec. 2 & sec. 3 */ - key_t = key[0]; - if (key_t > 8) { + key_t_value = key[0]; + if (key_t_value > 8) { return "invalid T value in DSA pubkey"; } @@ -1418,9 +1602,9 @@ expected_len = 1 + /* T */ 20 + /* Q */ - (64 + key_t*8) + /* P */ - (64 + key_t*8) + /* G */ - (64 + key_t*8); /* Y */ + (64 + key_t_value*8) + /* P */ + (64 + key_t_value*8) + /* G */ + (64 + key_t_value*8); /* Y */ if (keylen != expected_len ) { return "invalid DSA pubkey length"; } @@ -1430,11 +1614,11 @@ offset = 1; nettle_mpz_set_str_256_u(pubkey.q, 20, key+offset); offset += 20; - nettle_mpz_set_str_256_u(pubkey.p, (64 + key_t*8), key+offset); - offset += (64 + key_t*8); - nettle_mpz_set_str_256_u(pubkey.g, (64 + key_t*8), key+offset); - offset += (64 + key_t*8); - nettle_mpz_set_str_256_u(pubkey.y, (64 + key_t*8), key+offset); + nettle_mpz_set_str_256_u(pubkey.p, (64 + key_t_value*8), key+offset); + offset += (64 + key_t_value*8); + nettle_mpz_set_str_256_u(pubkey.g, (64 + key_t_value*8), key+offset); + offset += (64 + key_t_value*8); + nettle_mpz_set_str_256_u(pubkey.y, (64 + key_t_value*8), key+offset); /* Digest content of "buf" and verify its DSA signature in "sigblock"*/ res = _digest_nettle(SHA1_DIGEST_SIZE, (unsigned char*)sldns_buffer_begin(buf), @@ -1551,7 +1735,7 @@ { uint8_t digest[SHA256_DIGEST_SIZE]; mpz_t x, y; - nettle_ecc_point_init(&pubkey, &nettle_secp_256r1); + nettle_ecc_point_init(&pubkey, nettle_get_secp_256r1()); nettle_mpz_init_set_str_256_u(x, SHA256_DIGEST_SIZE, key); nettle_mpz_init_set_str_256_u(y, SHA256_DIGEST_SIZE, key+SHA256_DIGEST_SIZE); nettle_mpz_set_str_256_u(signature.r, SHA256_DIGEST_SIZE, sigblock); @@ -1568,7 +1752,7 @@ { uint8_t digest[SHA384_DIGEST_SIZE]; mpz_t x, y; - nettle_ecc_point_init(&pubkey, &nettle_secp_384r1); + nettle_ecc_point_init(&pubkey, nettle_get_secp_384r1()); nettle_mpz_init_set_str_256_u(x, SHA384_DIGEST_SIZE, key); nettle_mpz_init_set_str_256_u(y, SHA384_DIGEST_SIZE, key+SHA384_DIGEST_SIZE); nettle_mpz_set_str_256_u(signature.r, SHA384_DIGEST_SIZE, sigblock); @@ -1595,6 +1779,30 @@ } #endif +#ifdef USE_ED25519 +static char * +_verify_nettle_ed25519(sldns_buffer* buf, unsigned char* sigblock, + unsigned int sigblock_len, unsigned char* key, unsigned int keylen) +{ + int res = 0; + + if(sigblock_len != ED25519_SIGNATURE_SIZE) { + return "wrong ED25519 signature length"; + } + if(keylen != ED25519_KEY_SIZE) { + return "wrong ED25519 key length"; + } + + res = ed25519_sha512_verify((uint8_t*)key, sldns_buffer_limit(buf), + sldns_buffer_begin(buf), (uint8_t*)sigblock); + + if (!res) + return "ED25519 signature verification failed"; + else + return NULL; +} +#endif + /** * Check a canonical sig+rrset and signature against a dnskey * @param buf: buffer with data to verify, the first rrsig part and the @@ -1620,8 +1828,17 @@ return sec_status_bogus; } +#ifndef USE_DSA + if((algo == LDNS_DSA || algo == LDNS_DSA_NSEC3) &&(fake_dsa||fake_sha1)) + return sec_status_secure; +#endif +#ifndef USE_SHA1 + if(fake_sha1 && (algo == LDNS_DSA || algo == LDNS_DSA_NSEC3 || algo == LDNS_RSASHA1 || algo == LDNS_RSASHA1_NSEC3)) + return sec_status_secure; +#endif + switch(algo) { -#ifdef USE_DSA +#if defined(USE_DSA) && defined(USE_SHA1) case LDNS_DSA: case LDNS_DSA_NSEC3: *reason = _verify_nettle_dsa(buf, sigblock, sigblock_len, key, keylen); @@ -1631,12 +1848,18 @@ return sec_status_secure; #endif /* USE_DSA */ +#ifdef USE_SHA1 case LDNS_RSASHA1: case LDNS_RSASHA1_NSEC3: digest_size = (digest_size ? digest_size : SHA1_DIGEST_SIZE); +#endif + /* double fallthrough annotation to please gcc parser */ + /* fallthrough */ #ifdef USE_SHA2 + /* fallthrough */ case LDNS_RSASHA256: digest_size = (digest_size ? digest_size : SHA256_DIGEST_SIZE); + /* fallthrough */ case LDNS_RSASHA512: digest_size = (digest_size ? digest_size : SHA512_DIGEST_SIZE); @@ -1651,6 +1874,7 @@ #ifdef USE_ECDSA case LDNS_ECDSAP256SHA256: digest_size = (digest_size ? digest_size : SHA256_DIGEST_SIZE); + /* fallthrough */ case LDNS_ECDSAP384SHA384: digest_size = (digest_size ? digest_size : SHA384_DIGEST_SIZE); *reason = _verify_nettle_ecdsa(buf, digest_size, sigblock, @@ -1660,6 +1884,15 @@ else return sec_status_secure; #endif +#ifdef USE_ED25519 + case LDNS_ED25519: + *reason = _verify_nettle_ed25519(buf, sigblock, sigblock_len, + key, keylen); + if (*reason != NULL) + return sec_status_bogus; + else + return sec_status_secure; +#endif case LDNS_RSAMD5: case LDNS_ECC_GOST: default: --- contrib/unbound/validator/val_sigcrypt.c.orig +++ contrib/unbound/validator/val_sigcrypt.c @@ -51,6 +51,7 @@ #include "util/module.h" #include "util/net_help.h" #include "util/regional.h" +#include "util/config_file.h" #include "sldns/keyraw.h" #include "sldns/sbuffer.h" #include "sldns/parseutil.h" @@ -318,12 +319,17 @@ size_t dslen; uint8_t* digest; /* generated digest */ size_t digestlen = ds_digest_size_algo(ds_rrset, ds_idx); - + if(digestlen == 0) { verbose(VERB_QUERY, "DS fail: not supported, or DS RR " "format error"); return 0; /* not supported, or DS RR format error */ } +#ifndef USE_SHA1 + if(fake_sha1 && ds_get_digest_algo(ds_rrset, ds_idx)==LDNS_SHA1) + return 1; +#endif + /* check digest length in DS with length from hash function */ ds_get_sigdata(ds_rrset, ds_idx, &ds, &dslen); if(!ds || dslen != digestlen) { @@ -479,11 +485,12 @@ enum sec_status dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, - uint8_t* sigalg, char** reason) + uint8_t* sigalg, char** reason, sldns_pkt_section section, + struct module_qstate* qstate) { enum sec_status sec; size_t i, num; - rbtree_t* sortree = NULL; + rbtree_type* sortree = NULL; /* make sure that for all DNSKEY algorithms there are valid sigs */ struct algo_needs needs; int alg; @@ -506,7 +513,7 @@ } for(i=0; inow, rrset, - dnskey, i, &sortree, reason); + dnskey, i, &sortree, reason, section, qstate); /* see which algorithm has been fixed up */ if(sec == sec_status_secure) { if(!sigalg) @@ -547,11 +554,12 @@ enum sec_status dnskey_verify_rrset(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, - size_t dnskey_idx, char** reason) + size_t dnskey_idx, char** reason, sldns_pkt_section section, + struct module_qstate* qstate) { enum sec_status sec; size_t i, num, numchecked = 0; - rbtree_t* sortree = NULL; + rbtree_type* sortree = NULL; int buf_canon = 0; uint16_t tag = dnskey_calc_keytag(dnskey, dnskey_idx); int algo = dnskey_get_algo(dnskey, dnskey_idx); @@ -571,7 +579,8 @@ buf_canon = 0; sec = dnskey_verify_rrset_sig(env->scratch, env->scratch_buffer, ve, *env->now, rrset, - dnskey, dnskey_idx, i, &sortree, &buf_canon, reason); + dnskey, dnskey_idx, i, &sortree, &buf_canon, reason, + section, qstate); if(sec == sec_status_secure) return sec; numchecked ++; @@ -585,7 +594,8 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve, time_t now, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, size_t sig_idx, - struct rbtree_t** sortree, char** reason) + struct rbtree_type** sortree, char** reason, sldns_pkt_section section, + struct module_qstate* qstate) { /* find matching keys and check them */ enum sec_status sec = sec_status_bogus; @@ -610,7 +620,7 @@ /* see if key verifies */ sec = dnskey_verify_rrset_sig(env->scratch, env->scratch_buffer, ve, now, rrset, dnskey, i, - sig_idx, sortree, &buf_canon, reason); + sig_idx, sortree, &buf_canon, reason, section, qstate); if(sec == sec_status_secure) return sec; } @@ -627,7 +637,7 @@ */ struct canon_rr { /** rbtree node, key is this structure */ - rbnode_t node; + rbnode_type node; /** rrset the RR is in */ struct ub_packed_rrset_key* rrset; /** which RR in the rrset */ @@ -885,7 +895,7 @@ */ static void canonical_sort(struct ub_packed_rrset_key* rrset, struct packed_rrset_data* d, - rbtree_t* sortree, struct canon_rr* rrs) + rbtree_type* sortree, struct canon_rr* rrs) { size_t i; /* insert into rbtree to sort and detect duplicates */ @@ -900,7 +910,7 @@ } /** - * Inser canonical owner name into buffer. + * Insert canonical owner name into buffer. * @param buf: buffer to insert into at current position. * @param k: rrset with its owner name. * @param sig: signature with signer name and label count. @@ -1043,7 +1053,7 @@ int rrset_canonical_equal(struct regional* region, struct ub_packed_rrset_key* k1, struct ub_packed_rrset_key* k2) { - struct rbtree_t sortree1, sortree2; + struct rbtree_type sortree1, sortree2; struct canon_rr *rrs1, *rrs2, *p1, *p2; struct packed_rrset_data* d1=(struct packed_rrset_data*)k1->entry.data; struct packed_rrset_data* d2=(struct packed_rrset_data*)k2->entry.data; @@ -1115,12 +1125,15 @@ * signer name length. * @param sortree: if NULL is passed a new sorted rrset tree is built. * Otherwise it is reused. + * @param section: section of packet where this rrset comes from. + * @param qstate: qstate with region. * @return false on alloc error. */ static int rrset_canonical(struct regional* region, sldns_buffer* buf, struct ub_packed_rrset_key* k, uint8_t* sig, size_t siglen, - struct rbtree_t** sortree) + struct rbtree_type** sortree, sldns_pkt_section section, + struct module_qstate* qstate) { struct packed_rrset_data* d = (struct packed_rrset_data*)k->entry.data; uint8_t* can_owner = NULL; @@ -1129,8 +1142,8 @@ struct canon_rr* rrs; if(!*sortree) { - *sortree = (struct rbtree_t*)regional_alloc(region, - sizeof(rbtree_t)); + *sortree = (struct rbtree_type*)regional_alloc(region, + sizeof(rbtree_type)); if(!*sortree) return 0; if(d->count > RR_COUNT_MAX) @@ -1169,6 +1182,20 @@ canonicalize_rdata(buf, k, d->rr_len[walk->rr_idx]); } sldns_buffer_flip(buf); + + /* Replace RR owner with canonical owner for NSEC records in authority + * section, to prevent that a wildcard synthesized NSEC can be used in + * the non-existence proves. */ + if(ntohs(k->rk.type) == LDNS_RR_TYPE_NSEC && + section == LDNS_SECTION_AUTHORITY) { + k->rk.dname = regional_alloc_init(qstate->region, can_owner, + can_owner_len); + if(!k->rk.dname) + return 0; + k->rk.dname_len = can_owner_len; + } + + return 1; } @@ -1198,6 +1225,44 @@ (unsigned)incep, (unsigned)now); } +/** RFC 1982 comparison, uses unsigned integers, and tries to avoid + * compiler optimization (eg. by avoiding a-b<0 comparisons), + * this routine matches compare_serial(), for SOA serial number checks */ +static int +compare_1982(uint32_t a, uint32_t b) +{ + /* for 32 bit values */ + const uint32_t cutoff = ((uint32_t) 1 << (32 - 1)); + + if (a == b) { + return 0; + } else if ((a < b && b - a < cutoff) || (a > b && a - b > cutoff)) { + return -1; + } else { + return 1; + } +} + +/** if we know that b is larger than a, return the difference between them, + * that is the distance between them. in RFC1982 arith */ +static uint32_t +subtract_1982(uint32_t a, uint32_t b) +{ + /* for 32 bit values */ + const uint32_t cutoff = ((uint32_t) 1 << (32 - 1)); + + if(a == b) + return 0; + if(a < b && b - a < cutoff) { + return b-a; + } + if(a > b && a - b > cutoff) { + return ((uint32_t)0xffffffff) - (a-b-1); + } + /* wrong case, b smaller than a */ + return 0; +} + /** check rrsig dates */ static int check_dates(struct val_env* ve, uint32_t unow, @@ -1204,7 +1269,7 @@ uint8_t* expi_p, uint8_t* incep_p, char** reason) { /* read out the dates */ - int32_t expi, incep, now; + uint32_t expi, incep, now; memmove(&expi, expi_p, sizeof(expi)); memmove(&incep, incep_p, sizeof(incep)); expi = ntohl(expi); @@ -1218,21 +1283,21 @@ } now = ve->date_override; verbose(VERB_ALGO, "date override option %d", (int)now); - } else now = (int32_t)unow; + } else now = unow; /* check them */ - if(incep - expi > 0) { + if(compare_1982(incep, expi) > 0) { sigdate_error("verify: inception after expiration, " "signature bad", expi, incep, now); *reason = "signature inception after expiration"; return 0; } - if(incep - now > 0) { + if(compare_1982(incep, now) > 0) { /* within skew ? (calc here to avoid calculation normally) */ - int32_t skew = (expi-incep)/10; - if(skew < ve->skew_min) skew = ve->skew_min; - if(skew > ve->skew_max) skew = ve->skew_max; - if(incep - now > skew) { + uint32_t skew = subtract_1982(incep, expi)/10; + if(skew < (uint32_t)ve->skew_min) skew = ve->skew_min; + if(skew > (uint32_t)ve->skew_max) skew = ve->skew_max; + if(subtract_1982(now, incep) > skew) { sigdate_error("verify: signature bad, current time is" " before inception date", expi, incep, now); *reason = "signature before inception date"; @@ -1241,11 +1306,11 @@ sigdate_error("verify warning suspicious signature inception " " or bad local clock", expi, incep, now); } - if(now - expi > 0) { - int32_t skew = (expi-incep)/10; - if(skew < ve->skew_min) skew = ve->skew_min; - if(skew > ve->skew_max) skew = ve->skew_max; - if(now - expi > skew) { + if(compare_1982(now, expi) > 0) { + uint32_t skew = subtract_1982(incep, expi)/10; + if(skew < (uint32_t)ve->skew_min) skew = ve->skew_min; + if(skew > (uint32_t)ve->skew_max) skew = ve->skew_max; + if(subtract_1982(expi, now) > skew) { sigdate_error("verify: signature expired", expi, incep, now); *reason = "signature expired"; @@ -1291,7 +1356,7 @@ */ if(MIN_TTL > (time_t)origttl && d->ttl > MIN_TTL) { verbose(VERB_QUERY, "rrset TTL larger than original and minimum" - " TTL, adjusting TTL downwards to mimimum ttl"); + " TTL, adjusting TTL downwards to minimum ttl"); d->ttl = MIN_TTL; } else if(MIN_TTL <= origttl && d->ttl > (time_t)origttl) { @@ -1312,7 +1377,8 @@ struct val_env* ve, time_t now, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, size_t dnskey_idx, size_t sig_idx, - struct rbtree_t** sortree, int* buf_canon, char** reason) + struct rbtree_type** sortree, int* buf_canon, char** reason, + sldns_pkt_section section, struct module_qstate* qstate) { enum sec_status sec; uint8_t* sig; /* RRSIG rdata */ @@ -1411,7 +1477,7 @@ /* create rrset canonical format in buffer, ready for * signature */ if(!rrset_canonical(region, buf, rrset, sig+2, - 18 + signer_len, sortree)) { + 18 + signer_len, sortree, section, qstate)) { log_err("verify: failed due to alloc error"); return sec_status_unchecked; } --- contrib/unbound/validator/val_sigcrypt.h.orig +++ contrib/unbound/validator/val_sigcrypt.h @@ -44,10 +44,12 @@ #ifndef VALIDATOR_VAL_SIGCRYPT_H #define VALIDATOR_VAL_SIGCRYPT_H #include "util/data/packed_rrset.h" +#include "sldns/pkthdr.h" struct val_env; struct module_env; +struct module_qstate; struct ub_packed_rrset_key; -struct rbtree_t; +struct rbtree_type; struct regional; struct sldns_buffer; @@ -237,6 +239,8 @@ * @param sigalg: if nonNULL provide downgrade protection otherwise one * algorithm is enough. * @param reason: if bogus, a string returned, fixed or alloced in scratch. + * @param section: section of packet where this rrset comes from. + * @param qstate: qstate with region. * @return SECURE if one key in the set verifies one rrsig. * UNCHECKED on allocation errors, unsupported algorithms, malformed data, * and BOGUS on verification failures (no keys match any signatures). @@ -243,7 +247,8 @@ */ enum sec_status dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, - struct ub_packed_rrset_key* dnskey, uint8_t* sigalg, char** reason); + struct ub_packed_rrset_key* dnskey, uint8_t* sigalg, char** reason, + sldns_pkt_section section, struct module_qstate* qstate); /** * verify rrset against one specific dnskey (from rrset) @@ -253,12 +258,15 @@ * @param dnskey: DNSKEY rrset, keyset. * @param dnskey_idx: which key from the rrset to try. * @param reason: if bogus, a string returned, fixed or alloced in scratch. + * @param section: section of packet where this rrset comes from. + * @param qstate: qstate with region. * @return secure if *this* key signs any of the signatures on rrset. * unchecked on error or and bogus on bad signature. */ enum sec_status dnskey_verify_rrset(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, - struct ub_packed_rrset_key* dnskey, size_t dnskey_idx, char** reason); + struct ub_packed_rrset_key* dnskey, size_t dnskey_idx, char** reason, + sldns_pkt_section section, struct module_qstate* qstate); /** * verify rrset, with dnskey rrset, for a specific rrsig in rrset @@ -271,6 +279,8 @@ * @param sortree: reused sorted order. Stored in region. Pass NULL at start, * and for a new rrset. * @param reason: if bogus, a string returned, fixed or alloced in scratch. + * @param section: section of packet where this rrset comes from. + * @param qstate: qstate with region. * @return secure if any key signs *this* signature. bogus if no key signs it, * or unchecked on error. */ @@ -277,7 +287,8 @@ enum sec_status dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve, time_t now, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, size_t sig_idx, - struct rbtree_t** sortree, char** reason); + struct rbtree_type** sortree, char** reason, sldns_pkt_section section, + struct module_qstate* qstate); /** * verify rrset, with specific dnskey(from set), for a specific rrsig @@ -295,6 +306,8 @@ * pass false at start. pass old value only for same rrset and same * signature (but perhaps different key) for reuse. * @param reason: if bogus, a string returned, fixed or alloced in scratch. + * @param section: section of packet where this rrset comes from. + * @param qstate: qstate with region. * @return secure if this key signs this signature. unchecked on error or * bogus if it did not validate. */ @@ -302,7 +315,8 @@ struct sldns_buffer* buf, struct val_env* ve, time_t now, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, size_t dnskey_idx, size_t sig_idx, - struct rbtree_t** sortree, int* buf_canon, char** reason); + struct rbtree_type** sortree, int* buf_canon, char** reason, + sldns_pkt_section section, struct module_qstate* qstate); /** * canonical compare for two tree entries --- contrib/unbound/validator/val_utils.c.orig +++ contrib/unbound/validator/val_utils.c @@ -54,6 +54,7 @@ #include "util/net_help.h" #include "util/module.h" #include "util/regional.h" +#include "util/config_file.h" #include "sldns/wire2str.h" #include "sldns/parseutil.h" @@ -219,7 +220,7 @@ { size_t i; - if(subtype == VAL_CLASS_POSITIVE || subtype == VAL_CLASS_ANY) { + if(subtype == VAL_CLASS_POSITIVE) { /* check for the answer rrset */ for(i=skip; ian_numrrsets; i++) { if(query_dname_compare(qinf->qname, @@ -271,6 +272,29 @@ signer_name, signer_len, &matchcount); } } + } else if(subtype == VAL_CLASS_ANY) { + /* check for one of the answer rrset that has signatures, + * or potentially a DNAME is in use with a different qname */ + for(i=skip; ian_numrrsets; i++) { + if(query_dname_compare(qinf->qname, + rep->rrsets[i]->rk.dname) == 0) { + val_find_rrset_signer(rep->rrsets[i], + signer_name, signer_len); + if(*signer_name) + return; + } + } + /* no answer RRSIGs with qname, try a DNAME */ + if(skip < rep->an_numrrsets && + ntohs(rep->rrsets[skip]->rk.type) == + LDNS_RR_TYPE_DNAME) { + val_find_rrset_signer(rep->rrsets[skip], + signer_name, signer_len); + if(*signer_name) + return; + } + *signer_name = NULL; + *signer_len = 0; } else if(subtype == VAL_CLASS_REFERRAL) { /* find keys for the item at skip */ if(skip < rep->rrset_count) { @@ -311,7 +335,8 @@ enum sec_status val_verify_rrset(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* keys, - uint8_t* sigalg, char** reason) + uint8_t* sigalg, char** reason, sldns_pkt_section section, + struct module_qstate* qstate) { enum sec_status sec; struct packed_rrset_data* d = (struct packed_rrset_data*)rrset-> @@ -333,7 +358,8 @@ } log_nametypeclass(VERB_ALGO, "verify rrset", rrset->rk.dname, ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class)); - sec = dnskeyset_verify_rrset(env, ve, rrset, keys, sigalg, reason); + sec = dnskeyset_verify_rrset(env, ve, rrset, keys, sigalg, reason, + section, qstate); verbose(VERB_ALGO, "verify result: %s", sec_status_to_string(sec)); regional_free_all(env->scratch); @@ -366,7 +392,7 @@ enum sec_status val_verify_rrset_entry(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, struct key_entry_key* kkey, - char** reason) + char** reason, sldns_pkt_section section, struct module_qstate* qstate) { /* temporary dnskey rrset-key */ struct ub_packed_rrset_key dnskey; @@ -379,7 +405,8 @@ dnskey.rk.dname_len = kkey->namelen; dnskey.entry.key = &dnskey; dnskey.entry.data = kd->rrset_data; - sec = val_verify_rrset(env, ve, rrset, &dnskey, kd->algo, reason); + sec = val_verify_rrset(env, ve, rrset, &dnskey, kd->algo, reason, + section, qstate); return sec; } @@ -387,7 +414,8 @@ static enum sec_status verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, - struct ub_packed_rrset_key* ds_rrset, size_t ds_idx, char** reason) + struct ub_packed_rrset_key* ds_rrset, size_t ds_idx, char** reason, + struct module_qstate* qstate) { enum sec_status sec = sec_status_bogus; size_t i, num, numchecked = 0, numhashok = 0; @@ -418,7 +446,7 @@ /* Otherwise, we have a match! Make sure that the DNSKEY * verifies *with this key* */ sec = dnskey_verify_rrset(env, ve, dnskey_rrset, - dnskey_rrset, i, reason); + dnskey_rrset, i, reason, LDNS_SECTION_ANSWER, qstate); if(sec == sec_status_secure) { return sec; } @@ -454,7 +482,8 @@ enum sec_status val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, - struct ub_packed_rrset_key* ds_rrset, uint8_t* sigalg, char** reason) + struct ub_packed_rrset_key* ds_rrset, uint8_t* sigalg, char** reason, + struct module_qstate* qstate) { /* as long as this is false, we can consider this DS rrset to be * equivalent to no DS rrset. */ @@ -472,9 +501,14 @@ return sec_status_bogus; } - digest_algo = val_favorite_ds_algo(ds_rrset); - if(sigalg) + if(sigalg) { + /* harden against algo downgrade is enabled */ + digest_algo = val_favorite_ds_algo(ds_rrset); algo_needs_init_ds(&needs, ds_rrset, digest_algo, sigalg); + } else { + /* accept any key algo, any digest algo */ + digest_algo = -1; + } num = rrset_get_count(ds_rrset); for(i=0; i entry.data; @@ -764,6 +801,7 @@ if(labdiff > 0) { *wc = wn; dname_remove_labels(wc, &wl, labdiff); + *wc_len = wl; return 1; } return 1; @@ -886,7 +924,7 @@ } void -val_check_nonsecure(struct val_env* ve, struct reply_info* rep) +val_check_nonsecure(struct module_env* env, struct reply_info* rep) { size_t i; /* authority */ @@ -903,17 +941,24 @@ * Therefore the message is bogus. */ - /* check if authority consists of only an NS record + /* check if authority has an NS record * which is bad, and there is an answer section with * data. In that case, delete NS and additional to * be lenient and make a minimal response */ - if(rep->an_numrrsets != 0 && rep->ns_numrrsets == 1 && + if(rep->an_numrrsets != 0 && ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NS) { verbose(VERB_ALGO, "truncate to minimal"); - rep->ns_numrrsets = 0; rep->ar_numrrsets = 0; - rep->rrset_count = rep->an_numrrsets; + rep->rrset_count = rep->an_numrrsets + + rep->ns_numrrsets; + /* remove this unneeded authority rrset */ + memmove(rep->rrsets+i, rep->rrsets+i+1, + sizeof(struct ub_packed_rrset_key*)* + (rep->rrset_count - i - 1)); + rep->ns_numrrsets--; + rep->rrset_count--; + i--; return; } @@ -927,7 +972,7 @@ } } /* additional */ - if(!ve->clean_additional) + if(!env->cfg->val_clean_additional) return; for(i=rep->an_numrrsets+rep->ns_numrrsets; irrset_count; i++) { if(((struct packed_rrset_data*)rep->rrsets[i]->entry.data) @@ -1115,8 +1160,9 @@ qinfo.qname_len = nmlen; qinfo.qtype = LDNS_RR_TYPE_DS; qinfo.qclass = c; + qinfo.local_alias = NULL; /* do not add SOA to reply message, it is going to be used internal */ msg = val_neg_getmsg(env->neg_cache, &qinfo, region, env->rrset_cache, - env->scratch_buffer, *env->now, 0, topname); + env->scratch_buffer, *env->now, 0, topname, env->cfg); return msg; } --- contrib/unbound/validator/val_utils.h.orig +++ contrib/unbound/validator/val_utils.h @@ -42,10 +42,12 @@ #ifndef VALIDATOR_VAL_UTILS_H #define VALIDATOR_VAL_UTILS_H #include "util/data/packed_rrset.h" +#include "sldns/pkthdr.h" struct query_info; struct reply_info; struct val_env; struct module_env; +struct module_qstate; struct ub_packed_rrset_key; struct key_entry_key; struct regional; @@ -70,7 +72,7 @@ /** A NXDOMAIN response. */ VAL_CLASS_NAMEERROR, /** A CNAME/DNAME chain, and the offset is at the end of it, - * but there is no answer here, it can be NAMERROR or NODATA. */ + * but there is no answer here, it can be NAMEERROR or NODATA. */ VAL_CLASS_CNAMENOANSWER, /** A referral, from cache with a nonRD query. */ VAL_CLASS_REFERRAL, @@ -120,11 +122,14 @@ * @param sigalg: if nonNULL provide downgrade protection otherwise one * algorithm is enough. Algo list is constructed in here. * @param reason: reason of failure. Fixed string or alloced in scratch. + * @param section: section of packet where this rrset comes from. + * @param qstate: qstate with region. * @return security status of verification. */ enum sec_status val_verify_rrset(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* keys, - uint8_t* sigalg, char** reason); + uint8_t* sigalg, char** reason, sldns_pkt_section section, + struct module_qstate* qstate); /** * Verify RRset with keys from a keyset. @@ -133,11 +138,14 @@ * @param rrset: what to verify * @param kkey: key_entry to verify with. * @param reason: reason of failure. Fixed string or alloced in scratch. + * @param section: section of packet where this rrset comes from. + * @param qstate: qstate with region. * @return security status of verification. */ enum sec_status val_verify_rrset_entry(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* rrset, - struct key_entry_key* kkey, char** reason); + struct key_entry_key* kkey, char** reason, sldns_pkt_section section, + struct module_qstate* qstate); /** * Verify DNSKEYs with DS rrset. Like val_verify_new_DNSKEYs but @@ -150,6 +158,7 @@ * algorithm is enough. The list of signalled algorithms is returned, * must have enough space for ALGO_NEEDS_MAX+1. * @param reason: reason of failure. Fixed string or alloced in scratch. + * @param qstate: qstate with region. * @return: sec_status_secure if a DS matches. * sec_status_insecure if end of trust (i.e., unknown algorithms). * sec_status_bogus if it fails. @@ -156,7 +165,8 @@ */ enum sec_status val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, - struct ub_packed_rrset_key* ds_rrset, uint8_t* sigalg, char** reason); + struct ub_packed_rrset_key* ds_rrset, uint8_t* sigalg, char** reason, + struct module_qstate* qstate); /** * Verify DNSKEYs with DS and DNSKEY rrset. Like val_verify_DNSKEY_with_DS @@ -170,6 +180,7 @@ * algorithm is enough. The list of signalled algorithms is returned, * must have enough space for ALGO_NEEDS_MAX+1. * @param reason: reason of failure. Fixed string or alloced in scratch. + * @param qstate: qstate with region. * @return: sec_status_secure if a DS matches. * sec_status_insecure if end of trust (i.e., unknown algorithms). * sec_status_bogus if it fails. @@ -177,7 +188,8 @@ enum sec_status val_verify_DNSKEY_with_TA(struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, struct ub_packed_rrset_key* ta_ds, - struct ub_packed_rrset_key* ta_dnskey, uint8_t* sigalg, char** reason); + struct ub_packed_rrset_key* ta_dnskey, uint8_t* sigalg, char** reason, + struct module_qstate* qstate); /** * Verify new DNSKEYs with DS rrset. The DS contains hash values that should @@ -192,6 +204,7 @@ * @param downprot: if true provide downgrade protection otherwise one * algorithm is enough. * @param reason: reason of failure. Fixed string or alloced in scratch. + * @param qstate: qstate with region. * @return a KeyEntry. This will either contain the now trusted * dnskey_rrset, a "null" key entry indicating that this DS * rrset/DNSKEY pair indicate an secure end to the island of trust @@ -205,7 +218,8 @@ struct key_entry_key* val_verify_new_DNSKEYs(struct regional* region, struct module_env* env, struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset, - struct ub_packed_rrset_key* ds_rrset, int downprot, char** reason); + struct ub_packed_rrset_key* ds_rrset, int downprot, char** reason, + struct module_qstate* qstate); /** @@ -220,6 +234,7 @@ * @param downprot: if true provide downgrade protection otherwise one * algorithm is enough. * @param reason: reason of failure. Fixed string or alloced in scratch. + * @param qstate: qstate with region. * @return a KeyEntry. This will either contain the now trusted * dnskey_rrset, a "null" key entry indicating that this DS * rrset/DNSKEY pair indicate an secure end to the island of trust @@ -235,7 +250,7 @@ struct ub_packed_rrset_key* dnskey_rrset, struct ub_packed_rrset_key* ta_ds_rrset, struct ub_packed_rrset_key* ta_dnskey_rrset, - int downprot, char** reason); + int downprot, char** reason, struct module_qstate* qstate); /** * Determine if DS rrset is usable for validator or not. @@ -252,10 +267,11 @@ * the result of a wildcard expansion. If so, return the name of the * generating wildcard. * - * @param rrset The rrset to chedck. + * @param rrset The rrset to check. * @param wc: the wildcard name, if the rrset was synthesized from a wildcard. * unchanged if not. The wildcard name, without "*." in front, is * returned. This is a pointer into the rrset owner name. + * @param wc_len: the length of the returned wildcard name. * @return false if the signatures are inconsistent in indicating the * wildcard status; possible spoofing of wildcard response for other * responses is being tried. We lost the status which rrsig was verified @@ -264,7 +280,8 @@ * of service; but in that you could also have removed the real * signature anyway. */ -int val_rrset_wildcard(struct ub_packed_rrset_key* rrset, uint8_t** wc); +int val_rrset_wildcard(struct ub_packed_rrset_key* rrset, uint8_t** wc, + size_t* wc_len); /** * Chase the cname to the next query name. @@ -306,10 +323,10 @@ * So that unsigned data does not get let through to clients, when we have * found the data to be secure. * - * @param ve: validator environment with cleaning options. + * @param env: environment with cleaning options. * @param rep: reply to dump all nonsecure stuff out of. */ -void val_check_nonsecure(struct val_env* ve, struct reply_info* rep); +void val_check_nonsecure(struct module_env* env, struct reply_info* rep); /** * Mark all unchecked rrset entries not below a trust anchor as indeterminate. --- contrib/unbound/validator/validator.c.orig +++ contrib/unbound/validator/validator.c @@ -40,6 +40,7 @@ * According to RFC 4034. */ #include "config.h" +#include #include "validator/validator.h" #include "validator/val_anchor.h" #include "validator/val_kcache.h" @@ -51,6 +52,7 @@ #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" @@ -60,6 +62,7 @@ #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, @@ -112,8 +115,6 @@ { int c; val_env->bogus_ttl = (uint32_t)cfg->bogus_ttl; - val_env->clean_additional = cfg->val_clean_additional; - val_env->permissive_mode = cfg->val_permissive_mode; if(!env->anchors) env->anchors = anchors_create(); if(!env->anchors) { @@ -120,6 +121,8 @@ 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) { @@ -145,6 +148,8 @@ 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]); @@ -170,7 +175,6 @@ } env->modinfo[id] = (void*)val_env; env->need_to_validate = 1; - val_env->permissive_mode = 0; lock_basic_init(&val_env->bogus_lock); lock_protect(&val_env->bogus_lock, &val_env->num_rrset_bogus, sizeof(val_env->num_rrset_bogus)); @@ -181,6 +185,7 @@ log_err("validator: could not apply configuration settings."); return 0; } + return 1; } @@ -195,7 +200,9 @@ 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); @@ -363,14 +370,17 @@ * @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) + 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 module_qstate* newq; struct query_info ask; int valrec; ask.qname = name; @@ -377,23 +387,45 @@ ask.qname_len = namelen; ask.qtype = qtype; ask.qclass = qclass; + ask.local_alias = NULL; log_query_info(VERB_ALGO, "generate request", &ask); - fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub)); /* enable valrec flag to avoid recursion to the same validation * routine, this lookup is simply a lookup. DLVs need validation */ if(qtype == LDNS_RR_TYPE_DLV) valrec = 0; else valrec = 1; - if(!(*qstate->env->attach_sub)(qstate, &ask, - (uint16_t)(BIT_RD|flags), 0, valrec, &newq)){ - log_err("Could not generate request: out of memory"); + + 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) { + if(*newq) { /* add our blacklist to the query blacklist */ - sock_list_merge(&newq->blacklist, newq->region, + sock_list_merge(&(*newq)->blacklist, (*newq)->region, vq->chain_blacklist); } qstate->ext_state[id] = module_wait_subquery; @@ -401,6 +433,91 @@ } /** + * 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; iname, 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 interrested 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. @@ -415,10 +532,18 @@ 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); + 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) { - log_err("Could not prime trust anchor: out of memory"); + verbose(VERB_ALGO, "Could not prime trust anchor"); return 0; } /* ignore newq; validator does not need state created for that @@ -488,7 +613,8 @@ } /* Verify the answer rrset */ - sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason); + sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason, + LDNS_SECTION_ANSWER, qstate); /* If the (answer) rrset failed to validate, then this * message is BAD. */ if(sec != sec_status_secure) { @@ -517,7 +643,8 @@ for(i=chase_reply->an_numrrsets; ian_numrrsets+ chase_reply->ns_numrrsets; i++) { s = chase_reply->rrsets[i]; - sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason); + sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason, + LDNS_SECTION_AUTHORITY, qstate); /* If anything in the authority section fails to be secure, * we have a bad message. */ if(sec != sec_status_secure) { @@ -532,9 +659,11 @@ } } + /* 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 */ - if(!ve->clean_additional) - return 1; for(i=chase_reply->an_numrrsets+chase_reply->ns_numrrsets; irrset_count; i++) { s = chase_reply->rrsets[i]; @@ -543,7 +672,7 @@ val_find_rrset_signer(s, &sname, &slen); if(sname && query_dname_compare(sname, key_entry->name)==0) (void)val_verify_rrset_entry(env, ve, s, key_entry, - &reason); + &reason, LDNS_SECTION_ADDITIONAL, qstate); /* the additional section can fail to be secure, * it is optional, check signature in case we need * to clean the additional section later. */ @@ -657,6 +786,8 @@ 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; @@ -669,7 +800,7 @@ /* 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)) { + 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)); @@ -676,6 +807,12 @@ chase_reply->security = sec_status_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 @@ -856,6 +993,9 @@ 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; ian_numrrsets+ chase_reply->ns_numrrsets; i++) { @@ -863,9 +1003,19 @@ if(ntohs(s->rk.type) == LDNS_RR_TYPE_NSEC) { if(val_nsec_proves_name_error(s, qchase->qname)) has_valid_nsec = 1; - if(val_nsec_proves_no_wc(s, qchase->qname, - qchase->qname_len)) - has_valid_wnsec = 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; @@ -980,6 +1130,7 @@ /* 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; @@ -998,7 +1149,7 @@ /* 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)) { + 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), @@ -1087,6 +1238,7 @@ struct key_entry_key* kkey) { uint8_t* wc = NULL; + size_t wl; int wc_NSEC_ok = 0; int nsec3s_seen = 0; size_t i; @@ -1099,7 +1251,7 @@ /* 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)) { + 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)); @@ -1203,11 +1355,14 @@ uint8_t* ce = NULL; /* for wildcard nodata responses. This is the proven closest encloser. */ uint8_t* wc = NULL; /* for wildcard nodata responses. wildcard nsec */ - int nxdomain_valid_nsec = 0; /* if true, namerror has been proven */ + int nxdomain_valid_nsec = 0; /* if true, nameerror has been proven */ int nxdomain_valid_wnsec = 0; int nsec3s_seen = 0; /* nsec3s seen */ struct ub_packed_rrset_key* s; 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; ian_numrrsets+ @@ -1226,9 +1381,19 @@ ce = nsec_closest_encloser(qchase->qname, s); nxdomain_valid_nsec = 1; } - if(val_nsec_proves_no_wc(s, qchase->qname, - qchase->qname_len)) - nxdomain_valid_wnsec = 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; @@ -1508,6 +1673,7 @@ 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, @@ -1520,8 +1686,8 @@ 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)) { - log_err("mem error generating DNSKEY request"); + vq->qchase.qclass, BIT_CD, &newq, 0)) { + verbose(VERB_ALGO, "error generating DNSKEY request"); return val_error(qstate, id); } return 0; @@ -1592,8 +1758,8 @@ 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)) { - log_err("mem error generating DNSKEY request"); + vq->qchase.qclass, BIT_CD, &newq, 0)) { + verbose(VERB_ALGO, "error generating DNSKEY request"); return val_error(qstate, id); } return 0; @@ -1621,8 +1787,8 @@ } if(!generate_request(qstate, id, target_key_name, target_key_len, LDNS_RR_TYPE_DS, vq->qchase.qclass, - BIT_CD)) { - log_err("mem error generating DS request"); + BIT_CD, &newq, 0)) { + verbose(VERB_ALGO, "error generating DS request"); return val_error(qstate, id); } return 0; @@ -1631,8 +1797,8 @@ /* 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)) { - log_err("mem error generating DNSKEY request"); + vq->qchase.qclass, BIT_CD, &newq, 0)) { + verbose(VERB_ALGO, "error generating DNSKEY request"); return val_error(qstate, id); } @@ -1845,6 +2011,7 @@ { uint8_t* nm; size_t nm_len; + struct module_qstate* newq = NULL; /* there must be a DLV configured */ log_assert(qstate->env->anchors->dlv_anchor); /* this bool is true to avoid looping in the DLV checks */ @@ -1946,7 +2113,7 @@ vq->state = VAL_DLVLOOKUP_STATE; if(!generate_request(qstate, id, vq->dlv_lookup_name, vq->dlv_lookup_name_len, LDNS_RR_TYPE_DLV, - vq->qchase.qclass, 0)) { + vq->qchase.qclass, 0, &newq, 0)) { return val_error(qstate, id); } @@ -2040,10 +2207,14 @@ * 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(ve, vq->orig_msg->rep); + 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); + } } } @@ -2070,35 +2241,75 @@ vq->orig_msg->rep->ttl = ve->bogus_ttl; vq->orig_msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(vq->orig_msg->rep->ttl); - if(qstate->env->cfg->val_log_level >= 1 && + 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) - log_query_info(0, "validation failure", + 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 = errinf_to_str(qstate); + char* err = errinf_to_str_bogus(qstate); if(err) log_info("%s", err); free(err); } } + /* + * 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(ve->permissive_mode) + 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; + } + } + } /* 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(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo, - vq->orig_msg->rep, 0, qstate->prefetch_leeway, 0, NULL, - qstate->query_flags)) { - log_err("out of memory caching validator results"); + 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)) { + 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, + if(!dns_cache_store(qstate->env, &vq->orig_msg->qinfo, vq->orig_msg->rep, 1, 0, 0, NULL, qstate->query_flags)) { log_err("out of memory caching validator results"); @@ -2124,6 +2335,7 @@ processDLVLookup(struct module_qstate* qstate, struct val_qstate* vq, struct val_env* ve, int id) { + struct module_qstate* newq = NULL; /* see if this we are ready to continue normal resolution */ /* we may need more DLV lookups */ if(vq->dlv_status==dlv_error) @@ -2138,6 +2350,7 @@ if(vq->dlv_status == dlv_error) { verbose(VERB_QUERY, "failed DLV lookup"); + errinf(qstate, "failed DLV lookup"); return val_error(qstate, id); } else if(vq->dlv_status == dlv_success) { uint8_t* nm; @@ -2172,8 +2385,8 @@ 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)) { - log_err("mem error generating DNSKEY request"); + vq->qchase.qclass, BIT_CD, &newq, 0)) { + verbose(VERB_ALGO, "error generating DNSKEY request"); return val_error(qstate, id); } return 0; @@ -2214,7 +2427,7 @@ if(!generate_request(qstate, id, vq->dlv_lookup_name, vq->dlv_lookup_name_len, LDNS_RR_TYPE_DLV, - vq->qchase.qclass, 0)) { + vq->qchase.qclass, 0, &newq, 0)) { return val_error(qstate, id); } @@ -2280,6 +2493,7 @@ (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; @@ -2288,6 +2502,7 @@ 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 */ @@ -2385,7 +2600,7 @@ /* attempt to verify with trust anchor DS and DNSKEY */ kkey = val_verify_new_DNSKEYs_with_ta(qstate->region, qstate->env, ve, dnskey_rrset, ta->ds_rrset, ta->dnskey_rrset, downprot, - &reason); + &reason, qstate); if(!kkey) { log_err("out of memory: verifying prime TA"); return NULL; @@ -2475,7 +2690,7 @@ /* Verify only returns BOGUS or SECURE. If the rrset is * bogus, then we are done. */ sec = val_verify_rrset_entry(qstate->env, ve, ds, - vq->key_entry, &reason); + vq->key_entry, &reason, LDNS_SECTION_ANSWER, qstate); if(sec != sec_status_secure) { verbose(VERB_DETAIL, "DS rrset in DS response did " "not verify"); @@ -2522,7 +2737,7 @@ /* Try to prove absence of the DS with NSEC */ sec = val_nsec_prove_nodata_dsreply( qstate->env, ve, qinfo, msg->rep, vq->key_entry, - &proof_ttl, &reason); + &proof_ttl, &reason, qstate); switch(sec) { case sec_status_secure: verbose(VERB_DETAIL, "NSEC RRset for the " @@ -2550,7 +2765,8 @@ sec = nsec3_prove_nods(qstate->env, ve, msg->rep->rrsets + msg->rep->an_numrrsets, - msg->rep->ns_numrrsets, qinfo, vq->key_entry, &reason); + msg->rep->ns_numrrsets, qinfo, vq->key_entry, &reason, + qstate); switch(sec) { case sec_status_insecure: /* case insecure also continues to unsigned @@ -2611,7 +2827,7 @@ goto return_bogus; } sec = val_verify_rrset_entry(qstate->env, ve, cname, - vq->key_entry, &reason); + vq->key_entry, &reason, LDNS_SECTION_ANSWER, qstate); if(sec == sec_status_secure) { verbose(VERB_ALGO, "CNAME validated, " "proof that DS does not exist"); @@ -2777,7 +2993,7 @@ } downprot = qstate->env->cfg->harden_algo_downgrade; vq->key_entry = val_verify_new_DNSKEYs(qstate->region, qstate->env, - ve, dnskey, vq->ds_rrset, downprot, &reason); + ve, dnskey, vq->ds_rrset, downprot, &reason, qstate); if(!vq->key_entry) { log_err("out of memory in verify new DNSKEYs"); @@ -2851,8 +3067,10 @@ ta->name, ta->namelen, LDNS_RR_TYPE_DNSKEY, ta->dclass); } + if(ta->autr) { - if(!autr_process_prime(qstate->env, ve, ta, dnskey_rrset)) { + if(!autr_process_prime(qstate->env, ve, ta, dnskey_rrset, + qstate)) { /* trust anchor revoked, restart with less anchors */ vq->state = VAL_INIT_STATE; vq->trust_anchor_name = NULL; --- contrib/unbound/validator/validator.h.orig +++ contrib/unbound/validator/validator.h @@ -67,6 +67,13 @@ /** max number of query restarts, number of IPs to probe */ #define VAL_MAX_RESTART_COUNT 5 +/** Root key sentinel is ta preamble */ +#define SENTINEL_IS "root-key-sentinel-is-ta-" +/** Root key sentinel is not ta preamble */ +#define SENTINEL_NOT "root-key-sentinel-not-ta-" +/** Root key sentinal keytag length */ +#define SENTINEL_KEYTAG_LEN 5 + /** * Global state for the validator. */ @@ -93,20 +100,7 @@ * seconds. */ uint32_t bogus_ttl; - /** If set, the validator should clean the additional section of - * secure messages. - */ - int clean_additional; - /** - * 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. - */ - int permissive_mode; - - /** * Number of entries in the NSEC3 maximum iteration count table. * Keep this table short, and sorted by size */ @@ -126,7 +120,7 @@ size_t* nsec3_maxiter; /** lock on bogus counter */ - lock_basic_t bogus_lock; + lock_basic_type bogus_lock; /** number of times rrsets marked bogus */ size_t num_rrset_bogus; }; --- etc/rc.d/local_unbound.orig +++ etc/rc.d/local_unbound @@ -14,7 +14,7 @@ desc="Local caching forwarding resolver" rcvar="local_unbound_enable" -command="/usr/sbin/unbound" +command="/usr/sbin/local-unbound" extra_commands="anchor configtest reload setup" start_precmd="local_unbound_prestart" start_postcmd="local_unbound_poststart" @@ -44,7 +44,7 @@ # local_unbound_anchor() { - do_as_unbound /usr/sbin/unbound-anchor -a ${local_unbound_anchor} + do_as_unbound ${command}-anchor -a ${local_unbound_anchor} # we can't trust the exit code - check if the file exists [ -f ${local_unbound_anchor} ] } @@ -54,7 +54,7 @@ # local_unbound_configtest() { - do_as_unbound /usr/sbin/unbound-checkconf ${local_unbound_config} + do_as_unbound ${command}-checkconf ${local_unbound_config} } # @@ -64,7 +64,7 @@ local_unbound_setup() { echo "Performing initial setup." - /usr/sbin/local-unbound-setup -n \ + ${command}-setup -n \ -u unbound \ -w ${local_unbound_workdir} \ -c ${local_unbound_config} \ --- lib/libunbound/Makefile.orig +++ lib/libunbound/Makefile @@ -6,7 +6,7 @@ UNBOUNDDIR= ${SRCTOP}/contrib/unbound # Hold my beer and watch this -.PATH: ${UNBOUNDDIR} ${UNBOUNDDIR}/cachedb ${UNBOUNDDIR}/dns64 ${UNBOUNDDIR}/iterator ${UNBOUNDDIR}/sldns ${UNBOUNDDIR}/libunbound ${UNBOUNDDIR}/services ${UNBOUNDDIR}/services/cache ${UNBOUNDDIR}/util ${UNBOUNDDIR}/util/data ${UNBOUNDDIR}/util/storage ${UNBOUNDDIR}/validator +.PATH: ${UNBOUNDDIR} ${UNBOUNDDIR}/cachedb ${UNBOUNDDIR}/dns64 ${UNBOUNDDIR}/iterator ${UNBOUNDDIR}/sldns ${UNBOUNDDIR}/libunbound ${UNBOUNDDIR}/services ${UNBOUNDDIR}/services/cache ${UNBOUNDDIR}/util ${UNBOUNDDIR}/util/data ${UNBOUNDDIR}/respip ${UNBOUNDDIR}/util/storage ${UNBOUNDDIR}/validator LIB= unbound PRIVATELIB= @@ -13,22 +13,25 @@ PACKAGE= unbound CFLAGS+= -I${UNBOUNDDIR} -I${LDNSDIR} -I${.OBJDIR} +CFLAGS+= -I${SRCTOP}/usr.sbin/unbound -SRCS= alloc.c as112.c autotrust.c cachedb.c config_file.c configlexer.l \ - configparser.y context.c dname.c dns.c dns64.c dnstree.c \ - fptr_wlist.c infra.c iter_delegpt.c iter_donotq.c iter_fwd.c \ - iter_hints.c iter_priv.c iter_resptype.c iter_scrub.c iter_utils.c \ - iterator.c keyraw.c libunbound.c libworker.c listen_dnsport.c \ - localzone.c locks.c log.c lookup3.c lruhash.c mesh.c mini_event.c \ - modstack.c module.c msgencode.c msgparse.c msgreply.c net_help.c \ - netevent.c outbound_list.c outside_network.c packed_rrset.c parse.c \ - parseutil.c random.c rbtree.c regional.c rrdef.c rrset.c rtt.c \ - sbuffer.c slabhash.c str2wire.c timehist.c tube.c \ - ub_event_pluggable.c val_anchor.c val_kcache.c val_kentry.c \ - val_neg.c val_nsec.c val_nsec3.c val_secalgo.c val_sigcrypt.c \ - val_utils.c validator.c winsock_event.c wire2str.c +SRCS= alloc.c as112.c authzone.c autotrust.c cachedb.c config_file.c \ + configlexer.l configparser.y context.c dname.c dns.c dns64.c \ + dnstree.c edns.c fptr_wlist.c infra.c iter_delegpt.c iter_donotq.c \ + iter_fwd.c iter_hints.c iter_priv.c iter_resptype.c iter_scrub.c \ + iter_utils.c iterator.c keyraw.c libunbound.c libworker.c \ + listen_dnsport.c localzone.c locks.c log.c lookup3.c lruhash.c \ + mesh.c mini_event.c modstack.c module.c msgencode.c msgparse.c \ + msgreply.c net_help.c netevent.c outbound_list.c outside_network.c \ + packed_rrset.c parse.c parseutil.c random.c rbtree.c redis.c \ + regional.c respip.c rpz.c rrdef.c rrset.c rtt.c sbuffer.c slabhash.c \ + str2wire.c tcp_conn_limit.c timehist.c tube.c ub_event_pluggable.c \ + val_anchor.c val_kcache.c val_kentry.c val_neg.c val_nsec.c \ + val_nsec3.c val_secalgo.c val_sigcrypt.c val_utils.c validator.c \ + view.c winsock_event.c wire2str.c -WARNS?= 3 +WARNS?= 2 +NO_WTHREAD_SAFETY= true LIBADD= ssl crypto pthread --- tools/build/mk/OptionalObsoleteFiles.inc.orig +++ tools/build/mk/OptionalObsoleteFiles.inc @@ -8978,17 +8978,17 @@ OLD_FILES+=usr/lib32/private/libunbound.so OLD_LIBS+=usr/lib32/private/libunbound.so.5 OLD_FILES+=usr/lib32/private/libunbound_p.a +OLD_FILES+=usr/share/man/man5/local-unbound.conf.5.gz +OLD_FILES+=usr/share/man/man8/local-unbound-anchor.8.gz +OLD_FILES+=usr/share/man/man8/local-unbound-checkconf.8.gz +OLD_FILES+=usr/share/man/man8/local-unbound-control.8.gz +OLD_FILES+=usr/share/man/man8/local-unbound.8.gz .endif OLD_FILES+=usr/sbin/local-unbound-setup -OLD_FILES+=usr/sbin/unbound -OLD_FILES+=usr/sbin/unbound-anchor -OLD_FILES+=usr/sbin/unbound-checkconf -OLD_FILES+=usr/sbin/unbound-control -OLD_FILES+=usr/share/man/man5/unbound.conf.5.gz -OLD_FILES+=usr/share/man/man8/unbound-anchor.8.gz -OLD_FILES+=usr/share/man/man8/unbound-checkconf.8.gz -OLD_FILES+=usr/share/man/man8/unbound-control.8.gz -OLD_FILES+=usr/share/man/man8/unbound.8.gz +OLD_FILES+=usr/sbin/local-unbound +OLD_FILES+=usr/sbin/local-unbound-anchor +OLD_FILES+=usr/sbin/local-unbound-checkconf +OLD_FILES+=usr/sbin/local-unbound-control .endif .if ${MK_USB} == no --- usr.sbin/unbound/Makefile.orig +++ usr.sbin/unbound/Makefile @@ -1,7 +1,7 @@ # $FreeBSD$ SUBDIR= daemon anchor checkconf control -SUBDIR+= local-setup +SUBDIR+= setup SUBDIR_PARALLEL= .include --- usr.sbin/unbound/Makefile.inc.orig +++ usr.sbin/unbound/Makefile.inc @@ -3,4 +3,14 @@ NO_WERROR= true PACKAGE= unbound +.for man in ${MAN} +${man}: ${UNBOUNDDIR}/doc/${man:S/local-//} + sed -E \ + -e 's/\<(fI)?unbound\>/\1local-unbound/g' \ + -e 's/\<(fI)?Unbound\>/Local-unbound/g' \ + -e 's/\/local-unbound/\/unbound/g' \ + <${.ALLSRC} >${.TARGET} +CLEANFILES += ${man} +.endfor + .include "../Makefile.inc" --- usr.sbin/unbound/anchor/Makefile.orig +++ usr.sbin/unbound/anchor/Makefile @@ -7,10 +7,11 @@ .PATH: ${UNBOUNDDIR} ${UNBOUNDDIR}/smallapp ${UNBOUNDDIR}/doc -PROG= unbound-anchor +PROG= local-unbound-anchor SRCS= unbound-anchor.c CFLAGS+= -I${UNBOUNDDIR} -I${LDNSDIR} -I${EXPATDIR}/lib +CFLAGS+= -I${.CURDIR:H} -I${.CURDIR} LIBADD= unbound bsdxml ssl crypto pthread -MAN= unbound-anchor.8 +MAN= local-unbound-anchor.8 .include --- usr.sbin/unbound/checkconf/Makefile.orig +++ usr.sbin/unbound/checkconf/Makefile @@ -6,10 +6,11 @@ .PATH: ${UNBOUNDDIR} ${UNBOUNDDIR}/smallapp ${UNBOUNDDIR}/util ${UNBOUNDDIR}/doc -PROG= unbound-checkconf +PROG= local-unbound-checkconf SRCS= ub_event.c unbound-checkconf.c worker_cb.c CFLAGS+= -I${UNBOUNDDIR} -I${LDNSDIR} +CFLAGS+= -I${.CURDIR:H} -I${.CURDIR} LIBADD= unbound pthread -MAN= unbound-checkconf.8 +MAN= local-unbound-checkconf.8 .include --- usr.sbin/unbound/config.h.orig +++ usr.sbin/unbound/config.h @@ -0,0 +1,1338 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ +/* $FreeBSD$ */ + +/* apply the noreturn attribute to a function that exits the program */ +#define ATTR_NORETURN __attribute__((__noreturn__)) + +/* apply the weak attribute to a symbol */ +#define ATTR_WEAK __attribute__((weak)) + +/* Directory to chroot to */ +#define CHROOT_DIR "/var/unbound" + +/* 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 */ +#define CONFCMDLINE "--with-ssl=/usr --with-libexpat=/usr --disable-dnscrypt --disable-dnstap --enable-ecdsa --disable-event-api --enable-gost --with-libevent --disable-subnet --disable-tfo-client --disable-tfo-server --with-pthreads--prefix=/usr --localstatedir=/var/unbound --mandir=/usr/share/man --build=freebsd" + +/* Pathname to the Unbound configuration file */ +#define CONFIGFILE "/var/unbound/unbound.conf" + +/* 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 */ + +/* 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. */ +#define HAVE_ACCEPT4 1 + +/* Define to 1 if you have the `arc4random' function. */ +#define HAVE_ARC4RANDOM 1 + +/* Define to 1 if you have the `arc4random_uniform' function. */ +#define HAVE_ARC4RANDOM_UNIFORM 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_ARPA_INET_H 1 + +/* Whether the C compiler accepts the "format" attribute */ +#define HAVE_ATTR_FORMAT 1 + +/* Whether the C compiler accepts the "noreturn" attribute */ +#define HAVE_ATTR_NORETURN 1 + +/* Whether the C compiler accepts the "unused" attribute */ +#define HAVE_ATTR_UNUSED 1 + +/* Whether the C compiler accepts the "weak" attribute */ +#define HAVE_ATTR_WEAK 1 + +/* Define to 1 if you have the `chown' function. */ +#define HAVE_CHOWN 1 + +/* Define to 1 if you have the `chroot' function. */ +#define HAVE_CHROOT 1 + +/* Define to 1 if you have the `CRYPTO_cleanup_all_ex_data' function. */ +#define HAVE_CRYPTO_CLEANUP_ALL_EX_DATA 1 + +/* 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. */ +#define HAVE_CTIME_R 1 + +/* Define to 1 if you have the `daemon' function. */ +#define HAVE_DAEMON 1 + +/* 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. */ +#define HAVE_DECL_INET_NTOP 1 + +/* Define to 1 if you have the declaration of `inet_pton', and to 0 if you + don't. */ +#define HAVE_DECL_INET_PTON 1 + +/* Define to 1 if you have the declaration of `NID_ED25519', and to 0 if you + don't. */ +#define HAVE_DECL_NID_ED25519 1 + +/* Define to 1 if you have the declaration of `NID_ED448', and to 0 if you + don't. */ +#define HAVE_DECL_NID_ED448 1 + +/* Define to 1 if you have the declaration of `NID_secp384r1', and to 0 if you + don't. */ +#define HAVE_DECL_NID_SECP384R1 1 + +/* Define to 1 if you have the declaration of `NID_X9_62_prime256v1', and to 0 + if you don't. */ +#define HAVE_DECL_NID_X9_62_PRIME256V1 1 + +/* 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. */ +#define HAVE_DECL_SK_SSL_COMP_POP_FREE 1 + +/* Define to 1 if you have the declaration of + `SSL_COMP_get_compression_methods', and to 0 if you don't. */ +#define HAVE_DECL_SSL_COMP_GET_COMPRESSION_METHODS 1 + +/* Define to 1 if you have the declaration of `SSL_CTX_set_ecdh_auto', and to + 0 if you don't. */ +#define HAVE_DECL_SSL_CTX_SET_ECDH_AUTO 1 + +/* 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. */ +#define HAVE_DECL_XML_STOPPARSER 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the `DSA_SIG_set0' function. */ +/* #undef HAVE_DSA_SIG_SET0 */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_ENDIAN_H */ + +/* Define to 1 if you have the `endprotoent' function. */ +#define HAVE_ENDPROTOENT 1 + +/* Define to 1 if you have the `endpwent' function. */ +#define HAVE_ENDPWENT 1 + +/* Define to 1 if you have the `endservent' function. */ +#define HAVE_ENDSERVENT 1 + +/* Define to 1 if you have the `ERR_free_strings' function. */ +#define HAVE_ERR_FREE_STRINGS 1 + +/* Define to 1 if you have the `ERR_load_crypto_strings' function. */ +#define HAVE_ERR_LOAD_CRYPTO_STRINGS 1 + +/* 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 header file. */ +/* #undef HAVE_EVENT_H */ + +/* Define to 1 if you have the `EVP_aes_256_cbc' function. */ +#define HAVE_EVP_AES_256_CBC 1 + +/* Define to 1 if you have the `EVP_cleanup' function. */ +#define HAVE_EVP_CLEANUP 1 + +/* 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_MD_CTX_new' function. */ +/* #undef HAVE_EVP_MD_CTX_NEW */ + +/* Define to 1 if you have the `EVP_sha1' function. */ +#define HAVE_EVP_SHA1 1 + +/* Define to 1 if you have the `EVP_sha256' function. */ +#define HAVE_EVP_SHA256 1 + +/* Define to 1 if you have the `EVP_sha512' function. */ +#define HAVE_EVP_SHA512 1 + +/* 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 header file. */ +#define HAVE_EXPAT_H 1 + +/* Define to 1 if you have the `explicit_bzero' function. */ +#define HAVE_EXPLICIT_BZERO 1 + +/* Define to 1 if you have the `fcntl' function. */ +#define HAVE_FCNTL 1 + +/* Define to 1 if you have the `FIPS_mode' function. */ +#define HAVE_FIPS_MODE 1 + +/* Define to 1 if you have the `fork' function. */ +#define HAVE_FORK 1 + +/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */ +#define HAVE_FSEEKO 1 + +/* Define to 1 if you have the `fsync' function. */ +#define HAVE_FSYNC 1 + +/* Whether getaddrinfo is available */ +#define HAVE_GETADDRINFO 1 + +/* 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 header file. */ +#define HAVE_GETOPT_H 1 + +/* Define to 1 if you have the `getpwnam' function. */ +#define HAVE_GETPWNAM 1 + +/* Define to 1 if you have the `getrlimit' function. */ +#define HAVE_GETRLIMIT 1 + +/* Define to 1 if you have the `glob' function. */ +#define HAVE_GLOB 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_GLOB_H 1 + +/* Define to 1 if you have the `gmtime_r' function. */ +#define HAVE_GMTIME_R 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_GRP_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_HIREDIS_HIREDIS_H */ + +/* Define to 1 if you have the `HMAC_Init_ex' function. */ +#define HAVE_HMAC_INIT_EX 1 + +/* If you have HMAC_Update */ +#define HAVE_HMAC_UPDATE 1 + +/* Define to 1 if you have the `inet_aton' function. */ +#define HAVE_INET_ATON 1 + +/* Define to 1 if you have the `inet_ntop' function. */ +#define HAVE_INET_NTOP 1 + +/* Define to 1 if you have the `inet_pton' function. */ +#define HAVE_INET_PTON 1 + +/* Define to 1 if you have the `initgroups' function. */ +#define HAVE_INITGROUPS 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* if the function 'ioctlsocket' is available */ +/* #undef HAVE_IOCTLSOCKET */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_IPHLPAPI_H */ + +/* Define to 1 if you have the `isblank' function. */ +#define HAVE_ISBLANK 1 + +/* Define to 1 if you have the `kill' function. */ +#define HAVE_KILL 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_LIBKERN_OSBYTEORDER_H */ + +/* Define if we have LibreSSL */ +/* #undef HAVE_LIBRESSL */ + +/* Define to 1 if you have the `localtime_r' function. */ +#define HAVE_LOCALTIME_R 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LOGIN_CAP_H 1 + +/* If have GNU libc compatible malloc */ +#define HAVE_MALLOC 1 + +/* Define to 1 if you have the `memmove' function. */ +#define HAVE_MEMMOVE 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NETDB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NETINET_IN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NETINET_TCP_H 1 + +/* Use libnettle for crypto */ +/* #undef HAVE_NETTLE */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NETTLE_DSA_COMPAT_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NETTLE_EDDSA_H */ + +/* Use libnss for crypto */ +/* #undef HAVE_NSS */ + +/* Define to 1 if you have the `OpenSSL_add_all_digests' function. */ +#define HAVE_OPENSSL_ADD_ALL_DIGESTS 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_OPENSSL_BN_H 1 + +/* Define to 1 if you have the `OPENSSL_config' function. */ +#define HAVE_OPENSSL_CONFIG 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_OPENSSL_CONF_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_OPENSSL_DH_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_OPENSSL_DSA_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_OPENSSL_ENGINE_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_OPENSSL_ERR_H 1 + +/* 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 header file. */ +#define HAVE_OPENSSL_RAND_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_OPENSSL_RSA_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_OPENSSL_SSL_H 1 + +/* Define if you have POSIX threads libraries and header files. */ +#define HAVE_PTHREAD 1 + +/* Have PTHREAD_PRIO_INHERIT. */ +#define HAVE_PTHREAD_PRIO_INHERIT 1 + +/* Define to 1 if the system has the type `pthread_rwlock_t'. */ +#define HAVE_PTHREAD_RWLOCK_T 1 + +/* Define to 1 if the system has the type `pthread_spinlock_t'. */ +#define HAVE_PTHREAD_SPINLOCK_T 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_PWD_H 1 + +/* Define if you have Python libraries and header files. */ +/* #undef HAVE_PYTHON */ + +/* Define to 1 if you have the `random' function. */ +#define HAVE_RANDOM 1 + +/* Define to 1 if you have the `RAND_cleanup' function. */ +#define HAVE_RAND_CLEANUP 1 + +/* If we have reallocarray(3) */ +#define HAVE_REALLOCARRAY 1 + +/* Define to 1 if you have the `recvmsg' function. */ +#define HAVE_RECVMSG 1 + +/* Define to 1 if you have the `sendmsg' function. */ +#define HAVE_SENDMSG 1 + +/* Define to 1 if you have the `setregid' function. */ +/* #undef HAVE_SETREGID */ + +/* Define to 1 if you have the `setresgid' function. */ +#define HAVE_SETRESGID 1 + +/* Define to 1 if you have the `setresuid' function. */ +#define HAVE_SETRESUID 1 + +/* Define to 1 if you have the `setreuid' function. */ +/* #undef HAVE_SETREUID */ + +/* Define to 1 if you have the `setrlimit' function. */ +#define HAVE_SETRLIMIT 1 + +/* Define to 1 if you have the `setsid' function. */ +#define HAVE_SETSID 1 + +/* Define to 1 if you have the `setusercontext' function. */ +#define HAVE_SETUSERCONTEXT 1 + +/* Define to 1 if you have the `SHA512_Update' function. */ +/* #undef HAVE_SHA512_UPDATE */ + +/* Define to 1 if you have the `shmget' function. */ +#define HAVE_SHMGET 1 + +/* Define to 1 if you have the `sigprocmask' function. */ +#define HAVE_SIGPROCMASK 1 + +/* Define to 1 if you have the `sleep' function. */ +#define HAVE_SLEEP 1 + +/* Define to 1 if you have the `snprintf' function. */ +#define HAVE_SNPRINTF 1 + +/* Define to 1 if you have the `socketpair' function. */ +#define HAVE_SOCKETPAIR 1 + +/* Using Solaris threads */ +/* #undef HAVE_SOLARIS_THREADS */ + +/* Define to 1 if you have the `srandom' function. */ +#define HAVE_SRANDOM 1 + +/* Define if you have the SSL libraries installed. */ +#define HAVE_SSL /**/ + +/* 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_cb' function. */ +/* #undef HAVE_SSL_CTX_SET_TLSEXT_TICKET_KEY_CB */ + +/* Define to 1 if you have the `SSL_get0_peername' function. */ +/* #undef HAVE_SSL_GET0_PEERNAME */ + +/* Define to 1 if you have the `SSL_set1_host' function. */ +/* #undef HAVE_SSL_SET1_HOST */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STDARG_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDBOOL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strftime' function. */ +#define HAVE_STRFTIME 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strlcat' function. */ +#define HAVE_STRLCAT 1 + +/* Define to 1 if you have the `strlcpy' function. */ +#define HAVE_STRLCPY 1 + +/* Define to 1 if you have the `strptime' function. */ +#define HAVE_STRPTIME 1 + +/* Define to 1 if you have the `strsep' function. */ +#define HAVE_STRSEP 1 + +/* 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'. */ +#define HAVE_STRUCT_SOCKADDR_UN_SUN_LEN 1 + +/* Define if you have Swig libraries and header files. */ +/* #undef HAVE_SWIG */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYSLOG_H 1 + +/* Define to 1 if systemd should be used */ +/* #undef HAVE_SYSTEMD */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_ENDIAN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_IPC_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_RESOURCE_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SHA2_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SHM_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SYSCTL_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_UIO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_UN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_WAIT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_TIME_H 1 + +/* Define to 1 if you have the `tzset' function. */ +#define HAVE_TZSET 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `usleep' function. */ +#define HAVE_USLEEP 1 + +/* Define to 1 if you have the `vfork' function. */ +#define HAVE_VFORK 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_VFORK_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_WINDOWS_H */ + +/* Using Windows threads */ +/* #undef HAVE_WINDOWS_THREADS */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_WINSOCK2_H */ + +/* Define to 1 if `fork' works. */ +#define HAVE_WORKING_FORK 1 + +/* Define to 1 if `vfork' works. */ +#define HAVE_WORKING_VFORK 1 + +/* Define to 1 if you have the `writev' function. */ +#define HAVE_WRITEV 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_WS2TCPIP_H */ + +/* Define to 1 if you have the `X509_VERIFY_PARAM_set1_host' function. */ +#define HAVE_X509_VERIFY_PARAM_SET1_HOST 1 + +/* 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 */ +#define LEX_HAS_YYLEX_DESTROY 1 + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#define LT_OBJDIR ".libs/" + +/* Define to the maximum message length to pass to syslog. */ +#define MAXSYSLOGMSGLEN 10240 + +/* 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. */ +#define PACKAGE_BUGREPORT "unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "unbound" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "unbound 1.10.1" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "unbound" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.10.1" + +/* default pidfile location */ +#define PIDFILE "/var/unbound/unbound.pid" + +/* Define to necessary symbol if this constant uses a non-standard name on + your system. */ +/* #undef PTHREAD_CREATE_JOINABLE */ + +/* Define as the return type of signal handlers (`int' or `void'). */ +#define RETSIGTYPE void + +/* if REUSEPORT is enabled by default */ +#define REUSEPORT_DEFAULT 0 + +/* default rootkey location */ +#define ROOT_ANCHOR_FILE "/var/unbound/root.key" + +/* default rootcert location */ +#define ROOT_CERT_FILE "/var/unbound/icannbundle.pem" + +/* version number for resource files */ +#define RSRC_PACKAGE_VERSION 1,9,6,0 + +/* Directory to chdir to */ +#define RUN_DIR "/var/unbound" + +/* Shared data */ +#define SHARE_DIR "/var/unbound" + +/* The size of `size_t'. */ +#ifdef __LP64__ +#define SIZEOF_SIZE_T 8 +#else +#define SIZEOF_SIZE_T 4 +#endif + +/* The size of `time_t', as computed by sizeof. */ +#ifdef __i386__ +#define SIZEOF_TIME_T 4 +#else +#define SIZEOF_TIME_T 8 +#endif + +/* 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. */ +#define STDC_HEADERS 1 + +/* use default strptime. */ +#define STRPTIME_WORKS 1 + +/* Use win32 resources and API */ +/* #undef UB_ON_WINDOWS */ + +/* the SYSLOG_FACILITY to use, default LOG_DAEMON */ +#define UB_SYSLOG_FACILITY LOG_DAEMON + +/* default username */ +#define UB_USERNAME "unbound" + +/* 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. */ +#define USE_DSA 1 + +/* Define this to enable ECDSA support. */ +#define USE_ECDSA 1 + +/* Define this to enable an EVP workaround for older openssl */ +/* #undef USE_ECDSA_EVP_WORKAROUND */ + +/* Define this to enable ED25519 support. */ +#define USE_ED25519 1 + +/* Define this to enable ED448 support. */ +#define USE_ED448 1 + +/* 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 want to use internal select based events */ +#define USE_MINI_EVENT 1 + +/* 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. */ +#define USE_SHA1 1 + +/* Define this to enable SHA256 and SHA512 support. */ +#define USE_SHA2 1 + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# define _ALL_SOURCE 1 +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# define _POSIX_PTHREAD_SEMANTICS 1 +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# define _TANDEM_SOURCE 1 +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +#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 */ +#define WINVER 0x0502 + +/* 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[]'. */ +#define YYTEXT_POINTER 1 + +/* 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 */ + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `int' if 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 does not define. */ +/* #undef int16_t */ + +/* Define to `int' if does not define. */ +/* #undef int32_t */ + +/* Define to `long long' if does not define. */ +/* #undef int64_t */ + +/* Define to `signed char' if does not define. */ +/* #undef int8_t */ + +/* Define if replacement function should be used. */ +/* #undef malloc */ + +/* Define to `long int' if does not define. */ +/* #undef off_t */ + +/* Define to `int' if does not define. */ +/* #undef pid_t */ + +/* Define to 'int' if not defined */ +/* #undef rlim_t */ + +/* Define to `unsigned int' if does not define. */ +/* #undef size_t */ + +/* Define to 'int' if not defined */ +/* #undef socklen_t */ + +/* Define to `int' if does not define. */ +/* #undef ssize_t */ + +/* Define to 'unsigned char if not defined */ +/* #undef u_char */ + +/* Define to `int' if doesn't define. */ +/* #undef uid_t */ + +/* Define to `unsigned short' if does not define. */ +/* #undef uint16_t */ + +/* Define to `unsigned int' if does not define. */ +/* #undef uint32_t */ + +/* Define to `unsigned long long' if does not define. */ +/* #undef uint64_t */ + +/* Define to `unsigned char' if 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 +#include +#include +#include + +#if STDC_HEADERS +#include +#include +#endif + +#ifdef HAVE_STDARG_H +#include +#endif + +#ifdef HAVE_STDINT_H +#include +#endif + +#include + +#if HAVE_SYS_PARAM_H +#include +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +#ifdef HAVE_SYS_UIO_H +#include +#endif + +#ifdef HAVE_NETINET_IN_H +#include +#endif + +#ifdef HAVE_NETINET_TCP_H +#include +#endif + +#ifdef HAVE_ARPA_INET_H +#include +#endif + +#ifdef HAVE_WINSOCK2_H +#include +#endif + +#ifdef HAVE_WS2TCPIP_H +#include +#endif + +#ifndef USE_WINSOCK +#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 +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_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 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 + + --- usr.sbin/unbound/control/Makefile.orig +++ usr.sbin/unbound/control/Makefile @@ -6,10 +6,11 @@ .PATH: ${UNBOUNDDIR} ${UNBOUNDDIR}/smallapp ${UNBOUNDDIR}/util ${UNBOUNDDIR}/doc -PROG= unbound-control +PROG= local-unbound-control SRCS= ub_event.c unbound-control.c worker_cb.c CFLAGS+= -I${UNBOUNDDIR} -I${LDNSDIR} +CFLAGS+= -I${.CURDIR:H} -I${.CURDIR} LIBADD= unbound crypto ssl pthread -MAN= unbound-control.8 +MAN= local-unbound-control.8 .include --- usr.sbin/unbound/daemon/Makefile.orig +++ usr.sbin/unbound/daemon/Makefile @@ -4,13 +4,14 @@ LDNSDIR= ${SRCTOP}/contrib/ldns UNBOUNDDIR= ${SRCTOP}/contrib/unbound -.PATH: ${UNBOUNDDIR} ${UNBOUNDDIR}/daemon ${UNBOUNDDIR}/util ${UNBOUNDDIR}/doc +.PATH: ${UNBOUNDDIR} ${UNBOUNDDIR}/daemon ${UNBOUNDDIR}/util ${UNBOUNDDIR}/util/shm_side ${UNBOUNDDIR}/doc -PROG= unbound -SRCS= acl_list.c cachedump.c daemon.c remote.c stats.c ub_event.c \ - unbound.c worker.c +PROG= local-unbound +SRCS= acl_list.c cachedump.c daemon.c remote.c shm_main.c stats.c \ + ub_event.c unbound.c worker.c CFLAGS+= -I${UNBOUNDDIR} -I${LDNSDIR} +CFLAGS+= -I${.CURDIR:H} -I${.CURDIR} LIBADD= unbound util ssl crypto pthread -MAN= unbound.8 unbound.conf.5 +MAN= local-unbound.8 local-unbound.conf.5 .include --- usr.sbin/unbound/local-setup/Makefile.orig +++ usr.sbin/unbound/local-setup/Makefile @@ -1,6 +0,0 @@ -# $FreeBSD$ - -SCRIPTS= local-unbound-setup.sh -MAN= # - -.include --- usr.sbin/unbound/local-setup/Makefile.depend.orig +++ usr.sbin/unbound/local-setup/Makefile.depend @@ -1,11 +0,0 @@ -# $FreeBSD$ -# Autogenerated - do NOT edit! - -DIRDEPS = \ - - -.include - -.if ${DEP_RELDIR} == ${_DEP_RELDIR} -# local dependencies - needed for -jN in clean tree -.endif --- usr.sbin/unbound/setup/Makefile.orig +++ usr.sbin/unbound/setup/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +SCRIPTS= local-unbound-setup.sh +MAN= # + +.include --- usr.sbin/unbound/setup/Makefile.depend.orig +++ usr.sbin/unbound/setup/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif --- usr.sbin/unbound/setup/local-unbound-setup.sh.orig +++ usr.sbin/unbound/setup/local-unbound-setup.sh @@ -0,0 +1,460 @@ +#!/bin/sh +#- +# SPDX-License-Identifier: BSD-2-Clause-FreeBSD +# +# Copyright (c) 2013 Dag-Erling Smørgrav +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. 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. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. +# +# $FreeBSD$ +# + +# +# Configuration variables +# +user="" +unbound_conf="" +forward_conf="" +lanzones_conf="" +control_conf="" +control_socket="" +workdir="" +confdir="" +chrootdir="" +anchor="" +pidfile="" +resolv_conf="" +resolvconf_conf="" +service="" +start_unbound="" +forwarders="" + +# +# Global variables +# +self=$(basename $(realpath "$0")) +bkext=$(date "+%Y%m%d.%H%M%S") + +# +# Set default values for unset configuration variables. +# +set_defaults() { + : ${user:=unbound} + : ${workdir:=/var/unbound} + : ${confdir:=${workdir}/conf.d} + : ${unbound_conf:=${workdir}/unbound.conf} + : ${forward_conf:=${workdir}/forward.conf} + : ${lanzones_conf:=${workdir}/lan-zones.conf} + : ${control_conf:=${workdir}/control.conf} + : ${control_socket:=/var/run/local_unbound.ctl} + : ${anchor:=${workdir}/root.key} + : ${pidfile:=/var/run/local_unbound.pid} + : ${resolv_conf:=/etc/resolv.conf} + : ${resolvconf_conf:=/etc/resolvconf.conf} + : ${service:=local_unbound} + : ${start_unbound:=yes} +} + +# +# Verify that the configuration files are inside the working +# directory, and if so, set the chroot directory accordingly. +# +set_chrootdir() { + chrootdir="${workdir}" + for file in "${unbound_conf}" "${forward_conf}" \ + "${lanzones_conf}" "${control_conf}" "${anchor}" ; do + if [ "${file#${workdir%/}/}" = "${file}" ] ; then + echo "warning: ${file} is outside ${workdir}" >&2 + chrootdir="" + fi + done + if [ -z "${chrootdir}" ] ; then + echo "warning: disabling chroot" >&2 + fi +} + +# +# Scan through /etc/resolv.conf looking for uncommented nameserver +# lines that don't point to localhost and return their values. +# +get_nameservers() { + while read line ; do + local bareline=${line%%\#*} + local key=${bareline%% *} + local value=${bareline#* } + case ${key} in + nameserver) + case ${value} in + 127.0.0.1|::1|localhost|localhost.*) + ;; + *) + echo "${value}" + ;; + esac + ;; + esac + done +} + +# +# Scan through /etc/resolv.conf looking for uncommented nameserver +# lines. Comment out any that don't point to localhost. Finally, +# append a nameserver line that points to localhost, if there wasn't +# one already, and enable the edns0 option. +# +gen_resolv_conf() { + local localhost=no + local edns0=no + while read line ; do + local bareline=${line%%\#*} + local key=${bareline%% *} + local value=${bareline#* } + case ${key} in + nameserver) + case ${value} in + 127.0.0.1|::1|localhost|localhost.*) + localhost=yes + ;; + *) + echo -n "# " + ;; + esac + ;; + options) + case ${value} in + *edns0*) + edns0=yes + ;; + esac + ;; + esac + echo "${line}" + done + if [ "${localhost}" = "no" ] ; then + echo "nameserver 127.0.0.1" + fi + if [ "${edns0}" = "no" ] ; then + echo "options edns0" + fi +} + +# +# Boilerplate +# +do_not_edit() { + echo "# This file was generated by $self." + echo "# Modifications will be overwritten." +} + +# +# Generate resolvconf.conf so it updates forward.conf in addition to +# resolv.conf. Note "in addition to" rather than "instead of", +# because we still want it to update the domain name and search path +# if they change. Setting name_servers to "127.0.0.1" ensures that +# the libc resolver will try unbound first. +# +gen_resolvconf_conf() { + local style="$1" + do_not_edit + echo "resolv_conf=\"/dev/null\" # prevent updating ${resolv_conf}" + if [ "${style}" = "dynamic" ] ; then + echo "unbound_conf=\"${forward_conf}\"" + echo "unbound_pid=\"${pidfile}\"" + echo "unbound_service=\"${service}\"" + # resolvconf(8) likes to restart rather than reload + echo "unbound_restart=\"service ${service} reload\"" + else + echo "# Static DNS configuration" + fi +} + +# +# Generate forward.conf +# +gen_forward_conf() { + do_not_edit + echo "forward-zone:" + echo " name: ." + for forwarder ; do + if expr "${forwarder}" : "^[0-9A-Fa-f:.]\{1,\}$" >/dev/null ; then + echo " forward-addr: ${forwarder}" + else + echo " forward-host: ${forwarder}" + fi + done +} + +# +# Generate lan-zones.conf +# +gen_lanzones_conf() { + do_not_edit + echo "server:" + echo " # Unblock reverse lookups for LAN addresses" + echo " unblock-lan-zones: yes" + echo " insecure-lan-zones: yes" +} + +# +# Generate control.conf +# +gen_control_conf() { + do_not_edit + echo "remote-control:" + echo " control-enable: yes" + echo " control-interface: ${control_socket}" + echo " control-use-cert: no" +} + +# +# Generate unbound.conf +# +gen_unbound_conf() { + do_not_edit + echo "server:" + echo " username: ${user}" + echo " directory: ${workdir}" + echo " chroot: ${chrootdir}" + echo " pidfile: ${pidfile}" + echo " auto-trust-anchor-file: ${anchor}" + echo "" + if [ -f "${forward_conf}" ] ; then + echo "include: ${forward_conf}" + fi + if [ -f "${lanzones_conf}" ] ; then + echo "include: ${lanzones_conf}" + fi + if [ -f "${control_conf}" ] ; then + echo "include: ${control_conf}" + fi + if [ -d "${confdir}" ] ; then + echo "include: ${confdir}/*.conf" + fi +} + +# +# Rename a file we are about to replace. +# +backup() { + local file="$1" + if [ -f "${file}" ] ; then + local bkfile="${file}.${bkext}" + echo "Original ${file} saved as ${bkfile}" + mv "${file}" "${bkfile}" + fi +} + +# +# Replace one file with another, making a backup copy of the first, +# but only if the new file is different from the old. +# +replace() { + local file="$1" + local newfile="$2" + if [ ! -f "${file}" ] ; then + echo "${file} created" + mv "${newfile}" "${file}" + elif ! cmp -s "${file}" "${newfile}" ; then + backup "${file}" + mv "${newfile}" "${file}" + else + echo "${file} not modified" + rm "${newfile}" + fi +} + +# +# Print usage message and exit +# +usage() { + exec >&2 + echo "usage: $self [options] [forwarder ...]" + echo "options:" + echo " -n do not start unbound" + echo " -a path full path to trust anchor file" + echo " -C path full path to additional configuration directory" + echo " -c path full path to unbound configuration file" + echo " -f path full path to forwarding configuration" + echo " -O path full path to remote control socket" + echo " -o path full path to remote control configuration" + echo " -p path full path to pid file" + echo " -R path full path to resolvconf.conf" + echo " -r path full path to resolv.conf" + echo " -s service name of unbound service" + echo " -u user user to run unbound as" + echo " -w path full path to working directory" + exit 1 +} + +# +# Main +# +main() { + umask 022 + + # + # Parse and validate command-line options + # + while getopts "a:C:c:f:no:p:R:r:s:u:w:" option ; do + case $option in + a) + anchor="$OPTARG" + ;; + C) + confdir="$OPTARG" + ;; + c) + unbound_conf="$OPTARG" + ;; + f) + forward_conf="$OPTARG" + ;; + n) + start_unbound="no" + ;; + O) + control_socket="$OPTARG" + ;; + o) + control_conf="$OPTARG" + ;; + p) + pidfile="$OPTARG" + ;; + R) + resolvconf_conf="$OPTARG" + ;; + r) + resolv_conf="$OPTARG" + ;; + s) + service="$OPTARG" + ;; + u) + user="$OPTARG" + ;; + w) + workdir="$OPTARG" + ;; + *) + usage + ;; + esac + done + shift $((OPTIND-1)) + set_defaults + + # + # Get the list of forwarders, either from the command line or + # from resolv.conf. + # + forwarders="$@" + case "${forwarders}" in + [Nn][Oo][Nn][Ee]) + forwarders="none" + style=recursing + ;; + "") + echo "Extracting forwarders from ${resolv_conf}." + forwarders=$(get_nameservers <"${resolv_conf}") + style=dynamic + ;; + *) + style=static + ;; + esac + + # + # Generate forward.conf. + # + if [ -z "${forwarders}" ] ; then + echo -n "No forwarders found in ${resolv_conf##*/}, " + if [ -f "${forward_conf}" ] ; then + echo "using existing ${forward_conf##*/}." + else + echo "unbound will recurse." + fi + elif [ "${forwarders}" = "none" ] ; then + echo "Forwarding disabled, unbound will recurse." + backup "${forward_conf}" + else + local tmp_forward_conf=$(mktemp -u "${forward_conf}.XXXXX") + gen_forward_conf ${forwarders} | unexpand >"${tmp_forward_conf}" + replace "${forward_conf}" "${tmp_forward_conf}" + fi + + # + # Generate lan-zones.conf. + # + local tmp_lanzones_conf=$(mktemp -u "${lanzones_conf}.XXXXX") + gen_lanzones_conf | unexpand >"${tmp_lanzones_conf}" + replace "${lanzones_conf}" "${tmp_lanzones_conf}" + + # + # Generate control.conf. + # + local tmp_control_conf=$(mktemp -u "${control_conf}.XXXXX") + gen_control_conf | unexpand >"${tmp_control_conf}" + replace "${control_conf}" "${tmp_control_conf}" + + # + # Generate unbound.conf. + # + local tmp_unbound_conf=$(mktemp -u "${unbound_conf}.XXXXX") + set_chrootdir + gen_unbound_conf | unexpand >"${tmp_unbound_conf}" + replace "${unbound_conf}" "${tmp_unbound_conf}" + + # + # Start unbound, unless requested not to. Stop immediately if + # it is not enabled so we don't end up with a resolv.conf that + # points into nothingness. We could "onestart" it, but it + # wouldn't stick. + # + if [ "${start_unbound}" = "no" ] ; then + # skip + elif ! service "${service}" enabled ; then + echo "Please enable $service in rc.conf(5) and try again." + return 1 + elif ! service "${service}" restart ; then + echo "Failed to start $service." + return 1 + fi + + # + # Rewrite resolvconf.conf so resolvconf updates forward.conf + # instead of resolv.conf. + # + local tmp_resolvconf_conf=$(mktemp -u "${resolvconf_conf}.XXXXX") + gen_resolvconf_conf "${style}" | unexpand >"${tmp_resolvconf_conf}" + replace "${resolvconf_conf}" "${tmp_resolvconf_conf}" + + # + # Finally, rewrite resolv.conf. + # + local tmp_resolv_conf=$(mktemp -u "${resolv_conf}.XXXXX") + gen_resolv_conf <"${resolv_conf}" | unexpand >"${tmp_resolv_conf}" + replace "${resolv_conf}" "${tmp_resolv_conf}" +} + +main "$@"