diff --git a/.gitmodules b/.gitmodules index bc0b060e..6f120301 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,6 +2,9 @@ path = src/jsmn url = https://github.com/getdnsapi/jsmn.git branch = getdns +[submodule "src/yxml"] + path = src/yxml + url = git://g.blicky.net/yxml.git [submodule "stubby"] path = stubby url = https://github.com/getdnsapi/stubby.git diff --git a/.travis.yml b/.travis.yml index 6b3fc7b4..60a37347 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,6 +12,7 @@ addons: - check - libevent-dev - libev-dev + - libuv-dev - valgrind - clang - wget diff --git a/Makefile.in b/Makefile.in index 2a18e98d..61484a54 100644 --- a/Makefile.in +++ b/Makefile.in @@ -198,6 +198,7 @@ $(distdir): mkdir -p $(distdir)/src/gldns mkdir -p $(distdir)/src/tools mkdir -p $(distdir)/src/jsmn + mkdir -p $(distdir)/src/yxml mkdir -p $(distdir)/doc mkdir -p $(distdir)/spec mkdir -p $(distdir)/spec/example @@ -253,6 +254,9 @@ $(distdir): cp $(srcdir)/src/jsmn/*.[ch] $(distdir)/src/jsmn cp $(srcdir)/src/jsmn/LICENSE $(distdir)/src/jsmn cp $(srcdir)/src/jsmn/README.md $(distdir)/src/jsmn + cp $(srcdir)/src/yxml/*.[ch] $(distdir)/src/yxml + cp $(srcdir)/src/yxml/COPYING $(distdir)/src/yxml + cp $(srcdir)/src/yxml/yxml.pod $(distdir)/src/yxml rm -f $(distdir)/Makefile $(distdir)/src/Makefile $(distdir)/src/getdns/getdns.h $(distdir)/spec/example/Makefile $(distdir)/src/test/Makefile $(distdir)/doc/Makefile $(distdir)/src/config.h distcheck: $(distdir).tar.gz diff --git a/configure.ac b/configure.ac index 55648094..9ce934ff 100644 --- a/configure.ac +++ b/configure.ac @@ -163,7 +163,8 @@ AC_ARG_ENABLE(debug-stub, AC_HELP_STRING([--enable-debug-stub], [Enable stub deb AC_ARG_ENABLE(debug-daemon, AC_HELP_STRING([--enable-debug-daemon], [Enable daemon debugging messages])) AC_ARG_ENABLE(debug-sec, AC_HELP_STRING([--enable-debug-sec], [Enable dnssec debugging messages])) AC_ARG_ENABLE(debug-server, AC_HELP_STRING([--enable-debug-server], [Enable server debugging messages])) -AC_ARG_ENABLE(all-debugging, AC_HELP_STRING([--enable-all-debugging], [Enable all debugging messages])) +AC_ARG_ENABLE(debug-anchor, AC_HELP_STRING([--enable-debug-anchor], [Enable anchor debugging messages])) +AC_ARG_ENABLE(all-debugging, AC_HELP_STRING([--enable-all-debugging], [Enable scheduling, stub and dnssec debugging])) case "$enable_all_debugging" in yes) enable_debug_req=yes @@ -172,6 +173,7 @@ case "$enable_all_debugging" in enable_debug_daemon=yes enable_debug_sec=yes enable_debug_server=yes + enable_debug_anchor=yes ;; no|*) ;; @@ -218,6 +220,13 @@ case "$enable_debug_server" in no|*) ;; esac +case "$enable_debug_anchor" in + yes) + AC_DEFINE_UNQUOTED([ANCHOR_DEBUG], [1], [Define this enable printing of anchor debugging messages.]) + ;; + no|*) + ;; +esac dnl Hidden debugging options @@ -281,6 +290,33 @@ case "$enable_native_stub_dnssec" in ;; esac +# check wether strptime also works +AC_DEFUN([AC_CHECK_STRPTIME_WORKS], +[AC_REQUIRE([AC_PROG_CC]) +AC_MSG_CHECKING(whether strptime works) +if test c${cross_compiling} = cno; then +AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#define _XOPEN_SOURCE 600 +#include +int main(void) { struct tm tm; char *res; +res = strptime("2010-07-15T00:00:00+00:00", "%t%Y%t-%t%m%t-%t%d%tT%t%H%t:%t%M%t:%t%S%t", &tm); +if (!res) return 2; +res = strptime("20070207111842", "%Y%m%d%H%M%S", &tm); +if (!res) return 1; return 0; } +]])] , [eval "ac_cv_c_strptime_works=yes"], [eval "ac_cv_c_strptime_works=no"]) +else +eval "ac_cv_c_strptime_works=maybe" +fi +AC_MSG_RESULT($ac_cv_c_strptime_works) +if test $ac_cv_c_strptime_works = no; then +AC_LIBOBJ(strptime) +else +AC_DEFINE_UNQUOTED([STRPTIME_WORKS], 1, [use default strptime.]) +fi +])dnl + +AC_CHECK_FUNCS([strptime],[AC_CHECK_STRPTIME_WORKS],[AC_LIBOBJ([strptime])]) + # search to set include and library paths right # find libidn (no libidn on windows though) AC_CHECK_HEADERS([windows.h winsock.h stdio.h winsock2.h ws2tcpip.h],,, [AC_INCLUDES_DEFAULT]) @@ -1438,6 +1474,12 @@ static inline int _gldns_custom_vsnprintf(char *str, size_t size, const char *fo # endif #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_LIBUNBOUND # include # ifdef HAVE_UNBOUND_EVENT_H diff --git a/src/Makefile.in b/src/Makefile.in index 9f496d40..a9cc4695 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -91,12 +91,13 @@ COMPAT_OBJ=$(LIBOBJS:.o=.lo) UTIL_OBJ=rbtree.lo val_secalgo.lo lruhash.lo lookup3.lo locks.lo JSMN_OBJ=jsmn.lo +YXML_OBJ=yxml.lo YAML_OBJ=convert_yaml_to_json.lo EXTENSION_OBJ=$(DEFAULT_EVENTLOOP_OBJ) libevent.lo libev.lo -NON_C99_OBJS=context.lo libuv.lo +NON_C99_OBJS=libuv.lo context.lo anchor.lo .SUFFIXES: .c .o .a .lo .h @@ -128,9 +129,15 @@ $(JSMN_OBJ): $(YAML_OBJ): $(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) -c $(srcdir)/yaml/$(@:.lo=.c) -o $@ +$(YXML_OBJ): + $(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) -I$(srcdir)/yxml -DYXML_GETDNS -Wno-unused-parameter -c $(srcdir)/yxml/$(@:.lo=.c) -o $@ + $(EXTENSION_OBJ): $(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) $(WPEDANTICFLAG) -c $(srcdir)/extension/$(@:.lo=.c) -o $@ +anchor.lo: + $(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) $(WPEDANTICFLAG) $(C99COMPATFLAGS) -c $(srcdir)/anchor.c -o anchor.lo + context.lo: $(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) $(WPEDANTICFLAG) $(C99COMPATFLAGS) -c $(srcdir)/context.c -o context.lo @@ -177,9 +184,8 @@ libgetdns_ext_uv.la: libgetdns.la libuv.lo libgetdns_ext_ev.la: libgetdns.la libev.lo $(LIBTOOL) --tag=CC --mode=link $(CC) -o $@ libev.lo libgetdns.la $(LDFLAGS) $(EXTENSION_LIBEV_LDFLAGS) $(EXTENSION_LIBEV_EXT_LIBS) -rpath $(libdir) -version-info $(libversion) -no-undefined -export-symbols $(srcdir)/extension/libev.symbols - -libgetdns.la: $(GETDNS_OBJ) version.lo context.lo $(DEFAULT_EVENTLOOP_OBJ) $(GLDNS_OBJ) $(COMPAT_OBJ) $(UTIL_OBJ) $(JSMN_OBJ) $(YAML_OBJ) - $(LIBTOOL) --tag=CC --mode=link $(CC) -o $@ $(GETDNS_OBJ) version.lo context.lo $(DEFAULT_EVENTLOOP_OBJ) $(GLDNS_OBJ) $(COMPAT_OBJ) $(UTIL_OBJ) $(JSMN_OBJ) $(YAML_OBJ) $(LDFLAGS) -rpath $(libdir) -version-info $(libversion) -no-undefined -export-symbols $(srcdir)/libgetdns.symbols +libgetdns.la: $(GETDNS_OBJ) version.lo context.lo anchor.lo $(DEFAULT_EVENTLOOP_OBJ) $(GLDNS_OBJ) $(COMPAT_OBJ) $(UTIL_OBJ) $(JSMN_OBJ) $(YXML_OBJ) $(YAML_OBJ) + $(LIBTOOL) --tag=CC --mode=link $(CC) -o $@ $(GETDNS_OBJ) version.lo context.lo anchor.lo $(DEFAULT_EVENTLOOP_OBJ) $(GLDNS_OBJ) $(COMPAT_OBJ) $(UTIL_OBJ) $(JSMN_OBJ) $(YXML_OBJ) $(YAML_OBJ) $(LDFLAGS) -rpath $(libdir) -version-info $(libversion) -no-undefined -export-symbols $(srcdir)/libgetdns.symbols test: default cd test && $(MAKE) $@ @@ -231,7 +237,8 @@ Makefile: $(srcdir)/Makefile.in ../config.status depend: (cd $(srcdir) ; awk 'BEGIN{P=1}{if(P)print}/^# Dependencies/{P=0}' Makefile.in > Makefile.in.new ) - (blddir=`pwd`; cd $(srcdir) ; gcc -MM -I. -I"$$blddir" -Iutil/auxiliary *.c gldns/*.c compat/*.c util/*.c jsmn/*.c yaml/*.c extension/*.c| \ + + (blddir=`pwd`; cd $(srcdir) ; gcc -MM -I. -I"$$blddir" -Iyxml -Iutil/auxiliary *.c gldns/*.c compat/*.c util/*.c jsmn/*.c yxml/*.c yaml/*.c extension/*.c| \ sed -e "s? $$blddir/? ?g" \ -e 's? gldns/? $$(srcdir)/gldns/?g' \ -e 's? compat/? $$(srcdir)/compat/?g' \ @@ -239,6 +246,7 @@ depend: -e 's? util/? $$(srcdir)/util/?g' \ -e 's? jsmn/? $$(srcdir)/jsmn/?g' \ -e 's? yaml/? $$(srcdir)/yaml/?g' \ + -e 's? yxml/? $$(srcdir)/yxml/?g' \ -e 's? extension/? $$(srcdir)/extension/?g' \ -e 's? \([a-z_-]*\)\.\([ch]\)? $$(srcdir)/\1.\2?g' \ -e 's? \$$(srcdir)/config\.h? config.h?g' \ @@ -257,21 +265,34 @@ depend: FORCE: # Dependencies for gldns, utils, the extensions and compat functions +anchor.lo anchor.o: $(srcdir)/anchor.c \ + config.h \ + $(srcdir)/debug.h $(srcdir)/anchor.h \ + getdns/getdns.h \ + getdns/getdns_extra.h \ + $(srcdir)/types-internal.h $(srcdir)/util/rbtree.h $(srcdir)/util/orig-headers/rbtree.h $(srcdir)/context.h \ + $(srcdir)/extension/default_eventloop.h $(srcdir)/extension/poll_eventloop.h \ + $(srcdir)/types-internal.h $(srcdir)/ub_loop.h $(srcdir)/server.h $(srcdir)/util/lruhash.h \ + $(srcdir)/util/orig-headers/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/orig-headers/locks.h \ + $(srcdir)/util/auxiliary/util/log.h $(srcdir)/debug.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h \ + $(srcdir)/gldns/pkthdr.h $(srcdir)/dnssec.h $(srcdir)/gldns/rrdef.h $(srcdir)/yxml/yxml.h $(srcdir)/gldns/parseutil.h \ + $(srcdir)/gldns/str2wire.h $(srcdir)/gldns/rrdef.h $(srcdir)/general.h $(srcdir)/util-internal.h const-info.lo const-info.o: $(srcdir)/const-info.c \ getdns/getdns.h \ getdns/getdns_extra.h \ $(srcdir)/const-info.h context.lo context.o: $(srcdir)/context.c \ config.h \ - $(srcdir)/debug.h $(srcdir)/gldns/str2wire.h $(srcdir)/gldns/rrdef.h $(srcdir)/gldns/wire2str.h $(srcdir)/context.h \ + $(srcdir)/anchor.h \ getdns/getdns.h \ getdns/getdns_extra.h \ + $(srcdir)/debug.h $(srcdir)/gldns/str2wire.h $(srcdir)/gldns/rrdef.h $(srcdir)/gldns/wire2str.h $(srcdir)/context.h \ $(srcdir)/types-internal.h $(srcdir)/util/rbtree.h $(srcdir)/util/orig-headers/rbtree.h \ $(srcdir)/extension/default_eventloop.h $(srcdir)/extension/poll_eventloop.h \ $(srcdir)/types-internal.h $(srcdir)/ub_loop.h $(srcdir)/server.h $(srcdir)/util/lruhash.h \ $(srcdir)/util/orig-headers/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/orig-headers/locks.h \ - $(srcdir)/util/auxiliary/util/log.h $(srcdir)/debug.h $(srcdir)/util-internal.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h \ - $(srcdir)/gldns/gbuffer.h $(srcdir)/gldns/pkthdr.h $(srcdir)/dnssec.h $(srcdir)/gldns/rrdef.h $(srcdir)/stub.h $(srcdir)/list.h \ + $(srcdir)/util/auxiliary/util/log.h $(srcdir)/debug.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h \ + $(srcdir)/gldns/pkthdr.h $(srcdir)/util-internal.h $(srcdir)/dnssec.h $(srcdir)/gldns/rrdef.h $(srcdir)/stub.h $(srcdir)/list.h \ $(srcdir)/dict.h $(srcdir)/pubkey-pinning.h convert.lo convert.o: $(srcdir)/convert.c \ config.h \ @@ -307,11 +328,11 @@ dnssec.lo dnssec.o: $(srcdir)/dnssec.c \ $(srcdir)/extension/default_eventloop.h $(srcdir)/extension/poll_eventloop.h \ $(srcdir)/types-internal.h $(srcdir)/ub_loop.h $(srcdir)/server.h $(srcdir)/util/lruhash.h \ $(srcdir)/util/orig-headers/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/orig-headers/locks.h \ - $(srcdir)/util/auxiliary/util/log.h $(srcdir)/debug.h $(srcdir)/util-internal.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h \ - $(srcdir)/gldns/gbuffer.h $(srcdir)/gldns/pkthdr.h $(srcdir)/dnssec.h $(srcdir)/gldns/rrdef.h $(srcdir)/gldns/str2wire.h \ + $(srcdir)/util/auxiliary/util/log.h $(srcdir)/debug.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h \ + $(srcdir)/gldns/pkthdr.h $(srcdir)/util-internal.h $(srcdir)/dnssec.h $(srcdir)/gldns/rrdef.h $(srcdir)/gldns/str2wire.h \ $(srcdir)/gldns/rrdef.h $(srcdir)/gldns/wire2str.h $(srcdir)/gldns/keyraw.h $(srcdir)/gldns/parseutil.h \ $(srcdir)/general.h $(srcdir)/dict.h $(srcdir)/list.h $(srcdir)/util/val_secalgo.h \ - $(srcdir)/util/orig-headers/val_secalgo.h + $(srcdir)/util/orig-headers/val_secalgo.h $(srcdir)/anchor.h general.lo general.o: $(srcdir)/general.c \ config.h \ $(srcdir)/general.h \ @@ -322,9 +343,9 @@ general.lo general.o: $(srcdir)/general.c \ $(srcdir)/gldns/wire2str.h $(srcdir)/context.h $(srcdir)/extension/default_eventloop.h \ $(srcdir)/extension/poll_eventloop.h $(srcdir)/types-internal.h $(srcdir)/server.h $(srcdir)/util/lruhash.h \ $(srcdir)/util/orig-headers/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/orig-headers/locks.h \ - $(srcdir)/util/auxiliary/util/log.h $(srcdir)/debug.h $(srcdir)/util-internal.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h \ - $(srcdir)/gldns/gbuffer.h $(srcdir)/gldns/pkthdr.h $(srcdir)/dnssec.h $(srcdir)/gldns/rrdef.h $(srcdir)/stub.h $(srcdir)/dict.h \ - $(srcdir)/mdns.h + $(srcdir)/util/auxiliary/util/log.h $(srcdir)/debug.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h \ + $(srcdir)/gldns/pkthdr.h $(srcdir)/util-internal.h $(srcdir)/dnssec.h $(srcdir)/gldns/rrdef.h $(srcdir)/stub.h $(srcdir)/dict.h \ + $(srcdir)/mdns.h $(srcdir)/anchor.h list.lo list.o: $(srcdir)/list.c $(srcdir)/types-internal.h \ getdns/getdns.h \ getdns/getdns_extra.h \ @@ -344,8 +365,8 @@ mdns.lo mdns.o: $(srcdir)/mdns.c \ $(srcdir)/extension/default_eventloop.h $(srcdir)/extension/poll_eventloop.h \ $(srcdir)/types-internal.h $(srcdir)/ub_loop.h $(srcdir)/server.h $(srcdir)/util/lruhash.h \ $(srcdir)/util/orig-headers/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/orig-headers/locks.h \ - $(srcdir)/util/auxiliary/util/log.h $(srcdir)/debug.h $(srcdir)/general.h $(srcdir)/gldns/pkthdr.h $(srcdir)/gldns/rrdef.h \ - $(srcdir)/util-internal.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h $(srcdir)/mdns.h \ + $(srcdir)/util/auxiliary/util/log.h $(srcdir)/debug.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h \ + $(srcdir)/gldns/pkthdr.h $(srcdir)/general.h $(srcdir)/gldns/rrdef.h $(srcdir)/util-internal.h $(srcdir)/mdns.h \ $(srcdir)/util/auxiliary/util/fptr_wlist.h $(srcdir)/util/lookup3.h \ $(srcdir)/util/orig-headers/lookup3.h pubkey-pinning.lo pubkey-pinning.o: $(srcdir)/pubkey-pinning.c \ @@ -358,8 +379,8 @@ pubkey-pinning.lo pubkey-pinning.o: $(srcdir)/pubkey-pinning.c \ $(srcdir)/extension/default_eventloop.h $(srcdir)/extension/poll_eventloop.h \ $(srcdir)/types-internal.h $(srcdir)/ub_loop.h $(srcdir)/server.h $(srcdir)/util/lruhash.h \ $(srcdir)/util/orig-headers/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/orig-headers/locks.h \ - $(srcdir)/util/auxiliary/util/log.h $(srcdir)/debug.h $(srcdir)/util-internal.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h \ - $(srcdir)/gldns/gbuffer.h $(srcdir)/gldns/pkthdr.h + $(srcdir)/util/auxiliary/util/log.h $(srcdir)/debug.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h \ + $(srcdir)/gldns/pkthdr.h $(srcdir)/util-internal.h request-internal.lo request-internal.o: $(srcdir)/request-internal.c \ config.h \ $(srcdir)/types-internal.h \ @@ -394,7 +415,8 @@ server.lo server.o: $(srcdir)/server.c \ $(srcdir)/extension/default_eventloop.h $(srcdir)/extension/poll_eventloop.h \ $(srcdir)/types-internal.h $(srcdir)/ub_loop.h $(srcdir)/debug.h $(srcdir)/server.h $(srcdir)/util/lruhash.h \ $(srcdir)/util/orig-headers/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/orig-headers/locks.h \ - $(srcdir)/util/auxiliary/util/log.h $(srcdir)/debug.h + $(srcdir)/util/auxiliary/util/log.h $(srcdir)/debug.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h \ + $(srcdir)/gldns/pkthdr.h stub.lo stub.o: $(srcdir)/stub.c \ config.h \ $(srcdir)/debug.h $(srcdir)/stub.h \ @@ -417,8 +439,8 @@ sync.lo sync.o: $(srcdir)/sync.c \ $(srcdir)/extension/default_eventloop.h $(srcdir)/extension/poll_eventloop.h \ $(srcdir)/types-internal.h $(srcdir)/ub_loop.h $(srcdir)/debug.h $(srcdir)/server.h $(srcdir)/util/lruhash.h \ $(srcdir)/util/orig-headers/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/orig-headers/locks.h \ - $(srcdir)/util/auxiliary/util/log.h $(srcdir)/debug.h $(srcdir)/general.h $(srcdir)/util-internal.h $(srcdir)/rr-iter.h \ - $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h $(srcdir)/gldns/pkthdr.h $(srcdir)/dnssec.h $(srcdir)/gldns/rrdef.h $(srcdir)/stub.h \ + $(srcdir)/util/auxiliary/util/log.h $(srcdir)/debug.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h \ + $(srcdir)/gldns/pkthdr.h $(srcdir)/general.h $(srcdir)/util-internal.h $(srcdir)/dnssec.h $(srcdir)/gldns/rrdef.h $(srcdir)/stub.h \ $(srcdir)/gldns/wire2str.h ub_loop.lo ub_loop.o: $(srcdir)/ub_loop.c $(srcdir)/ub_loop.h \ config.h \ @@ -485,6 +507,8 @@ sha512.lo sha512.o: $(srcdir)/compat/sha512.c \ config.h strlcpy.lo strlcpy.o: $(srcdir)/compat/strlcpy.c \ config.h +strptime.lo strptime.o: $(srcdir)/compat/strptime.c \ + config.h locks.lo locks.o: $(srcdir)/util/locks.c \ config.h \ $(srcdir)/util/locks.h $(srcdir)/util/orig-headers/locks.h $(srcdir)/util/auxiliary/util/log.h $(srcdir)/debug.h @@ -511,6 +535,7 @@ val_secalgo.lo val_secalgo.o: $(srcdir)/util/val_secalgo.c \ $(srcdir)/gldns/rrdef.h $(srcdir)/util/auxiliary/sldns/keyraw.h $(srcdir)/gldns/keyraw.h \ $(srcdir)/util/auxiliary/sldns/sbuffer.h $(srcdir)/gldns/gbuffer.h jsmn.lo jsmn.o: $(srcdir)/jsmn/jsmn.c $(srcdir)/jsmn/jsmn.h +yxml.lo yxml.o: $(srcdir)/yxml/yxml.c $(srcdir)/yxml/yxml.h libev.lo libev.o: $(srcdir)/extension/libev.c \ config.h \ $(srcdir)/types-internal.h \ @@ -540,4 +565,4 @@ select_eventloop.lo select_eventloop.o: $(srcdir)/extension/select_eventloop.c \ $(srcdir)/extension/select_eventloop.h \ getdns/getdns.h \ getdns/getdns_extra.h \ - $(srcdir)/debug.h $(srcdir)/types-internal.h $(srcdir)/util/rbtree.h $(srcdir)/util/orig-headers/rbtree.h + $(srcdir)/types-internal.h $(srcdir)/util/rbtree.h $(srcdir)/util/orig-headers/rbtree.h $(srcdir)/debug.h diff --git a/src/anchor.c b/src/anchor.c new file mode 100644 index 00000000..86338d4f --- /dev/null +++ b/src/anchor.c @@ -0,0 +1,1916 @@ +/** + * + * /brief functions for DNSSEC trust anchor management + */ +/* + * Copyright (c) 2017, NLnet Labs, Inc. + * 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 names of the copyright holders 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 Verisign, Inc. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "debug.h" +#include "anchor.h" +#include +#include +#include +#include +#include +#include +#include "types-internal.h" +#include "context.h" +#include "dnssec.h" +#include "yxml/yxml.h" +#include "gldns/parseutil.h" +#include "gldns/gbuffer.h" +#include "gldns/str2wire.h" +#include "gldns/wire2str.h" +#include "gldns/pkthdr.h" +#include "gldns/keyraw.h" +#include "general.h" +#include "util-internal.h" + +/* get key usage out of its extension, returns 0 if no key_usage extension */ +static unsigned long +_getdns_get_usage_of_ex(X509* cert) +{ + unsigned long val = 0; + ASN1_BIT_STRING* s; + + if((s=X509_get_ext_d2i(cert, NID_key_usage, NULL, NULL))) { + if(s->length > 0) { + val = s->data[0]; + if(s->length > 1) + val |= s->data[1] << 8; + } + ASN1_BIT_STRING_free(s); + } + return val; +} + +/** get valid signers from the list of signers in the signature */ +static STACK_OF(X509)* +_getdns_get_valid_signers(PKCS7* p7, const char* p7signer) +{ + int i; + STACK_OF(X509)* validsigners = sk_X509_new_null(); + STACK_OF(X509)* signers = PKCS7_get0_signers(p7, NULL, 0); + unsigned long usage = 0; + if(!validsigners) { + DEBUG_ANCHOR("ERROR %s(): Failed to allocated validsigners\n" + , __FUNC__); + sk_X509_free(signers); + return NULL; + } + if(!signers) { + DEBUG_ANCHOR("ERROR %s(): Failed to allocated signers\n" + , __FUNC__); + sk_X509_free(validsigners); + return NULL; + } + for(i=0; iptr == 0 || ta->ptr >= ta->end; } + +static ta_iter *ta_iter_next(ta_iter *ta) +{ + yxml_ret_t r = YXML_OK; + yxml_t ta_x; + const char *ta_start; + int level; + char value[2048]; + char *cur, *tmp; + enum { VALIDFROM, VALIDUNTIL } attr_type; + enum { KEYTAG, ALGORITHM, DIGESTTYPE, DIGEST } elem_type; + + cur = value; + value[0] = 0; + + if (!ta->zone[0]) { + DEBUG_ANCHOR("Determine start of \n"); + /* Determine start of */ + while (!ta_iter_done(ta) && + ( yxml_parse(&ta->x, *ta->ptr) != YXML_ELEMSTART + || strcasecmp(ta->x.elem, "trustanchor"))) + ta->ptr++; + if (ta_iter_done(ta)) return NULL; + ta_start = ta->ptr; + ta_x = ta->x; + + DEBUG_ANCHOR("Find \n"); + /* Find */ + level = 0; + while (!ta_iter_done(ta) && !ta->zone[0]) { + switch ((r = yxml_parse(&ta->x, *ta->ptr))) { + case YXML_ELEMSTART: + level += 1; + if (level == 1 && + strcasecmp(ta->x.elem, "zone") == 0) { + cur = value; + *cur = 0; + } + break; + + case YXML_ELEMEND: + level -= 1; + if (level < 0) + /* End of section, + * try the next section + */ + return ta_iter_next(ta); + + else if (level == 0 && cur) { + /* content ready */ + (void) strncpy( ta->zone, value + , sizeof(ta->zone)); + + /* Reset to start of */ + cur = NULL; + ta->ptr = ta_start; + ta->x = ta_x; + } + break; + + case YXML_CONTENT: + if (!cur || level != 1) + break; + tmp = ta->x.data; + while (*tmp && cur < value + sizeof(value)) + *cur++ = *tmp++; + if (cur >= value + sizeof(value)) + cur = NULL; + else + *cur = 0; + break; + default: + break; + } + ta->ptr++; + } + if (ta_iter_done(ta)) + return NULL; + } + assert(ta->zone[0]); + + DEBUG_ANCHOR("Zone: %s, Find \n", ta->zone); + level = 0; + while (!ta_iter_done(ta)) { + r = yxml_parse(&ta->x, *ta->ptr); + + if (r == YXML_ELEMSTART) { + level += 1; + DEBUG_ANCHOR("elem start: %s, level: %d\n", ta->x.elem, level); + if (level == 1 && + strcasecmp(ta->x.elem, "keydigest") == 0) + break; + + } else if (r == YXML_ELEMEND) { + level -= 1; + if (level < 0) { + /* End of section */ + ta->zone[0] = 0; + return ta_iter_next(ta); + } + } + ta->ptr++; + } + if (ta_iter_done(ta)) + return NULL; + + DEBUG_ANCHOR("Found , Parse attributes\n"); + + ta->validFrom = ta->validUntil = 0; + *ta->keytag = *ta->algorithm = *ta->digesttype = *ta->digest = 0; + + cur = NULL; + value[0] = 0; + attr_type = -1; + + while (!ta_iter_done(ta)) { + switch ((r = yxml_parse(&ta->x, *ta->ptr))) { + case YXML_ELEMSTART: + break; + + case YXML_ELEMEND: + /* End of section, try next */ + return ta_iter_next(ta); + + case YXML_ATTRSTART: + DEBUG_ANCHOR("attrstart: %s\n", ta->x.attr); + if (strcasecmp(ta->x.attr, "validfrom") == 0) + attr_type = VALIDFROM; + + else if (strcasecmp(ta->x.attr, "validuntil") == 0) + attr_type = VALIDUNTIL; + else + break; + + cur = value; + *cur = 0; + break; + + case YXML_ATTREND: + if (!cur) + break; + cur = NULL; + DEBUG_ANCHOR("attrval: %s\n", value); + switch (attr_type) { + case VALIDFROM: + ta->validFrom = _getdns_xml_convertdate(value); + break; + case VALIDUNTIL: + ta->validUntil = _getdns_xml_convertdate(value); + break; + } + break; + + case YXML_ATTRVAL: + if (!cur) + break; + tmp = ta->x.data; + while (*tmp && cur < value + sizeof(value)) + *cur++ = *tmp++; + if (cur >= value + sizeof(value)) + cur = NULL; + else + *cur = 0; + break; + case YXML_OK: + case YXML_CONTENT: + break; + default: + DEBUG_ANCHOR("r: %d\n", (int)r); + return NULL; + break; + } + if (r == YXML_ELEMSTART) + break; + ta->ptr++; + } + if (ta_iter_done(ta)) + return NULL; + + assert(r == YXML_ELEMSTART); + DEBUG_ANCHOR("Within , Parse child elements\n"); + + cur = NULL; + value[0] = 0; + elem_type = -1; + + for (;;) { + switch (r) { + case YXML_ELEMSTART: + level += 1; + DEBUG_ANCHOR("elem start: %s, level: %d\n", ta->x.elem, level); + if (level != 2) + break; + + else if (strcasecmp(ta->x.elem, "keytag") == 0) + elem_type = KEYTAG; + + else if (strcasecmp(ta->x.elem, "algorithm") == 0) + elem_type = ALGORITHM; + + else if (strcasecmp(ta->x.elem, "digesttype") == 0) + elem_type = DIGESTTYPE; + + else if (strcasecmp(ta->x.elem, "digest") == 0) + elem_type = DIGEST; + else + break; + + cur = value; + *cur = 0; + break; + + case YXML_ELEMEND: + level -= 1; + if (level < 0) { + /* End of section */ + ta->zone[0] = 0; + return ta_iter_next(ta); + + } else if (level != 1 || !cur) + break; + + cur = NULL; + DEBUG_ANCHOR("elem end: %s\n", value); + switch (elem_type) { + case KEYTAG: + (void) strncpy( ta->keytag, value + , sizeof(ta->keytag)); + break; + case ALGORITHM: + (void) strncpy( ta->algorithm, value + , sizeof(ta->algorithm)); + break; + case DIGESTTYPE: + (void) strncpy( ta->digesttype, value + , sizeof(ta->digesttype)); + break; + case DIGEST: + (void) strncpy( ta->digest, value + , sizeof(ta->digest)); + break; + } + break; + + case YXML_CONTENT: + if (!cur) + break; + tmp = ta->x.data; + while (*tmp && cur < value + sizeof(value)) + *cur++ = *tmp++; + if (cur >= value + sizeof(value)) + cur = NULL; + else + *cur = 0; + break; + + default: + break; + } + if (level == 0) + break; + ta->ptr++; + if (ta_iter_done(ta)) + return NULL; + r = yxml_parse(&ta->x, *ta->ptr); + } + return ta->validFrom + && *ta->keytag && *ta->algorithm + && *ta->digesttype && *ta->digest ? ta : ta_iter_next(ta); +} + +static ta_iter *ta_iter_init(ta_iter *ta, const char *doc, size_t doc_len) +{ + ta->ptr = ta->start = doc; + ta->end = ta->start + doc_len; + yxml_init(&ta->x, ta->yxml_buf, sizeof(ta->yxml_buf)); + ta->zone[0] = 0; + return ta_iter_next(ta); +} + +static uint16_t _getdns_parse_xml_trust_anchors_buf( + gldns_buffer *gbuf, uint64_t *now_ms, char *xml_data, size_t xml_len) +{ + ta_iter ta_spc, *ta; + uint16_t ta_count = 0; + size_t pkt_start = gldns_buffer_position(gbuf); + + /* Empty header */ + gldns_buffer_write_u32(gbuf, 0); + gldns_buffer_write_u32(gbuf, 0); + gldns_buffer_write_u32(gbuf, 0); + + for ( ta = ta_iter_init(&ta_spc, (char *)xml_data, xml_len) + ; ta; ta = ta_iter_next(ta)) { + + if (*now_ms == 0) *now_ms = _getdns_get_now_ms(); + if ((time_t)(*now_ms / 1000) < ta->validFrom) + DEBUG_ANCHOR("Disregarding trust anchor " + "%s for %s which is not yet valid", + ta->keytag, ta->zone); + + else if (ta->validUntil != 0 + && (time_t)(*now_ms / 1000) > ta->validUntil) + DEBUG_ANCHOR("Disregarding trust anchor " + "%s for %s which is not valid anymore", + ta->keytag, ta->zone); + + else { + uint8_t zone[256]; + size_t zone_len = sizeof(zone); + uint8_t digest[sizeof(ta->digest)/2]; + size_t digest_len = sizeof(digest); + uint16_t keytag; + uint8_t algorithm; + uint8_t digesttype; + char *endptr; + + DEBUG_ANCHOR( "Installing trust anchor: " + "%s IN DS %s %s %s %s\n" + , ta->zone + , ta->keytag + , ta->algorithm + , ta->digesttype + , ta->digest + ); + if (gldns_str2wire_dname_buf(ta->zone, zone, &zone_len)) { + DEBUG_ANCHOR("Not installing trust anchor because " + "of unparsable zone: \"%s\"", ta->zone); + continue; + } + keytag = (uint16_t)strtol(ta->keytag, &endptr, 10); + if (endptr == ta->keytag || *endptr != 0) { + DEBUG_ANCHOR("Not installing trust anchor because " + "of unparsable keytag: \"%s\"", ta->keytag); + continue; + } + algorithm = (uint16_t)strtol(ta->algorithm, &endptr, 10); + if (endptr == ta->algorithm || *endptr != 0) { + DEBUG_ANCHOR("Not installing trust anchor because " + "of unparsable algorithm: \"%s\"", ta->algorithm); + continue; + } + digesttype = (uint16_t)strtol(ta->digesttype, &endptr, 10); + if (endptr == ta->digesttype || *endptr != 0) { + DEBUG_ANCHOR("Not installing trust anchor because " + "of unparsable digesttype: \"%s\"", ta->digesttype); + continue; + } + if (gldns_str2wire_hex_buf(ta->digest, digest, &digest_len)) { + DEBUG_ANCHOR("Not installing trust anchor because " + "of unparsable digest: \"%s\"", ta->digest); + continue; + } + gldns_buffer_write(gbuf, zone, zone_len); + gldns_buffer_write_u16(gbuf, GETDNS_RRTYPE_DS); + gldns_buffer_write_u16(gbuf, GETDNS_RRCLASS_IN); + gldns_buffer_write_u32(gbuf, 3600); + gldns_buffer_write_u16(gbuf, digest_len + 4); /* rdata_len */ + gldns_buffer_write_u16(gbuf, keytag); + gldns_buffer_write_u8(gbuf, algorithm); + gldns_buffer_write_u8(gbuf, digesttype); + gldns_buffer_write(gbuf, digest, digest_len); + ta_count += 1; + } + } + gldns_buffer_write_u16_at(gbuf, pkt_start+GLDNS_ANCOUNT_OFF, ta_count); + return ta_count; +} + +static uint8_t *tas_validate(struct mem_funcs *mf, + const getdns_bindata *xml_bd, const getdns_bindata *p7s_bd, + const getdns_bindata *crt_bd, const char *p7signer, + uint64_t *now_ms, uint8_t *tas, size_t *tas_len) +{ + BIO *xml = NULL, *p7s = NULL, *crt = NULL; + X509 *x = NULL; + X509_STORE *store = NULL; + uint8_t *success = NULL; + + if (!(xml = BIO_new_mem_buf(xml_bd->data, xml_bd->size))) + DEBUG_ANCHOR("ERROR %s(): Failed allocating xml BIO\n" + , __FUNC__); + + else if (!(p7s = BIO_new_mem_buf(p7s_bd->data, p7s_bd->size))) + DEBUG_ANCHOR("ERROR %s(): Failed allocating p7s BIO\n" + , __FUNC__); + + else if (!(crt = BIO_new_mem_buf(crt_bd->data, crt_bd->size))) + DEBUG_ANCHOR("ERROR %s(): Failed allocating crt BIO\n" + , __FUNC__); + + else if (!(x = PEM_read_bio_X509(crt, NULL, 0, NULL))) + DEBUG_ANCHOR("ERROR %s(): Parsing builtin certificate\n" + , __FUNC__); + + else if (!(store = X509_STORE_new())) + DEBUG_ANCHOR("ERROR %s(): Failed allocating store\n" + , __FUNC__); + + else if (!X509_STORE_add_cert(store, x)) + DEBUG_ANCHOR("ERROR %s(): Adding certificate to store\n" + , __FUNC__); + + else if (_getdns_verify_p7sig(xml, p7s, store, p7signer)) { + gldns_buffer gbuf; + + gldns_buffer_init_vfixed_frm_data(&gbuf, tas, *tas_len); + + if (!_getdns_parse_xml_trust_anchors_buf(&gbuf, now_ms, + (char *)xml_bd->data, xml_bd->size)) + DEBUG_ANCHOR("Failed to parse trust anchor XML data"); + + else if (gldns_buffer_position(&gbuf) > *tas_len) { + *tas_len = gldns_buffer_position(&gbuf); + if ((success = GETDNS_XMALLOC(*mf, uint8_t, *tas_len))) { + gldns_buffer_init_frm_data(&gbuf, success, *tas_len); + if (!_getdns_parse_xml_trust_anchors_buf(&gbuf, + now_ms, (char *)xml_bd->data, xml_bd->size)) { + + DEBUG_ANCHOR("Failed to re-parse trust" + " anchor XML data\n"); + GETDNS_FREE(*mf, success); + success = NULL; + } + } else + DEBUG_ANCHOR("Could not allocate space for " + "trust anchors\n"); + } else { + success = tas; + *tas_len = gldns_buffer_position(&gbuf); + } + } else { + DEBUG_ANCHOR("Verifying trust-anchors failed!\n"); + } + if (store) X509_STORE_free(store); + if (x) X509_free(x); + if (crt) BIO_free(crt); + if (xml) BIO_free(xml); + if (p7s) BIO_free(p7s); + return success; +} + +void _getdns_context_equip_with_anchor( + getdns_context *context, uint64_t *now_ms) +{ + uint8_t xml_spc[4096], *xml_data = NULL; + uint8_t p7s_spc[4096], *p7s_data = NULL; + size_t xml_len, p7s_len; + const char *verify_email = NULL; + const char *verify_CA = NULL; + getdns_return_t r; + + BIO *xml = NULL, *p7s = NULL, *crt = NULL; + X509 *x = NULL; + X509_STORE *store = NULL; + + if ((r = getdns_context_get_trust_anchor_verify_CA( + context, ".", &verify_CA))) + DEBUG_ANCHOR("ERROR %s(): Getting trust anchor verify" + " CA: \"%s\"\n", __FUNC__ + , getdns_get_errorstr_by_id(r)); + + else if (!verify_CA || !*verify_CA) + DEBUG_ANCHOR("NOTICE: Trust anchor verification explicitely " + "disabled by empty verify CA\n"); + + else if ((r = getdns_context_get_trust_anchor_verify_email( + context, ".", &verify_email))) + DEBUG_ANCHOR("ERROR %s(): Getting trust anchor verify email " + "address: \"%s\"\n", __FUNC__ + , getdns_get_errorstr_by_id(r)); + + else if (!verify_email || !*verify_email) + DEBUG_ANCHOR("NOTICE: Trust anchor verification explicitely " + "disabled by empty verify email\n"); + + else if (!(xml_data = _getdns_context_get_priv_file(context, + "root-anchors.xml", xml_spc, sizeof(xml_spc), &xml_len))) + DEBUG_ANCHOR("DEBUG %s(): root-anchors.xml not present\n" + , __FUNC__); + + else if (!(p7s_data = _getdns_context_get_priv_file(context, + "root-anchors.p7s", p7s_spc, sizeof(p7s_spc), &p7s_len))) + DEBUG_ANCHOR("DEBUG %s(): root-anchors.p7s not present\n" + , __FUNC__); + + else if (!(xml = BIO_new_mem_buf(xml_data, xml_len))) + DEBUG_ANCHOR("ERROR %s(): Failed allocating xml BIO\n" + , __FUNC__); + + else if (!(p7s = BIO_new_mem_buf(p7s_data, p7s_len))) + DEBUG_ANCHOR("ERROR %s(): Failed allocating p7s BIO\n" + , __FUNC__); + + else if (!(crt = BIO_new_mem_buf((void *)verify_CA, -1))) + DEBUG_ANCHOR("ERROR %s(): Failed allocating crt BIO\n" + , __FUNC__); + + else if (!(x = PEM_read_bio_X509(crt, NULL, 0, NULL))) + DEBUG_ANCHOR("ERROR %s(): Parsing builtin certificate\n" + , __FUNC__); + + else if (!(store = X509_STORE_new())) + DEBUG_ANCHOR("ERROR %s(): Failed allocating store\n" + , __FUNC__); + + else if (!X509_STORE_add_cert(store, x)) + DEBUG_ANCHOR("ERROR %s(): Adding certificate to store\n" + , __FUNC__); + + else if (_getdns_verify_p7sig(xml, p7s, store, verify_email)) { + uint8_t ta_spc[sizeof(context->trust_anchors_spc)]; + size_t ta_len; + uint8_t *ta = NULL; + gldns_buffer gbuf; + + gldns_buffer_init_vfixed_frm_data( + &gbuf, ta_spc, sizeof(ta_spc)); + + if (!_getdns_parse_xml_trust_anchors_buf(&gbuf, now_ms, + (char *)xml_data, xml_len)) + DEBUG_ANCHOR("Failed to parse trust anchor XML data"); + else if ((ta_len = gldns_buffer_position(&gbuf)) > sizeof(ta_spc)) { + if ((ta = GETDNS_XMALLOC(context->mf, uint8_t, ta_len))) { + gldns_buffer_init_frm_data(&gbuf, ta, + gldns_buffer_position(&gbuf)); + if (!_getdns_parse_xml_trust_anchors_buf( + &gbuf, now_ms, (char *)xml_data, xml_len)) { + DEBUG_ANCHOR("Failed to re-parse trust" + " anchor XML data"); + GETDNS_FREE(context->mf, ta); + } else { + context->trust_anchors = ta; + context->trust_anchors_len = ta_len; + context->trust_anchors_source = GETDNS_TASRC_XML; + _getdns_ta_notify_dnsreqs(context); + } + } else + DEBUG_ANCHOR("Could not allocate space for XML file"); + } else { + (void)memcpy(context->trust_anchors_spc, ta_spc, ta_len); + context->trust_anchors = context->trust_anchors_spc; + context->trust_anchors_len = ta_len; + context->trust_anchors_source = GETDNS_TASRC_XML; + _getdns_ta_notify_dnsreqs(context); + } + DEBUG_ANCHOR("ta: %p, ta_len: %d\n", + (void *)context->trust_anchors, (int)context->trust_anchors_len); + + } else { + DEBUG_ANCHOR("Verifying trust-anchors failed!\n"); + } + if (store) X509_STORE_free(store); + if (x) X509_free(x); + if (crt) BIO_free(crt); + if (xml) BIO_free(xml); + if (p7s) BIO_free(p7s); + if (xml_data && xml_data != xml_spc) + GETDNS_FREE(context->mf, xml_data); + if (p7s_data && p7s_data != p7s_spc) + GETDNS_FREE(context->mf, p7s_data); +} + +static const char tas_write_p7s_buf[] = +"GET %s HTTP/1.1\r\n" +"Host: %s\r\n" +"\r\n"; + +static const char tas_write_xml_p7s_buf[] = +"GET %s HTTP/1.1\r\n" +"Host: %s\r\n" +"\r\n" +"GET %s HTTP/1.1\r\n" +"Host: %s\r\n" +"\r\n"; + + +#if defined(ANCHOR_DEBUG) && ANCHOR_DEBUG +static inline const char * rt_str(uint16_t rt) +{ return rt == GETDNS_RRTYPE_A ? "A" : rt == GETDNS_RRTYPE_AAAA ? "AAAA" : "?"; } +#endif + +static int tas_busy(tas_connection *a) +{ + return a && a->req != NULL; +} + +static int tas_fetching(tas_connection *a) +{ + return a->fd >= 0; +} + +static void tas_rinse(getdns_context *context, tas_connection *a) +{ + if (a->event.ev) + GETDNS_CLEAR_EVENT(a->loop, &a->event); + a->event.ev = NULL; + if (a->fd >= 0) + close(a->fd); + a->fd = -1; + if (a->xml.data) + GETDNS_FREE(context->mf, a->xml.data); + a->xml.data = NULL; + a->xml.size = 0; + if (a->tcp.read_buf && a->tcp.read_buf != context->tas_hdr_spc) + GETDNS_FREE(context->mf, a->tcp.read_buf); + a->tcp.read_buf = NULL; +} + +static void tas_cleanup(getdns_context *context, tas_connection *a) +{ + tas_rinse(context, a); + if (a->req) + _getdns_context_cancel_request(a->req->owner); + if (a->http) + GETDNS_FREE(context->mf, (void *)a->http); + (void) memset(a, 0, sizeof(*a)); + a->fd = -1; +} + +static void tas_success(getdns_context *context, tas_connection *a) +{ + tas_connection *other = &context->a == a ? &context->aaaa : &context->a; + + tas_cleanup(context, a); + tas_cleanup(context, other); + + DEBUG_ANCHOR("Successfully fetched new trust anchors\n"); + context->trust_anchors_source = GETDNS_TASRC_XML; + _getdns_ta_notify_dnsreqs(context); +} + +static void tas_fail(getdns_context *context, tas_connection *a) +{ + tas_connection *other = &context->a == a ? &context->aaaa : &context->a; +#if defined(ANCHOR_DEBUG) && ANCHOR_DEBUG + uint16_t rt = &context->a == a ? GETDNS_RRTYPE_A : GETDNS_RRTYPE_AAAA; + uint16_t ort = rt == GETDNS_RRTYPE_A ? GETDNS_RRTYPE_AAAA : GETDNS_RRTYPE_A; +#endif + tas_cleanup(context, a); + + if (!tas_busy(other)) { + DEBUG_ANCHOR("Fatal error fetching trust anchor: " + "%s connection failed too\n", rt_str(rt)); + context->trust_anchors_source = GETDNS_TASRC_FAILED; + _getdns_ta_notify_dnsreqs(context); + } else + DEBUG_ANCHOR("%s connection failed, waiting for %s\n" + , rt_str(rt), rt_str(ort)); +} + +static void tas_connect(getdns_context *context, tas_connection *a); +static void tas_next(getdns_context *context, tas_connection *a) +{ + tas_connection *other = a == &context->a ? &context->aaaa : &context->a; + + DEBUG_ANCHOR("Try next address\n"); + + if (a->rr) { + if (!(a->rr = _getdns_rrtype_iter_next(a->rr))) + tas_fail(context, a); + else tas_rinse(context, a); + } + if (other->rr) + tas_connect(context, other); + + else if (a->rr) + tas_connect(context, a); +} + +static void tas_timeout_cb(void *userarg) +{ + getdns_dns_req *dnsreq = (getdns_dns_req *)userarg; + getdns_context *context = (getdns_context *)dnsreq->user_pointer; + tas_connection *a; + + if (dnsreq->netreqs[0]->request_type == GETDNS_RRTYPE_A) + a = &context->a; + else a = &context->aaaa; + + DEBUG_ANCHOR("Trust anchor fetch timeout\n"); + GETDNS_CLEAR_EVENT(a->loop, &a->event); + tas_next(context, a); +} + + +static void tas_reconnect_cb(void *userarg) +{ + getdns_dns_req *dnsreq = (getdns_dns_req *)userarg; + getdns_context *context = (getdns_context *)dnsreq->user_pointer; + tas_connection *a; + + if (dnsreq->netreqs[0]->request_type == GETDNS_RRTYPE_A) + a = &context->a; + else a = &context->aaaa; + + DEBUG_ANCHOR("Waiting for second document timeout. Reconnecting...\n"); + GETDNS_CLEAR_EVENT(a->loop, &a->event); + close(a->fd); + a->fd = -1; + if (a->state == TAS_READ_PS7_HDR) { + a->state = TAS_RETRY; + tas_connect(context, a); + } else + tas_next(context, a); +} + +static void tas_read_cb(void *userarg); +static void tas_write_cb(void *userarg); +static void tas_doc_read(getdns_context *context, tas_connection *a) +{ + DEBUG_ANCHOR("doc (size: %d): \"%.*s\"\n", + (int)a->tcp.read_buf_len, + (int)a->tcp.read_buf_len, (char *)a->tcp.read_buf); + + assert(a->tcp.read_pos == a->tcp.read_buf + a->tcp.read_buf_len); + assert(context); + + if (a->state == TAS_READ_XML_DOC) { + if (a->xml.data) + GETDNS_FREE(context->mf, a->xml.data); + a->xml.data = a->tcp.read_buf; + a->xml.size = a->tcp.read_buf_len; + } else + assert(a->state == TAS_READ_PS7_DOC || + a->state == TAS_RETRY_PS7_DOC); + + a->state += 1; + GETDNS_CLEAR_EVENT(a->loop, &a->event); + if (a->state == TAS_DONE || a->state == TAS_RETRY_DONE) { + getdns_bindata p7s_bd; + uint8_t *tas = context->trust_anchors_spc; + size_t tas_len = sizeof(context->trust_anchors_spc); + const char *verify_email = NULL; + getdns_bindata verify_CA; + getdns_return_t r; + uint64_t now_ms = 0; + + p7s_bd.data = a->tcp.read_buf; + p7s_bd.size = a->tcp.read_buf_len; + + if ((r = getdns_context_get_trust_anchor_verify_CA( + context, ".", (const char **)&verify_CA.data))) + DEBUG_ANCHOR("ERROR %s(): Getting trust anchor verify" + " CA: \"%s\"\n", __FUNC__ + , getdns_get_errorstr_by_id(r)); + + else if (!(verify_CA.size = strlen((const char *)verify_CA.data))) + ; /* pass */ + + else if ((r = getdns_context_get_trust_anchor_verify_email( + context, ".", &verify_email))) + DEBUG_ANCHOR("ERROR %s(): Getting trust anchor verify" + " email address: \"%s\"\n", __FUNC__ + , getdns_get_errorstr_by_id(r)); + + else if (!(tas = tas_validate(&context->mf, &a->xml, &p7s_bd, + &verify_CA, verify_email, &now_ms, tas, &tas_len))) + ; /* pass */ + + else { + context->trust_anchors = tas; + context->trust_anchors_len = tas_len; + (void) _getdns_context_write_priv_file( + context, "root-anchors.xml", &a->xml); + (void) _getdns_context_write_priv_file( + context, "root-anchors.p7s", &p7s_bd); + tas_success(context, a); + return; + } + tas_fail(context, a); + return; + } + /* First try to read signatures immediately */ + a->state += 1; + assert(a->state == TAS_READ_PS7_HDR); + a->tcp.read_buf = context->tas_hdr_spc; + a->tcp.read_buf_len = sizeof(context->tas_hdr_spc); + + /* Check for surplus read bytes, for the P7S headers */ + if (a->tcp.to_read > 0) { + a->tcp.read_pos = a->tcp.read_buf + a->tcp.to_read; + a->tcp.to_read = sizeof(context->tas_hdr_spc) + - a->tcp.to_read; + } else { + a->tcp.read_pos = a->tcp.read_buf; + a->tcp.to_read = sizeof(context->tas_hdr_spc); + } + GETDNS_SCHEDULE_EVENT(a->loop, a->fd, 50, + getdns_eventloop_event_init(&a->event, a->req->owner, + tas_read_cb, NULL, tas_reconnect_cb)); + return; +} + +static void tas_read_cb(void *userarg) +{ + getdns_dns_req *dnsreq = (getdns_dns_req *)userarg; + getdns_context *context = (getdns_context *)dnsreq->user_pointer; + tas_connection *a; + ssize_t n, i; + + if (dnsreq->netreqs[0]->request_type == GETDNS_RRTYPE_A) + a = &context->a; + else a = &context->aaaa; + + DEBUG_ANCHOR( "state: %d, to_read: %d\n" + , (int)a->state, (int)a->tcp.to_read); + + n = read(a->fd, a->tcp.read_pos, a->tcp.to_read); + if (n == 0) { + DEBUG_ANCHOR("Connection closed\n"); + GETDNS_CLEAR_EVENT(a->loop, &a->event); + close(a->fd); + a->fd = -1; + if (a->state == TAS_READ_PS7_HDR) { + a->state = TAS_RETRY; + tas_connect(context, a); + } else + tas_next(context, a); + return; + + } else if (n > 0 && ( a->state == TAS_READ_XML_DOC + || a->state == TAS_READ_PS7_DOC + || a->state == TAS_RETRY_PS7_DOC)) { + + assert(n <= (ssize_t)a->tcp.to_read); + + DEBUG_ANCHOR("read: %d bytes at %p, for doc %p of size %d\n", + (int)n, (void *)a->tcp.read_pos + , (void *)a->tcp.read_buf, (int)a->tcp.read_buf_len); + a->tcp.read_pos += n; + a->tcp.to_read -= n; + if (a->tcp.to_read == 0) + tas_doc_read(context, a); + return; + + } else if (n > 0) { + ssize_t p = 0; + int doc_len = -1; + int len; + char *ln; + char *endptr; + + n += a->tcp.read_pos - a->tcp.read_buf; + for (i = 0; i < (n - 1); i++) { + if (a->tcp.read_buf[i] != '\r' || + a->tcp.read_buf[i+1] != '\n') + continue; + + len = (int)(i - p); + ln = (char *)&a->tcp.read_buf[p]; + + DEBUG_ANCHOR("line: \"%.*s\"\n", len, ln); + if (len >= 16 && + !strncasecmp(ln, "Content-Length: ", 16)) { + ln[len] = 0; + doc_len = (int)strtol(ln + 16, &endptr , 10); + if (endptr == ln || *endptr != 0) + doc_len = -1; + } + if (i - p == 0) { + i += 2; + break; + } + p = i + 2; + i++; + } + if (doc_len > 0) { + uint8_t *doc = GETDNS_XMALLOC( + context->mf, uint8_t, doc_len + 1); + doc[doc_len] = 0; + + DEBUG_ANCHOR("i: %d, n: %d, doc_len: %d\n" + , (int)i, (int)n, doc_len); + if (!doc) + DEBUG_ANCHOR("Memory error"); + else { + ssize_t surplus = n - i; + + a->state += 1; + /* With pipelined read, the buffer might + * contain the full document, plus a piece + * of the headers of the next document! + * Currently context->tas_hdr_spc is kept + * small enough to anticipate this. + */ + if (surplus <= 0) { + a->tcp.read_pos = doc; + a->tcp.to_read = doc_len; + } else if (surplus > doc_len) { + (void) memcpy( + doc, a->tcp.read_buf + i, doc_len); + a->tcp.read_pos = doc + doc_len; + + /* Special value to indicate a begin + * of the next reply is already + * present. Detectable by: + * (read_pos == read_buf + read_buf_len) + * && to_read > 0; + */ + a->tcp.to_read = surplus - doc_len; + (void) memmove(a->tcp.read_buf, + a->tcp.read_buf + i + doc_len, + surplus - doc_len); + } else { + assert(surplus <= doc_len); + (void) memcpy( + doc, a->tcp.read_buf + i, surplus); + a->tcp.read_pos = doc + surplus; + a->tcp.to_read = doc_len - surplus; + } + a->tcp.read_buf = doc; + a->tcp.read_buf_len = doc_len; + + if (a->tcp.read_pos == doc + doc_len) + tas_doc_read(context, a); + return; + } + } + } else if (_getdns_EWOULDBLOCK) + return; + + DEBUG_ANCHOR("Read error: %d %s\n", (int)n, strerror(errno)); + GETDNS_CLEAR_EVENT(a->loop, &a->event); + tas_next(context, a); +} + +static void tas_write_cb(void *userarg) +{ + getdns_dns_req *dnsreq = (getdns_dns_req *)userarg; + getdns_context *context = (getdns_context *)dnsreq->user_pointer; + tas_connection *a; + ssize_t written; + + if (dnsreq->netreqs[0]->request_type == GETDNS_RRTYPE_A) + a = &context->a; + else a = &context->aaaa; + + DEBUG_ANCHOR( "state: %d, to_write: %d\n" + , (int)a->state, (int)a->tcp.write_buf_len); + + written = write(a->fd, a->tcp.write_buf, a->tcp.write_buf_len); + if (written >= 0) { + assert(written <= (ssize_t)a->tcp.write_buf_len); + + a->tcp.write_buf += written; + a->tcp.write_buf_len -= written; + if (a->tcp.write_buf_len > 0) + /* Write remainder */ + return; + + a->state += 1; + a->tcp.read_buf = context->tas_hdr_spc; + a->tcp.read_buf_len = sizeof(context->tas_hdr_spc); + a->tcp.read_pos = a->tcp.read_buf; + a->tcp.to_read = sizeof(context->tas_hdr_spc); + GETDNS_CLEAR_EVENT(a->loop, &a->event); + DEBUG_ANCHOR("All written, schedule read\n"); + GETDNS_SCHEDULE_EVENT(a->loop, a->fd, 2000, + getdns_eventloop_event_init(&a->event, a->req->owner, + tas_read_cb, NULL, tas_timeout_cb)); + return; + + } else if (_getdns_EWOULDBLOCK) + return; + + DEBUG_ANCHOR("Write error: %s\n", strerror(errno)); + GETDNS_CLEAR_EVENT(a->loop, &a->event); + tas_next(context, a); +} + +static getdns_return_t _getdns_get_tas_url_hostname( + getdns_context *context, char *hostname, const char **path) +{ + getdns_return_t r; + const char *url; + char *next_slash; + size_t s; + + if ((r = getdns_context_get_trust_anchor_url(context, ".", &url))) + return r; + + if ((next_slash = strchr(url + 7 /* "http://" */, '/'))) { + if (next_slash - url - 7 > 254) + return GETDNS_RETURN_NO_SUCH_LIST_ITEM; + if (path) + *path = next_slash; + strncpy(hostname, url + 7, next_slash - url - 7); + hostname[next_slash - url - 7] = 0; + } else { + if (path) + *path = url + strlen(url); + strncpy(hostname, url + 7, 254); + hostname[254] = 0; + } + s = strlen(hostname); + if (s && s < 255 && hostname[s - 1] != '.') { + hostname[s] = '.'; + hostname[s+1] = '\0'; + } + return GETDNS_RETURN_GOOD; +} + +static void tas_connect(getdns_context *context, tas_connection *a) +{ +#if defined(ANCHOR_DEBUG) && ANCHOR_DEBUG + char a_buf[40]; +#endif + int r; + +#ifdef HAVE_FCNTL + int flag; +#elif defined(HAVE_IOCTLSOCKET) + unsigned long on = 1; +#endif + + if (a->rr->rr_i.nxt - (a->rr->rr_i.rr_type + 10) != + ( a->req->request_type == GETDNS_RRTYPE_A ? 4 + : a->req->request_type == GETDNS_RRTYPE_AAAA ? 16 : -1)) { + + tas_next(context, a); + return; + } + DEBUG_ANCHOR("Initiating connection to %s\n" + , inet_ntop(( a->req->request_type == GETDNS_RRTYPE_A + ? AF_INET : AF_INET6) + , a->rr->rr_i.rr_type + 10, a_buf, sizeof(a_buf))); + + if ((a->fd = socket(( a->req->request_type == GETDNS_RRTYPE_A + ? AF_INET : AF_INET6), SOCK_STREAM, IPPROTO_TCP)) == -1) { + DEBUG_ANCHOR("Error creating socket: %s\n", strerror(errno)); + tas_next(context, a); + return; + } +#ifdef HAVE_FCNTL + if((flag = fcntl(a->fd, F_GETFL)) != -1) { + flag |= O_NONBLOCK; + if(fcntl(a->fd, F_SETFL, flag) == -1) { + /* ignore error, continue blockingly */ + } + } +#elif defined(HAVE_IOCTLSOCKET) + if(ioctlsocket(a->fd, FIONBIO, &on) != 0) { + /* ignore error, continue blockingly */ + } +#endif + if (a->req->request_type == GETDNS_RRTYPE_A) { + struct sockaddr_in addr; + + addr.sin_family = AF_INET; + addr.sin_port = htons(80); + (void) memcpy(&addr.sin_addr, a->rr->rr_i.rr_type + 10, 4); + r = connect(a->fd, (struct sockaddr *)&addr, sizeof(addr)); + } else { + struct sockaddr_in6 addr; + + addr.sin6_family = AF_INET6; + addr.sin6_port = htons(80); + addr.sin6_flowinfo = 0; + (void) memcpy(&addr.sin6_addr, a->rr->rr_i.rr_type + 10, 16); + addr.sin6_scope_id = 0; + r = connect(a->fd, (struct sockaddr *)&addr, sizeof(addr)); + } + if (r == 0 || (r == -1 && (_getdns_EINPROGRESS || + _getdns_EWOULDBLOCK))) { + char tas_hostname[256]; + const char *path = "", *fmt; + getdns_return_t R; + char *write_buf; + size_t buf_sz, path_len, hostname_len; + + a->state += 1; + if (a->http) { + GETDNS_FREE(context->mf, (void *)a->http); + a->http = NULL; + a->tcp.write_buf = NULL; + a->tcp.write_buf_len = 0; + a->tcp.written = 0; + } + if ((R = _getdns_get_tas_url_hostname( + context, tas_hostname, &path))) { + DEBUG_ANCHOR("ERROR %s(): Could not get_tas_url_hostname" + ": \"%s\"", __FUNC__ + , getdns_get_errorstr_by_id(r)); + goto error; + } + hostname_len = strlen(tas_hostname); + if (hostname_len > 0 && tas_hostname[hostname_len - 1] == '.') + tas_hostname[--hostname_len] = '\0'; + path_len = strlen(path); + if (path_len < 4) { + DEBUG_ANCHOR("ERROR %s(): path of tas_url \"%s\" too " + "small\n", __FUNC__, path); + goto error; + } + if (a->state == TAS_RETRY_GET_PS7) { + buf_sz = sizeof(tas_write_p7s_buf) + + 1 * (hostname_len - 2) + 1 * (path_len - 2) + 1; + fmt = tas_write_p7s_buf; + } else { + buf_sz = sizeof(tas_write_xml_p7s_buf) + + 2 * (hostname_len - 2) + 2 * (path_len - 2) + 1; + fmt = tas_write_xml_p7s_buf; + } + if (!(write_buf = GETDNS_XMALLOC(context->mf, char, buf_sz))) { + DEBUG_ANCHOR("ERROR %s(): Could not allocate write " + "buffer\n", __FUNC__); + goto error; + } + if (a->state == TAS_RETRY_GET_PS7) { + (void) snprintf( write_buf, buf_sz, fmt + , path, tas_hostname); + write_buf[4 + path_len - 3] = + write_buf[4 + path_len - 3] == 'X' ? 'P' : 'p'; + write_buf[4 + path_len - 2] = '7'; + write_buf[4 + path_len - 1] = + write_buf[4 + path_len - 1] == 'L' ? 'S' : 's'; + } else { + (void) snprintf( write_buf, buf_sz, fmt + , path, tas_hostname + , path, tas_hostname); + write_buf[29 + hostname_len + 2 * path_len - 3] = + write_buf[29 + hostname_len + 2 * path_len - 3] == 'X' + ? 'P' : 'p'; + write_buf[29 + hostname_len + 2 * path_len - 2] = '7'; + write_buf[29 + hostname_len + 2 * path_len - 1] = + write_buf[29 + hostname_len + 2 * path_len - 1] == 'L' + ? 'S' : 's'; + } + DEBUG_ANCHOR("Write buffer: \"%s\"\n", write_buf); + a->tcp.write_buf = (uint8_t *)(a->http = write_buf); + a->tcp.write_buf_len = buf_sz - 1; + a->tcp.written = 0; + + GETDNS_SCHEDULE_EVENT(a->loop, a->fd, 2000, + getdns_eventloop_event_init(&a->event, a->req->owner, + NULL, tas_write_cb, tas_timeout_cb)); + DEBUG_ANCHOR("Scheduled write with event\n"); + return; + } else + DEBUG_ANCHOR("Connect error: %s\n", strerror(errno)); + +error: + tas_next(context, a); +} + +static void tas_happy_eyeballs_cb(void *userarg) +{ + getdns_dns_req *dnsreq = (getdns_dns_req *)userarg; + getdns_context *context = (getdns_context *)dnsreq->user_pointer; + + assert(dnsreq->netreqs[0]->request_type == GETDNS_RRTYPE_A); + if (tas_fetching(&context->aaaa)) + return; + else { + DEBUG_ANCHOR("AAAA came too late, clearing Happy Eyeballs timer\n"); + GETDNS_CLEAR_EVENT(context->a.loop, &context->a.event); + tas_connect(context, &context->a); + } +} + +static void _tas_hostname_lookup_cb(getdns_dns_req *dnsreq) +{ + getdns_context *context = (getdns_context *)dnsreq->user_pointer; + tas_connection *a; + + if (dnsreq->netreqs[0]->request_type == GETDNS_RRTYPE_A) + a = &context->a; + else a = &context->aaaa; + + a->rrset = _getdns_rrset_answer( + &a->rrset_spc, a->req->response, a->req->response_len); + + if (!a->rrset) { +#if defined(ANCHOR_DEBUG) && ANCHOR_DEBUG + char tas_hostname[256] = ""; + (void) _getdns_get_tas_url_hostname(context, tas_hostname, NULL); + DEBUG_ANCHOR("%s lookup for %s returned no response\n" + , rt_str(a->req->request_type), tas_hostname); +#endif + } else if (a->req->response_len < dnsreq->name_len + 12 || + !_getdns_dname_equal(a->req->response + 12, dnsreq->name) || + a->rrset->rr_type != a->req->request_type) { +#if defined(ANCHOR_DEBUG) && ANCHOR_DEBUG + char tas_hostname[256] = ""; + (void) _getdns_get_tas_url_hostname(context, tas_hostname, NULL); + DEBUG_ANCHOR("%s lookup for %s returned wrong response\n" + , rt_str(a->req->request_type), tas_hostname); +#endif + } else if (!(a->rr = _getdns_rrtype_iter_init(&a->rr_spc, a->rrset))) { +#if defined(ANCHOR_DEBUG) && ANCHOR_DEBUG + char tas_hostname[256] = ""; + (void) _getdns_get_tas_url_hostname(context, tas_hostname, NULL); + DEBUG_ANCHOR("%s lookup for %s returned no addresses\n" + , rt_str(a->req->request_type), tas_hostname); +#endif + } else { + tas_connection *other = a == &context->a ? &context->aaaa + : &context->a; + a->loop = dnsreq->loop; + + if (tas_fetching(other)) + ; /* pass */ + + else if (a == &context->a && tas_busy(other)) { + DEBUG_ANCHOR("Postponing connection initiation: " + "Happy Eyeballs\n"); + GETDNS_SCHEDULE_EVENT(a->loop, a->fd, 25, + getdns_eventloop_event_init(&a->event, + a->req->owner, NULL, NULL, tas_happy_eyeballs_cb)); + } else { + if (other->event.ev && + other->event.timeout_cb == tas_happy_eyeballs_cb) { + DEBUG_ANCHOR("Clearing Happy Eyeballs timer\n"); + GETDNS_CLEAR_EVENT(other->loop, &other->event); + } + tas_connect(context, a); + } + return; + } + tas_fail(context, a); +} + +void _getdns_start_fetching_ta(getdns_context *context, getdns_eventloop *loop) +{ + getdns_return_t r; + size_t scheduled; + char tas_hostname[256]; + const char *verify_CA; + const char *verify_email; + + if ((r = _getdns_get_tas_url_hostname(context, tas_hostname, NULL))) { + DEBUG_ANCHOR("ERROR %s(): Could not get_tas_url_hostname" + ": \"%s\"", __FUNC__ + , getdns_get_errorstr_by_id(r)); + return; + + } else if ((r = getdns_context_get_trust_anchor_verify_CA( + context, ".", &verify_CA))) { + DEBUG_ANCHOR("ERROR %s(): Could not get verify CA" + ": \"%s\"", __FUNC__ + , getdns_get_errorstr_by_id(r)); + return; + + } else if (!verify_CA || !*verify_CA) { + DEBUG_ANCHOR("NOTICE: Trust anchor fetching explicitely " + "disabled by empty verify CA\n"); + return; + + } else if ((r = getdns_context_get_trust_anchor_verify_email( + context, ".", &verify_email))) { + DEBUG_ANCHOR("ERROR %s(): Could not get verify email address" + ": \"%s\"", __FUNC__ + , getdns_get_errorstr_by_id(r)); + return; + + } else if (!verify_email || !*verify_email) { + DEBUG_ANCHOR("NOTICE: Trust anchor fetching explicitely " + "disabled by empty verify email address\n"); + return; + + } else if (!_getdns_context_can_write_appdata(context)) { + DEBUG_ANCHOR("NOTICE %s(): Not fetching TA, because " + "non writeable appdata directory\n", __FUNC__); + return; + } + DEBUG_ANCHOR("Hostname: %s\n", tas_hostname); + DEBUG_ANCHOR("%s on the %ssynchronous loop\n", __FUNC__, + loop == &context->sync_eventloop.loop ? "" : "a"); + + while (!context->sys_ctxt) { /* Used as breakable if. Never repeats. */ + if ((r = getdns_context_create_with_extended_memory_functions( + &context->sys_ctxt, 1, context->mf.mf_arg, + context->mf.mf.ext.malloc, context->mf.mf.ext.realloc, + context->mf.mf.ext.free))) + DEBUG_ANCHOR("Could not create system context: %s\n" + , getdns_get_errorstr_by_id(r)); + + else if ((r = getdns_context_set_eventloop( + context->sys_ctxt, loop))) + DEBUG_ANCHOR("Could not configure %ssynchronous loop " + "with system context: %s\n" + , ( loop == &context->sync_eventloop.loop + ? "" : "a" ) + , getdns_get_errorstr_by_id(r)); + + else if ((r = getdns_context_set_resolution_type( + context->sys_ctxt, GETDNS_RESOLUTION_STUB))) + DEBUG_ANCHOR("Could not configure system context for " + "stub resolver: %s\n" + , getdns_get_errorstr_by_id(r)); + else + break; + + getdns_context_destroy(context->sys_ctxt); + context->sys_ctxt = NULL; + DEBUG_ANCHOR("Fatal error fetching trust anchor: " + "missing system context\n"); + context->trust_anchors_source = GETDNS_TASRC_FAILED; + _getdns_ta_notify_dnsreqs(context); + return; + } + scheduled = 0; +#if 1 + context->a.state = TAS_LOOKUP_ADDRESSES; + if ((r = _getdns_general_loop(context->sys_ctxt, loop, + tas_hostname, GETDNS_RRTYPE_A, NULL, context, + &context->a.req, NULL, _tas_hostname_lookup_cb))) { + DEBUG_ANCHOR("Error scheduling A lookup for %s: %s\n" + , tas_hostname, getdns_get_errorstr_by_id(r)); + } else + scheduled += 1; +#endif + +#if 1 + context->aaaa.state = TAS_LOOKUP_ADDRESSES; + if ((r = _getdns_general_loop(context->sys_ctxt, loop, + tas_hostname, GETDNS_RRTYPE_AAAA, NULL, context, + &context->aaaa.req, NULL, _tas_hostname_lookup_cb))) { + DEBUG_ANCHOR("Error scheduling AAAA lookup for %s: %s\n" + , tas_hostname, getdns_get_errorstr_by_id(r)); + } else + scheduled += 1; +#endif + + if (!scheduled) { + DEBUG_ANCHOR("Fatal error fetching trust anchor: Unable to " + "schedule address requests for %s\n" + , tas_hostname); + context->trust_anchors_source = GETDNS_TASRC_FAILED; + _getdns_ta_notify_dnsreqs(context); + } else + context->trust_anchors_source = GETDNS_TASRC_FETCHING; +} + + +static int _uint16_cmp(const void *a, const void *b) +{ return (int)*(uint16_t *)a - (int)*(uint16_t *)b; } + +static int _uint8x16_cmp(const void *a, const void *b) +{ return memcmp(a, b, RRSIG_RDATA_LEN); } + +static void +_getdns_init_ksks(_getdns_ksks *ksks, _getdns_rrset *dnskey_set) +{ + _getdns_rrtype_iter *rr, rr_space; + _getdns_rrsig_iter *rrsig, rrsig_space; + + assert(ksks); + assert(dnskey_set); + assert(dnskey_set->rr_type == GETDNS_RRTYPE_DNSKEY); + + ksks->n = 0; + for ( rr = _getdns_rrtype_iter_init(&rr_space, dnskey_set) + ; rr && ksks->n < MAX_KSKS + ; rr = _getdns_rrtype_iter_next(rr)) { + + if (rr->rr_i.nxt - rr->rr_i.rr_type < 12 + || !(rr->rr_i.rr_type[11] & 1)) + continue; /* Not a KSK */ + + ksks->ids[ksks->n++] = gldns_calc_keytag_raw( + rr->rr_i.rr_type + 10, + rr->rr_i.nxt - rr->rr_i.rr_type - 10); + } + qsort(ksks->ids, ksks->n, sizeof(uint16_t), _uint16_cmp); + + ksks->n_rrsigs = 0; + for ( rrsig = _getdns_rrsig_iter_init(&rrsig_space, dnskey_set) + ; rrsig && ksks->n_rrsigs < MAX_KSKS + ; rrsig = _getdns_rrsig_iter_next(rrsig)) { + + if (rrsig->rr_i.nxt - rrsig->rr_i.rr_type < 28) + continue; + + (void) memcpy(ksks->rrsigs[ksks->n_rrsigs++], + rrsig->rr_i.rr_type + 12, RRSIG_RDATA_LEN); + } + qsort(ksks->rrsigs, ksks->n_rrsigs, RRSIG_RDATA_LEN, _uint8x16_cmp); +} + + +static int +_getdns_ksks_equal(_getdns_ksks *a, _getdns_ksks *b) +{ + return a == b + || ( a != NULL && b != NULL + && a->n == b->n + && memcmp(a->ids, b->ids, a->n * sizeof(uint16_t)) == 0 + && a->n_rrsigs == b->n_rrsigs + && memcmp(a->rrsigs, b->rrsigs, a->n_rrsigs * RRSIG_RDATA_LEN) == 0); +} + +static void _getdns_context_read_root_ksk(getdns_context *context) +{ + FILE *fp; + struct gldns_file_parse_state pst; + size_t len, dname_len; + uint8_t buf_spc[4096], *buf = buf_spc, *ptr = buf_spc; + size_t buf_sz = sizeof(buf_spc); + _getdns_rrset root_dnskey; + uint8_t *root_dname = (uint8_t *)"\00"; + + + if (!(fp = _getdns_context_get_priv_fp(context, "root.key"))) + return; + + for (;;) { + size_t n_rrs = 0; + + *pst.origin = 0; + pst.origin_len = 1; + *pst.prev_rr = 0; + pst.prev_rr_len = 1; + pst.default_ttl = 0; + pst.lineno = 1; + + (void) memset(buf, 0, 12); + ptr += 12; + + while (!feof(fp)) { + len = buf + buf_sz - ptr; + dname_len = 0; + if (gldns_fp2wire_rr_buf(fp, ptr, &len, &dname_len, &pst)) + break; + if ((ptr += len) > buf + buf_sz) + break; + if (len) + n_rrs += 1; + if (dname_len && dname_len < sizeof(pst.prev_rr)) { + memcpy(pst.prev_rr, ptr, dname_len); + pst.prev_rr_len = dname_len; + } + } + if (ptr <= buf + buf_sz) { + gldns_write_uint16(buf + GLDNS_ANCOUNT_OFF, n_rrs); + break; + } + rewind(fp); + if (buf == buf_spc) + buf_sz = 65536; + else { + GETDNS_FREE(context->mf, buf); + buf_sz *= 2; + } + if (!(buf = GETDNS_XMALLOC(context->mf, uint8_t, buf_sz))) { + DEBUG_ANCHOR("ERROR %s(): Memory error\n", __FUNC__); + break;; + } + ptr = buf; + }; + fclose(fp); + if (!buf) + return; + + root_dnskey.name = root_dname; + root_dnskey.rr_class = GETDNS_RRCLASS_IN; + root_dnskey.rr_type = GETDNS_RRTYPE_DNSKEY; + root_dnskey.pkt = buf; + root_dnskey.pkt_len = ptr - buf; + root_dnskey.sections = SECTION_ANSWER; + + _getdns_init_ksks(&context->root_ksk, &root_dnskey); + + if (buf && buf != buf_spc) + GETDNS_FREE(context->mf, buf); +} + +void +_getdns_context_update_root_ksk( + getdns_context *context, _getdns_rrset *dnskey_set) +{ + _getdns_ksks root_ksk_seen; + _getdns_rrtype_iter *rr, rr_space; + _getdns_rrsig_iter *rrsig, rrsig_space; + char str_spc[4096], *str_buf, *str_pos; + int sz_needed; + int remaining; + size_t str_sz = 0; + getdns_bindata root_key_bd; + + _getdns_init_ksks(&root_ksk_seen, dnskey_set); + if (_getdns_ksks_equal(&context->root_ksk, &root_ksk_seen)) + return; /* root DNSKEY rrset already known */ + + /* Try to read root DNSKEY rrset from root.key */ + _getdns_context_read_root_ksk(context); + if (_getdns_ksks_equal(&context->root_ksk, &root_ksk_seen)) + return; /* root DNSKEY rrset same as the safed one */ + + /* Different root DNSKEY rrset. Perhaps because of failure to read + * from disk. If we cannot write to our appdata directory, bail out + */ + if (context->can_write_appdata == PROP_UNABLE) + return; + + /* We might be able to write or we do not know whether we can write + * to the appdata directory. In the latter we'll try to write to + * find out. The section below converts the wireformat DNSKEY rrset + * to presentationformat. + */ + str_pos = str_buf = str_spc; + remaining = sizeof(str_spc); + for (;;) { + for ( rr = _getdns_rrtype_iter_init(&rr_space, dnskey_set) + ; rr ; rr = _getdns_rrtype_iter_next(rr)) { + + sz_needed = gldns_wire2str_rr_buf((uint8_t *)rr->rr_i.pos, + rr->rr_i.nxt - rr->rr_i.pos, str_pos, + (size_t)(remaining > 0 ? remaining : 0)); + + str_pos += sz_needed; + remaining -= sz_needed; + } + for ( rrsig = _getdns_rrsig_iter_init(&rrsig_space, dnskey_set) + ; rrsig + ; rrsig = _getdns_rrsig_iter_next(rrsig)) { + sz_needed = gldns_wire2str_rr_buf((uint8_t *)rrsig->rr_i.pos, + rrsig->rr_i.nxt - rrsig->rr_i.pos, str_pos, + (size_t)(remaining > 0 ? remaining : 0)); + + str_pos += sz_needed; + remaining -= sz_needed; + } + if (remaining > 0) { + *str_pos = 0; + if (str_buf == str_spc) + str_sz = sizeof(str_spc) - remaining; + break; + } + if (str_buf != str_spc) { + DEBUG_ANCHOR("ERROR %s(): Buffer size determination " + "error\n", __FUNC__); + if (str_buf) + GETDNS_FREE(context->mf, str_buf); + + return; + } + if (!(str_pos = str_buf = GETDNS_XMALLOC( context->mf, char, + (str_sz = sizeof(str_spc) - remaining) + 1))) { + DEBUG_ANCHOR("ERROR %s(): Memory error\n", __FUNC__); + return; + } + remaining = str_sz + 1; + DEBUG_ANCHOR("Retrying with buf size: %d\n", remaining); + }; + + /* Write presentation format DNSKEY rrset to "root.key" file */ + root_key_bd.size = str_sz; + root_key_bd.data = (void *)str_buf; + if (_getdns_context_write_priv_file( + context, "root.key", &root_key_bd)) { + size_t i; + + /* A new "root.key" file was written. When they contain + * key_id's which are not in "root-anchors.xml", then update + * "root-anchors.xml". + */ + + for (i = 0; i < context->root_ksk.n; i++) { + _getdns_rrset_iter tas_iter_spc, *ta; + + for ( ta = _getdns_rrset_iter_init(&tas_iter_spc + , context->trust_anchors + , context->trust_anchors_len + , SECTION_ANSWER) + ; ta ; ta = _getdns_rrset_iter_next(ta)) { + _getdns_rrtype_iter *rr, rr_space; + _getdns_rrset *rrset; + + if (!(rrset = _getdns_rrset_iter_value(ta))) + continue; + + if (*rrset->name != '\0') + continue; /* Not a root anchor */ + + if (rrset->rr_type == GETDNS_RRTYPE_DS) { + for ( rr = _getdns_rrtype_iter_init( + &rr_space, rrset) + ; rr + ; rr = _getdns_rrtype_iter_next(rr) + ) { + if (rr->rr_i.nxt - + rr->rr_i.rr_type < 12) + continue; + + DEBUG_ANCHOR("DS with id: %d\n" + , (int)gldns_read_uint16(rr->rr_i.rr_type + 10)); + if (gldns_read_uint16( + rr->rr_i.rr_type + 10) == + context->root_ksk.ids[i]) + break; + } + if (rr) + break; + continue; + } + if (rrset->rr_type != GETDNS_RRTYPE_DNSKEY) + continue; + + for ( rr = _getdns_rrtype_iter_init(&rr_space + , rrset) + ; rr ; rr = _getdns_rrtype_iter_next(rr)) { + + + if (rr->rr_i.nxt-rr->rr_i.rr_type < 12 + || !(rr->rr_i.rr_type[11] & 1)) + continue; /* Not a KSK */ + + if (gldns_calc_keytag_raw( + rr->rr_i.rr_type + 10, + rr->rr_i.nxt-rr->rr_i.rr_type - 10) + == context->root_ksk.ids[i]) + break; + } + if (rr) + break; + } + if (!ta) { + DEBUG_ANCHOR("NOTICE %s(): Key with id %d " + "*not* found in TA.\n" + "\"root-anchors.xml\" need " + "updating.\n", __FUNC__ + , context->root_ksk.ids[i]); + context->trust_anchors_source = + GETDNS_TASRC_XML_UPDATE; + break; + } + DEBUG_ANCHOR("DEBUG %s(): Key with id %d found in TA\n" + , __FUNC__, context->root_ksk.ids[i]); + } + } + if (str_buf && str_buf != str_spc) + GETDNS_FREE(context->mf, str_buf); +} + +/* anchor.c */ diff --git a/src/anchor.h b/src/anchor.h new file mode 100644 index 00000000..3c826384 --- /dev/null +++ b/src/anchor.h @@ -0,0 +1,59 @@ +/** + * + * /brief functions for DNSSEC trust anchor management + * + */ + +/* + * Copyright (c) 2017, NLnet Labs + * 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 names of the copyright holders 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 Verisign, Inc. 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. + */ + +#ifndef ANCHOR_H_ +#define ANCHOR_H_ + +#include "getdns/getdns.h" +#include "getdns/getdns_extra.h" +#include +#include "rr-iter.h" + +void _getdns_context_equip_with_anchor(getdns_context *context, uint64_t *now_ms); + +void _getdns_start_fetching_ta(getdns_context *context, getdns_eventloop *loop); + +#define MAX_KSKS 16 +#define RRSIG_RDATA_LEN 16 +typedef struct _getdns_ksks { + size_t n; + uint16_t ids[MAX_KSKS]; + size_t n_rrsigs; + uint8_t rrsigs[MAX_KSKS][RRSIG_RDATA_LEN]; +} _getdns_ksks; + +void _getdns_context_update_root_ksk( + getdns_context *context, _getdns_rrset *dnskey_set); + +#endif +/* anchor.h */ diff --git a/src/compat/strptime.c b/src/compat/strptime.c new file mode 100644 index 00000000..10ec3157 --- /dev/null +++ b/src/compat/strptime.c @@ -0,0 +1,345 @@ +/** strptime workaround (for oa macos leopard) + * This strptime follows the man strptime (2001-11-12) + * conforming to SUSv2, POSIX.1-2001 + * + * This very simple version of strptime has no: + * - E alternatives + * - O alternatives + * - Glibc additions + * - Does not process week numbers + * - Does not properly processes year day + * + * LICENSE + * Copyright (c) 2008, NLnet Labs, Matthijs Mekking + * 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 NLnetLabs 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 "config.h" + +#ifndef HAVE_CONFIG_H +#include +#endif + +#ifndef STRPTIME_WORKS + +#define TM_YEAR_BASE 1900 + +#include +#include + +static const char *abb_weekdays[] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL +}; +static const char *full_weekdays[] = { + "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday", NULL +}; +static const char *abb_months[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL +}; +static const char *full_months[] = { + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December", NULL +}; +static const char *ampm[] = { + "am", "pm", NULL +}; + +static int +match_string(const char **buf, const char **strs) +{ + int i = 0; + + for (i = 0; strs[i] != NULL; i++) { + int len = strlen(strs[i]); + if (strncasecmp (*buf, strs[i], len) == 0) { + *buf += len; + return i; + } + } + return -1; +} + +static int +str2int(const char **buf, int max) +{ + int ret=0, count=0; + + while (*buf[0] != '\0' && isdigit((unsigned char)*buf[0]) && counttm_wday = ret; + break; + case 'b': /* month name, abbreviated or full */ + case 'B': + case 'h': + ret = match_string(&s, full_months); + if (ret < 0) + ret = match_string(&s, abb_months); + if (ret < 0) { + return NULL; + } + tm->tm_mon = ret; + break; + case 'c': /* date and time representation */ + if (!(s = unbound_strptime(s, "%x %X", tm))) { + return NULL; + } + break; + case 'C': /* century number */ + ret = str2int(&s, 2); + if (ret < 0 || ret > 99) { /* must be in [00,99] */ + return NULL; + } + + if (split_year) { + tm->tm_year = ret*100 + (tm->tm_year%100); + } + else { + tm->tm_year = ret*100 - TM_YEAR_BASE; + split_year = 1; + } + break; + case 'd': /* day of month */ + case 'e': + ret = str2int(&s, 2); + if (ret < 1 || ret > 31) { /* must be in [01,31] */ + return NULL; + } + tm->tm_mday = ret; + break; + case 'D': /* equivalent to %m/%d/%y */ + if (!(s = unbound_strptime(s, "%m/%d/%y", tm))) { + return NULL; + } + break; + case 'H': /* hour */ + ret = str2int(&s, 2); + if (ret < 0 || ret > 23) { /* must be in [00,23] */ + return NULL; + } + tm->tm_hour = ret; + break; + case 'I': /* 12hr clock hour */ + ret = str2int(&s, 2); + if (ret < 1 || ret > 12) { /* must be in [01,12] */ + return NULL; + } + if (ret == 12) /* actually [0,11] */ + ret = 0; + tm->tm_hour = ret; + break; + case 'j': /* day of year */ + ret = str2int(&s, 2); + if (ret < 1 || ret > 366) { /* must be in [001,366] */ + return NULL; + } + tm->tm_yday = ret; + break; + case 'm': /* month */ + ret = str2int(&s, 2); + if (ret < 1 || ret > 12) { /* must be in [01,12] */ + return NULL; + } + /* months go from 0-11 */ + tm->tm_mon = (ret-1); + break; + case 'M': /* minute */ + ret = str2int(&s, 2); + if (ret < 0 || ret > 59) { /* must be in [00,59] */ + return NULL; + } + tm->tm_min = ret; + break; + case 'n': /* arbitrary whitespace */ + case 't': + while (isspace((unsigned char)*s)) + s++; + break; + case 'p': /* am pm */ + ret = match_string(&s, ampm); + if (ret < 0) { + return NULL; + } + if (tm->tm_hour < 0 || tm->tm_hour > 11) { /* %I */ + return NULL; + } + + if (ret == 1) /* pm */ + tm->tm_hour += 12; + break; + case 'r': /* equivalent of %I:%M:%S %p */ + if (!(s = unbound_strptime(s, "%I:%M:%S %p", tm))) { + return NULL; + } + break; + case 'R': /* equivalent of %H:%M */ + if (!(s = unbound_strptime(s, "%H:%M", tm))) { + return NULL; + } + break; + case 'S': /* seconds */ + ret = str2int(&s, 2); + /* 60 may occur for leap seconds */ + /* earlier 61 was also allowed */ + if (ret < 0 || ret > 60) { /* must be in [00,60] */ + return NULL; + } + tm->tm_sec = ret; + break; + case 'T': /* equivalent of %H:%M:%S */ + if (!(s = unbound_strptime(s, "%H:%M:%S", tm))) { + return NULL; + } + break; + case 'U': /* week number, with the first Sun of Jan being w1 */ + ret = str2int(&s, 2); + if (ret < 0 || ret > 53) { /* must be in [00,53] */ + return NULL; + } + /** it is hard (and not necessary for nsd) to determine time + * data from week number. + **/ + break; + case 'w': /* day of week */ + ret = str2int(&s, 1); + if (ret < 0 || ret > 6) { /* must be in [0,6] */ + return NULL; + } + tm->tm_wday = ret; + break; + case 'W': /* week number, with the first Mon of Jan being w1 */ + ret = str2int(&s, 2); + if (ret < 0 || ret > 53) { /* must be in [00,53] */ + return NULL; + } + /** it is hard (and not necessary for nsd) to determine time + * data from week number. + **/ + break; + case 'x': /* date format */ + if (!(s = unbound_strptime(s, "%m/%d/%y", tm))) { + return NULL; + } + break; + case 'X': /* time format */ + if (!(s = unbound_strptime(s, "%H:%M:%S", tm))) { + return NULL; + } + break; + case 'y': /* last two digits of a year */ + ret = str2int(&s, 2); + if (ret < 0 || ret > 99) { /* must be in [00,99] */ + return NULL; + } + if (split_year) { + tm->tm_year = ((tm->tm_year/100) * 100) + ret; + } + else { + split_year = 1; + + /** currently: + * if in [0,68] we are in 21th century, + * if in [69,99] we are in 20th century. + **/ + if (ret < 69) /* 2000 */ + ret += 100; + tm->tm_year = ret; + } + break; + case 'Y': /* year */ + ret = str2int(&s, 4); + if (ret < 0 || ret > 9999) { + return NULL; + } + tm->tm_year = ret - TM_YEAR_BASE; + break; + case '\0': + default: /* unsupported, cannot match format */ + return NULL; + break; + } + } + else { /* literal */ + /* if input cannot match format, return NULL */ + if (*s != c) + return NULL; + s++; + } + + format++; + } + + /* return pointer to remainder of s */ + return (char*) s; +} + +#endif /* STRPTIME_WORKS */ diff --git a/src/const-info.c b/src/const-info.c index 776e6795..9caf93cd 100644 --- a/src/const-info.c +++ b/src/const-info.c @@ -82,6 +82,9 @@ static struct const_info consts_info[] = { { 622, "GETDNS_CONTEXT_CODE_ROUND_ROBIN_UPSTREAMS", GETDNS_CONTEXT_CODE_ROUND_ROBIN_UPSTREAMS_TEXT }, { 623, "GETDNS_CONTEXT_CODE_TLS_BACKOFF_TIME", GETDNS_CONTEXT_CODE_TLS_BACKOFF_TIME_TEXT }, { 624, "GETDNS_CONTEXT_CODE_TLS_CONNECTION_RETRIES", GETDNS_CONTEXT_CODE_TLS_CONNECTION_RETRIES_TEXT }, + { 625, "GETDNS_CONTEXT_CODE_TRUST_ANCHOR_URL", GETDNS_CONTEXT_CODE_TRUST_ANCHOR_URL_TEXT }, + { 626, "GETDNS_CONTEXT_CODE_TRUST_ANCHOR_VERIFY_CA", GETDNS_CONTEXT_CODE_TRUST_ANCHOR_VERIFY_CA_TEXT }, + { 627, "GETDNS_CONTEXT_CODE_TRUST_ANCHOR_VERIFY_EMAIL", GETDNS_CONTEXT_CODE_TRUST_ANCHOR_VERIFY_EMAIL_TEXT }, { 700, "GETDNS_CALLBACK_COMPLETE", GETDNS_CALLBACK_COMPLETE_TEXT }, { 701, "GETDNS_CALLBACK_CANCEL", GETDNS_CALLBACK_CANCEL_TEXT }, { 702, "GETDNS_CALLBACK_TIMEOUT", GETDNS_CALLBACK_TIMEOUT_TEXT }, @@ -174,6 +177,9 @@ static struct const_name_info consts_name_info[] = { { "GETDNS_CONTEXT_CODE_TLS_BACKOFF_TIME", 623 }, { "GETDNS_CONTEXT_CODE_TLS_CONNECTION_RETRIES", 624 }, { "GETDNS_CONTEXT_CODE_TLS_QUERY_PADDING_BLOCKSIZE", 620 }, + { "GETDNS_CONTEXT_CODE_TRUST_ANCHOR_URL", 625 }, + { "GETDNS_CONTEXT_CODE_TRUST_ANCHOR_VERIFY_CA", 626 }, + { "GETDNS_CONTEXT_CODE_TRUST_ANCHOR_VERIFY_EMAIL", 627 }, { "GETDNS_CONTEXT_CODE_UPSTREAM_RECURSIVE_SERVERS", 603 }, { "GETDNS_DNSSEC_BOGUS", 401 }, { "GETDNS_DNSSEC_INDETERMINATE", 402 }, diff --git a/src/context.c b/src/context.c index d87756a6..4eaef000 100644 --- a/src/context.c +++ b/src/context.c @@ -35,11 +35,13 @@ */ #include "config.h" +#include "anchor.h" #ifndef USE_WINSOCK #include #include #include +#include #else #include #include @@ -68,7 +70,6 @@ typedef unsigned short in_port_t; #endif #include -#include "config.h" #ifdef HAVE_LIBUNBOUND #include #endif @@ -1362,6 +1363,37 @@ static void _getdns_check_expired_pending_netreqs_cb(void *arg) _getdns_check_expired_pending_netreqs((getdns_context *)arg, &now_ms); } +static const char *_getdns_default_root_anchor_url = + "http://data.iana.org/root-anchors/root-anchors.xml"; + +/* The ICANN CA fetched at 24 Sep 2010. Valid to 2028 */ +static const char *_getdns_default_root_anchor_verify_CA = +"-----BEGIN CERTIFICATE-----\n" +"MIIDdzCCAl+gAwIBAgIBATANBgkqhkiG9w0BAQsFADBdMQ4wDAYDVQQKEwVJQ0FO\n" +"TjEmMCQGA1UECxMdSUNBTk4gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAUBgNV\n" +"BAMTDUlDQU5OIFJvb3QgQ0ExCzAJBgNVBAYTAlVTMB4XDTA5MTIyMzA0MTkxMloX\n" +"DTI5MTIxODA0MTkxMlowXTEOMAwGA1UEChMFSUNBTk4xJjAkBgNVBAsTHUlDQU5O\n" +"IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRYwFAYDVQQDEw1JQ0FOTiBSb290IENB\n" +"MQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKDb\n" +"cLhPNNqc1NB+u+oVvOnJESofYS9qub0/PXagmgr37pNublVThIzyLPGCJ8gPms9S\n" +"G1TaKNIsMI7d+5IgMy3WyPEOECGIcfqEIktdR1YWfJufXcMReZwU4v/AdKzdOdfg\n" +"ONiwc6r70duEr1IiqPbVm5T05l1e6D+HkAvHGnf1LtOPGs4CHQdpIUcy2kauAEy2\n" +"paKcOcHASvbTHK7TbbvHGPB+7faAztABLoneErruEcumetcNfPMIjXKdv1V1E3C7\n" +"MSJKy+jAqqQJqjZoQGB0necZgUMiUv7JK1IPQRM2CXJllcyJrm9WFxY0c1KjBO29\n" +"iIKK69fcglKcBuFShUECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B\n" +"Af8EBAMCAf4wHQYDVR0OBBYEFLpS6UmDJIZSL8eZzfyNa2kITcBQMA0GCSqGSIb3\n" +"DQEBCwUAA4IBAQAP8emCogqHny2UYFqywEuhLys7R9UKmYY4suzGO4nkbgfPFMfH\n" +"6M+Zj6owwxlwueZt1j/IaCayoKU3QsrYYoDRolpILh+FPwx7wseUEV8ZKpWsoDoD\n" +"2JFbLg2cfB8u/OlE4RYmcxxFSmXBg0yQ8/IoQt/bxOcEEhhiQ168H2yE5rxJMt9h\n" +"15nu5JBSewrCkYqYYmaxyOC3WrVGfHZxVI7MpIFcGdvSb2a1uyuua8l0BKgk3ujF\n" +"0/wsHNeP22qNyVO+XVBzrM8fk8BSUFuiT/6tZTYXRtEt5aKQZgXbKU5dUF3jT9qg\n" +"j/Br5BZw3X/zd325TvnswzMC1+ljLzHnQGGk\n" +"-----END CERTIFICATE-----\n"; + +static const char *_getdns_default_root_anchor_verify_email = + "dnssec@iana.org"; + + /* * getdns_context_create * @@ -1461,6 +1493,20 @@ getdns_context_create_with_extended_memory_functions( result->suffixes = no_suffixes; result->suffixes_len = sizeof(no_suffixes); + result->trust_anchors_source = GETDNS_TASRC_NONE; + result->can_write_appdata = PROP_UNKNOWN; + result->root_anchor_url = _getdns_default_root_anchor_url; + result->root_anchor_verify_email + = _getdns_default_root_anchor_verify_email; + result->root_anchor_verify_CA = _getdns_default_root_anchor_verify_CA; + + (void) memset(&result->root_ksk, 0, sizeof(result->root_ksk)); + + (void) memset(&result->a, 0, sizeof(result->a)); + (void) memset(&result->aaaa, 0, sizeof(result->aaaa)); + result->a.fd = -1; + result->aaaa.fd = -1; + gldns_buffer_init_vfixed_frm_data(&gbuf, result->trust_anchors_spc , sizeof(result->trust_anchors_spc)); @@ -1478,12 +1524,16 @@ getdns_context_create_with_extended_memory_functions( , result->trust_anchors , result->trust_anchors_len); if (!_getdns_parse_ta_file(NULL, &gbuf)) { + GETDNS_FREE(result->mf, result->trust_anchors); result->trust_anchors = NULL; result->trust_anchors_len = 0; - } + } else + result->trust_anchors_source = GETDNS_TASRC_ZONE; } - } else + } else { result->trust_anchors = result->trust_anchors_spc; + result->trust_anchors_source = GETDNS_TASRC_ZONE; + } result->upstreams = NULL; @@ -1517,6 +1567,9 @@ getdns_context_create_with_extended_memory_functions( result->return_call_reporting = 0; result->specify_class = GETDNS_RRCLASS_IN; + result->sys_ctxt = NULL; + result->ta_notify = NULL; + /* state data used to detect changes to the system config files */ result->fchg_resolvconf = NULL; @@ -1553,7 +1606,15 @@ getdns_context_create_with_extended_memory_functions( #endif /* Only initialise SSL once and ideally in a thread-safe manner */ if (ssl_init == false) { +#if OPENSSL_VERSION_NUMBER < 0x10100000 || defined(HAVE_LIBRESSL) + OpenSSL_add_all_algorithms(); SSL_library_init(); +#else + OPENSSL_init_crypto( OPENSSL_INIT_ADD_ALL_CIPHERS + | OPENSSL_INIT_ADD_ALL_DIGESTS + | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); + (void)OPENSSL_init_ssl(0, NULL); +#endif ssl_init = true; } #ifdef HAVE_PTHREAD @@ -1561,7 +1622,6 @@ getdns_context_create_with_extended_memory_functions( #else /* XXX implement Windows-style unlock here */ #endif - #ifdef HAVE_LIBUNBOUND result->unbound_ctx = NULL; if ((r = rebuild_ub_ctx(result))) @@ -1636,6 +1696,10 @@ getdns_context_destroy(struct getdns_context *context) return; context->destroying = 1; + + if (context->sys_ctxt) + getdns_context_destroy(context->sys_ctxt); + /* cancel all outstanding requests */ cancel_outstanding_requests(context); @@ -2648,9 +2712,11 @@ getdns_context_set_dnssec_trust_anchors( context->trust_anchors = _getdns_list2wire(value, context->trust_anchors_spc, &context->trust_anchors_len, &context->mf); + context->trust_anchors_source = GETDNS_TASRC_APP; } else { context->trust_anchors = NULL; context->trust_anchors_len = 0; + context->trust_anchors_source = GETDNS_TASRC_NONE; } dispatch_updated(context, GETDNS_CONTEXT_CODE_DNSSEC_TRUST_ANCHORS); return GETDNS_RETURN_GOOD; @@ -4542,4 +4608,326 @@ getdns_context_config(getdns_context *context, const getdns_dict *config_dict) return r; } +static size_t _getdns_get_appdata(char *path) +{ + size_t len; + +#ifdef USE_WINSOCK +# define APPDATA_SUBDIR "getdns" + + if (! SUCCEEDED(SHGetFolderPath(NULL, + CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, path))) + DEBUG_ANCHOR("ERROR %s(): Could not get \%AppData\% directory\n" + , __FUNC__); + + else if ((len = strlen(path)) + sizeof(APPDATA_SUBDIR) + 2 >= PATH_MAX) + DEBUG_ANCHOR("ERROR %s(): Home path too long for appdata\n" + , __FUNC__); +#else +# define APPDATA_SUBDIR ".getdns" + struct passwd *p = getpwuid(getuid()); + char *home = NULL; + + if (!(home = p ? p->pw_dir : getenv("HOME"))) + DEBUG_ANCHOR("ERROR %s(): Could not get home directory\n" + , __FUNC__); + + else if ((len = strlen(home)) + sizeof(APPDATA_SUBDIR) + 2 >= PATH_MAX) + DEBUG_ANCHOR("ERROR %s(): Home path too long for appdata\n" + , __FUNC__); + + else if (!strcpy(path, home)) + ; /* strcpy returns path always */ +#endif + else { + if (len == 0 || ( path[len - 1] != '/' + && path[len - 1] != '\\')) { + path[len++] = '/'; + path[len ] = '\0'; + } + (void) strcpy(path + len, APPDATA_SUBDIR); + len += sizeof(APPDATA_SUBDIR) - 1; + + if (mkdir(path, 0755) < 0 && errno != EEXIST) + DEBUG_ANCHOR("ERROR %s(): Could not mkdir %s: %s\n" + , __FUNC__, path, strerror(errno)); + else { + path[len++] = '/'; + path[len ] = '\0'; + return len; + } + } + return 0; +} + +FILE *_getdns_context_get_priv_fp(getdns_context *context, const char *fn) +{ + char path[PATH_MAX]; + FILE *f = NULL; + size_t len; + + (void) context; + if (!(len = _getdns_get_appdata(path))) + DEBUG_ANCHOR("ERROR %s(): Could nog get application data path\n" + , __FUNC__); + + else if (len + strlen(fn) >= sizeof(path)) + DEBUG_ANCHOR("ERROR %s(): Application data too long\n", __FUNC__); + + else if (!strcpy(path + len, fn)) + ; /* strcpy returns path + len always */ + + else if (!(f = fopen(path, "r"))) + DEBUG_ANCHOR("ERROR %s(): Opening \"%s\": %s\n" + , __FUNC__, path, strerror(errno)); + + return f; +} + +uint8_t *_getdns_context_get_priv_file(getdns_context *context, + const char *fn, uint8_t *buf, size_t buf_len, size_t *file_sz) +{ + FILE *f = NULL; + + if (!(f = _getdns_context_get_priv_fp(context, fn))) + ; /* pass */ + + else if ((*file_sz = fread(buf, 1, buf_len, f)) < (buf_len - 1) && feof(f)) { + buf[*file_sz] = 0; + (void) fclose(f); + return buf; + } + else if (fseek(f, 0, SEEK_END) < 0) + DEBUG_ANCHOR("ERROR %s(): Determining size of \"%s\": %s\n" + , __FUNC__, fn, strerror(errno)); + + else if (!(buf = GETDNS_XMALLOC( + context->mf, uint8_t, (buf_len = ftell(f) + 1)))) + DEBUG_ANCHOR("ERROR %s(): Allocating %d memory for \"%s\"\n" + , __FUNC__, (int)buf_len, fn); + + else { + rewind(f); + if ((*file_sz = fread(buf, 1, buf_len, f)) >= buf_len || !feof(f)) { + GETDNS_FREE(context->mf, buf); + DEBUG_ANCHOR("ERROR %s(): Reading \"%s\": %s\n" + , __FUNC__, fn, strerror(errno)); + } + else { + buf[*file_sz] = 0; + (void) fclose(f); + return buf; + } + } + if (f) + (void) fclose(f); + return NULL; +} + + +int _getdns_context_write_priv_file(getdns_context *context, + const char *fn, getdns_bindata *content) +{ + char path[PATH_MAX], tmpfn[PATH_MAX]; + int fd = -1; + FILE *f = NULL; + size_t len; + + if (!(len = _getdns_get_appdata(path))) + DEBUG_ANCHOR("ERROR %s(): Could nog get application data path\n" + , __FUNC__); + + else if (len + 6 >= sizeof(tmpfn) + || len + strlen(fn) >= sizeof(path)) + DEBUG_ANCHOR("ERROR %s(): Application data too long\n", __FUNC__); + + + else if (snprintf(tmpfn, sizeof(tmpfn), "%sXXXXXX", path) < 0) + DEBUG_ANCHOR("ERROR %s(): Creating temporary filename template\n" + , __FUNC__); + + else if (!strcpy(path + len, fn)) + ; /* strcpy returns path + len always */ + + else if ((fd = mkstemp(tmpfn)) < 0) + DEBUG_ANCHOR("ERROR %s(): Creating temporary file: %s\n" + , __FUNC__, strerror(errno)); + + else if (!(f = fdopen(fd, "w"))) + DEBUG_ANCHOR("ERROR %s(): Opening temporary file: %s\n" + , __FUNC__, strerror(errno)); + + else if (fwrite(content->data, 1, content->size, f) < content->size) + DEBUG_ANCHOR("ERROR %s(): Writing temporary file: %s\n" + , __FUNC__, strerror(errno)); + + else if (fclose(f) < 0) + DEBUG_ANCHOR("ERROR %s(): Closing temporary file: %s\n" + , __FUNC__, strerror(errno)); + + else if (rename(tmpfn, path) < 0) + DEBUG_ANCHOR("ERROR %s(): Renaming temporary file: %s\n" + , __FUNC__, strerror(errno)); + else { + context->can_write_appdata = PROP_ABLE; + return 1; + } + if (f) + (void) fclose(f); + + else if (fd >= 0) + (void) close(fd); + + context->can_write_appdata = PROP_UNABLE; + return 0; +} + +int _getdns_context_can_write_appdata(getdns_context *context) +{ + char test_fn[30], path[PATH_MAX]; + size_t len; + getdns_bindata test_content = { 4, (void *)"TEST" }; + + if (context->can_write_appdata == PROP_ABLE) + return 1; + + else if (context->can_write_appdata == PROP_UNABLE) + return 0; + + (void) snprintf( test_fn, sizeof(test_fn) + , "write-test-%d.tmp", arc4random()); + + if (!_getdns_context_write_priv_file(context, test_fn, &test_content)) + return 0; + + if (!(len = _getdns_get_appdata(path))) + DEBUG_ANCHOR("ERROR %s(): Could nog get application data path\n" + , __FUNC__); + + else if (len + strlen(test_fn) >= sizeof(path)) + DEBUG_ANCHOR("ERROR %s(): Application data too long\n", __FUNC__); + + else if (!strcpy(path + len, test_fn)) + ; /* strcpy returns path + len always */ + + else if (unlink(path) < 0) + DEBUG_ANCHOR("ERROR %s(): Unlinking write test file \"%s\": %s\n" + , __FUNC__, path, strerror(errno)); + + return 1; +} + +getdns_return_t +getdns_context_set_trust_anchor_url( + getdns_context *context, const char *zone, const char *url) +{ + const char *path; + size_t path_len; + + if (!context || !url) + return GETDNS_RETURN_INVALID_PARAMETER; + + if (zone && !(zone[0] == '.' && zone[1] == '\0')) + return GETDNS_RETURN_NOT_IMPLEMENTED; + + if (! ((url[0] == 'h' || url[0] == 'H') + && (url[1] == 't' || url[1] == 'T') + && (url[2] == 't' || url[2] == 'T') + && (url[3] == 'p' || url[3] == 'P') + && url[4] == ':' && url[5] == '/' && url[6] == '/' + && (path = strchr(url + 7, '/')))) + return GETDNS_RETURN_NOT_IMPLEMENTED; + + path_len = strlen(path); + if (! ( path_len >= 5 + && path[path_len - 4] == '.' + && (path[path_len - 3] == 'x' || path[path_len - 3] == 'X') + && (path[path_len - 2] == 'm' || path[path_len - 2] == 'M') + && (path[path_len - 1] == 'l' || path[path_len - 1] == 'L'))) + return GETDNS_RETURN_NOT_IMPLEMENTED; + + context->root_anchor_url = url; + dispatch_updated(context, GETDNS_CONTEXT_CODE_TRUST_ANCHOR_URL); + return GETDNS_RETURN_GOOD; +} + +getdns_return_t +getdns_context_get_trust_anchor_url( + getdns_context *context, const char *zone, const char **url) +{ + if (!context || !url) + return GETDNS_RETURN_INVALID_PARAMETER; + + if (zone && (zone[0] != '.' || zone[1] != '\0')) + return GETDNS_RETURN_NOT_IMPLEMENTED; + + *url = context && context->root_anchor_url + ? context->root_anchor_url + : _getdns_default_root_anchor_url; + return GETDNS_RETURN_GOOD; +} + +getdns_return_t +getdns_context_set_trust_anchor_verify_CA( + getdns_context *context, const char *zone, const char *verify_CA) +{ + if (!context || !verify_CA) + return GETDNS_RETURN_INVALID_PARAMETER; + + if (zone && (zone[0] != '.' || zone[1] != '\0')) + return GETDNS_RETURN_NOT_IMPLEMENTED; + + context->root_anchor_verify_CA = verify_CA; + dispatch_updated(context, GETDNS_CONTEXT_CODE_TRUST_ANCHOR_VERIFY_CA); + return GETDNS_RETURN_GOOD; +} + +getdns_return_t +getdns_context_get_trust_anchor_verify_CA( + getdns_context *context, const char *zone, const char **verify_CA) +{ + if (!verify_CA) + return GETDNS_RETURN_INVALID_PARAMETER; + + if (zone && (zone[0] != '.' || zone[1] != '\0')) + return GETDNS_RETURN_NOT_IMPLEMENTED; + + *verify_CA = context && context->root_anchor_verify_CA + ? context->root_anchor_verify_CA + : _getdns_default_root_anchor_verify_CA; + return GETDNS_RETURN_GOOD; +} + +getdns_return_t +getdns_context_set_trust_anchor_verify_email( + getdns_context *context, const char *zone, const char *verify_email) +{ + if (!context || !verify_email) + return GETDNS_RETURN_INVALID_PARAMETER; + + if (zone && (zone[0] != '.' || zone[1] != '\0')) + return GETDNS_RETURN_NOT_IMPLEMENTED; + + context->root_anchor_verify_email = verify_email; + dispatch_updated(context, GETDNS_CONTEXT_CODE_TRUST_ANCHOR_VERIFY_EMAIL); + return GETDNS_RETURN_GOOD; +} + +getdns_return_t +getdns_context_get_trust_anchor_verify_email( + getdns_context *context, const char *zone, const char **verify_email) +{ + if (!verify_email) + return GETDNS_RETURN_INVALID_PARAMETER; + + if (zone && (zone[0] != '.' || zone[1] != '\0')) + return GETDNS_RETURN_NOT_IMPLEMENTED; + + *verify_email = context && context->root_anchor_verify_email + ? context->root_anchor_verify_email + : _getdns_default_root_anchor_verify_email; + return GETDNS_RETURN_GOOD; +} + + /* context.c */ diff --git a/src/context.h b/src/context.h index 8690bba7..8225d10b 100644 --- a/src/context.h +++ b/src/context.h @@ -48,6 +48,8 @@ #ifdef HAVE_MDNS_SUPPORT #include "util/lruhash.h" #endif +#include "rr-iter.h" +#include "anchor.h" struct getdns_dns_req; struct ub_ctx; @@ -92,6 +94,16 @@ typedef enum getdns_conn_state { GETDNS_CONN_BACKOFF } getdns_conn_state_t; +typedef enum getdns_tasrc { + GETDNS_TASRC_NONE, + GETDNS_TASRC_ZONE, + GETDNS_TASRC_APP, + GETDNS_TASRC_FETCHING, + GETDNS_TASRC_XML, + GETDNS_TASRC_XML_UPDATE, + GETDNS_TASRC_FAILED +} getdns_tasrc; + typedef enum getdns_tsig_algo { GETDNS_NO_TSIG = 0, /* Do not use tsig */ GETDNS_HMAC_MD5 = 1, /* 128 bits */ @@ -103,6 +115,7 @@ typedef enum getdns_tsig_algo { GETDNS_HMAC_SHA512 = 7 } getdns_tsig_algo; + typedef struct getdns_tsig_info { getdns_tsig_algo alg; const char *name; @@ -256,6 +269,44 @@ typedef struct getdns_upstreams { getdns_upstream upstreams[]; } getdns_upstreams; +typedef enum tas_state { + TAS_LOOKUP_ADDRESSES = 0, + TAS_WRITE_GET_XML, + TAS_READ_XML_HDR, + TAS_READ_XML_DOC, + TAS_WRITE_GET_PS7, + TAS_READ_PS7_HDR, + TAS_READ_PS7_DOC, + TAS_DONE, + TAS_RETRY, + TAS_RETRY_GET_PS7, + TAS_RETRY_PS7_HDR, + TAS_RETRY_PS7_DOC, + TAS_RETRY_DONE +} tas_state; + +typedef enum _getdns_property { + PROP_INHERIT = 0, + PROP_UNKNOWN = 1, + PROP_UNABLE = 2, + PROP_ABLE = 3 +} _getdns_property; + +typedef struct tas_connection { + getdns_eventloop *loop; + getdns_network_req *req; + _getdns_rrset_spc rrset_spc; + _getdns_rrset *rrset; + _getdns_rrtype_iter rr_spc; + _getdns_rrtype_iter *rr; + int fd; + getdns_eventloop_event event; + tas_state state; + getdns_tcp_state tcp; + char *http; + getdns_bindata xml; +} tas_connection; + struct getdns_context { /* Context values */ getdns_resolution_t resolution_type; @@ -277,8 +328,23 @@ struct getdns_context { const uint8_t *suffixes; /* Length of all suffixes in the suffix buffer */ size_t suffixes_len; + uint8_t *trust_anchors; size_t trust_anchors_len; + getdns_tasrc trust_anchors_source; + + tas_connection a; + tas_connection aaaa; + uint8_t tas_hdr_spc[512]; + + _getdns_property can_write_appdata; + + const char *root_anchor_url; + const char *root_anchor_verify_CA; + const char *root_anchor_verify_email; + + _getdns_ksks root_ksk; + getdns_upstreams *upstreams; uint16_t limit_outstanding_queries; uint32_t dnssec_allowed_skew; @@ -373,6 +439,18 @@ struct getdns_context { unsigned return_call_reporting : 1; uint16_t specify_class; + /* + * Context for doing system queries. + * For example to resolve data.iana.org or to resolver the addresses + * of upstreams without specified addresses. + */ + getdns_context *sys_ctxt; + + /* List of dnsreqs that want to be notified when we have fetched a + * trust anchor from data.iana.org. + */ + getdns_dns_req *ta_notify; + /* * state data used to detect changes to the system config files */ @@ -471,6 +549,15 @@ void _getdns_upstreams_dereference(getdns_upstreams *upstreams); void _getdns_upstream_shutdown(getdns_upstream *upstream); +FILE *_getdns_context_get_priv_fp(getdns_context *context, const char *fn); +uint8_t *_getdns_context_get_priv_file(getdns_context *context, + const char *fn, uint8_t *buf, size_t buf_len, size_t *file_sz); + +int _getdns_context_write_priv_file(getdns_context *context, + const char *fn, getdns_bindata *content); + +int _getdns_context_can_write_appdata(getdns_context *context); + void _getdns_upstream_reset(getdns_upstream *upstream); #endif /* _GETDNS_CONTEXT_H_ */ diff --git a/src/debug.h b/src/debug.h index f779840b..9d181140 100644 --- a/src/debug.h +++ b/src/debug.h @@ -47,41 +47,41 @@ #ifdef GETDNS_ON_WINDOWS #define DEBUG_ON(...) do { \ - struct timeval tv; \ - struct tm tm; \ - char buf[10]; \ - time_t tsec; \ + struct timeval tv_dEbUgSyM; \ + struct tm tm_dEbUgSyM; \ + char buf_dEbUgSyM[10]; \ + time_t tsec_dEbUgSyM; \ \ - gettimeofday(&tv, NULL); \ - tsec = (time_t) tv.tv_sec; \ - gmtime_s(&tm, (const time_t *) &tsec); \ - strftime(buf, 10, "%H:%M:%S", &tm); \ - fprintf(stderr, "[%s.%.6d] ", buf, (int)tv.tv_usec); \ + gettimeofday(&tv_dEbUgSyM, NULL); \ + tsec = (time_t) tv_dEbUgSyM.tv_sec; \ + gmtime_s(&tm_dEbUgSyM, (const time_t *) &tsec_dEbUgSyM); \ + strftime(buf_dEbUgSyM, 10, "%H:%M:%S", &tm_dEbUgSyM); \ + fprintf(stderr, "[%s.%.6d] ", buf_dEbUgSyM, (int)tv_dEbUgSyM.tv_usec); \ fprintf(stderr, __VA_ARGS__); \ } while (0) #else #define DEBUG_ON(...) do { \ - struct timeval tv; \ - struct tm tm; \ - char buf[10]; \ + struct timeval tv_dEbUgSyM; \ + struct tm tm_dEbUgSyM; \ + char buf_dEbUgSyM[10]; \ \ - gettimeofday(&tv, NULL); \ - gmtime_r(&tv.tv_sec, &tm); \ - strftime(buf, 10, "%H:%M:%S", &tm); \ - fprintf(stderr, "[%s.%.6d] ", buf, (int)tv.tv_usec); \ + gettimeofday(&tv_dEbUgSyM, NULL); \ + gmtime_r(&tv_dEbUgSyM.tv_sec, &tm_dEbUgSyM); \ + strftime(buf_dEbUgSyM, 10, "%H:%M:%S", &tm_dEbUgSyM); \ + fprintf(stderr, "[%s.%.6d] ", buf_dEbUgSyM, (int)tv_dEbUgSyM.tv_usec); \ fprintf(stderr, __VA_ARGS__); \ } while (0) #endif #define DEBUG_NL(...) do { \ - struct timeval tv; \ - struct tm tm; \ - char buf[10]; \ + struct timeval tv_dEbUgSyM; \ + struct tm tm_dEbUgSyM; \ + char buf_dEbUgSyM[10]; \ \ - gettimeofday(&tv, NULL); \ - gmtime_r(&tv.tv_sec, &tm); \ - strftime(buf, 10, "%H:%M:%S", &tm); \ - fprintf(stderr, "[%s.%.6d] ", buf, (int)tv.tv_usec); \ + gettimeofday(&tv_dEbUgSyM, NULL); \ + gmtime_r(&tv_dEbUgSyM.tv_sec, &tm_dEbUgSyM); \ + strftime(buf_dEbUgSyM, 10, "%H:%M:%S", &tm_dEbUgSyM); \ + fprintf(stderr, "[%s.%.6d] ", buf_dEbUgSyM, (int)tv_dEbUgSyM.tv_usec); \ fprintf(stderr, __VA_ARGS__); \ fprintf(stderr, "\n"); \ } while (0) @@ -162,14 +162,25 @@ static inline void debug_req(const char *msg, getdns_network_req *netreq) #define DEBUG_MDNS(...) DEBUG_OFF(__VA_ARGS__) #endif +#if defined(ANCHOR_DEBUG) && ANCHOR_DEBUG +#include +#define DEBUG_ANCHOR(...) DEBUG_ON(__VA_ARGS__) +#else +#define DEBUG_ANCHOR(...) DEBUG_OFF(__VA_ARGS__) +#endif + #if (defined(REQ_DEBUG) && REQ_DEBUG) || \ (defined(SCHED_DEBUG) && SCHED_DEBUG) || \ (defined(STUB_DEBUG) && STUB_DEBUG) || \ (defined(DAEMON_DEBUG) && DAEMON_DEBUG) || \ (defined(SEC_DEBUG) && SEC_DEBUG) || \ (defined(SERVER_DEBUG) && SERVER_DEBUG) || \ - (defined(MDNS_DEBUG) && MDNS_DEBUG) + (defined(MDNS_DEBUG) && MDNS_DEBUG) || \ + (defined(ANCHOR_DEBUG) && ANCHOR_DEBUG) #define DEBUGGING 1 +static inline int +_getdns_ERR_print_errors_cb_f(const char *str, size_t len, void *u) +{ DEBUG_ON("%.*s (u: %p)\n", (int)len, str, u); return 1; } #endif #endif diff --git a/src/dnssec.c b/src/dnssec.c index 005604b9..3a2fc866 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -209,6 +209,7 @@ #include "dict.h" #include "list.h" #include "util/val_secalgo.h" +#include "anchor.h" #define SIGNATURE_VERIFIED 0x10000 #define NSEC3_ITERATION_COUNT_HIGH 0x20000 @@ -2544,8 +2545,13 @@ static int chain_node_get_trusted_keys( } else return GETDNS_DNSSEC_BOGUS; - if (GETDNS_DNSSEC_SECURE != (s = chain_node_get_trusted_keys( - mf, now, skew, node->parent, ta, keys))) + s = chain_node_get_trusted_keys(mf, now, skew, node->parent, ta, keys); + /* Set dnssec status on root DNSKEY request (for TA management) */ + if (!node->parent && node->dnskey_req && + node->dnskey.name && *node->dnskey.name == 0) + node->dnskey_req->dnssec_status = s; + + if (s != GETDNS_DNSSEC_SECURE) return s; /* keys is an authenticated dnskey rrset always now (i.e. ZSK) */ @@ -2750,6 +2756,33 @@ static void chain_set_netreq_dnssec_status(chain_head *chain, _getdns_rrset_iter } } } + +static void chain_clear_netreq_dnssec_status(chain_head *chain) +{ + chain_head *head; + size_t node_count; + chain_node *node; + + /* The netreq status is the worst for any head */ + for (head = chain; head; head = head->next) { + if (!head->netreq) + continue; + + head->netreq->dnssec_status = GETDNS_DNSSEC_INDETERMINATE; + for ( node_count = head->node_count, node = head->parent + ; node && node_count ; node_count--, node = node->parent ) { + + node->ds_signer = -1; + node->dnskey_signer = -1; + + if ( ! node->parent && node->dnskey_req + && node->dnskey.name && !*node->dnskey.name) { + node->dnskey_req->dnssec_status = + GETDNS_DNSSEC_INDETERMINATE; + } + } + } +} #endif /* The DNSSEC status of all heads for a chain structure is evaluated by @@ -2997,6 +3030,22 @@ static void append_empty_ds2val_chain_list( if (_getdns_list_append_this_dict(val_chain_list, rr_dict)) getdns_dict_destroy(rr_dict); } +static inline chain_node *_to_the_root(chain_node *node) +{ + while (node->parent) node = node->parent; + return node; +} + +int _getdns_bogus(getdns_dns_req *dnsreq) +{ + getdns_network_req **netreq_p, *netreq; + + for (netreq_p = dnsreq->netreqs; (netreq = *netreq_p) ; netreq_p++) { + if (netreq->dnssec_status == GETDNS_DNSSEC_BOGUS) + return 1; + } + return 0; +} static void check_chain_complete(chain_head *chain) { @@ -3017,23 +3066,33 @@ static void check_chain_complete(chain_head *chain) dnsreq = chain->netreq->owner; context = dnsreq->context; + if (dnsreq->waiting_for_ta) { + getdns_dns_req **d; + + for (d = &context->ta_notify; *d; d = &(*d)->ta_notify) { + if (*d == dnsreq) { + *d = dnsreq->ta_notify; + dnsreq->ta_notify = NULL; + break; + } + } + + } else { + if (context->trust_anchors_source == GETDNS_TASRC_FETCHING) { + dnsreq->waiting_for_ta = 1; + dnsreq->ta_notify = context->ta_notify; + context->ta_notify = dnsreq; + return; + } + } #ifdef STUB_NATIVE_DNSSEC - /* Perform validation only on GETDNS_RESOLUTION_STUB (unbound_id == -1) - * Or when asked for the validation chain (to identify the RRSIGs that - * signed the RRSETs, so that only those will be included in the - * validation chain) - * In any case we must have a trust anchor. - */ - if (( chain->netreq->unbound_id == -1 - || dnsreq->dnssec_return_validation_chain) - && context->trust_anchors) + if (context->trust_anchors) chain_set_netreq_dnssec_status(chain,_getdns_rrset_iter_init(&tas_iter, context->trust_anchors, context->trust_anchors_len, SECTION_ANSWER)); #else - if (dnsreq->dnssec_return_validation_chain - && context->trust_anchors) + if (context->trust_anchors) (void) chain_validate_dnssec(priv_getdns_context_mf(context), time(NULL), context->dnssec_allowed_skew, @@ -3042,10 +3101,52 @@ static void check_chain_complete(chain_head *chain) , context->trust_anchors_len , SECTION_ANSWER)); #endif + if (context->trust_anchors_source == GETDNS_TASRC_XML) { + if ((head = chain) && (node = _to_the_root(head->parent)) && + node->dnskey.name && *node->dnskey.name == 0) + _getdns_context_update_root_ksk(context,&node->dnskey); + + } else if (_getdns_bogus(dnsreq)) { + DEBUG_ANCHOR("Request was bogus!\n"); + + if ((head = chain) && (node = _to_the_root(head->parent)) + && node->dnskey.name && *node->dnskey.name == 0 + && node->dnskey_req->dnssec_status == GETDNS_DNSSEC_BOGUS){ + + DEBUG_ANCHOR("root DNSKEY set was bogus!\n"); + if (!dnsreq->waiting_for_ta) { + uint64_t now = 0; + + dnsreq->waiting_for_ta = 1; + _getdns_context_equip_with_anchor( + context, &now); + + if (context->trust_anchors_source + == GETDNS_TASRC_XML) { + chain_clear_netreq_dnssec_status(chain); + check_chain_complete(chain); + return; + } + _getdns_start_fetching_ta( + context, dnsreq->loop); + + if (dnsreq->waiting_for_ta && + context->trust_anchors_source + == GETDNS_TASRC_FETCHING) { + + chain_clear_netreq_dnssec_status(chain); + dnsreq->ta_notify = context->ta_notify; + context->ta_notify = dnsreq; + return; + } + } + } + } + #ifdef DNSSEC_ROADBLOCK_AVOIDANCE if ( dnsreq->dnssec_roadblock_avoidance && !dnsreq->avoid_dnssec_roadblocks - && dnsreq->netreqs[0]->dnssec_status == GETDNS_DNSSEC_BOGUS) { + && _getdns_bogus(dnsreq)) { getdns_network_req **netreq_p, *netreq; uint64_t now_ms = 0; @@ -3095,6 +3196,7 @@ static void check_chain_complete(chain_head *chain) return; } #endif + dnsreq->waiting_for_ta = 0; val_chain_list = dnsreq->dnssec_return_validation_chain ? getdns_list_create_with_context(context) : NULL; @@ -3161,6 +3263,45 @@ static void check_chain_complete(chain_head *chain) _getdns_call_user_callback(dnsreq, response_dict); } +void _getdns_ta_notify_dnsreqs(getdns_context *context) +{ + getdns_dns_req **dnsreq_p, *dnsreq = NULL; + uint64_t now_ms = 0; + + assert(context); + + if (context->trust_anchors_source == GETDNS_TASRC_NONE || + context->trust_anchors_source == GETDNS_TASRC_FETCHING) + return; + + dnsreq_p = &context->ta_notify; + while ((dnsreq = *dnsreq_p)) { + assert(dnsreq->waiting_for_ta); + + if (dnsreq->chain) + check_chain_complete(dnsreq->chain); + else { + getdns_network_req *netreq, **netreq_p; + int r = GETDNS_RETURN_GOOD; + + (void) _getdns_context_prepare_for_resolution(context, 0); + + *dnsreq_p = dnsreq->ta_notify; + for ( netreq_p = dnsreq->netreqs + ; !r && (netreq = *netreq_p) + ; netreq_p++ ) { + + if (!(r = _getdns_submit_netreq(netreq, &now_ms))) + continue; + if (r == DNS_REQ_FINISHED) + break; + _getdns_netreq_change_state(netreq, NET_REQ_ERRORED); + } + } + assert(*dnsreq_p != dnsreq); + } +} + void _getdns_validation_chain_timeout(getdns_dns_req *dnsreq) { chain_head *head = dnsreq->chain, *next; @@ -3243,6 +3384,7 @@ void _getdns_get_validation_chain(getdns_dns_req *dnsreq) if (dnsreq->avoid_dnssec_roadblocks && chain->lock == 0) ; /* pass */ + else for (netreq_p = dnsreq->netreqs; (netreq = *netreq_p) ; netreq_p++) { if (! netreq->response || netreq->response_len < GLDNS_HEADER_SIZE @@ -3253,7 +3395,10 @@ void _getdns_get_validation_chain(getdns_dns_req *dnsreq) netreq->dnssec_status = GETDNS_DNSSEC_INSECURE; continue; - } + + } else if (netreq->unbound_id != -1) + netreq->dnssec_status = GETDNS_DNSSEC_INDETERMINATE; + add_pkt2val_chain( &dnsreq->my_mf, &chain , netreq->response, netreq->response_len , netreq diff --git a/src/dnssec.h b/src/dnssec.h index b0334d52..ef1b4747 100644 --- a/src/dnssec.h +++ b/src/dnssec.h @@ -48,6 +48,7 @@ void _getdns_get_validation_chain(getdns_dns_req *dns_req); void _getdns_cancel_validation_chain(getdns_dns_req *dns_req); void _getdns_validation_chain_timeout(getdns_dns_req *dns_req); +void _getdns_ta_notify_dnsreqs(getdns_context *context); uint16_t _getdns_parse_ta_file(time_t *ta_mtime, gldns_buffer *gbuf); @@ -66,6 +67,8 @@ static inline int _dnssec_rdata_to_canonicalize(uint16_t rr_type) || rr_type == GLDNS_RR_TYPE_DNAME || rr_type == GLDNS_RR_TYPE_RRSIG; } +int _getdns_bogus(getdns_dns_req *dns_req); + #endif /* dnssec.h */ diff --git a/src/extension/libuv.c b/src/extension/libuv.c index ec9257be..f1b7114e 100644 --- a/src/extension/libuv.c +++ b/src/extension/libuv.c @@ -172,6 +172,9 @@ getdns_libuv_timeout_cb(uv_timer_t *timer, int status) #endif { getdns_eventloop_event *el_ev = (getdns_eventloop_event *)timer->data; +#ifndef HAVE_NEW_UV_TIMER_CB + (void)status; +#endif assert(el_ev->timeout_cb); DEBUG_UV("enter libuv_timeout_cb(el_ev = %p, el_ev->ev = %p)\n" , el_ev, el_ev->ev); diff --git a/src/extension/poll_eventloop.c b/src/extension/poll_eventloop.c index e796de10..b4f204b1 100644 --- a/src/extension/poll_eventloop.c +++ b/src/extension/poll_eventloop.c @@ -296,7 +296,8 @@ poll_read_cb(int fd, getdns_eventloop_event *event) (void)fd; #endif DEBUG_SCHED( "%s(fd: %d, event: %p)\n", __FUNC__, fd, (void *)event); - event->read_cb(event->userarg); + if (event && event->read_cb) + event->read_cb(event->userarg); } static void @@ -306,14 +307,16 @@ poll_write_cb(int fd, getdns_eventloop_event *event) (void)fd; #endif DEBUG_SCHED( "%s(fd: %d, event: %p)\n", __FUNC__, fd, (void *)event); - event->write_cb(event->userarg); + if (event && event->write_cb) + event->write_cb(event->userarg); } static void poll_timeout_cb(getdns_eventloop_event *event) { DEBUG_SCHED( "%s(event: %p)\n", __FUNC__, (void *)event); - event->timeout_cb(event->userarg); + if (event && event->timeout_cb) + event->timeout_cb(event->userarg); } static void diff --git a/src/extension/select_eventloop.h b/src/extension/select_eventloop.h index e830a2ac..88f82a14 100644 --- a/src/extension/select_eventloop.h +++ b/src/extension/select_eventloop.h @@ -34,6 +34,7 @@ #include "config.h" #include "getdns/getdns.h" #include "getdns/getdns_extra.h" +#include "types-internal.h" /* No more than select's capability queries can be outstanding, * The number of outstanding timeouts should be less or equal then diff --git a/src/general.c b/src/general.c index 2420a47c..d8a2bdf8 100644 --- a/src/general.c +++ b/src/general.c @@ -54,6 +54,7 @@ #include "dict.h" #include "mdns.h" #include "debug.h" +#include "anchor.h" void _getdns_call_user_callback(getdns_dns_req *dnsreq, getdns_dict *response) { @@ -213,13 +214,15 @@ _getdns_check_dns_req_complete(getdns_dns_req *dns_req) #endif #ifdef STUB_NATIVE_DNSSEC - || (dns_req->context->resolution_type == GETDNS_RESOLUTION_STUB + || ( dns_req->context->resolution_type == GETDNS_RESOLUTION_STUB && !dns_req->avoid_dnssec_roadblocks && (dns_req->dnssec_return_status || dns_req->dnssec_return_only_secure || dns_req->dnssec_return_all_statuses )) #endif + || ( dns_req->context->resolution_type == GETDNS_RESOLUTION_RECURSING + && _getdns_bogus(dns_req)) )) { /* Reschedule timeout for this DNS request */ @@ -235,6 +238,7 @@ _getdns_check_dns_req_complete(getdns_dns_req *dns_req) #if defined(REQ_DEBUG) && REQ_DEBUG debug_req("getting validation chain for ", *dns_req->netreqs); #endif + DEBUG_ANCHOR("Valchain lookup\n"); _getdns_get_validation_chain(dns_req); } else _getdns_call_user_callback( @@ -570,11 +574,6 @@ getdns_general_ns(getdns_context *context, getdns_eventloop *loop, if (extensions && (r = validate_extensions(extensions))) return r; - /* Set up the context assuming we won't use the specified namespaces. - This is (currently) identical to setting up a pure DNS namespace */ - if ((r = _getdns_context_prepare_for_resolution(context, 0))) - return r; - /* create the request */ if (!(req = _getdns_dns_req_new( context, loop, name, request_type, extensions, &now_ms))) @@ -590,7 +589,28 @@ getdns_general_ns(getdns_context *context, getdns_eventloop *loop, _getdns_context_track_outbound_request(req); - if (!usenamespaces) + if (req->dnssec_extension_set) { + if (context->trust_anchors_source == GETDNS_TASRC_XML_UPDATE) + _getdns_start_fetching_ta(context, loop); + + else if (context->trust_anchors_source == GETDNS_TASRC_NONE) { + _getdns_context_equip_with_anchor(context, &now_ms); + if (context->trust_anchors_source == GETDNS_TASRC_NONE) { + _getdns_start_fetching_ta(context, loop); + } + } + } + if (!usenamespaces) { + if (context->trust_anchors_source == GETDNS_TASRC_FETCHING + && context->resolution_type == GETDNS_RESOLUTION_RECURSING + && context->resolution_type != context->resolution_type_set) { + req->waiting_for_ta = 1; + req->ta_notify = context->ta_notify; + context->ta_notify = req; + return GETDNS_RETURN_GOOD; + } + (void) _getdns_context_prepare_for_resolution(context, 0); + /* issue all network requests */ for ( netreq_p = req->netreqs ; !r && (netreq = *netreq_p) @@ -605,7 +625,7 @@ getdns_general_ns(getdns_context *context, getdns_eventloop *loop, } } - else for (i = 0; i < context->namespace_count; i++) { + } else for (i = 0; i < context->namespace_count; i++) { if (context->namespaces[i] == GETDNS_NAMESPACE_LOCALNAMES) { if (!(r = _getdns_context_local_namespace_resolve( @@ -639,6 +659,15 @@ getdns_general_ns(getdns_context *context, getdns_eventloop *loop, } #endif /* HAVE_MDNS_SUPPORT */ } else if (context->namespaces[i] == GETDNS_NAMESPACE_DNS) { + if (context->trust_anchors_source == GETDNS_TASRC_FETCHING + && context->resolution_type == GETDNS_RESOLUTION_RECURSING + && context->resolution_type != context->resolution_type_set) { + req->waiting_for_ta = 1; + req->ta_notify = context->ta_notify; + context->ta_notify = req; + return GETDNS_RETURN_GOOD; + } + (void) _getdns_context_prepare_for_resolution(context, 0); /* TODO: We will get a good return code here even if the name is not found (NXDOMAIN). We should consider diff --git a/src/getdns/getdns_extra.h.in b/src/getdns/getdns_extra.h.in index 45f354cd..c042a875 100644 --- a/src/getdns/getdns_extra.h.in +++ b/src/getdns/getdns_extra.h.in @@ -79,6 +79,13 @@ extern "C" { #define GETDNS_CONTEXT_CODE_TLS_BACKOFF_TIME_TEXT "Change related to getdns_context_set_tls_backoff_time" #define GETDNS_CONTEXT_CODE_TLS_CONNECTION_RETRIES 624 #define GETDNS_CONTEXT_CODE_TLS_CONNECTION_RETRIES_TEXT "Change related to getdns_context_set_tls_connection_retries" + +#define GETDNS_CONTEXT_CODE_TRUST_ANCHOR_URL 625 +#define GETDNS_CONTEXT_CODE_TRUST_ANCHOR_URL_TEXT "Change related to getdns_context_set_trust_anchor_url" +#define GETDNS_CONTEXT_CODE_TRUST_ANCHOR_VERIFY_CA 626 +#define GETDNS_CONTEXT_CODE_TRUST_ANCHOR_VERIFY_CA_TEXT "Change related to getdns_context_set_trust_anchor_verify_ca" +#define GETDNS_CONTEXT_CODE_TRUST_ANCHOR_VERIFY_EMAIL 627 +#define GETDNS_CONTEXT_CODE_TRUST_ANCHOR_VERIFY_EMAIL_TEXT "Change related to getdns_context_set_trust_anchor_verify_email" /** @} */ @@ -537,6 +544,28 @@ getdns_return_t getdns_context_set_logfunc(getdns_context *context, void *userarg, uint64_t system, getdns_loglevel_type level, getdns_logfunc_type func); +/** + * + */ +getdns_return_t +getdns_context_set_trust_anchor_url( + getdns_context *context, const char *zone, const char *url); + +/** + * + */ +getdns_return_t +getdns_context_set_trust_anchor_verify_CA( + getdns_context *context, const char *zone, const char *verify_CA); + +/** + * + */ +getdns_return_t +getdns_context_set_trust_anchor_verify_email( + getdns_context *context, const char *zone, const char *verify_email); + + /** * Get the current resolution type setting from this context. * @see getdns_context_set_resolution_type @@ -902,6 +931,28 @@ getdns_return_t getdns_context_get_update_callback(getdns_context *context, void **userarg, void (**value) (getdns_context *, getdns_context_code_t, void *)); +/** + * + */ +getdns_return_t +getdns_context_get_trust_anchor_url( + getdns_context *context, const char *zone, const char **url); + +/** + * + */ +getdns_return_t +getdns_context_get_trust_anchor_verify_CA( + getdns_context *context, const char *zone, const char **verify_CA); + +/** + * + */ +getdns_return_t +getdns_context_get_trust_anchor_verify_email( + getdns_context *context, const char *zone, const char **verify_email); + + /** @} */ diff --git a/src/gldns/keyraw.c b/src/gldns/keyraw.c index 3f6f1a20..365d6a0e 100644 --- a/src/gldns/keyraw.c +++ b/src/gldns/keyraw.c @@ -95,7 +95,7 @@ gldns_rr_dnskey_key_size_raw(const unsigned char* keydata, } } -uint16_t gldns_calc_keytag_raw(uint8_t* key, size_t keysize) +uint16_t gldns_calc_keytag_raw(const uint8_t* key, size_t keysize) { if(keysize < 4) { return 0; diff --git a/src/gldns/keyraw.h b/src/gldns/keyraw.h index 60e2bfde..3d19e96f 100644 --- a/src/gldns/keyraw.h +++ b/src/gldns/keyraw.h @@ -44,7 +44,7 @@ size_t gldns_rr_dnskey_key_size_raw(const unsigned char *keydata, * \param[in] keysize length of key data. * \return the keytag */ -uint16_t gldns_calc_keytag_raw(uint8_t* key, size_t keysize); +uint16_t gldns_calc_keytag_raw(const uint8_t* key, size_t keysize); #if GLDNS_BUILD_CONFIG_HAVE_SSL /** diff --git a/src/libgetdns.symbols b/src/libgetdns.symbols index 36262fce..950a72d9 100644 --- a/src/libgetdns.symbols +++ b/src/libgetdns.symbols @@ -33,6 +33,9 @@ getdns_context_get_tls_authentication getdns_context_get_tls_backoff_time getdns_context_get_tls_connection_retries getdns_context_get_tls_query_padding_blocksize +getdns_context_get_trust_anchor_url +getdns_context_get_trust_anchor_verify_CA +getdns_context_get_trust_anchor_verify_email getdns_context_get_update_callback getdns_context_get_upstream_recursive_servers getdns_context_process_async @@ -67,6 +70,9 @@ getdns_context_set_tls_authentication getdns_context_set_tls_backoff_time getdns_context_set_tls_connection_retries getdns_context_set_tls_query_padding_blocksize +getdns_context_set_trust_anchor_url +getdns_context_set_trust_anchor_verify_CA +getdns_context_set_trust_anchor_verify_email getdns_context_set_update_callback getdns_context_set_upstream_recursive_servers getdns_context_set_use_threads diff --git a/src/request-internal.c b/src/request-internal.c index 15f97179..c5a5b0a9 100644 --- a/src/request-internal.c +++ b/src/request-internal.c @@ -914,6 +914,7 @@ _getdns_dns_req_new(getdns_context *context, getdns_eventloop *loop, dnssec_return_full_validation_chain; result->dnssec_return_validation_chain = dnssec_return_validation_chain || dnssec_return_full_validation_chain; + result->dnssec_extension_set = dnssec_extension_set; result->edns_cookies = edns_cookies; #ifdef DNSSEC_ROADBLOCK_AVOIDANCE result->dnssec_roadblock_avoidance = dnssec_roadblock_avoidance; @@ -940,8 +941,10 @@ _getdns_dns_req_new(getdns_context *context, getdns_eventloop *loop, result->upstreams->referenced++; result->finished_next = NULL; + result->ta_notify = NULL; result->freed = NULL; result->validating = 0; + result->waiting_for_ta = 0; result->is_dns_request = 1; result->request_timed_out = 0; result->chain = NULL; diff --git a/src/stub.c b/src/stub.c index 8ca9a6dc..9c2a2cee 100644 --- a/src/stub.c +++ b/src/stub.c @@ -66,18 +66,6 @@ #include "general.h" #include "pubkey-pinning.h" -#ifdef USE_WINSOCK -typedef u_short sa_family_t; -#define _getdns_EWOULDBLOCK (WSAGetLastError() == WSATRY_AGAIN ||\ - WSAGetLastError() == WSAEWOULDBLOCK) -#define _getdns_EINPROGRESS (WSAGetLastError() == WSAEINPROGRESS) -#define _getdns_EMFILE (WSAGetLastError() == WSAEMFILE) -#else -#define _getdns_EWOULDBLOCK (errno == EAGAIN || errno == EWOULDBLOCK) -#define _getdns_EINPROGRESS (errno == EINPROGRESS) -#define _getdns_EMFILE (errno == EMFILE) -#endif - /* WSA TODO: * STUB_TCP_WOULDBLOCK added to deal with edge triggered event loops (versus * level triggered). See also lines containing WSA TODO below... diff --git a/src/test/tpkg/100-compile.tpkg/100-compile.pre b/src/test/tpkg/100-compile.tpkg/100-compile.pre index 6b76edba..ea1cacee 100644 --- a/src/test/tpkg/100-compile.tpkg/100-compile.pre +++ b/src/test/tpkg/100-compile.tpkg/100-compile.pre @@ -25,4 +25,4 @@ done rm -fr "${BUILDDIR}/build" mkdir "${BUILDDIR}/build" cd "${BUILDDIR}/build" -"${SRCROOT}/configure" $* --prefix "${BUILDDIR}/install" +"${SRCROOT}/configure" $* --prefix "${BUILDDIR}/install" --enable-debug-anchor diff --git a/src/test/tpkg/310-dependencies.tpkg/310-dependencies.test b/src/test/tpkg/310-dependencies.tpkg/310-dependencies.test index 985c420d..ef88d699 100644 --- a/src/test/tpkg/310-dependencies.tpkg/310-dependencies.test +++ b/src/test/tpkg/310-dependencies.tpkg/310-dependencies.test @@ -15,6 +15,11 @@ find . -type f -name "Makefile.in" -print0 | xargs -0 rm -f && ( ) ( cd "${BUILDDIR}/build-event-loops" + if test "`hostname`" != "bonobo" + then + echo Sorry, running dependency test on bonobo only + exit 0 + fi if ! ./config.status --config | grep -q 'enable-all-drafts.*--with-libevent.*--with-libev.*--with-libuv' then echo Skipping because not covering enough code diff --git a/src/test/tpkg/330-event-loops-unit-tests.tpkg/330-event-loops-unit-tests.test b/src/test/tpkg/330-event-loops-unit-tests.tpkg/330-event-loops-unit-tests.test index 31a7dd1f..10ab6ded 100644 --- a/src/test/tpkg/330-event-loops-unit-tests.tpkg/330-event-loops-unit-tests.test +++ b/src/test/tpkg/330-event-loops-unit-tests.tpkg/330-event-loops-unit-tests.test @@ -5,10 +5,43 @@ [ -f .tpkg.var.test ] && source .tpkg.var.test cd "${BUILDDIR}/build-event-loops" -if make -j 4 test +make -j 4 test +if test -e "${BUILDDIR}/build-event-loops/src/test/fails" then - if test -e "${BUILDDIR}/build-event-loops/src/test/fails" + if test -e "${BUILDDIR}/build-event-loops/src/test/check_getdns.failed" then - exit 1 + echo "" + echo "********************" + echo "*** check_getdns ***" + echo "********************" + cat "${BUILDDIR}/build-event-loops/src/test/check_getdns.log" fi + if test -e "${BUILDDIR}/build-event-loops/src/test/check_getdns_event.failed" + then + echo "" + echo "**************************" + echo "*** check_getdns_event ***" + echo "**************************" + cat "${BUILDDIR}/build-event-loops/src/test/check_getdns_event.log" + fi + if test -e "${BUILDDIR}/build-event-loops/src/test/check_getdns_ev.failed" + then + echo "" + echo "***********************" + echo "*** check_getdns_ev ***" + echo "***********************" + cat "${BUILDDIR}/build-event-loops/src/test/check_getdns_ev.log" + fi + if test -e "${BUILDDIR}/build-event-loops/src/test/check_getdns_uv.failed" + then + echo "" + echo "***********************" + echo "*** check_getdns_uv ***" + echo "***********************" + cat "${BUILDDIR}/build-event-loops/src/test/check_getdns_uv.log" + fi + exit 1 +else + exit 0 fi + diff --git a/src/test/tpkg/run-all-lcov.sh b/src/test/tpkg/run-all-lcov.sh index 8a083980..38f4b51a 100755 --- a/src/test/tpkg/run-all-lcov.sh +++ b/src/test/tpkg/run-all-lcov.sh @@ -25,4 +25,4 @@ do done lcov $LCOV_MERGE -o run-all.info genhtml run-all.info --output-directory coverage-html -"${TPKG}" r +"${TPKG}" -n -1 r diff --git a/src/test/tpkg/run-all.sh b/src/test/tpkg/run-all.sh index 2c1a23f8..94a5623a 100755 --- a/src/test/tpkg/run-all.sh +++ b/src/test/tpkg/run-all.sh @@ -17,4 +17,4 @@ do # trap keyboard interrupt (control-c) trap control_c 2 done -"${TPKG}" r +"${TPKG}" -n -1 r diff --git a/src/test/tpkg/run-parallel.sh b/src/test/tpkg/run-parallel.sh index a3ef22e0..1bde085a 100755 --- a/src/test/tpkg/run-parallel.sh +++ b/src/test/tpkg/run-parallel.sh @@ -31,7 +31,7 @@ do fi done echo "${ALL}" >> Makefile -printf '\t"%s" r\n\n' "${TPKG}" >> Makefile +printf '\t"%s" -n -1 r\n\n' "${TPKG}" >> Makefile printf 'clean:\n\t"%s" clean\n\trm -fr build build-stub-only build-event-loops build-static-analysis install scan-build-reports .tpkg.var.master *.info\n\n' "${TPKG}" >> Makefile for P in ${OTHERS} do diff --git a/src/test/tpkg/tpkg b/src/test/tpkg/tpkg index 0eddaedb..3701b723 100755 --- a/src/test/tpkg/tpkg +++ b/src/test/tpkg/tpkg @@ -193,6 +193,8 @@ function usage() { out " -p PRI\tlog using PRI as priority" out " -k\t\tdon't remove test directory when creating/executing a tpkg package" out " -n NUM\tif less than NUM of the tests are passed exit with 1" + out " \t\tOtherwise exit with 0. When NUM is -1, no tests may fail" + out " \t\tOnly valid when running tpkg report" out " \t\tOtherwise exit with 0. Only valid when running tpkg report" out " -b DIR\tuse DIR is a base directory in stead of ." out " -a ARGS\tpass the string ARGS through to the test scripts" @@ -330,11 +332,12 @@ function report() { if [[ $passed -lt $TPKG_PASS ]]; then exit 1 fi - elif [[ $failed -gt 0 ]]; then - exit 1 - else - exit 0 + elif [[ $TPKG_PASS -lt 0 ]]; then + if [[ $failed -gt 0 ]]; then + exit 1 + fi fi + exit 0 } # clone test1 to test2 diff --git a/src/types-internal.h b/src/types-internal.h index 7834fc32..56b7a2f0 100644 --- a/src/types-internal.h +++ b/src/types-internal.h @@ -171,7 +171,7 @@ typedef enum network_req_state_enum /* State for async tcp stub resolving */ typedef struct getdns_tcp_state { - uint8_t *write_buf; + const uint8_t *write_buf; size_t write_buf_len; size_t written; @@ -300,6 +300,7 @@ typedef struct getdns_dns_req { unsigned dnssec_return_all_statuses : 1; unsigned dnssec_return_validation_chain : 1; unsigned dnssec_return_full_validation_chain : 1; + unsigned dnssec_extension_set : 1; #ifdef DNSSEC_ROADBLOCK_AVOIDANCE unsigned dnssec_roadblock_avoidance : 1; unsigned avoid_dnssec_roadblocks : 1; @@ -324,6 +325,7 @@ typedef struct getdns_dns_req { * freed is touched by _getdns_submit_netreq only */ unsigned validating : 1; + unsigned waiting_for_ta : 1; int *freed; /* Validation chain to be canceled when this request is canceled */ @@ -365,6 +367,11 @@ typedef struct getdns_dns_req { */ struct getdns_dns_req *finished_next; + /* Linked list pointer for dns requests, which need to validate DNSSEC + * and are waiting for the root trust-anchors fetch. + */ + struct getdns_dns_req *ta_notify; + /* network requests for this dns request. * The array is terminated with NULL. * diff --git a/src/util-internal.h b/src/util-internal.h index 6f8de235..3f4b9959 100644 --- a/src/util-internal.h +++ b/src/util-internal.h @@ -218,5 +218,18 @@ INLINE uint64_t _getdns_ms_until_expiry2(uint64_t expires, uint64_t *now_ms) return *now_ms >= expires ? 0 : expires - *now_ms; } + +#ifdef USE_WINSOCK +typedef u_short sa_family_t; +#define _getdns_EWOULDBLOCK (WSAGetLastError() == WSATRY_AGAIN ||\ + WSAGetLastError() == WSAEWOULDBLOCK) +#define _getdns_EINPROGRESS (WSAGetLastError() == WSAEINPROGRESS) +#define _getdns_EMFILE (WSAGetLastError() == WSAEMFILE) +#else +#define _getdns_EWOULDBLOCK (errno == EAGAIN || errno == EWOULDBLOCK) +#define _getdns_EINPROGRESS (errno == EINPROGRESS) +#define _getdns_EMFILE (errno == EMFILE) +#endif + #endif /* util-internal.h */ diff --git a/src/util/val_secalgo.h b/src/util/val_secalgo.h index 55bf423e..08f40e83 100644 --- a/src/util/val_secalgo.h +++ b/src/util/val_secalgo.h @@ -45,6 +45,8 @@ #define sec_status_insecure _getdns_sec_status_insecure #define sec_status_unchecked _getdns_sec_status_unchecked #define sec_status_bogus _getdns_sec_status_bogus +#define fake_sha1 _getdns_fake_sha1 +#define fake_dsa _getdns_fake_dsa enum sec_status { sec_status_bogus = 0 , sec_status_unchecked = 0 diff --git a/src/yxml b/src/yxml new file mode 160000 index 00000000..10f968b0 --- /dev/null +++ b/src/yxml @@ -0,0 +1 @@ +Subproject commit 10f968b0e78b9aeee357d0de81a46b445c3fb27b diff --git a/stubby b/stubby index 6d97f432..61142309 160000 --- a/stubby +++ b/stubby @@ -1 +1 @@ -Subproject commit 6d97f4323f2706a2dfcffc7172bd3f71223974a8 +Subproject commit 6114230904d9413514d521358ca7956aac99e822