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 d46dd307..60a37347 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,9 +8,11 @@ addons: packages: - libunbound-dev - libidn11-dev + - libyaml-dev - check - libevent-dev - libev-dev + - libuv-dev - valgrind - clang - wget diff --git a/ChangeLog b/ChangeLog index dc26f768..eb4cc8cf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,33 @@ +* 2017-1?-??: Version 1.2.1 + * Bugfix #348: Fix a linking issue in stubby when libbsd is present + Thanks Remi Gacogne + * Fix Makefile dependencies for parallel install. + Thanks ilovezfs + +* 2017-09-29: Version 1.2.0 + * Bugfix of rc1: authentication of first query with TLS + Thanks Travis Burtrum + * A function to set the location for library specific data, + like trust-anchors: getdns_context_set_appdata(). + * Zero configuration DNSSEC - build upon the scheme + described in RFC7958. The URL from which to fetch + the trust anchor, the verification CA and email + can be set with the new getdns_context_set_trust_anchor_url(), + getdns_context_set_trust_anchor_verify_CA() and + getdns_context_set_trust_anchor_verify_email() functions. + The default values are to fetch from IANA and to validate + with the ICANN CA. + * Update of Stubby with yaml configuration file and + logging from a certain severity support. + * Fix tpkg exit status on test failure. Thanks Jim Hague. + * Refined logging levels for upstream statistics + * Reuse (best behaving) backed-off TLS upstreams when non are usable. + * Let TLS upstreams back-off a incremental amount of time. + Back-off time starts with 1 second and is doubled each failure, but + will not exceed the time given by getdns_context_set_tls_backoff_time() + * Make TLS upstream management more resilient to temporary outages + (like laptop sleeps) + * 2017-09-04: Version 1.1.3 * Small bugfixes that came out of static analysis * No annotations with the output of getdns_query anymore, diff --git a/Makefile.in b/Makefile.in index 422f6d80..1096dc3b 100644 --- a/Makefile.in +++ b/Makefile.in @@ -71,21 +71,26 @@ install: getdns.pc getdns_ext_event.pc install-lib @INSTALL_GETDNS_QUERY@ $(INSTALL) -m 644 $(srcdir)/spec/index.html $(DESTDIR)$(docdir)/spec cd doc && $(MAKE) install @echo "***" - @echo "*** !!! IMPORTANT !!!! libgetdns needs a DNSSEC trust anchor!" + @echo "*** !!! IMPORTANT !!!!" + @echo "***" + @echo "*** From release 1.2.0, getdns comes with built-in DNSSEC" + @echo "*** trust anchor management. External trust anchor management," + @echo "*** for example with unbound-anchor, is no longer necessary" + @echo "*** and no longer recommended." + @echo "***" + @echo "*** Previously installed trust anchors, in the default location -" @echo "***" - @echo "*** For the library to be able to perform DNSSEC, the root" - @echo "*** trust anchor needs to be present in presentation format" - @echo "*** in the file: " @echo "*** @TRUST_ANCHOR_FILE@" @echo "***" - @echo "*** We recomend using unbound-anchor to retrieve and install" - @echo "*** the root trust anchor like this: " - @echo "*** mkdir -p `dirname @TRUST_ANCHOR_FILE@`" - @echo "*** unbound-anchor -a \"@TRUST_ANCHOR_FILE@\"" + @echo "*** - will be preferred and used for DNSSEC validation, however" + @echo "*** getdns will fallback to trust-anchors obtained via built-in" + @echo "*** trust anchor management when the anchors from the default" + @echo "*** location fail to validate the root DNSKEY rrset." @echo "***" - @echo "*** We strongly recommend package maintainers to provide the" - @echo "*** root trust anchor by installing it with unbound-anchor" - @echo "*** at package installation time from the post-install script." + @echo "*** To prevent expired DNSSEC trust anchors to be used for" + @echo "*** validation, we strongly recommend removing the trust anchors" + @echo "*** on the default location when there is no active external" + @echo "*** trust anchor management keeping it up-to-date." @echo "***" uninstall: @UNINSTALL_GETDNS_QUERY@ @@ -198,11 +203,13 @@ $(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 mkdir -p $(distdir)/stubby mkdir -p $(distdir)/stubby/src + mkdir -p $(distdir)/stubby/src/yaml cp $(srcdir)/configure.ac $(distdir) cp $(srcdir)/configure $(distdir) cp $(srcdir)/AUTHORS $(distdir) @@ -245,14 +252,18 @@ $(distdir): cp $(srcdir)/spec/example/*.[ch] $(distdir)/spec/example cp $(srcdir)/src/tools/Makefile.in $(distdir)/src/tools cp $(srcdir)/src/tools/*.[ch] $(distdir)/src/tools - cp $(srcdir)/stubby/stubby.conf.example $(distdir)/stubby + cp $(srcdir)/stubby/stubby.yml.example $(distdir)/stubby cp $(srcdir)/stubby/stubby-setdns-macos.sh $(distdir)/stubby - cp $(srcdir)/stubby/src/stubby.c $(distdir)/stubby/src + cp $(srcdir)/stubby/src/*.[ch] $(distdir)/stubby/src + cp $(srcdir)/stubby/src/yaml/*.[ch] $(distdir)/stubby/src/yaml cp $(srcdir)/stubby/COPYING $(distdir)/stubby cp $(srcdir)/stubby/README.md $(distdir)/stubby 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/README.md b/README.md index b54c8f73..aacfc289 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ If you are building from git, you need to do the following before building: # autoreconf -fi -As well as building the getdns library 2 other tools are installed by default by the above process: +As well as building the getdns library two other tools may be installed: * getdns_query: a command line test script wrapper for getdns * stubby: an experimental DNS Privacy enabled client @@ -112,7 +112,7 @@ The implementation works with a variety of event loops, each built as a separate ## Stubby * Stubby is an experimental implementation of a DNS Privacy enabled stub resolver than encrypts DNS queries using TLS. It is currently suitable for advanced/technical users - all feedback is welcome! -* Details on how to use Stubby can be found in the [Stubby Reference Guide](https://getdnsapi.net/blog/dns-privacy-daemon-stubby). +* Details on how to use Stubby can be found in the [Stubby Reference Guide](https://dnsprivacy.org/wiki/x/JYAT). * Also see [dnsprivacy.org](https://dnsprivacy.org) for more information on DNS Privacy. ## Regression Tests @@ -121,8 +121,6 @@ A suite of regression tests are included with the library, if you make changes o want to sanity check things on your system take a look at src/test. You will need to install [libcheck](https://libcheck.github.io/check/). The check library is also available from many of the package repositories for the more popular operating systems. -The regression tests do not work with --enable-stub-only. - ## DNSSEC dependencies For the library to be DNSSEC capable, it needs to know the root trust anchor. @@ -131,13 +129,16 @@ The library will try to load the root trust anchor from or more `DS` or `DNSKEY` resource records in presentation (i.e. zone file) format. Note that this is different than the format of BIND.keys. -The best way to setup or update the root trust anchor is by using -[`unbound-anchor`](https://www.unbound.net/documentation/unbound-anchor.html). -To setup the library with the root trust anchor at the default location, -execute the following steps as root: +##$ Zero configuration DNSSEC - # mkdir -p /etc/unbound - # unbound-anchor -a /etc/unbound/getdns-root.key +When the root trust anchor is not installed in the default location and a DNSSEC query is done, getdns will try to use the trust anchors published here: http://data.iana.org/root-anchors/root-anchors.xml . +It will validate these anchors with the ICANN Certificate Authority certificate following the procedure described in [RFC7958]. +The `root-anchors.xml` and `root-anchors.p7s` S/MIME signature will be cached in the `$HOME/.getdns` directory. + +When using trust-anchors from the `root-anchors.xml` file, getdns will track the keys in the root DNSKEY rrset and store a copy in $HOME/.getdns/root.key. +Only when the KSK DNSKEY's change, a new version of `root-anchors.xml` is tried to be retrieved from [data.iana.org](https://data.iana.org/root-anchors/). + +A installed trust-anchor from the default location (`/etc/unbound/getdns-root.key`) that fails to validate the root DNSKEY RRset, will also trigger the "Zero configuration DNSSEC" procedure described above. Support ======= @@ -206,7 +207,7 @@ The primary platforms targeted are Linux and FreeBSD, other platform are support * RHEL/CentOS 6.4 * OSX 10.8 -* Ubuntu 14.04 +* Ubuntu 16.04 * Microsoft Windows 8.1 We intend to add Android and other platforms to future releases as we have time to port it. @@ -272,7 +273,7 @@ To install the [event loop integration libraries](https://getdnsapi.net/doxygen/ Note that in order to compile the examples, the `--with-libevent` switch is required. -As of the 0.2.0 release, when installing via Homebrew, the trust anchor is expected to be located at `$(brew --prefix)/etc/getdns-root.key`. Additionally, the OpenSSL library installed by Homebrew is linked against. Note that the Homebrew OpenSSL installation clones the Keychain certificates to the default OpenSSL location so TLS certificate authentication should work out of the box. +Additionally, the OpenSSL library installed by Homebrew is linked against. Note that the Homebrew OpenSSL installation clones the Keychain certificates to the default OpenSSL location so TLS certificate authentication should work out of the box. ### Microsoft Windows 8.1 diff --git a/configure.ac b/configure.ac index 4eea1694..20756f56 100644 --- a/configure.ac +++ b/configure.ac @@ -36,12 +36,12 @@ sinclude(./m4/acx_getaddrinfo.m4) sinclude(./m4/ax_check_compile_flag.m4) sinclude(./m4/pkg.m4) -AC_INIT([getdns], [1.1.3], [users@getdnsapi.net], [], [https://getdnsapi.net]) +AC_INIT([getdns], [1.2.1], [users@getdnsapi.net], [], [https://getdnsapi.net]) # Dont forget to put a dash in front of the release candidate!!! # That is how it is done with semantic versioning! # -AC_SUBST(RELEASE_CANDIDATE, []) +AC_SUBST(RELEASE_CANDIDATE, [rc1]) # Set current date from system if not set AC_ARG_WITH([current-date], @@ -51,7 +51,7 @@ AC_ARG_WITH([current-date], [CURRENT_DATE="`date -u +%Y-%m-%dT%H:%M:%SZ`"]) AC_SUBST(GETDNS_VERSION, ["AC_PACKAGE_VERSION$RELEASE_CANDIDATE"]) -AC_SUBST(GETDNS_NUMERIC_VERSION, [0x01010300]) +AC_SUBST(GETDNS_NUMERIC_VERSION, [0x010200C1]) AC_SUBST(API_VERSION, ["December 2015"]) AC_SUBST(API_NUMERIC_VERSION, [0x07df0c00]) GETDNS_COMPILATION_COMMENT="AC_PACKAGE_NAME $GETDNS_VERSION configured on $CURRENT_DATE for the $API_VERSION version of the API" @@ -84,10 +84,12 @@ GETDNS_COMPILATION_COMMENT="AC_PACKAGE_NAME $GETDNS_VERSION configured on $CURRE # getdns-1.0.0 had libversion 5:1:4 # getdns-1.1.0 had libversion 6:0:0 # getdns-1.1.1 had libversion 6:1:0 -# getdns-1.1.2 has libversion 7:0:1 -# getdns-1.1.3 will have libversion 7:1:1 +# getdns-1.1.2 had libversion 7:0:1 +# getdns-1.1.3 had libversion 7:1:1 +# getdns-1.2.0 had libversion 8:0:2 +# getdns-1.2.1 will have libversion 8:1:2 # -GETDNS_LIBVERSION=7:1:1 +GETDNS_LIBVERSION=8:1:2 AC_SUBST(GETDNS_COMPILATION_COMMENT) AC_SUBST(GETDNS_LIBVERSION) @@ -100,7 +102,18 @@ AC_PROG_CC AC_PROG_CPP # Checks for programs. +HOSTOS="unix" AC_CANONICAL_HOST +case "${host_os}" in +cygwin*|mingw*) + HOSTOS=windows + ;; +darwin*) + HOSTOS=macos + ;; +esac +AC_SUBST(HOSTOS) + CFLAGS="$CFLAGS" WPEDANTICFLAG="" @@ -162,7 +175,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 @@ -171,6 +185,7 @@ case "$enable_all_debugging" in enable_debug_daemon=yes enable_debug_sec=yes enable_debug_server=yes + enable_debug_anchor=yes ;; no|*) ;; @@ -217,6 +232,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 @@ -232,7 +254,7 @@ esac DEFAULT_EVENTLOOP=select_eventloop -AC_CHECK_HEADERS([sys/poll.h poll.h sys/resource.h],,, [AC_INCLUDES_DEFAULT]) +AC_CHECK_HEADERS([signal.h sys/poll.h poll.h sys/resource.h sys/types.h],,, [AC_INCLUDES_DEFAULT]) AC_ARG_ENABLE(poll-eventloop, AC_HELP_STRING([--disable-poll-eventloop], [Disable default eventloop based on poll (default=enabled if available)])) case "$enable_poll_eventloop" in no) @@ -280,11 +302,66 @@ 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]) ACX_CHECK_GETADDRINFO_WITH_INCLUDES +AC_ARG_WITH(fd-setsize, AS_HELP_STRING([--with-fd-setsize=size], + [Set maximum file descriptor number that can be used by select]), + [], [withval="no"]) +case "$withval" in +no) + ;; +*) + AC_DEFINE_UNQUOTED([FD_SETSIZE], [$withval], [Alternate value for the FD_SETSIZE]) + my_enable_unbound_event_api=1 + ;; +esac + +#---- check for pthreads library +AC_ARG_WITH(libpthread, AS_HELP_STRING([--without-libpthread], + [Disable libpthread (default is autodetect)]), + [], [withval="yes"]) + +case "$withval" in +yes) + AC_SEARCH_LIBS([pthread_mutex_init],[pthread], [ + AC_DEFINE([HAVE_PTHREAD], [1], [Have pthreads library]) + LIBS="-lpthread $LIBS" + ], [AC_MSG_WARN([pthreads not available])]) + ;; +*) + ;; +esac + USE_NSS="no" # openssl if test $USE_NSS = "no"; then @@ -512,7 +589,7 @@ case "$enable_ed25519" in no) ;; *) - if test $USE_NSS = "no" -a $USE_NETTLE = "no"; then + if test "$USE_NSS" = "no" -a "$USE_NETTLE" = "no"; then AC_CHECK_DECLS([NID_ED25519], [ AC_DEFINE_UNQUOTED([USE_ED25519], [1], [Define this to enable ED25519 support.]) use_ed25519="yes" @@ -577,6 +654,22 @@ case "$enable_stub_only" in ;; esac +my_with_yaml=0 +AC_ARG_ENABLE(yaml-config,) +case "$enable_yaml_config" in + yes) + AC_DEFINE_UNQUOTED([USE_YAML_CONFIG], [1], [Define this to enable YAML config support.]) + AC_DEFINE_UNQUOTED([HAVE_GETDNS_YAML2DICT], [1], [Define this to enable getdns_yaml2dict function.]) + + GETDNS_XTRA_OBJS="convert_yaml_to_json.lo" + my_with_yaml=1 + ;; + no|*) + GETDNS_XTRA_OBJS="" + ;; +esac +AC_SUBST(GETDNS_XTRA_OBJS) + if test "$USE_WINSOCK" = 1; then AC_MSG_NOTICE([ Building on Windows ... YES! ]) AC_DEFINE_UNQUOTED([GETDNS_ON_WINDOWS], [1], [Define this to enable Windows build.]) @@ -584,6 +677,30 @@ if test "$USE_WINSOCK" = 1; then LIBS="$LIBS -lgdi32 -liphlpapi" fi +dnl sigset_t or _sigset_t? MinGW is latter by default. +AC_CHECK_TYPES([sigset_t], + [], + [AC_CHECK_TYPES([_sigset_t], + [], + [AC_MSG_ERROR([Can't find type `sigset_t' or type `_sigset_t'])], + [AC_INCLUDES_DEFAULT +#ifdef HAVE_SIGNAL_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif + ]) + ], + [AC_INCLUDES_DEFAULT +#ifdef HAVE_SIGNAL_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +]) +AC_CHECK_FUNCS(sigemptyset sigfillset sigaddset) my_with_libidn=1 AC_ARG_WITH(libidn, AS_HELP_STRING([--with-libidn=pathname], @@ -691,11 +808,6 @@ then ]) fi -if test $found_all_libs = 0 -then - AC_MSG_ERROR([Missing dependencies: $MISSING_DEPS]) -fi - AC_PATH_PROG([DOXYGEN], [doxygen]) if test -z "$DOXYGEN"; then AC_MSG_WARN([doxygen not found, continuing without]) @@ -1010,6 +1122,7 @@ AC_SUBST(GETDNS_QUERY) AC_SUBST(INSTALL_GETDNS_QUERY) AC_SUBST(UNINSTALL_GETDNS_QUERY) +stubby_with_yaml=0 AC_ARG_WITH(stubby, AS_HELP_STRING([--with-stubby], [Compile and install stubby, the (stub) resolver daemon]), [], [withval="no"]) @@ -1017,39 +1130,89 @@ if test x_$withval = x_yes; then STUBBY="stubby" INSTALL_STUBBY="install-stubby" UNINSTALL_STUBBY="uninstall-stubby" + if test $my_with_yaml = 0 + then + STUBBY_XTRA_OBJS="convert_yaml_to_json.lo gbuffer.lo" + stubby_with_yaml=1 + my_with_yaml=1 + fi else STUBBY="" INSTALL_STUBBY="" UNINSTALL_STUBBY="" + STUBBY_XTRA_OBJS="" fi AC_SUBST(STUBBY) AC_SUBST(INSTALL_STUBBY) AC_SUBST(UNINSTALL_STUBBY) +AC_SUBST(STUBBY_XTRA_OBJS) -AC_ARG_WITH(fd-setsize, AS_HELP_STRING([--with-fd-setsize=size], - [Set maximum file descriptor number that can be used by select]), - [], [withval="no"]) -case "$withval" in - no) - ;; - *) - AC_DEFINE_UNQUOTED([FD_SETSIZE], [$withval], [Alternate value for the FD_SETSIZE]) - my_enable_unbound_event_api=1 - ;; -esac +STUBBY_LIBS="" +STUBBY_LDFLAGS="" + +if test $my_with_yaml = 1 +then + if test $stubby_with_yaml = 1 + then + getdns_LIBS="$LIBS" + getdns_LDFLAGS="$LDFLAGS" + LIBS="$initial_LIBS" + LDFLAGS="$initial_LDFLAGS" + fi + AC_ARG_WITH(libyaml, AS_HELP_STRING([--with-libyaml=pathname], + [path to libyaml (default: search /usr/local ..)]), + [], [withval="yes"]) + if test x_$withval = x_yes; then + for dir in /usr/local /opt/local /usr/pkg /usr/sfw; do + if test -f "$dir/include/yaml.h"; then + CFLAGS="$CFLAGS -I$dir/include" + LDFLAGS="$LDFLAGS -L$dir/lib" + AC_MSG_NOTICE([Found libyaml in $dir]) + break + fi + done + else + if test x_$withval != x_no; then + CFLAGS="$CFLAGS -I$withval/include" + LDFLAGS="$LDFLAGS -L$withval/lib" + else + if test $stubby_with_yaml = 1 + then + AC_MSG_ERROR([libyaml required for building Stubby]) + fi + my_with_yaml=0 + fi + fi + if test $my_with_yaml = 1 + then + AC_MSG_NOTICE([Checking for dependency libyaml]) + AC_CHECK_LIB([yaml], [yaml_parser_parse], [], [ + MISSING_DEPS="${MISSING_DEPS}${MISSING_SEP}libyaml" + MISSING_SEP=", " + found_all_libs=0 + ]) + fi + if test $stubby_with_yaml = 1 + then + STUBBY_LDFLAGS="$LDFLAGS" + STUBBY_LIBS="$LIBS" + LIBS="$getdns_LIBS" + LDFLAGS="$getdns_LDFLAGS" + fi +fi +AC_SUBST(STUBBY_LDFLAGS) +AC_SUBST(STUBBY_LIBS) + +if test $found_all_libs = 0 +then + AC_MSG_ERROR([Missing dependencies: $MISSING_DEPS]) +fi AC_CONFIG_FILES([Makefile src/Makefile src/version.c src/getdns/getdns.h src/getdns/getdns_extra.h spec/example/Makefile src/test/Makefile src/tools/Makefile doc/Makefile getdns.pc getdns_ext_event.pc]) if [ test -n "$DOXYGEN" ] then AC_CONFIG_FILES([src/Doxyfile]) fi - -#---- check for pthreads library -AC_SEARCH_LIBS([pthread_mutex_init],[pthread], [ - AC_DEFINE([HAVE_PTHREAD], [1], [Have pthreads library]) - LIBS="-lpthread $LIBS" -], [AC_MSG_WARN([pthreads not available])]) - AC_MSG_CHECKING([whether the C compiler (${CC-cc}) supports the __func__ variable]) AC_LANG_PUSH(C) AC_COMPILE_IFELSE( @@ -1064,7 +1227,7 @@ dnl ----- Start of "Things needed for gldns" section dnl ----- dnl --------------------------------------------------------------------------- -AC_CHECK_HEADERS([stdarg.h stdint.h netinet/in.h arpa/inet.h netdb.h sys/socket.h time.h sys/time.h sys/select.h endian.h],,, [AC_INCLUDES_DEFAULT]) +AC_CHECK_HEADERS([stdarg.h stdint.h netinet/in.h arpa/inet.h netdb.h sys/socket.h time.h sys/time.h sys/select.h endian.h limits.h sys/limits.h],,, [AC_INCLUDES_DEFAULT]) dnl Check the printf-format attribute (if any) dnl result in HAVE_ATTR_FORMAT. @@ -1107,11 +1270,12 @@ fi # system implementation. PKG_CHECK_MODULES([LIBBSD],[libbsd-overlay],[ LIBS="$LIBS $LIBBSD_LIBS" +STUBBY_LIBS="$STUBBY_LIBS $LIBBSD_LIBS" CFLAGS="$CFLAGS $LIBBSD_CFLAGS" ],[ AC_MSG_WARN([libbsd not found or usable; using embedded code instead]) ]) -AC_CHECK_DECLS([strlcpy,arc4random,arc4random_uniform]) +AC_CHECK_DECLS([inet_pton,inet_ntop,strlcpy,arc4random,arc4random_uniform]) AC_REPLACE_FUNCS(inet_pton) AC_REPLACE_FUNCS(inet_ntop) AC_REPLACE_FUNCS(strlcpy) @@ -1187,14 +1351,45 @@ AH_BOTTOM([ # ifndef FD_SETSIZE # define FD_SETSIZE 1024 # endif -# define PRIsz "%Iu" + +/* the version of the windows API enabled */ +# ifndef WINVER +# define WINVER 0x0600 // 0x0502 +# endif +# ifndef _WIN32_WINNT +# define _WIN32_WINNT 0x0600 // 0x0502 +# endif +# ifdef HAVE_WS2TCPIP_H +# include +# endif + +# ifdef _MSC_VER +# if _MSC_VER >= 1800 +# define PRIsz "zu" +# else +# define PRIsz "Iu" +# endif +# else +# define PRIsz "Iu" +# endif + +# ifdef HAVE_WINSOCK2_H +# include +# endif + +/* detect if we need to cast to unsigned int for FD_SET to avoid warnings */ +# ifdef HAVE_WINSOCK2_H +# define FD_SET_T (u_int) +# else +# define FD_SET_T +# endif /* Windows wants us to use _strdup instead of strdup */ # ifndef strdup # define strdup _strdup # endif #else -# define PRIsz "%zu" +# define PRIsz "zu" #endif #include @@ -1203,34 +1398,6 @@ AH_BOTTOM([ #include #include -/* the version of the windows API enabled */ -#ifndef WINVER -#define WINVER 0x0600 // 0x0502 -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0600 // 0x0502 -#endif -#ifdef HAVE_WINSOCK2_H -#include -#endif - -#ifdef HAVE_WS2TCPIP_H -#include -#endif - -#ifndef USE_WINSOCK -#define ARG_LL "%ll" -#else -#define ARG_LL "%I64" -#endif - -/* detect if we need to cast to unsigned int for FD_SET to avoid warnings */ -#ifdef HAVE_WINSOCK2_H -#define FD_SET_T (u_int) -#else -#define FD_SET_T -#endif - #ifdef __cplusplus extern "C" { #endif @@ -1277,18 +1444,21 @@ void SHA512_Final(uint8_t[SHA512_DIGEST_LENGTH], SHA512_CTX*); unsigned char *SHA512(void* data, unsigned int data_len, unsigned char *digest); #endif /* COMPAT_SHA512 */ -#ifndef HAVE_INET_PTON +#ifndef HAVE_DECL_INET_PTON int inet_pton(int af, const char* src, void* dst); #endif /* HAVE_INET_PTON */ -#ifndef HAVE_INET_NTOP +#ifndef HAVE_DECL_INET_NTOP const char *inet_ntop(int af, const void *src, char *dst, size_t size); #endif #ifdef USE_WINSOCK +# ifndef _CUSTOM_VSNPRINTF +# define _CUSTOM_VSNPRINTF static inline int _gldns_custom_vsnprintf(char *str, size_t size, const char *format, va_list ap) { int r = vsnprintf(str, size, format, ap); return r == -1 ? _vscprintf(format, ap) : r; } -# define vsnprintf _gldns_custom_vsnprintf +# define vsnprintf _gldns_custom_vsnprintf +# endif #endif #ifdef __cplusplus @@ -1315,6 +1485,10 @@ static inline int _gldns_custom_vsnprintf(char *str, size_t size, const char *fo #include #endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif + #ifdef HAVE_NETINET_IN_H #include #endif @@ -1331,6 +1505,20 @@ static inline int _gldns_custom_vsnprintf(char *str, size_t size, const char *fo #include #endif +#ifdef HAVE_LIMITS_H +#include +#endif + +#ifdef HAVE_SYS_LIMITS_H +#include +#endif + +#ifdef PATH_MAX +#define _GETDNS_PATH_MAX PATH_MAX +#else +#define _GETDNS_PATH_MAX 2048 +#endif + #ifndef PRIu64 #define PRIu64 "llu" #endif @@ -1363,6 +1551,29 @@ static inline int _gldns_custom_vsnprintf(char *str, size_t size, const char *fo # endif #endif +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(HAVE_STRPTIME) || !defined(STRPTIME_WORKS) +#define strptime unbound_strptime +struct tm; +char *strptime(const char *s, const char *format, struct tm *tm); +#endif + +#if !defined(HAVE_SIGSET_T) && defined(HAVE__SIGSET_T) +typedef _sigset_t sigset_t; +#endif +#if !defined(HAVE_SIGEMPTYSET) +# define sigemptyset(pset) (*(pset) = 0) +#endif +#if !defined(HAVE_SIGFILLSET) +# define sigfillset(pset) (*(pset) = (sigset_t)-1) +#endif +#if !defined(HAVE_SIGADDSET) +# define sigaddset(pset, num) (*(pset) |= (1L<<(num))) +#endif + #ifdef HAVE_LIBUNBOUND # include # ifdef HAVE_UNBOUND_EVENT_H @@ -1380,6 +1591,10 @@ int ub_resolve_event(struct ub_ctx* ctx, const char* name, int rrtype, # endif # endif #endif + +#ifdef __cplusplus +} +#endif ]) dnl --------------------------------------------------------------------------- diff --git a/src/Makefile.in b/src/Makefile.in index 22056063..594f714b 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -55,11 +55,13 @@ stubbysrcdir = $(srcdir)/../stubby LIBTOOL = ../libtool CC=@CC@ -CFLAGS=-I$(srcdir) -I. -I$(srcdir)/util/auxiliary @CFLAGS@ @CPPFLAGS@ $(XTRA_CFLAGS) +CFLAGS=-I$(srcdir) -I. -I$(srcdir)/util/auxiliary -I$(stubbysrcdir)/src @CFLAGS@ @CPPFLAGS@ $(XTRA_CFLAGS) WPEDANTICFLAG=@WPEDANTICFLAG@ WNOERRORFLAG=@WNOERRORFLAG@ LDFLAGS=@LDFLAGS@ @LIBS@ +STUBBY_LDFLAGS=@STUBBY_LDFLAGS@ @STUBBY_LIBS@ + EXTENSION_LIBEVENT_LIB=@EXTENSION_LIBEVENT_LIB@ EXTENSION_LIBEVENT_EXT_LIBS=@EXTENSION_LIBEVENT_EXT_LIBS@ EXTENSION_LIBEVENT_LDFLAGS=@EXTENSION_LIBEVENT_LDFLAGS@ @@ -75,7 +77,7 @@ C99COMPATFLAGS=@C99COMPATFLAGS@ DEFAULT_EVENTLOOP_OBJ=@DEFAULT_EVENTLOOP@.lo GETDNS_OBJ=const-info.lo convert.lo dict.lo dnssec.lo general.lo \ - list.lo request-internal.lo pubkey-pinning.lo rr-dict.lo \ + list.lo request-internal.lo platform.lo pubkey-pinning.lo rr-dict.lo \ rr-iter.lo server.lo stub.lo sync.lo ub_loop.lo util-internal.lo \ mdns.lo @@ -91,10 +93,15 @@ 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 +GETDNS_XTRA_OBJS=@GETDNS_XTRA_OBJS@ +STUBBY_XTRA_OBJS=@STUBBY_XTRA_OBJS@ 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 @@ -123,9 +130,18 @@ $(UTIL_OBJ): $(JSMN_OBJ): $(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) -DJSMN_GETDNS -c $(srcdir)/jsmn/$(@:.lo=.c) -o $@ +$(YAML_OBJ): + $(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) -c $(stubbysrcdir)/src/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 @@ -144,7 +160,7 @@ install-headers: getdns/getdns.h getdns/getdns_extra.h uninstall-headers: rm -rf $(DESTDIR)$(includedir)/getdns -install-libs: libgetdns.la +install-libs: libgetdns.la $(EXTENSION_LIBEVENT_LIB) $(EXTENSION_LIBUV_LIB) $(EXTENSION_LIBEV_LIB) $(INSTALL) -m 755 -d $(DESTDIR)$(libdir) $(LIBTOOL) --mode=install cp libgetdns.la $(DESTDIR)$(libdir) if test $(have_libevent) = 1 ; then $(LIBTOOL) --mode=install cp $(EXTENSION_LIBEVENT_LIB) $(DESTDIR)$(libdir) ; fi @@ -172,9 +188,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) - $(LIBTOOL) --tag=CC --mode=link $(CC) -o $@ $(GETDNS_OBJ) version.lo context.lo $(DEFAULT_EVENTLOOP_OBJ) $(GLDNS_OBJ) $(COMPAT_OBJ) $(UTIL_OBJ) $(JSMN_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) $(GETDNS_XTRA_OBJS) + $(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) $(GETDNS_XTRA_OBJS) $(LDFLAGS) -rpath $(libdir) -version-info $(libversion) -no-undefined -export-symbols $(srcdir)/libgetdns.symbols test: default cd test && $(MAKE) $@ @@ -185,17 +200,29 @@ getdns_query: default stubby.lo: $(stubbysrcdir)/src/stubby.c $(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) $(WPEDANTICFLAG) -DSTUBBYCONFDIR=\"$(sysconfdir)/stubby\" -DRUNSTATEDIR=\"$(runstatedir)\" -c $< -o $@ -stubby: stubby.lo libgetdns.la - $(LIBTOOL) --tag=CC --mode=link $(CC) -o $@ stubby.lo $(LDFLAGS) libgetdns.la +stubby: stubby.lo libgetdns.la $(STUBBY_XTRA_OBJS) + $(LIBTOOL) --tag=CC --mode=link $(CC) -o $@ stubby.lo $(STUBBY_XTRA_OBJS) $(STUBBY_LDFLAGS) libgetdns.la -install-stubby: stubby $(stubbysrcdir)/stubby.conf.example $(stubbysrcdir)/stubby-setdns-macos.sh - $(INSTALL) -m 755 -d $(DESTDIR)$(bindir) - $(LIBTOOL) --mode=install cp stubby $(DESTDIR)$(bindir) +install-stubby-files-unix: $(stubbysrcdir)/stubby.yml.example + $(INSTALL) -m 755 -d $(DESTDIR)$(stubbyconfdir) + test -f $(DESTDIR)$(stubbyconfdir)/stubby.yml || \ + $(INSTALL_DATA) $(stubbysrcdir)/stubby.yml.example $(DESTDIR)$(stubbyconfdir)/stubby.yml + +install-stubby-files-macos: $(stubbysrcdir)/stubby-setdns-macos.sh install-stubby-files-unix $(INSTALL) -m 755 -d $(DESTDIR)$(sbindir) $(INSTALL) -m 755 $(stubbysrcdir)/stubby-setdns-macos.sh $(DESTDIR)$(sbindir) + +stubby.yml.windows: $(stubbysrcdir)/stubby.yml.example + awk "{sub(/$$/,\"\r\")}1" $(stubbysrcdir)/stubby.yml.example > stubby.yml.windows + +install-stubby-files-windows: stubby.yml.windows $(INSTALL) -m 755 -d $(DESTDIR)$(stubbyconfdir) - test -f $(DESTDIR)$(stubbyconfdir)/stubby.conf || \ - $(INSTALL_DATA) $(stubbysrcdir)/stubby.conf.example $(DESTDIR)$(stubbyconfdir)/stubby.conf + test -f $(DESTDIR)$(stubbyconfdir)/stubby.yml || \ + $(INSTALL_DATA) stubby.yml.windows $(DESTDIR)$(stubbyconfdir)/stubby.yml + +install-stubby: stubby install-stubby-files-@HOSTOS@ + $(INSTALL) -m 755 -d $(DESTDIR)$(bindir) + $(LIBTOOL) --mode=install cp stubby $(DESTDIR)$(bindir) uninstall-stubby: $(LIBTOOL) --mode=uninstall rm -f $(DESTDIR)$(bindir)/stubby @@ -226,14 +253,17 @@ 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 extension/*.c| \ + + (blddir=`pwd`; cd $(srcdir) ; gcc -MM -I. -I"$$blddir" -Iyxml -Iutil/auxiliary -I../stubby/src *.c gldns/*.c compat/*.c util/*.c jsmn/*.c yxml/*.c extension/*.c ../stubby/src/*.c | \ sed -e "s? $$blddir/? ?g" \ -e 's? gldns/? $$(srcdir)/gldns/?g' \ -e 's? compat/? $$(srcdir)/compat/?g' \ -e 's? util/auxiliary/util/? $$(srcdir)/util/auxiliary/util/?g' \ -e 's? util/? $$(srcdir)/util/?g' \ -e 's? jsmn/? $$(srcdir)/jsmn/?g' \ + -e 's? yxml/? $$(srcdir)/yxml/?g' \ -e 's? extension/? $$(srcdir)/extension/?g' \ + -e 's? \.\./stubby/? $$(stubbysrcdir)/?g' \ -e 's? \([a-z_-]*\)\.\([ch]\)? $$(srcdir)/\1.\2?g' \ -e 's? \$$(srcdir)/config\.h? config.h?g' \ -e 's? \$$(srcdir)/getdns/getdns_extra\.h? getdns/getdns_extra.h?g' \ @@ -251,22 +281,36 @@ 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)/rr-iter.h $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h $(srcdir)/gldns/pkthdr.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)/dnssec.h $(srcdir)/gldns/rrdef.h $(srcdir)/yxml/yxml.h \ + $(srcdir)/gldns/parseutil.h $(srcdir)/gldns/str2wire.h $(srcdir)/gldns/rrdef.h $(srcdir)/gldns/wire2str.h \ + $(srcdir)/gldns/keyraw.h $(srcdir)/general.h $(srcdir)/util-internal.h $(srcdir)/platform.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)/rr-iter.h $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h $(srcdir)/gldns/pkthdr.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)/dict.h $(srcdir)/pubkey-pinning.h + $(srcdir)/util/auxiliary/util/log.h $(srcdir)/debug.h $(srcdir)/util-internal.h $(srcdir)/platform.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 \ getdns/getdns.h \ @@ -276,9 +320,9 @@ convert.lo convert.o: $(srcdir)/convert.c \ $(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)/rr-iter.h \ - $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h $(srcdir)/gldns/pkthdr.h $(srcdir)/gldns/wire2str.h \ + $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h $(srcdir)/gldns/pkthdr.h $(srcdir)/anchor.h $(srcdir)/gldns/wire2str.h \ $(srcdir)/gldns/str2wire.h $(srcdir)/gldns/rrdef.h $(srcdir)/gldns/parseutil.h $(srcdir)/const-info.h $(srcdir)/dict.h \ - $(srcdir)/list.h $(srcdir)/jsmn/jsmn.h $(srcdir)/convert.h + $(srcdir)/list.h $(srcdir)/jsmn/jsmn.h $(stubbysrcdir)/src/yaml/convert_yaml_to_json.h $(srcdir)/convert.h dict.lo dict.o: $(srcdir)/dict.c \ config.h \ $(srcdir)/types-internal.h \ @@ -289,7 +333,7 @@ dict.lo dict.o: $(srcdir)/dict.c \ $(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)/rr-iter.h $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h \ - $(srcdir)/gldns/pkthdr.h $(srcdir)/dict.h $(srcdir)/list.h $(srcdir)/const-info.h $(srcdir)/gldns/wire2str.h \ + $(srcdir)/gldns/pkthdr.h $(srcdir)/anchor.h $(srcdir)/dict.h $(srcdir)/list.h $(srcdir)/const-info.h $(srcdir)/gldns/wire2str.h \ $(srcdir)/gldns/parseutil.h dnssec.lo dnssec.o: $(srcdir)/dnssec.c \ config.h \ @@ -301,10 +345,10 @@ 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)/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/auxiliary/util/log.h $(srcdir)/debug.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h \ + $(srcdir)/gldns/pkthdr.h $(srcdir)/anchor.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 general.lo general.o: $(srcdir)/general.c \ config.h \ @@ -316,9 +360,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)/anchor.h $(srcdir)/util-internal.h $(srcdir)/dnssec.h $(srcdir)/gldns/rrdef.h $(srcdir)/stub.h \ + $(srcdir)/dict.h $(srcdir)/mdns.h $(srcdir)/platform.h list.lo list.o: $(srcdir)/list.c $(srcdir)/types-internal.h \ getdns/getdns.h \ getdns/getdns_extra.h \ @@ -328,7 +372,7 @@ list.lo list.o: $(srcdir)/list.c $(srcdir)/types-internal.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)/rr-iter.h $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h \ - $(srcdir)/gldns/pkthdr.h $(srcdir)/list.h $(srcdir)/dict.h + $(srcdir)/gldns/pkthdr.h $(srcdir)/anchor.h $(srcdir)/list.h $(srcdir)/dict.h mdns.lo mdns.o: $(srcdir)/mdns.c \ config.h \ $(srcdir)/debug.h $(srcdir)/context.h \ @@ -338,10 +382,12 @@ 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/fptr_wlist.h $(srcdir)/util/lookup3.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)/anchor.h $(srcdir)/general.h $(srcdir)/gldns/rrdef.h $(srcdir)/util-internal.h \ + $(srcdir)/platform.h $(srcdir)/mdns.h $(srcdir)/util/auxiliary/util/fptr_wlist.h $(srcdir)/util/lookup3.h \ $(srcdir)/util/orig-headers/lookup3.h +platform.lo platform.o: $(srcdir)/platform.c $(srcdir)/platform.h \ + config.h pubkey-pinning.lo pubkey-pinning.o: $(srcdir)/pubkey-pinning.c \ config.h \ $(srcdir)/debug.h \ @@ -352,8 +398,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)/anchor.h $(srcdir)/util-internal.h request-internal.lo request-internal.o: $(srcdir)/request-internal.c \ config.h \ $(srcdir)/types-internal.h \ @@ -364,8 +410,8 @@ request-internal.lo request-internal.o: $(srcdir)/request-internal.c \ $(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)/rr-iter.h $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h \ - $(srcdir)/gldns/pkthdr.h $(srcdir)/gldns/rrdef.h $(srcdir)/gldns/str2wire.h $(srcdir)/gldns/rrdef.h $(srcdir)/dict.h \ - $(srcdir)/convert.h $(srcdir)/general.h + $(srcdir)/gldns/pkthdr.h $(srcdir)/anchor.h $(srcdir)/gldns/rrdef.h $(srcdir)/gldns/str2wire.h $(srcdir)/gldns/rrdef.h \ + $(srcdir)/dict.h $(srcdir)/convert.h $(srcdir)/general.h rr-dict.lo rr-dict.o: $(srcdir)/rr-dict.c $(srcdir)/rr-dict.h \ config.h \ getdns/getdns.h \ @@ -375,7 +421,8 @@ rr-dict.lo rr-dict.o: $(srcdir)/rr-dict.c $(srcdir)/rr-dict.h \ $(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)/rr-iter.h $(srcdir)/gldns/pkthdr.h $(srcdir)/dict.h + $(srcdir)/util/auxiliary/util/log.h $(srcdir)/debug.h $(srcdir)/rr-iter.h $(srcdir)/gldns/pkthdr.h $(srcdir)/anchor.h \ + $(srcdir)/dict.h rr-iter.lo rr-iter.o: $(srcdir)/rr-iter.c $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h \ config.h \ getdns/getdns.h \ @@ -388,7 +435,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 $(srcdir)/anchor.h $(srcdir)/util-internal.h $(srcdir)/platform.h stub.lo stub.o: $(srcdir)/stub.c \ config.h \ $(srcdir)/debug.h $(srcdir)/stub.h \ @@ -400,8 +448,8 @@ stub.lo stub.o: $(srcdir)/stub.c \ $(srcdir)/rr-dict.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)/util-internal.h $(srcdir)/general.h $(srcdir)/pubkey-pinning.h + $(srcdir)/util/orig-headers/locks.h $(srcdir)/util/auxiliary/util/log.h $(srcdir)/debug.h $(srcdir)/anchor.h \ + $(srcdir)/util-internal.h $(srcdir)/platform.h $(srcdir)/general.h $(srcdir)/pubkey-pinning.h sync.lo sync.o: $(srcdir)/sync.c \ getdns/getdns.h \ config.h \ @@ -411,9 +459,9 @@ 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)/gldns/wire2str.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)/anchor.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 \ getdns/getdns.h \ @@ -428,8 +476,8 @@ util-internal.lo util-internal.o: $(srcdir)/util-internal.c \ $(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)/rr-iter.h \ - $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h $(srcdir)/gldns/pkthdr.h $(srcdir)/gldns/str2wire.h $(srcdir)/gldns/rrdef.h \ - $(srcdir)/dnssec.h $(srcdir)/gldns/rrdef.h + $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h $(srcdir)/gldns/pkthdr.h $(srcdir)/anchor.h $(srcdir)/gldns/str2wire.h \ + $(srcdir)/gldns/rrdef.h $(srcdir)/dnssec.h $(srcdir)/gldns/rrdef.h gbuffer.lo gbuffer.o: $(srcdir)/gldns/gbuffer.c \ config.h \ $(srcdir)/gldns/gbuffer.h @@ -479,6 +527,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 @@ -505,6 +555,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 \ @@ -525,13 +576,24 @@ libuv.lo libuv.o: $(srcdir)/extension/libuv.c \ $(srcdir)/util/rbtree.h $(srcdir)/util/orig-headers/rbtree.h $(srcdir)/getdns/getdns_ext_libuv.h poll_eventloop.lo poll_eventloop.o: $(srcdir)/extension/poll_eventloop.c \ config.h \ - $(srcdir)/extension/poll_eventloop.h \ + $(srcdir)/util-internal.h $(srcdir)/context.h \ getdns/getdns.h \ getdns/getdns_extra.h \ - $(srcdir)/types-internal.h $(srcdir)/util/rbtree.h $(srcdir)/util/orig-headers/rbtree.h $(srcdir)/debug.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)/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)/rr-iter.h $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h \ + $(srcdir)/gldns/pkthdr.h $(srcdir)/anchor.h $(srcdir)/platform.h select_eventloop.lo select_eventloop.o: $(srcdir)/extension/select_eventloop.c \ config.h \ - $(srcdir)/extension/select_eventloop.h \ + $(srcdir)/debug.h $(srcdir)/types-internal.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)/util/rbtree.h $(srcdir)/util/orig-headers/rbtree.h $(srcdir)/platform.h \ + $(srcdir)/extension/select_eventloop.h +stubby.lo stubby.o: $(stubbysrcdir)/src/stubby.c \ + config.h \ + getdns/getdns.h \ + getdns/getdns_extra.h \ + $(stubbysrcdir)/src/yaml/convert_yaml_to_json.h diff --git a/src/anchor.c b/src/anchor.c new file mode 100644 index 00000000..a5a31b2e --- /dev/null +++ b/src/anchor.c @@ -0,0 +1,1925 @@ +/** + * + * /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" +#include "platform.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_anchors_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_anchors_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)\n", (int)a->tcp.read_buf_len); + + 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_anchors_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_anchors_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); + +#ifdef USE_WINSOCK + n = recv(a->fd, (char *)a->tcp.read_pos, a->tcp.to_read, 0); +#else + n = read(a->fd, a->tcp.read_pos, a->tcp.to_read); +#endif + 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_socketerror() == _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); + + +#ifdef USE_WINSOCK + DEBUG_ANCHOR("sending to: %d\n", a->fd); + written = send(a->fd, (const char *)a->tcp.write_buf, a->tcp.write_buf_len, 0); +#else + written = write(a->fd, a->tcp.write_buf, a->tcp.write_buf_len); +#endif + 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_socketerror() == _getdns_EWOULDBLOCK || _getdns_socketerror() == _getdns_EINPROGRESS) + 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_anchors_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_socketerror() == _getdns_EINPROGRESS || + _getdns_socketerror() == _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_anchors_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_anchors_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..2c582ed1 100644 --- a/src/const-info.c +++ b/src/const-info.c @@ -82,6 +82,10 @@ 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_ANCHORS_URL", GETDNS_CONTEXT_CODE_TRUST_ANCHORS_URL_TEXT }, + { 626, "GETDNS_CONTEXT_CODE_TRUST_ANCHORS_VERIFY_CA", GETDNS_CONTEXT_CODE_TRUST_ANCHORS_VERIFY_CA_TEXT }, + { 627, "GETDNS_CONTEXT_CODE_TRUST_ANCHORS_VERIFY_EMAIL", GETDNS_CONTEXT_CODE_TRUST_ANCHORS_VERIFY_EMAIL_TEXT }, + { 628, "GETDNS_CONTEXT_CODE_APPDATA_DIR", GETDNS_CONTEXT_CODE_APPDATA_DIR_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 }, @@ -150,6 +154,7 @@ static struct const_name_info consts_name_info[] = { { "GETDNS_CALLBACK_COMPLETE", 700 }, { "GETDNS_CALLBACK_ERROR", 703 }, { "GETDNS_CALLBACK_TIMEOUT", 702 }, + { "GETDNS_CONTEXT_CODE_APPDATA_DIR", 628 }, { "GETDNS_CONTEXT_CODE_APPEND_NAME", 607 }, { "GETDNS_CONTEXT_CODE_DNSSEC_ALLOWED_SKEW", 614 }, { "GETDNS_CONTEXT_CODE_DNSSEC_TRUST_ANCHORS", 609 }, @@ -174,6 +179,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_ANCHORS_URL", 625 }, + { "GETDNS_CONTEXT_CODE_TRUST_ANCHORS_VERIFY_CA", 626 }, + { "GETDNS_CONTEXT_CODE_TRUST_ANCHORS_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 76ee766a..994a9ac4 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 @@ -52,6 +54,7 @@ typedef unsigned short in_port_t; #include #include #include +#include #endif #include @@ -68,7 +71,6 @@ typedef unsigned short in_port_t; #endif #include -#include "config.h" #ifdef HAVE_LIBUNBOUND #include #endif @@ -78,6 +80,7 @@ typedef unsigned short in_port_t; #include "context.h" #include "types-internal.h" #include "util-internal.h" +#include "platform.h" #include "dnssec.h" #include "stub.h" #include "list.h" @@ -702,11 +705,7 @@ _getdns_upstreams_dereference(getdns_upstreams *upstreams) } if (upstream->fd != -1) { -#ifdef USE_WINSOCK - closesocket(upstream->fd); -#else - close(upstream->fd); -#endif + _getdns_closesocket(upstream->fd); } while (pin) { sha256_pin_t *nextpin = pin->next; @@ -734,65 +733,65 @@ void _getdns_upstream_log(getdns_upstream *upstream, uint64_t system, va_end(args); } -void -_getdns_upstream_shutdown(getdns_upstream *upstream) -{ - /*Set condition to tear down asap to stop any further scheduling*/ - upstream->conn_state = GETDNS_CONN_TEARDOWN; - /* Update total stats for the upstream.*/ - upstream->total_responses+=upstream->responses_received; - upstream->total_timeouts+=upstream->responses_timeouts; - /* Need the last auth state when using session resumption*/ - upstream->last_tls_auth_state = upstream->tls_auth_state; - /* Keep track of the best auth state this upstream has had*/ - if (upstream->tls_auth_state > upstream->best_tls_auth_state) - upstream->best_tls_auth_state = upstream->tls_auth_state; - _getdns_upstream_log(upstream, GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_DEBUG, - "%-40s : Conn closed : Transport=%s - Resp=%d,Timeouts=%d,Auth=%s,Keepalive(ms)=%d\n", - upstream->addr_str, - (upstream->transport == GETDNS_TRANSPORT_TLS ? "TLS" : "TCP"), - (int)upstream->responses_received, (int)upstream->responses_timeouts, - _getdns_auth_str(upstream->tls_auth_state), (int)upstream->keepalive_timeout); - _getdns_upstream_log(upstream, GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_DEBUG, - "%-40s : Upstream stats: Transport=%s - Resp=%d,Timeouts=%d,Best_auth=%s\n", - upstream->addr_str, - (upstream->transport == GETDNS_TRANSPORT_TLS ? "TLS" : "TCP"), - (int)upstream->total_responses, (int)upstream->total_timeouts, - _getdns_auth_str(upstream->best_tls_auth_state)); - _getdns_upstream_log(upstream, GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_DEBUG, - "%-40s : Upstream stats: Transport=%s - Conns=%d,Conn_fails=%d,Conn_shutdowns=%d,Backoffs=%d\n", - upstream->addr_str, - (upstream->transport == GETDNS_TRANSPORT_TLS ? "TLS" : "TCP"), - (int)upstream->conn_completed, (int)upstream->conn_setup_failed, - (int)upstream->conn_shutdowns, (int)upstream->conn_backoffs); +static void +upstream_backoff(getdns_upstream *upstream) { + upstream->conn_state = GETDNS_CONN_BACKOFF; + /* Increase the backoff interval incrementally up to the tls_backoff_time*/ + if (upstream->conn_backoff_interval < upstream->upstreams->tls_backoff_time) { + if (upstream->conn_backoff_interval < (UINT16_MAX-1)/2) + upstream->conn_backoff_interval *= 2; + else + upstream->conn_backoff_interval = upstream->upstreams->tls_backoff_time; + } + if (upstream->conn_backoff_interval < upstream->upstreams->tls_backoff_time) + upstream->conn_retry_time = time(NULL) + upstream->conn_backoff_interval; + else + upstream->conn_retry_time = time(NULL) + upstream->upstreams->tls_backoff_time; + upstream->total_responses = 0; + upstream->total_timeouts = 0; + upstream->conn_completed = 0; + upstream->conn_setup_failed = 0; + upstream->conn_shutdowns = 0; + upstream->conn_backoffs++; + _getdns_upstream_log(upstream, GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_NOTICE, + "%-40s : !Backing off this upstream - Will retry again in %ds at %s", + upstream->addr_str, + upstream->conn_backoff_interval, + asctime(gmtime(&upstream->conn_retry_time))); +} +static void +_getdns_upstream_reset(getdns_upstream *upstream) +{ /* Back off connections that never got up service at all (probably no TCP service or incompatible TLS version/cipher). Leave choice between working upstreams to the stub. This back-off should be time based for TLS according to RFC7858. For now, use the same basis if we simply can't get TCP service either.*/ - uint16_t conn_retries = upstream->upstreams->tls_connection_retries; /* [TLS1]TODO: This arbitrary logic at the moment - review and improve!*/ + + /*This is the configured number of retries to attempt*/ + uint16_t conn_retries = upstream->upstreams->tls_connection_retries; + if (upstream->conn_setup_failed >= conn_retries + || ((int)upstream->conn_shutdowns >= conn_retries*GETDNS_TRANSPORT_FAIL_MULT && upstream->total_responses == 0) + || (upstream->conn_completed >= conn_retries && upstream->total_responses == 0 && upstream->total_timeouts > GETDNS_TRANSPORT_FAIL_MULT)) { - upstream->conn_state = GETDNS_CONN_BACKOFF; - upstream->conn_retry_time = time(NULL) + upstream->upstreams->tls_backoff_time; - upstream->total_responses = 0; - upstream->total_timeouts = 0; - upstream->conn_completed = 0; - upstream->conn_setup_failed = 0; - upstream->conn_shutdowns = 0; - upstream->conn_backoffs++; - _getdns_upstream_log(upstream, GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_DEBUG, - "%-40s : !Backing off this upstream - Will retry as new upstream at %s", - upstream->addr_str, - asctime(gmtime(&upstream->conn_retry_time))); + upstream_backoff(upstream); } + + /* If we didn't backoff it would be nice to reset the conn_backoff_interval + if the upstream is working well again otherwise it would get stuck at the + tls_backoff_time forever... How about */ + if (upstream->conn_state != GETDNS_CONN_BACKOFF && + upstream->responses_received > 1) + upstream->conn_backoff_interval = 1; + // Reset per connection counters upstream->queries_sent = 0; upstream->responses_received = 0; @@ -802,17 +801,17 @@ _getdns_upstream_shutdown(getdns_upstream *upstream) /* Now TLS stuff*/ upstream->tls_auth_state = GETDNS_AUTH_NONE; + if (upstream->event.ev && upstream->loop) { + upstream->loop->vmt->clear( + upstream->loop, &upstream->event); + } if (upstream->tls_obj != NULL) { SSL_shutdown(upstream->tls_obj); SSL_free(upstream->tls_obj); upstream->tls_obj = NULL; } if (upstream->fd != -1) { -#ifdef USE_WINSOCK - closesocket(upstream->fd); -#else - close(upstream->fd); -#endif + _getdns_closesocket(upstream->fd); upstream->fd = -1; } /* Set connection ready for use again*/ @@ -820,6 +819,39 @@ _getdns_upstream_shutdown(getdns_upstream *upstream) upstream->conn_state = GETDNS_CONN_CLOSED; } +void +_getdns_upstream_shutdown(getdns_upstream *upstream) +{ + /* Update total stats for the upstream.*/ + upstream->total_responses+=upstream->responses_received; + upstream->total_timeouts+=upstream->responses_timeouts; + /* Need the last auth state when using session resumption*/ + upstream->last_tls_auth_state = upstream->tls_auth_state; + /* Keep track of the best auth state this upstream has had*/ + if (upstream->tls_auth_state > upstream->best_tls_auth_state) + upstream->best_tls_auth_state = upstream->tls_auth_state; + _getdns_upstream_log(upstream, GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_DEBUG, + "%-40s : Conn closed: %s - Resps=%6d, Timeouts =%6d, Curr_auth =%7s, Keepalive(ms)=%6d\n", + upstream->addr_str, + (upstream->transport == GETDNS_TRANSPORT_TLS ? "TLS" : "TCP"), + (int)upstream->responses_received, (int)upstream->responses_timeouts, + _getdns_auth_str(upstream->tls_auth_state), (int)upstream->keepalive_timeout); + _getdns_upstream_log(upstream, GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_INFO, + "%-40s : Upstream : %s - Resps=%6d, Timeouts =%6d, Best_auth =%7s\n", + upstream->addr_str, + (upstream->transport == GETDNS_TRANSPORT_TLS ? "TLS" : "TCP"), + (int)upstream->total_responses, (int)upstream->total_timeouts, + _getdns_auth_str(upstream->best_tls_auth_state)); + _getdns_upstream_log(upstream, GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_INFO, + "%-40s : Upstream : %s - Conns=%6d, Conn_fails=%6d, Conn_shuts=%7d, Backoffs =%6d\n", + upstream->addr_str, + (upstream->transport == GETDNS_TRANSPORT_TLS ? "TLS" : "TCP"), + (int)upstream->conn_completed, (int)upstream->conn_setup_failed, + (int)upstream->conn_shutdowns, (int)upstream->conn_backoffs); + + _getdns_upstream_reset(upstream); +} + static int tls_is_in_transports_list(getdns_context *context) { @@ -944,6 +976,7 @@ upstream_init(getdns_upstream *upstream, upstream->conn_shutdowns = 0; upstream->conn_setup_failed = 0; upstream->conn_retry_time = 0; + upstream->conn_backoff_interval = 1; upstream->conn_backoffs = 0; upstream->total_responses = 0; upstream->total_timeouts = 0; @@ -1329,6 +1362,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_trust_anchors_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_trust_anchors_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_trust_anchors_verify_email = + "dnssec@iana.org"; + + /* * getdns_context_create * @@ -1428,6 +1492,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->trust_anchors_url = NULL; + result->trust_anchors_verify_email = NULL; + result->trust_anchors_verify_CA = NULL; + result->appdata_dir = NULL; + + (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)); @@ -1445,12 +1523,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; @@ -1484,6 +1566,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; @@ -1520,7 +1605,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 @@ -1528,7 +1621,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))) @@ -1603,6 +1695,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); @@ -1665,10 +1761,20 @@ getdns_context_destroy(struct getdns_context *context) _getdns_traverse_postorder(&context->local_hosts, destroy_local_host, context); - getdns_dict_destroy(context->header); getdns_dict_destroy(context->add_opt_parameters); + if (context->trust_anchors_url) + GETDNS_FREE(context->mf, context->trust_anchors_url); + if (context->trust_anchors_verify_CA) + GETDNS_FREE( context->mf + , context->trust_anchors_verify_CA); + if (context->trust_anchors_verify_email) + GETDNS_FREE( context->mf + , context->trust_anchors_verify_email); + if (context->appdata_dir) + GETDNS_FREE(context->mf, context->appdata_dir); + #ifdef USE_WINSOCK WSACleanup(); #endif @@ -2442,15 +2548,15 @@ getdns_context_set_dns_root_servers( if (addr_bd->size == 16 && inet_ntop(AF_INET6, addr_bd->data, dst, sizeof(dst))) - fprintf(fh,". NS "PRIsz".root-servers.getdnsapi.net.\n" - PRIsz".root-servers.getdnsapi.net. AAAA %s\n", + fprintf(fh,". NS %"PRIsz".root-servers.getdnsapi.net.\n" + "%"PRIsz".root-servers.getdnsapi.net. AAAA %s\n", i, i, dst); else if (addr_bd->size == 4 && inet_ntop(AF_INET, addr_bd->data, dst, sizeof(dst))) - fprintf(fh,". NS "PRIsz".root-servers.getdnsapi.net.\n" - PRIsz".root-servers.getdnsapi.net. A %s\n", + fprintf(fh,". NS %"PRIsz".root-servers.getdnsapi.net.\n" + "%"PRIsz".root-servers.getdnsapi.net. A %s\n", i, i, dst); } fclose(fh); @@ -2615,9 +2721,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; @@ -2666,11 +2774,14 @@ getdns_context_set_upstream_recursive_servers(struct getdns_context *context, struct addrinfo hints; RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER); - RETURN_IF_NULL(upstream_list, GETDNS_RETURN_INVALID_PARAMETER); - r = getdns_list_get_length(upstream_list, &count); - if (count == 0 || r != GETDNS_RETURN_GOOD) { - return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; + if ( !upstream_list + || (r = getdns_list_get_length(upstream_list, &count)) + || count == 0) { + _getdns_upstreams_dereference(context->upstreams); + context->upstreams = NULL; + dispatch_updated(context, + GETDNS_CONTEXT_CODE_UPSTREAM_RECURSIVE_SERVERS); } memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ @@ -3411,16 +3522,13 @@ _getdns_ns_dns_setup(struct getdns_context *context) } getdns_return_t -_getdns_context_prepare_for_resolution(struct getdns_context *context, - int usenamespaces) +_getdns_context_prepare_for_resolution(getdns_context *context) { - size_t i; getdns_return_t r; RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER); - if (context->destroying) { - return GETDNS_RETURN_BAD_CONTEXT; - } + if (context->destroying) + return GETDNS_RETURN_BAD_CONTEXT; /* Transport can in theory be set per query in stub mode */ if (context->resolution_type == GETDNS_RESOLUTION_STUB && @@ -3497,40 +3605,21 @@ _getdns_context_prepare_for_resolution(struct getdns_context *context, */ - if (! usenamespaces) { - r = _getdns_ns_dns_setup(context); - if (r == GETDNS_RETURN_GOOD) - context->resolution_type_set = context->resolution_type; - return r; - } - - r = GETDNS_RETURN_GOOD; - for (i = 0; i < context->namespace_count; i++) { - switch (context->namespaces[i]) { - case GETDNS_NAMESPACE_DNS: - r = _getdns_ns_dns_setup(context); - break; - - default: - r = GETDNS_RETURN_BAD_CONTEXT; - break; - } - if (r != GETDNS_RETURN_GOOD) - return r; /* try again later (resolution_type_set) */ - } - context->resolution_type_set = context->resolution_type; + r = _getdns_ns_dns_setup(context); + if (r == GETDNS_RETURN_GOOD) + context->resolution_type_set = context->resolution_type; return r; } /* _getdns_context_prepare_for_resolution */ char * _getdns_strdup(const struct mem_funcs *mfs, const char *s) { - size_t sz = strlen(s) + 1; - char *r = GETDNS_XMALLOC(*mfs, char, sz); - if (r) - return memcpy(r, s, sz); - else + size_t sz; + char *r; + if (!s || !(r = GETDNS_XMALLOC(*mfs, char, (sz = strlen(s) + 1)))) return NULL; + else + return memcpy(r, s, sz); } struct getdns_bindata * @@ -3659,12 +3748,15 @@ getdns_context_get_eventloop(getdns_context *context, getdns_eventloop **loop) return GETDNS_RETURN_GOOD; } +static size_t _getdns_get_appdata(getdns_context *context, char *path); static getdns_dict* _get_context_settings(getdns_context* context) { getdns_dict *result = getdns_dict_create_with_context(context); getdns_list *list; size_t i; + const char *str_value; + char appdata_dir[_GETDNS_PATH_MAX] = ""; if (!result) return NULL; @@ -3685,6 +3777,8 @@ _get_context_settings(getdns_context* context) || ( context->edns_maximum_udp_payload_size != -1 && getdns_dict_set_int(result, "edns_maximum_udp_payload_size", context->edns_maximum_udp_payload_size)) + || getdns_dict_set_int(result, "edns_client_subnet_private", + context->edns_client_subnet_private) || getdns_dict_set_int(result, "edns_extended_rcode", context->edns_extended_rcode) || getdns_dict_set_int(result, "edns_version", @@ -3700,7 +3794,12 @@ _get_context_settings(getdns_context* context) || getdns_dict_set_int(result, "tls_backoff_time", context->tls_backoff_time) || getdns_dict_set_int(result, "tls_connection_retries", - context->tls_connection_retries)) + context->tls_connection_retries) + || getdns_dict_set_int(result, "tls_query_padding_blocksize", + context->tls_query_padding_blocksize) + || getdns_dict_set_int(result, "resolution_type", + context->resolution_type) + ) goto error; /* list fields */ @@ -3719,6 +3818,14 @@ _get_context_settings(getdns_context* context) getdns_list_destroy(list); goto error; } + if (getdns_context_get_dnssec_trust_anchors(context, &list)) + ; /* pass */ + + else if (list && _getdns_dict_set_this_list( + result, "dnssec_trust_anchors", list)) { + getdns_list_destroy(list); + goto error; + } if (context->dns_transport_count > 0) { /* create a namespace list */ if (!(list = getdns_list_create_with_context(context))) @@ -3754,6 +3861,15 @@ _get_context_settings(getdns_context* context) return NULL; } } + (void) _getdns_get_appdata(context, appdata_dir); + (void) getdns_dict_util_set_string(result, "appdata_dir", appdata_dir); + if (!getdns_context_get_trust_anchors_url(context, &str_value) && str_value) + (void) getdns_dict_util_set_string(result, "trust_anchors_url", str_value); + if (!getdns_context_get_trust_anchors_verify_CA(context, &str_value) && str_value) + (void) getdns_dict_util_set_string(result, "trust_anchors_verify_CA", str_value); + if (!getdns_context_get_trust_anchors_verify_email(context, &str_value) && str_value) + (void) getdns_dict_util_set_string(result, "trust_anchors_verify_email", str_value); + return result; error: getdns_dict_destroy(result); @@ -3771,9 +3887,24 @@ getdns_context_get_api_information(getdns_context* context) && ! getdns_dict_util_set_string( result, "version_string", GETDNS_VERSION) + && ! getdns_dict_set_int( + result, "version_number", getdns_get_version_number()) + + && ! getdns_dict_util_set_string( + result, "api_version_string", getdns_get_api_version()) + + && ! getdns_dict_set_int( + result, "api_version_number", getdns_get_api_version_number()) + && ! getdns_dict_util_set_string( result, "implementation_string", PACKAGE_URL) + && ! getdns_dict_util_set_string( + result, "compilation_comment", GETDNS_COMPILATION_COMMENT) + + && ! getdns_dict_util_set_string( + result, "trust_anchor_file", TRUST_ANCHOR_FILE) + && ! getdns_dict_set_int( result, "resolution_type", context->resolution_type) @@ -4375,6 +4506,12 @@ static getdns_return_t _get_list_or_read_file(const getdns_dict *config_dict, else r = GETDNS_RETURN_INVALID_PARAMETER; \ } +#define CONTEXT_SETTING_STRING(X) \ + } else if (_streq(setting, #X )) { \ + if (!(r = getdns_dict_get_bindata(config_dict, #X , &bd))) \ + r = getdns_context_set_ ## X( \ + context, (char *)bd->data); + static getdns_return_t _getdns_context_config_setting(getdns_context *context, const getdns_dict *config_dict, const getdns_bindata *setting) @@ -4386,6 +4523,7 @@ _getdns_context_config_setting(getdns_context *context, getdns_transport_list_t dns_transport_list[100]; size_t count, i; uint32_t n; + getdns_bindata *bd; int destroy_list = 0; if (_streq(setting, "all_context")) { @@ -4423,6 +4561,10 @@ _getdns_context_config_setting(getdns_context *context, CONTEXT_SETTING_INT(tls_backoff_time) CONTEXT_SETTING_INT(tls_connection_retries) CONTEXT_SETTING_INT(tls_query_padding_blocksize) + CONTEXT_SETTING_STRING(trust_anchors_url) + CONTEXT_SETTING_STRING(trust_anchors_verify_CA) + CONTEXT_SETTING_STRING(trust_anchors_verify_email) + CONTEXT_SETTING_STRING(appdata_dir) /**************************************/ /**** ****/ @@ -4475,8 +4617,14 @@ _getdns_context_config_setting(getdns_context *context, /**** Ignored context settings ****/ /**** ****/ /************************************/ - } else if (!_streq(setting, "implementation_string") && - !_streq(setting, "version_string")) { + } else if (!_streq(setting, "implementation_string") + && !_streq(setting, "version_string") + && !_streq(setting, "version_number") + && !_streq(setting, "api_version_string") + && !_streq(setting, "api_version_number") + && !_streq(setting, "trust_anchor_file") + && !_streq(setting, "compilation_comment") + ) { r = GETDNS_RETURN_NOT_IMPLEMENTED; } return r; @@ -4505,4 +4653,360 @@ getdns_context_config(getdns_context *context, const getdns_dict *config_dict) return r; } +static size_t _getdns_get_appdata(getdns_context *context, char *path) +{ + size_t len = 0; + +#ifdef USE_WINSOCK +# define SLASHTOK '\\' +# define APPDATA_SUBDIR "getdns" + + if (context->appdata_dir) { + (void) strcpy(path, context->appdata_dir); + len = strlen(path); + + } else 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 >= _GETDNS_PATH_MAX) + DEBUG_ANCHOR("ERROR %s(): Home path too long for appdata\n" + , __FUNC__); +#else +# define SLASHTOK '/' +# define APPDATA_SUBDIR ".getdns" + struct passwd *p = getpwuid(getuid()); + char *home = NULL; + + if (context->appdata_dir) { + (void) strcpy(path, context->appdata_dir); + len = strlen(path); + + } else 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 >= _GETDNS_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++] = SLASHTOK; + path[len ] = '\0'; + } + (void) strcpy(path + len, APPDATA_SUBDIR); + len += sizeof(APPDATA_SUBDIR) - 1; + } + if (len) { + if (path[len - 1] == '/' || path[len - 1] == '\\') { + path[--len] = '\0'; + } + if (0 > +#ifdef USE_WINSOCK + mkdir(path) +#else + mkdir(path, 0755) +#endif + && errno != EEXIST) + DEBUG_ANCHOR("ERROR %s(): Could not mkdir %s: %s\n" + , __FUNC__, path, strerror(errno)); + else { + path[len++] = SLASHTOK; + path[len ] = '\0'; + return len; + } + } + return 0; +} + +FILE *_getdns_context_get_priv_fp(getdns_context *context, const char *fn) +{ + char path[_GETDNS_PATH_MAX]; + FILE *f = NULL; + size_t len; + + (void) context; + if (!(len = _getdns_get_appdata(context, 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[_GETDNS_PATH_MAX], tmpfn[_GETDNS_PATH_MAX]; + int fd = -1; + FILE *f = NULL; + size_t len; + + if (!(len = _getdns_get_appdata(context, 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[_GETDNS_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(context, 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_anchors_url( + getdns_context *context, const char *url) +{ + const char *path; + size_t path_len; + + if (!context) + return GETDNS_RETURN_INVALID_PARAMETER; + + if (url) { + 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; + } + if (context->trust_anchors_url) + GETDNS_FREE(context->mf, context->trust_anchors_url); + context->trust_anchors_url = _getdns_strdup(&context->mf, url); + + dispatch_updated(context, GETDNS_CONTEXT_CODE_TRUST_ANCHORS_URL); + return GETDNS_RETURN_GOOD; +} + +getdns_return_t +getdns_context_get_trust_anchors_url( + getdns_context *context, const char **url) +{ + if (!context || !url) + return GETDNS_RETURN_INVALID_PARAMETER; + + *url = context && context->trust_anchors_url + ? context->trust_anchors_url + : _getdns_default_trust_anchors_url; + return GETDNS_RETURN_GOOD; +} + +getdns_return_t +getdns_context_set_trust_anchors_verify_CA( + getdns_context *context, const char *verify_CA) +{ + if (!context) + return GETDNS_RETURN_INVALID_PARAMETER; + + if (context->trust_anchors_verify_CA) + GETDNS_FREE(context->mf, context->trust_anchors_verify_CA); + context->trust_anchors_verify_CA = + _getdns_strdup(&context->mf, verify_CA); + + dispatch_updated( context + , GETDNS_CONTEXT_CODE_TRUST_ANCHORS_VERIFY_CA); + return GETDNS_RETURN_GOOD; +} + +getdns_return_t +getdns_context_get_trust_anchors_verify_CA( + getdns_context *context, const char **verify_CA) +{ + if (!verify_CA) + return GETDNS_RETURN_INVALID_PARAMETER; + + *verify_CA = context && context->trust_anchors_verify_CA + ? context->trust_anchors_verify_CA + : _getdns_default_trust_anchors_verify_CA; + return GETDNS_RETURN_GOOD; +} + +getdns_return_t +getdns_context_set_trust_anchors_verify_email( + getdns_context *context, const char *verify_email) +{ + if (!context) + return GETDNS_RETURN_INVALID_PARAMETER; + + if (context->trust_anchors_verify_email) + GETDNS_FREE(context->mf, context->trust_anchors_verify_email); + context->trust_anchors_verify_email = + _getdns_strdup(&context->mf, verify_email); + + dispatch_updated( context + , GETDNS_CONTEXT_CODE_TRUST_ANCHORS_VERIFY_EMAIL); + return GETDNS_RETURN_GOOD; +} + +getdns_return_t +getdns_context_get_trust_anchors_verify_email( + getdns_context *context, const char **verify_email) +{ + if (!verify_email) + return GETDNS_RETURN_INVALID_PARAMETER; + + *verify_email = context && context->trust_anchors_verify_email + ? context->trust_anchors_verify_email + : _getdns_default_trust_anchors_verify_email; + return GETDNS_RETURN_GOOD; +} + +getdns_return_t +getdns_context_set_appdata_dir( + getdns_context *context, const char *appdata_dir) +{ + if (!context) + return GETDNS_RETURN_INVALID_PARAMETER; + + if (context->appdata_dir) + GETDNS_FREE(context->mf, context->appdata_dir); + context->appdata_dir = _getdns_strdup(&context->mf, appdata_dir); + + dispatch_updated(context, GETDNS_CONTEXT_CODE_APPDATA_DIR); + return GETDNS_RETURN_GOOD; +} + + /* context.c */ diff --git a/src/context.h b/src/context.h index 03164522..1a6d93a4 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; @@ -170,6 +183,7 @@ typedef struct getdns_upstream { size_t conn_shutdowns; size_t conn_setup_failed; time_t conn_retry_time; + uint16_t conn_backoff_interval; size_t conn_backoffs; size_t total_responses; size_t total_timeouts; @@ -255,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; @@ -276,8 +328,24 @@ 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]; + + char *trust_anchors_url; + char *trust_anchors_verify_CA; + char *trust_anchors_verify_email; + + _getdns_ksks root_ksk; + + char *appdata_dir; + _getdns_property can_write_appdata; + getdns_upstreams *upstreams; uint16_t limit_outstanding_queries; uint32_t dnssec_allowed_skew; @@ -372,6 +440,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 */ @@ -415,11 +495,9 @@ void _getdns_context_log(getdns_context *context, uint64_t system, * Sets up the unbound contexts with stub or recursive behavior * if needed. * @param context previously initialized getdns_context - * @param usenamespaces if 0 then only use the DNS, else use context namespace list * @return GETDNS_RETURN_GOOD on success */ -getdns_return_t _getdns_context_prepare_for_resolution(struct getdns_context *context, - int usenamespaces); +getdns_return_t _getdns_context_prepare_for_resolution(getdns_context *context); /* Register a getdns_dns_req with context. * - Without pluggable unbound event API, @@ -470,4 +548,13 @@ 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); + #endif /* _GETDNS_CONTEXT_H_ */ diff --git a/src/convert.c b/src/convert.c index 180db8f5..71f25491 100644 --- a/src/convert.c +++ b/src/convert.c @@ -32,10 +32,10 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "config.h" #include #include #include -#include "config.h" #ifndef USE_WINSOCK #include #endif @@ -54,6 +54,7 @@ #include "dict.h" #include "list.h" #include "jsmn/jsmn.h" +#include "yaml/convert_yaml_to_json.h" #include "convert.h" #include "debug.h" @@ -1802,3 +1803,101 @@ getdns_str2int(const char *str, uint32_t *value) return GETDNS_RETURN_GOOD; } +#ifdef USE_YAML_CONFIG +getdns_return_t +getdns_yaml2dict(const char *str, getdns_dict **dict) +{ + char *jsonstr; + + if (!str || !dict) + return GETDNS_RETURN_INVALID_PARAMETER; + + jsonstr = yaml_string_to_json_string(str); + if (jsonstr) { + getdns_return_t res = getdns_str2dict(jsonstr, dict); + free(jsonstr); + return res; + } else { + return GETDNS_RETURN_GENERIC_ERROR; + } +} +#endif /* USE_YAML_CONFIG */ + +/* WT: I am not certain about the value of yaml2list... + * I don't see how yaml2bindata and yaml2int would be different from + * the str2bindata and str2int ones. + */ +#if 0 + +getdns_return_t +getdns_yaml2list(const char *str, getdns_list **list) +{ +#ifdef USE_YAML_CONFIG + char *jsonstr; + + if (!str || !list) + return GETDNS_RETURN_INVALID_PARAMETER; + + jsonstr = yaml_string_to_json_string(str); + if (jsonstr) { + getdns_return_t res = getdns_str2list(jsonstr, list); + free(jsonstr); + return res; + } else { + return GETDNS_RETURN_GENERIC_ERROR; + } +#else /* USE_YAML_CONFIG */ + (void) str; + (void) list; + return GETDNS_RETURN_NOT_IMPLEMENTED; +#endif /* USE_YAML_CONFIG */ +} + +getdns_return_t +getdns_yaml2bindata(const char *str, getdns_bindata **bindata) +{ +#ifdef USE_YAML_CONFIG + char *jsonstr; + + if (!str || !bindata) + return GETDNS_RETURN_INVALID_PARAMETER; + + jsonstr = yaml_string_to_json_string(str); + if (jsonstr) { + getdns_return_t res = getdns_str2bindata(jsonstr, bindata); + free(jsonstr); + return res; + } else { + return GETDNS_RETURN_GENERIC_ERROR; + } +#else /* USE_YAML_CONFIG */ + (void) str; + (void) bindata; + return GETDNS_RETURN_NOT_IMPLEMENTED; +#endif /* USE_YAML_CONFIG */ +} + +getdns_return_t +getdns_yaml2int(const char *str, uint32_t *value) +{ +#ifdef USE_YAML_CONFIG + char *jsonstr; + + if (!str || !value) + return GETDNS_RETURN_INVALID_PARAMETER; + + jsonstr = yaml_string_to_json_string(str); + if (jsonstr) { + getdns_return_t res = getdns_str2int(jsonstr, value); + free(jsonstr); + return res; + } else { + return GETDNS_RETURN_GENERIC_ERROR; + } +#else /* USE_YAML_CONFIG */ + (void) str; + (void) value; + return GETDNS_RETURN_NOT_IMPLEMENTED; +#endif /* USE_YAML_CONFIG */ +} +#endif diff --git a/src/debug.h b/src/debug.h index f779840b..d048def5 100644 --- a/src/debug.h +++ b/src/debug.h @@ -47,45 +47,58 @@ #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_dEbUgSyM = (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) + +#define DEBUG_NL(...) do { \ + struct timeval tv_dEbUgSyM; \ + struct tm tm_dEbUgSyM; \ + char buf_dEbUgSyM[10]; \ + time_t tsec_dEbUgSyM; \ + \ + gettimeofday(&tv_dEbUgSyM, NULL); \ + tsec_dEbUgSyM = (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]; \ +#define DEBUG_NL(...) do { \ + 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) - +#endif #define DEBUG_OFF(...) do {} while (0) @@ -162,14 +175,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/dict.c b/src/dict.c index d6986788..99e347ad 100644 --- a/src/dict.c +++ b/src/dict.c @@ -34,8 +34,8 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include #include "config.h" +#include #ifndef USE_WINSOCK #include #include diff --git a/src/dnssec.c b/src/dnssec.c index b631d3d3..afcf9e1e 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 @@ -601,7 +602,7 @@ static chain_head *add_rrset2val_chain(struct mem_funcs *mf, dname_len = *labels - last_label[-1] + 1; head_sz = (sizeof(chain_head) + dname_len + 7) / 8 * 8; node_count = last_label - labels - max_labels; - DEBUG_SEC( PRIsz" labels in common. "PRIsz" labels to allocate\n" + DEBUG_SEC( "%"PRIsz" labels in common. %"PRIsz" labels to allocate\n" , max_labels, node_count); if (! (region = GETDNS_XMALLOC(*mf, uint8_t, head_sz + @@ -801,11 +802,14 @@ static void add_pkt2val_chain(struct mem_funcs *mf, if (is_synthesized_cname(rrset)) continue; + if (!(rrsig = _getdns_rrsig_iter_init(&rrsig_spc, rrset)) + && _getdns_rr_iter_section(&i->rr_i) != SECTION_ANSWER) + continue; /* No sigs in authority section is okayish */ + if (!(head = add_rrset2val_chain(mf, chain_p, rrset, netreq))) continue; - for ( rrsig = _getdns_rrsig_iter_init(&rrsig_spc, rrset), n_rrsigs = 0 - ; rrsig + for ( n_rrsigs = 0; rrsig ; rrsig = _getdns_rrsig_iter_next(rrsig), n_rrsigs++) { /* Signature, so lookup DS/DNSKEY at signer's name */ @@ -933,6 +937,17 @@ static void val_chain_sched_soa(chain_head *head, const uint8_t *dname) val_chain_sched_soa_node(node); } +static chain_head *_dnskey_query(const chain_node *node) +{ + chain_head *head; + + for (head = node->chains; head; head = head->next) + if (head->rrset.rr_type == GETDNS_RRTYPE_DNSKEY && + head->parent == node) + return head; + return NULL; +} + static void val_chain_node_cb(getdns_dns_req *dnsreq); static void val_chain_sched_node(chain_node *node) { @@ -950,13 +965,27 @@ static void val_chain_sched_node(chain_node *node) DEBUG_SEC("schedule DS & DNSKEY lookup for %s\n", name); node->lock++; - if (! node->dnskey_req /* not scheduled */ && - _getdns_general_loop(context, loop, name, GETDNS_RRTYPE_DNSKEY, - CD_extension(node->chains->netreq->owner), - node, &node->dnskey_req, NULL, val_chain_node_cb)) + if (! node->dnskey_req) { + chain_head *head; - node->dnskey_req = NULL; + /* Reuse the DNSKEY query if this node is scheduled in the + * context of validating a DNSKEY query, because libunbound + * does not callback from a callback for the same query. + */ + if ((head = _dnskey_query(node))) { + DEBUG_SEC("Found DNSKEY head: %p\n", (void *)head); + node->dnskey_req = head->netreq; + node->dnskey.pkt = head->netreq->response; + node->dnskey.pkt_len = head->netreq->response_len; + + } else if (_getdns_general_loop( + context, loop, name, GETDNS_RRTYPE_DNSKEY, + CD_extension(node->chains->netreq->owner), + node, &node->dnskey_req, NULL, val_chain_node_cb)) + + node->dnskey_req = NULL; + } if (! node->ds_req && node->parent /* not root */ && _getdns_general_loop(context, loop, name, GETDNS_RRTYPE_DS, CD_extension(node->chains->netreq->owner), @@ -1423,7 +1452,7 @@ static int _getdns_verify_rrsig(struct mem_funcs *mf, /* More space needed for val_rrset */ val_rrset = GETDNS_XMALLOC(*mf, _getdns_rr_iter, n_rrs); } - DEBUG_SEC( "sizes: "PRIsz" rrs, "PRIsz" bytes for validation buffer\n" + DEBUG_SEC( "sizes: %"PRIsz" rrs, %"PRIsz" bytes for validation buffer\n" , n_rrs, valbuf_sz); qsort(val_rrset, n_rrs, sizeof(_getdns_rr_iter), _rr_iter_rdata_cmp); @@ -1480,7 +1509,7 @@ static int _getdns_verify_rrsig(struct mem_funcs *mf, gldns_buffer_write_u16_at(&valbuf, pos, (uint16_t)(gldns_buffer_position(&valbuf) - pos - 2)); } - DEBUG_SEC( "written to valbuf: "PRIsz" bytes\n" + DEBUG_SEC( "written to valbuf: %"PRIsz" bytes\n" , gldns_buffer_position(&valbuf)); assert(gldns_buffer_position(&valbuf) <= valbuf_sz); @@ -1902,7 +1931,7 @@ static int ds_authenticates_keys(struct mem_funcs *mf, if (digest_buf != digest_buf_spc) GETDNS_FREE(*mf, digest_buf); - DEBUG_SEC("HASH length mismatch "PRIsz" != "PRIsz"\n", + DEBUG_SEC("HASH length mismatch %"PRIsz" != %"PRIsz"\n", digest_len, ds->rr_i.nxt - ds->rr_i.rr_type-14); continue; } @@ -1925,7 +1954,7 @@ static int ds_authenticates_keys(struct mem_funcs *mf, max_supported_result = SIGNATURE_VERIFIED | keytag; } } - DEBUG_SEC("valid_dsses: "PRIsz", supported_dsses: "PRIsz"\n", + DEBUG_SEC("valid_dsses: %"PRIsz", supported_dsses: %"PRIsz"\n", valid_dsses, supported_dsses); if (valid_dsses && !supported_dsses) return NO_SUPPORTED_ALGORITHMS; @@ -2522,6 +2551,11 @@ static int chain_node_get_trusted_keys( node->dnskey_signer = keytag; return GETDNS_DNSSEC_SECURE; } + /* ta is the DNSKEY for this name? */ + if (_dname_equal(ta->name, node->dnskey.name)) { + *keys = ta; + return GETDNS_DNSSEC_SECURE; + } /* ta is parent's ZSK */ if ((keytag = key_proves_nonexistance( mf, now, skew, ta, &node->ds, NULL))) { @@ -2544,8 +2578,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 +2789,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 @@ -2905,6 +2971,26 @@ static void append_rrset2val_chain_list( _getdns_list_append_this_dict(val_chain_list, rr_dict)) getdns_dict_destroy(rr_dict); + /* Append the other RRSIGs, which were not used for validation too, + * because other validators might not have the same algorithm support. + */ + for ( rrsig = _getdns_rrsig_iter_init(&rrsig_spc, rrset) + ; rrsig + ; rrsig = _getdns_rrsig_iter_next(rrsig)) { + + if (rrsig->rr_i.nxt < rrsig->rr_i.rr_type + 28) + continue; + + if (gldns_read_uint16(rrsig->rr_i.rr_type + 26) + == (signer & 0xFFFF)) + continue; + + orig_ttl = gldns_read_uint32(rrsig->rr_i.rr_type + 14); + if ((rr_dict = _getdns_rr_iter2rr_dict_canonical( + &val_chain_list->mf, &rrsig->rr_i, &orig_ttl)) && + _getdns_list_append_this_dict(val_chain_list, rr_dict)) + getdns_dict_destroy(rr_dict); + } if (val_rrset != val_rrset_spc) GETDNS_FREE(val_chain_list->mf, val_rrset); } @@ -2999,6 +3085,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 _cleanup_chain(chain_head *chain, getdns_list *val_chain_list, int full) @@ -3078,30 +3180,40 @@ static void check_chain_complete(chain_head *chain) _getdns_rrset_iter tas_iter; if ((o = count_outstanding_requests(chain)) > 0) { - DEBUG_SEC(PRIsz" outstanding requests\n", o); + DEBUG_SEC("%"PRIsz" outstanding requests\n", o); return; } DEBUG_SEC("Chain done!\n"); 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, @@ -3110,10 +3222,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; @@ -3167,6 +3321,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; @@ -3185,6 +3340,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); + + *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; @@ -3224,7 +3418,7 @@ void _getdns_validation_chain_timeout(getdns_dns_req *dnsreq) void _getdns_cancel_validation_chain(getdns_dns_req *dnsreq) { - chain_head *head = dnsreq->chain, *next; + chain_head *head = dnsreq->chain, *next, *dnskey_head; chain_node *node; size_t node_count; @@ -3236,7 +3430,10 @@ void _getdns_cancel_validation_chain(getdns_dns_req *dnsreq) ; node_count ; node_count--, node = node->parent ) { - if (node->dnskey_req) + if (node->dnskey_req && + !( (dnskey_head = _dnskey_query(node)) + && dnskey_head->netreq == node->dnskey_req)) + _getdns_context_cancel_request( node->dnskey_req->owner); @@ -3267,6 +3464,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 @@ -3277,7 +3475,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 @@ -3415,13 +3616,17 @@ getdns_validate_dnssec3(const getdns_list *records_to_validate, fflush(stdout); #endif - if (!records_to_validate || !support_records || !trust_anchors) + if (!records_to_validate || !trust_anchors) return GETDNS_RETURN_INVALID_PARAMETER; mf = (struct mem_funcs *)&records_to_validate->mf; /* First convert everything to wire format */ - if (!(support = _getdns_list2wire(support_records, + + if (!support_records) + (void) memset((support = support_buf), 0, GLDNS_HEADER_SIZE); + + else if (!(support = _getdns_list2wire(support_records, support_buf, &support_len, mf))) return GETDNS_RETURN_MEMORY_ERROR; @@ -3440,7 +3645,7 @@ getdns_validate_dnssec3(const getdns_list *records_to_validate, for (i = 0; !getdns_list_get_dict(records_to_validate,i,&reply); i++) { - DEBUG_SEC("REPLY "PRIsz", r: %d\n", i, r); + DEBUG_SEC("REPLY %"PRIsz", r: %d\n", i, r); if (to_val != to_val_buf) GETDNS_FREE(*mf, to_val); to_val_len = sizeof(to_val_buf); @@ -3468,7 +3673,7 @@ getdns_validate_dnssec3(const getdns_list *records_to_validate, break; } } - DEBUG_SEC("REPLY "PRIsz", r: %d\n", i, r); + DEBUG_SEC("REPLY %"PRIsz", r: %d\n", i, r); exit_free_to_val: if (to_val != to_val_buf) 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..6924ebe1 100644 --- a/src/extension/poll_eventloop.c +++ b/src/extension/poll_eventloop.c @@ -27,13 +27,8 @@ #include "config.h" -#ifdef HAVE_SYS_POLL_H -#include -#else -#ifndef USE_WINSOCK -#include -#endif -#endif +#include "util-internal.h" +#include "platform.h" #ifdef HAVE_SYS_RESOURCE_H #include #endif @@ -148,7 +143,7 @@ static uint64_t get_now_plus(uint64_t amount) uint64_t now; if (gettimeofday(&tv, NULL)) { - perror("gettimeofday() failed"); + _getdns_perror("gettimeofday() failed"); exit(EXIT_FAILURE); } now = tv.tv_sec * 1000000 + tv.tv_usec; @@ -296,7 +291,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 +302,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 @@ -408,11 +406,9 @@ poll_eventloop_run_once(getdns_eventloop *loop, int blocking) { Sleep(poll_timeout); } else - if (WSAPoll(poll_loop->pfds, poll_loop->fd_events_free, poll_timeout) < 0) { -#else - if (poll(poll_loop->pfds, poll_loop->fd_events_free, poll_timeout) < 0) { #endif - perror("poll() failed"); + if (_getdns_poll(poll_loop->pfds, poll_loop->fd_events_free, poll_timeout) < 0) { + _getdns_perror("poll() failed"); exit(EXIT_FAILURE); } now = get_now_plus(0); diff --git a/src/extension/select_eventloop.c b/src/extension/select_eventloop.c index 47769afd..7980d0da 100644 --- a/src/extension/select_eventloop.c +++ b/src/extension/select_eventloop.c @@ -27,9 +27,10 @@ #include "config.h" -#include "extension/select_eventloop.h" #include "debug.h" #include "types-internal.h" +#include "platform.h" +#include "extension/select_eventloop.h" static uint64_t get_now_plus(uint64_t amount) { @@ -37,7 +38,7 @@ static uint64_t get_now_plus(uint64_t amount) uint64_t now; if (gettimeofday(&tv, NULL)) { - perror("gettimeofday() failed"); + _getdns_perror("gettimeofday() failed"); exit(EXIT_FAILURE); } now = tv.tv_sec * 1000000 + tv.tv_usec; @@ -235,20 +236,21 @@ select_eventloop_run_once(getdns_eventloop *loop, int blocking) tv.tv_usec = (long)((timeout - now) % 1000000); } #ifdef USE_WINSOCK - if (max_fd == -1) - { - if (timeout != TIMEOUT_FOREVER) - { - uint32_t timeout_ms = (tv.tv_usec / 1000) + (tv.tv_sec * 1000); - Sleep(timeout_ms); - } - } else + if (max_fd == -1) { + if (timeout != TIMEOUT_FOREVER) { + uint32_t timeout_ms = (tv.tv_usec / 1000) + (tv.tv_sec * 1000); + Sleep(timeout_ms); + } + } else { #endif if (select(max_fd + 1, &readfds, &writefds, NULL, (timeout == TIMEOUT_FOREVER ? NULL : &tv)) < 0) { - perror("select() failed"); + _getdns_perror("select() failed"); exit(EXIT_FAILURE); } +#ifdef USE_WINSOCK + } +#endif now = get_now_plus(0); for (fd = 0; fd < (int)FD_SETSIZE; fd++) { if (select_loop->fd_events[fd] && 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..b263c0a9 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,18 @@ _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 + && (dns_req->dnssec_return_status || + dns_req->dnssec_return_only_secure || + dns_req->dnssec_return_all_statuses) + && _getdns_bogus(dns_req)) )) { /* Reschedule timeout for this DNS request */ @@ -235,6 +241,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( @@ -442,14 +449,12 @@ _getdns_submit_netreq(getdns_network_req *netreq, uint64_t *now_ms) if (_getdns_ub_loop_enabled(&context->ub_loop)) ub_resolve_r = ub_resolve_event(context->unbound_ctx, name, netreq->request_type, dns_req->request_class, - netreq, ub_resolve_event_callback, &(netreq->unbound_id)) ? - GETDNS_RETURN_GENERIC_ERROR : GETDNS_RETURN_GOOD; + netreq, ub_resolve_event_callback, &(netreq->unbound_id)); else #endif ub_resolve_r = ub_resolve_async(context->unbound_ctx, name, netreq->request_type, dns_req->request_class, - netreq, ub_resolve_callback, &(netreq->unbound_id)) ? - GETDNS_RETURN_GENERIC_ERROR : GETDNS_RETURN_GOOD; + netreq, ub_resolve_callback, &(netreq->unbound_id)); if (dnsreq_freed) return DNS_REQ_FINISHED; dns_req->freed = NULL; @@ -570,11 +575,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,9 +590,31 @@ 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; + } + if ((r = _getdns_context_prepare_for_resolution(context))) + ; /* pass */ + /* issue all network requests */ - for ( netreq_p = req->netreqs + else for ( netreq_p = req->netreqs ; !r && (netreq = *netreq_p) ; netreq_p++) { if ((r = _getdns_submit_netreq(netreq, &now_ms))) { @@ -605,7 +627,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 +661,16 @@ 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; + } + if ((r = _getdns_context_prepare_for_resolution(context))) + break; /* 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 571eeae7..fb671ad9 100644 --- a/src/getdns/getdns_extra.h.in +++ b/src/getdns/getdns_extra.h.in @@ -79,6 +79,16 @@ 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_ANCHORS_URL 625 +#define GETDNS_CONTEXT_CODE_TRUST_ANCHORS_URL_TEXT "Change related to getdns_context_set_trust_anchors_url" +#define GETDNS_CONTEXT_CODE_TRUST_ANCHORS_VERIFY_CA 626 +#define GETDNS_CONTEXT_CODE_TRUST_ANCHORS_VERIFY_CA_TEXT "Change related to getdns_context_set_trust_anchors_verify_ca" +#define GETDNS_CONTEXT_CODE_TRUST_ANCHORS_VERIFY_EMAIL 627 +#define GETDNS_CONTEXT_CODE_TRUST_ANCHORS_VERIFY_EMAIL_TEXT "Change related to getdns_context_set_trust_anchors_verify_email" +#define GETDNS_CONTEXT_CODE_APPDATA_DIR 628 +#define GETDNS_CONTEXT_CODE_APPDATA_DIR_TEXT "Change related to getdns_context_set_appdata_dir" + /** @} */ @@ -520,7 +530,7 @@ typedef enum getdns_loglevel_type { #define GETDNS_LOG_CRIT_TEXT "Critical conditions" #define GETDNS_LOG_ERR_TEXT "Error conditions" #define GETDNS_LOG_WARNING_TEXT "Warning conditions" -#define GETDNS_LOG_NOTICE_TEXT "normal, but significant, condition" +#define GETDNS_LOG_NOTICE_TEXT "Normal, but significant, condition" #define GETDNS_LOG_INFO_TEXT "Informational message" #define GETDNS_LOG_DEBUG_TEXT "Debug-level message" @@ -531,12 +541,145 @@ typedef void (*getdns_logfunc_type) (void *userarg, uint64_t log_systems, getdns_loglevel_type, const char *, va_list ap); /** - * + * Register a function that will be called when there is something to log + * equally or more severe than the given level for the given system. + * @param[in] context The context from which to get the setting + * @param[in] userarg A user defined argument to be passed to the + * log function. + * @param[in] system A bitwise ORed collection of systems for which the log + * function should be called. Currently only logging + * information about upstream statistics is available; + * i.e.: GETDNS_LOG_UPSTREAM_STATS + * @param[in] level A severity level. The log function will be called + * only for messages with an equal or more severe level. + * More severe has a lower value. + * @param[in] func The log function to call with the user argument, + * the system for which the log message, the severity + * level, a printf style format string and the arguments + * for the format string, as parameter. + * @see getdns_loglevel_type + * @see getdns_logfunc_type + * @return GETDNS_RETURN_GOOD when successful + * @return GETDNS_RETURN_INVALID_PARAMETER when context was NULL. */ getdns_return_t getdns_context_set_logfunc(getdns_context *context, void *userarg, uint64_t system, getdns_loglevel_type level, getdns_logfunc_type func); +/** + * Define the location for storing library specific data. The location should + * be writable for the current user using the application with which the + * library is linked. Currently this is only used for storing data concerning + * zero configuration dnssec. + * @param[in] context The context from which to get the setting + * @param[in] appdata_dir A user writable location in which the library can + * store data. The last element of the path is tried + * to be created if it does not exist. When NULL is + * given, the default location is used which is + * ${HOME}/.getdns/ on Unix line systems (Linux, BSD's, + * MacOS), and %AppData%\getnds\ on Windows. + * @return GETDNS_RETURN_GOOD when successful + * @return GETDNS_RETURN_INVALID_PARAMETER when context was NULL. + */ +getdns_return_t +getdns_context_set_appdata_dir( + getdns_context *context, const char *appdata_dir); + +/** + * Sets the url for the location of the XML file from which to fetch the + * trust anchors with Zero configuration DNSSEC. The url should be for + * http, and the file should have the .xml extension. + * + * Alongside the XML file, also the S/MIME signature that will be used to + * validate the XML file, will be fetched from the url with the .xml extension + * replaced by .p7s. + * + * When successfully validated with the verify Certificate Authority and the + * verify email address, the context will be equipped with the DNSSEC trust + * anchors defined within the XML file as described in RFC7958. + * The XML file will also be stored together with the .p7s file in the + * appdata directory with the names "root-anchors.xml" and "root-anchors.p7s" + * respectively. + * + * When the trust-anchors from the XML file are used, the root DNSKEY is + * tracked and stored in the appdata directory too as "root.key" + * + * Trust anchors from the XML file will be tried when: + * - There were no other trust anchors provided, either by the default + * trust anchor file "@TRUST_ANCHOR_FILE@", or set with the + * getdns_context_set_dnssec_trust_anchors() function. + * - or the available trust anchors (from the default location or set by + * the application) caused the root DNSKEY rrset to be BOGUS. + * + * Trust anchors from the XML file will be read from the root-anchors.xml + * file in appdata directory and will only be used when validation with the + * S/MIME signatures in root-anchors.p7s succeeds with the verification + * Certificate Authority and the verification email address. + * + * A (new) version of "root-anchors.xml" and "root-anchors.p7s" will be + * fetched when: + * - The appdata directory is writeable by the current used, but the + * "root-anchors.xml" or "root-anchors.p7s" files were not available. + * - or there is a new root DNSKEY RRset (or signature) and it contains + * key_ids which were not in "root-anchors.xml." + * + * @see getdns_context_get_trust_anchors_url + * @see getdns_context_set_trust_anchors_verify_CA + * @see getdns_context_set_trust_anchors_verify_email + * @param[in] context The context to configure + * @param[in] url The url including the XML file from which the + * trust anchors (and the S/MIME signature) will be + * fetched. Default is: + * http://data.iana.org/root-anchors/root-anchors.xml + * When NULL is given, the default will be used. + * @return GETDNS_RETURN_GOOD when successful + * @return GETDNS_RETURN_INVALID_PARAMETER when context was NULL. + */ +getdns_return_t +getdns_context_set_trust_anchors_url(getdns_context *context, const char *url); + +/** + * Sets the public certificate for the Certificate Authority with which to + * validate the XML file with the S/MIME signatures fetch from the url + * given with the getdns_context_set_trust_anchors_url() function. + * @see getdns_context_get_trust_anchors_verify_CA + * @see getdns_context_set_trust_anchors_url + * @see getdns_context_set_trust_anchors_verify_email + * @param[in] context The context to configure + * @param[in] verify_CA The certificate of the Certificate Authority with + * which to validate the XML trust anchors. + * The default is the ICANN Root CA, which is valid + * till Dec 18 2029. + * When NULL is given, the default will be used. + * When an empty string is given, Zero configuration + * DNSSEC will be disabled. + * @return GETDNS_RETURN_GOOD when successful + * @return GETDNS_RETURN_INVALID_PARAMETER when context was NULL. + */ +getdns_return_t +getdns_context_set_trust_anchors_verify_CA( + getdns_context *context, const char *verify_CA); + +/** + * Sets the email address for the Subject of the signer's certificate from the + * p7s signature file with which to validate the XML file fetched from the url + * given with the getdns_context_set_trust_anchors_url() function. + * @see getdns_context_get_trust_anchors_verify_email + * @see getdns_context_set_trust_anchors_url + * @see getdns_context_set_trust_anchors_verify_CA + * @param[in] context The context to configure + * @param[in] verify_email Only signatures from this name are allowed. + * The default dnssec@iana.org. + * When NULL is given, the default will be used. + * When an empty string is given, Zero configuration + * DNSSEC will be disabled. + * @return GETDNS_RETURN_GOOD when successful + * @return GETDNS_RETURN_INVALID_PARAMETER when context was NULL. + */ +getdns_return_t +getdns_context_set_trust_anchors_verify_email( + getdns_context *context, const char *verify_email); + /** * Get the current resolution type setting from this context. * @see getdns_context_set_resolution_type @@ -902,6 +1045,96 @@ getdns_return_t getdns_context_get_update_callback(getdns_context *context, void **userarg, void (**value) (getdns_context *, getdns_context_code_t, void *)); + +/** + * Gets the url for the location of the XML file from which to fetch the + * trust anchors with Zero configuration DNSSEC. + * + * Alongside the XML file, also the S/MIME signature that will be used to + * validate the XML file, will be fetched from the url with the .xml extension + * replaced by .p7s. + * + * When successfully validated with the verify Certificate Authority and the + * verify email address, the context will be equipped with the DNSSEC trust + * anchors defined within the XML file as described in RFC7958. + * The XML file will also be stored together with the .p7s file in the + * appdata directory with the names "root-anchors.xml" and "root-anchors.p7s" + * respectively. + * + * When the trust-anchors from the XML file are used, the root DNSKEY is + * tracked and stored in the appdata directory too as "root.key" + * + * Trust anchors from the XML file will be tried when: + * - There were no other trust anchors provided, either by the default + * trust anchor file "@TRUST_ANCHOR_FILE@", or set with the + * getdns_context_set_dnssec_trust_anchors() function. + * - or the available trust anchors (from the default location or set by + * the application) caused the root DNSKEY rrset to be BOGUS. + * + * Trust anchors from the XML file will be read from the root-anchors.xml + * file in appdata directory and will only be used when validation with the + * S/MIME signatures in root-anchors.p7s succeeds with the verification + * Certificate Authority and the verification email address. + * + * A (new) version of "root-anchors.xml" and "root-anchors.p7s" will be + * fetched when: + * - The appdata directory is writeable by the current used, but the + * "root-anchors.xml" or "root-anchors.p7s" files were not available. + * - or there is a new root DNSKEY RRset (or signature) and it contains + * key_ids which were not in "root-anchors.xml." + * + * @see getdns_context_set_trust_anchors_url + * @see getdns_context_get_trust_anchors_verify_CA + * @see getdns_context_get_trust_anchors_verify_email + * @param[in] context The context to configure + * @param[out] url The url including the XML file, from which the + * trust anchors (and the S/MIME signature) will be + * fetched. Default is: + * http://data.iana.org/root-anchors/root-anchors.xml + * @return GETDNS_RETURN_GOOD when successful + * @return GETDNS_RETURN_INVALID_PARAMETER when context was NULL. + */ +getdns_return_t +getdns_context_get_trust_anchors_url( + getdns_context *context, const char **url); + +/** + * Gets the public certificate for the Certificate Authority with which to + * validate the XML file with the S/MIME signatures fetch from the url + * given with the getdns_context_set_trust_anchors_url() function. + * @see getdns_context_set_trust_anchors_verify_CA + * @see getdns_context_get_trust_anchors_url + * @see getdns_context_get_trust_anchors_verify_email + * @param[in] context The context to configure + * @param[out] verify_CA The certificate of the Certificate Authority with + * which to validate the XML trust anchors. + * The default is the ICANN Root CA, which is valid + * till Dec 18 2029. + * @return GETDNS_RETURN_GOOD when successful + * @return GETDNS_RETURN_INVALID_PARAMETER when context was NULL. + */ +getdns_return_t +getdns_context_get_trust_anchors_verify_CA( + getdns_context *context, const char **verify_CA); + +/** + * Gets the email address for the Subject of the signer's certificate from the + * p7s signature file with which to validate the XML file fetched from the url + * given with the getdns_context_set_trust_anchors_url() function. + * @see getdns_context_set_trust_anchors_verify_email + * @see getdns_context_get_trust_anchors_url + * @see getdns_context_get_trust_anchors_verify_CA + * @param[in] context The context to configure + * @param[out] verify_email Only signatures from this name are allowed. + * The default dnssec@iana.org. + * @return GETDNS_RETURN_GOOD when successful + * @return GETDNS_RETURN_INVALID_PARAMETER when context was NULL. + */ +getdns_return_t +getdns_context_get_trust_anchors_verify_email( + getdns_context *context, const char **verify_email); + + /** @} */ @@ -1723,6 +1956,7 @@ getdns_str2bindata(const char *str, getdns_bindata **bindata); getdns_return_t getdns_str2int(const char *str, uint32_t *value); + /** @} */ 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/gldns/wire2str.c b/src/gldns/wire2str.c index 2718925e..f6fda2e5 100644 --- a/src/gldns/wire2str.c +++ b/src/gldns/wire2str.c @@ -1059,11 +1059,7 @@ int gldns_wire2str_tsigtime_scan(uint8_t** d, size_t* dl, char** s, size_t* sl) d4 = (*d)[4]; d5 = (*d)[5]; tsigtime = (d0<<40) | (d1<<32) | (d2<<24) | (d3<<16) | (d4<<8) | d5; -#ifndef USE_WINSOCK - w = gldns_str_print(s, sl, "%llu", (long long)tsigtime); -#else - w = gldns_str_print(s, sl, "%I64u", (long long)tsigtime); -#endif + w = gldns_str_print(s, sl, "%"PRIu64, (uint64_t)tsigtime); (*d)+=6; (*dl)-=6; return w; @@ -1746,13 +1742,8 @@ int gldns_wire2str_edns_llq_print(char** s, size_t* sl, uint8_t* data, if(error_code < llq_errors_num) w += gldns_str_print(s, sl, " %s", llq_errors[error_code]); else w += gldns_str_print(s, sl, " error %d", (int)error_code); -#ifndef USE_WINSOCK - w += gldns_str_print(s, sl, " id %llx lease-life %lu", - (unsigned long long)llq_id, (unsigned long)lease_life); -#else - w += gldns_str_print(s, sl, " id %I64x lease-life %lu", - (unsigned long long)llq_id, (unsigned long)lease_life); -#endif + w += gldns_str_print(s, sl, " id %"PRIx64" lease-life %lu", + (uint64_t)llq_id, (unsigned long)lease_life); return w; } diff --git a/src/libgetdns.symbols b/src/libgetdns.symbols index 43564ecd..0d741b2a 100644 --- a/src/libgetdns.symbols +++ b/src/libgetdns.symbols @@ -33,10 +33,14 @@ 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_anchors_url +getdns_context_get_trust_anchors_verify_CA +getdns_context_get_trust_anchors_verify_email getdns_context_get_update_callback getdns_context_get_upstream_recursive_servers getdns_context_process_async getdns_context_run +getdns_context_set_appdata_dir getdns_context_set_append_name getdns_context_set_context_update_callback getdns_context_set_dns_root_servers @@ -67,6 +71,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_anchors_url +getdns_context_set_trust_anchors_verify_CA +getdns_context_set_trust_anchors_verify_email getdns_context_set_update_callback getdns_context_set_upstream_recursive_servers getdns_context_set_use_threads diff --git a/src/mdns.c b/src/mdns.c index 073c48cb..30bdab6e 100644 --- a/src/mdns.c +++ b/src/mdns.c @@ -26,28 +26,11 @@ #include "gldns/pkthdr.h" #include "gldns/rrdef.h" #include "util-internal.h" +#include "platform.h" #include "mdns.h" #ifdef HAVE_MDNS_SUPPORT -#ifdef USE_WINSOCK -typedef u_short sa_family_t; -#define _getdns_EWOULDBLOCK (WSAGetLastError() == WSATRY_AGAIN ||\ - WSAGetLastError() == WSAEWOULDBLOCK) -#define _getdns_EINPROGRESS (WSAGetLastError() == WSAEINPROGRESS) -#else -#define _getdns_EWOULDBLOCK (errno == EAGAIN || errno == EWOULDBLOCK) -#define _getdns_EINPROGRESS (errno == EINPROGRESS) -#define SOCKADDR struct sockaddr -#define SOCKADDR_IN struct sockaddr_in -#define SOCKADDR_IN6 struct sockaddr_in6 -#define SOCKET int -#define IP_MREQ struct ip_mreq -#define IPV6_MREQ struct ipv6_mreq -#define BOOL int -#define TRUE 1 -#endif - /* Define IPV6_ADD_MEMBERSHIP for FreeBSD and Mac OS X */ #ifndef IPV6_ADD_MEMBERSHIP #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP @@ -1148,7 +1131,8 @@ mdns_udp_multicast_read_cb(void *userarg) sizeof(cnx->response), 0, NULL, NULL); - if (read == -1 && _getdns_EWOULDBLOCK) + if (read == -1 && (_getdns_socketerror() == _getdns_EWOULDBLOCK || + _getdns_socketerror() == _getdns_ECONNRESET)) return; /* TODO: this will stop the receive loop! */ if (read >= GLDNS_HEADER_SIZE) @@ -1353,11 +1337,7 @@ static int mdns_open_ipv4_multicast(SOCKADDR_STORAGE* mcast_dest, int* mcast_des if (ret != 0 && fd4 != -1) { -#ifdef USE_WINSOCK - closesocket(fd4); -#else - close(fd4); -#endif + _getdns_closesocket(fd4); fd4 = -1; } @@ -1428,11 +1408,7 @@ static int mdns_open_ipv6_multicast(SOCKADDR_STORAGE* mcast_dest, int* mcast_des if (ret != 0 && fd6 != -1) { -#ifdef USE_WINSOCK - closesocket(fd6); -#else - close(fd6); -#endif + _getdns_closesocket(fd6); fd6 = -1; } @@ -1514,11 +1490,7 @@ static getdns_return_t mdns_delayed_network_init(struct getdns_context *context) GETDNS_CLEAR_EVENT(context->extension , &context->mdns_connection[i].event); -#ifdef USE_WINSOCK - closesocket(context->mdns_connection[i].fd); -#else - close(context->mdns_connection[i].fd); -#endif + _getdns_closesocket(context->mdns_connection[i].fd); } } @@ -1657,11 +1629,7 @@ void _getdns_mdns_context_destroy(struct getdns_context *context) /* suppress the receive event */ GETDNS_CLEAR_EVENT(context->extension, &context->mdns_connection[i].event); /* close the socket */ -#ifdef USE_WINSOCK - closesocket(context->mdns_connection[i].fd); -#else - close(context->mdns_connection[i].fd); -#endif + _getdns_closesocket(context->mdns_connection[i].fd); } GETDNS_FREE(context->mf, context->mdns_connection); @@ -1686,11 +1654,7 @@ _getdns_cancel_mdns_request(getdns_network_req *netreq) { mdns_cleanup(netreq); if (netreq->fd >= 0) { -#ifdef USE_WINSOCK - closesocket(netreq->fd); -#else - close(netreq->fd); -#endif + _getdns_closesocket(netreq->fd); } } @@ -1706,11 +1670,7 @@ mdns_timeout_cb(void *userarg) /* Check the required cleanup */ mdns_cleanup(netreq); if (netreq->fd >= 0) -#ifdef USE_WINSOCK - closesocket(netreq->fd); -#else - close(netreq->fd); -#endif + _getdns_closesocket(netreq->fd); _getdns_netreq_change_state(netreq, NET_REQ_TIMED_OUT); if (netreq->owner->user_callback) { netreq->debug_end_time = _getdns_get_time_as_uintt64(); @@ -1745,7 +1705,8 @@ mdns_udp_read_cb(void *userarg) * i.e. overflow */ 0, NULL, NULL); - if (read == -1 && _getdns_EWOULDBLOCK) + if (read == -1 && (_getdns_socketerror() == _getdns_EWOULDBLOCK || + _getdns_socketerror() == _getdns_ECONNRESET)) return; if (read < GLDNS_HEADER_SIZE) @@ -1759,11 +1720,7 @@ mdns_udp_read_cb(void *userarg) // TODO: check that the source address originates from the local network. // TODO: check TTL = 255 -#ifdef USE_WINSOCK - closesocket(netreq->fd); -#else - close(netreq->fd); -#endif + _getdns_closesocket(netreq->fd); /* * TODO: how to handle an MDNS response with TC bit set? * Ignore it for now, as we do not support any kind of TCP fallback @@ -1814,11 +1771,7 @@ mdns_udp_write_cb(void *userarg) netreq->fd, (const void *)netreq->query, pkt_len, 0, (struct sockaddr *)&mdns_mcast_v4, sizeof(mdns_mcast_v4))) { -#ifdef USE_WINSOCK - closesocket(netreq->fd); -#else - close(netreq->fd); -#endif + _getdns_closesocket(netreq->fd); return; } GETDNS_SCHEDULE_EVENT( diff --git a/src/mdns.h b/src/mdns.h index b7c7d20c..79ec05fe 100644 --- a/src/mdns.h +++ b/src/mdns.h @@ -24,13 +24,11 @@ #ifdef HAVE_MDNS_SUPPORT #include "getdns/getdns.h" #include "types-internal.h" +#include "util-internal.h" +#include "platform.h" #include "util/lruhash.h" #include "config.h" -#ifndef USE_WINSOCK -#define SOCKADDR_STORAGE struct sockaddr_storage -#endif - getdns_return_t _getdns_submit_mdns_request(getdns_network_req *netreq); diff --git a/src/mk-symfiles.sh b/src/mk-symfiles.sh index 597657b7..099181e6 100755 --- a/src/mk-symfiles.sh +++ b/src/mk-symfiles.sh @@ -8,6 +8,7 @@ write_symbols() { } write_symbols libgetdns.symbols getdns/getdns.h.in getdns/getdns_extra.h.in +#echo getdns_yaml2dict >> libgetdns.symbols echo plain_mem_funcs_user_arg >> libgetdns.symbols echo priv_getdns_context_mf >> libgetdns.symbols write_symbols extension/libevent.symbols getdns/getdns_ext_libevent.h diff --git a/src/platform.c b/src/platform.c new file mode 100644 index 00000000..61f2bdb6 --- /dev/null +++ b/src/platform.c @@ -0,0 +1,69 @@ +/** + * + * \file platform.c + * @brief general functions with platform-dependent implementations + * + */ + +/* + * Copyright (c) 2017, NLnet Labs, Sinodun + * 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 "platform.h" + +#include + +#ifdef USE_WINSOCK + +void _getdns_perror(const char *str) +{ + char msg[256]; + int errid = WSAGetLastError(); + + *msg = '\0'; + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + errid, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + msg, + sizeof(msg), + NULL); + if (*msg == '\0') + sprintf(msg, "Unknown error: %d", errid); + if (str && *str != '\0') + fprintf(stderr, "%s: ", str); + fputs(msg, stderr); +} + +#else + +void _getdns_perror(const char *str) +{ + perror(str); +} + +#endif diff --git a/src/platform.h b/src/platform.h new file mode 100644 index 00000000..e0ec5454 --- /dev/null +++ b/src/platform.h @@ -0,0 +1,84 @@ +/** + * + * \file platform.h + * @brief general functions with platform-dependent implementations + * + */ + +/* + * Copyright (c) 2017, NLnet Labs, Sinodun + * 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 PLATFORM_H +#define PLATFORM_H + +#include "config.h" + +#ifdef USE_WINSOCK +typedef u_short sa_family_t; +#define _getdns_EAGAIN (WSATRY_AGAIN) +#define _getdns_EWOULDBLOCK (WSAEWOULDBLOCK) +#define _getdns_EINPROGRESS (WSAEINPROGRESS) +#define _getdns_EMFILE (WSAEMFILE) +#define _getdns_ECONNRESET (WSAECONNRESET) + +#define _getdns_closesocket(fd) closesocket(fd) +#define _getdns_poll(fdarray, nsockets, timer) WSAPoll(fdarray, nsockets, timer) +#define _getdns_socketerror() (WSAGetLastError()) + +#else /* USE_WINSOCK */ + +#ifdef HAVE_SYS_POLL_H +# include +#else +# include +#endif + +#define _getdns_EAGAIN (EAGAIN) +#define _getdns_EWOULDBLOCK (EWOULDBLOCK) +#define _getdns_EINPROGRESS (EINPROGRESS) +#define _getdns_EMFILE (EMFILE) +#define _getdns_ECONNRESET (ECONNRESET) + +#define SOCKADDR struct sockaddr +#define SOCKADDR_IN struct sockaddr_in +#define SOCKADDR_IN6 struct sockaddr_in6 +#define SOCKADDR_STORAGE struct sockaddr_storage +#define SOCKET int + +#define IP_MREQ struct ip_mreq +#define IPV6_MREQ struct ipv6_mreq +#define BOOL int +#define TRUE 1 + +#define _getdns_closesocket(fd) close(fd) +#define _getdns_poll(fdarray, nsockets, timer) poll(fdarray, nsockets, timer) +#define _getdns_socketerror() (errno) +#endif + +void _getdns_perror(const char *str); + +#endif diff --git a/src/pubkey-pinning.c b/src/pubkey-pinning.c index a960ab66..1b9674fd 100644 --- a/src/pubkey-pinning.c +++ b/src/pubkey-pinning.c @@ -437,14 +437,14 @@ _getdns_verify_pinset_match(const sha256_pin_t *pinset, /* digest the cert with sha256 */ len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(x), NULL); if (len > (int)sizeof(raw)) { - DEBUG_STUB("%s %-35s: Pubkey %d is larger than "PRIsz" octets\n", + DEBUG_STUB("%s %-35s: Pubkey %d is larger than %"PRIsz" octets\n", STUB_DEBUG_SETUP_TLS, __FUNC__, i, sizeof(raw)); continue; } next = raw; i2d_X509_PUBKEY(X509_get_X509_PUBKEY(x), &next); if (next - raw != len) { - DEBUG_STUB("%s %-35s: Pubkey %d claimed it needed %d octets, really needed "PRIsz"\n", + DEBUG_STUB("%s %-35s: Pubkey %d claimed it needed %d octets, really needed %"PRIsz"\n", STUB_DEBUG_SETUP_TLS, __FUNC__, i, len, next - raw); continue; } @@ -453,7 +453,7 @@ _getdns_verify_pinset_match(const sha256_pin_t *pinset, /* compare it */ for (p = pinset; p; p = p->next) if (0 == memcmp(buf, p->pin, sizeof(p->pin))) { - DEBUG_STUB("%s %-35s: Pubkey %d matched pin %p ("PRIsz")\n", + DEBUG_STUB("%s %-35s: Pubkey %d matched pin %p (%"PRIsz")\n", STUB_DEBUG_SETUP_TLS, __FUNC__, i, (void*)p, sizeof(p->pin)); return GETDNS_RETURN_GOOD; } else diff --git a/src/request-internal.c b/src/request-internal.c index 15f97179..c9f27db0 100644 --- a/src/request-internal.c +++ b/src/request-internal.c @@ -107,6 +107,12 @@ network_req_cleanup(getdns_network_req *net_req) { assert(net_req); + if (net_req->query_id_registered) { + (void) _getdns_rbtree_delete( + net_req->query_id_registered, net_req->node.key); + net_req->query_id_registered = NULL; + net_req->node.key = NULL; + } if (net_req->response && (net_req->response < net_req->wire_data || net_req->response > net_req->wire_data+ net_req->wire_data_sz)) GETDNS_FREE(net_req->owner->my_mf, net_req->response); @@ -123,6 +129,12 @@ netreq_reset(getdns_network_req *net_req) */ net_req->unbound_id = -1; _getdns_netreq_change_state(net_req, NET_REQ_NOT_SENT); + if (net_req->query_id_registered) { + (void) _getdns_rbtree_delete(net_req->query_id_registered, + (void *)(intptr_t)GLDNS_ID_WIRE(net_req->query)); + net_req->query_id_registered = NULL; + net_req->node.key = NULL; + } net_req->dnssec_status = GETDNS_DNSSEC_INDETERMINATE; net_req->tsig_status = GETDNS_DNSSEC_INDETERMINATE; net_req->response_len = 0; @@ -170,7 +182,12 @@ network_req_init(getdns_network_req *net_req, getdns_dns_req *owner, net_req->transport_count = owner->context->dns_transport_count; memcpy(net_req->transports, owner->context->dns_transports, net_req->transport_count * sizeof(getdns_transport_list_t)); - net_req->tls_auth_min = owner->context->tls_auth_min; + net_req->tls_auth_min = + owner->context->tls_auth == GETDNS_AUTHENTICATION_REQUIRED + && owner->context->dns_transport_count == 1 + && owner->context->dns_transports[0] == GETDNS_TRANSPORT_TLS + ? GETDNS_AUTHENTICATION_REQUIRED + : GETDNS_AUTHENTICATION_NONE; net_req->follow_redirects = owner->context->follow_redirects; @@ -191,6 +208,11 @@ network_req_init(getdns_network_req *net_req, getdns_dns_req *owner, /* Scheduling, touch only via _getdns_netreq_change_state! */ net_req->state = NET_REQ_NOT_SENT; + /* A registered netreq (on a statefull transport) + * Deregister on reset and cleanup. + */ + net_req->query_id_registered = NULL; + net_req->node.key = NULL; if (max_query_sz == 0) { net_req->query = NULL; @@ -914,6 +936,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 +963,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/server.c b/src/server.c index 05267b60..f29cb603 100644 --- a/src/server.c +++ b/src/server.c @@ -39,6 +39,8 @@ #include "types-internal.h" #include "debug.h" #include "util/rbtree.h" +#include "util-internal.h" +#include "platform.h" #include "server.h" #define DNS_REQUEST_SZ 4096 @@ -135,11 +137,7 @@ static void tcp_connection_destroy(tcp_connection *conn) loop->vmt->clear(loop, &conn->event); if (conn->fd >= 0) -#ifdef USE_WINSOCK - (void) closesocket(conn->fd); -#else - (void) close(conn->fd); -#endif + (void) _getdns_closesocket(conn->fd); GETDNS_FREE(*mf, conn->read_buf); for (cur = conn->to_write; cur; cur = next) { @@ -189,7 +187,8 @@ static void tcp_write_cb(void *userarg) } to_write = conn->to_write; if (conn->fd == -1 || - (written = send(conn->fd, &to_write->write_buf[to_write->written], + (written = send(conn->fd, + (const void *)&to_write->write_buf[to_write->written], to_write->write_buf_len - to_write->written, 0)) == -1) { /* IO error, close connection */ @@ -284,11 +283,7 @@ getdns_reply( (struct sockaddr *)&conn->remote_in, conn->addrlen) == -1) { /* IO error, cleanup this listener */ loop->vmt->clear(loop, &conn->l->event); -#ifdef USE_WINSOCK - closesocket(conn->l->fd); -#else - close(conn->l->fd); -#endif + _getdns_closesocket(conn->l->fd); conn->l->fd = -1; } /* Unlink this connection */ @@ -367,7 +362,8 @@ static void tcp_read_cb(void *userarg) (void) loop->vmt->schedule(loop, conn->fd, DOWNSTREAM_IDLE_TIMEOUT, &conn->event); - if ((bytes_read = recv(conn->fd, conn->read_pos, conn->to_read, 0)) < 0) { + if ((bytes_read = recv(conn->fd, + (void *)conn->read_pos, conn->to_read, 0)) < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) return; /* Come back to do the read later */ @@ -481,11 +477,7 @@ static void tcp_accept_cb(void *userarg) &conn->super.remote_in, &conn->super.addrlen)) == -1) { /* IO error, cleanup this listener */ loop->vmt->clear(loop, &l->event); -#ifdef USE_WINSOCK - closesocket(l->fd); -#else - close(l->fd); -#endif + _getdns_closesocket(l->fd); l->fd = -1; GETDNS_FREE(*mf, conn); return; @@ -553,13 +545,17 @@ static void udp_read_cb(void *userarg) conn->addrlen = sizeof(conn->remote_in); if ((len = recvfrom(l->fd, (void *)buf, sizeof(buf), 0, (struct sockaddr *)&conn->remote_in, &conn->addrlen)) == -1) { + if (_getdns_socketerror() == _getdns_ECONNRESET) { + /* + * WINSOCK gives ECONNRESET on ICMP Port Unreachable + * being received. Ignore it. + * */ + GETDNS_FREE(*mf, conn); + return; + } /* IO error, cleanup this listener. */ loop->vmt->clear(loop, &l->event); -#ifdef USE_WINSOCK - closesocket(l->fd); -#else - close(l->fd); -#endif + _getdns_closesocket(l->fd); l->fd = -1; #if 0 && defined(SERVER_DEBUG) && SERVER_DEBUG @@ -708,11 +704,7 @@ static void remove_listeners(listen_set *set) continue; loop->vmt->clear(loop, &l->event); -#ifdef USE_WINSOCK - closesocket(l->fd); -#else - close(l->fd); -#endif + _getdns_closesocket(l->fd); l->fd = -1; if (l->transport != GETDNS_TRANSPORT_TCP) diff --git a/src/stub.c b/src/stub.c index dd084164..49af9d8c 100644 --- a/src/stub.c +++ b/src/stub.c @@ -38,17 +38,6 @@ */ #define INTERCEPT_COM_DS 0 -#ifdef USE_POLL_DEFAULT_EVENTLOOP -# ifdef HAVE_SYS_POLL_H -# include -# else -#ifdef USE_WINSOCK -#define poll(fdarray, nbsockets, timer) WSAPoll(fdarray, nbsockets, timer) -#else -# include -#endif -# endif -#endif #include "debug.h" #include #include @@ -63,21 +52,10 @@ #include "rr-iter.h" #include "context.h" #include "util-internal.h" +#include "platform.h" #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... @@ -105,8 +83,7 @@ static void upstream_write_cb(void *userarg); static void upstream_idle_timeout_cb(void *userarg); static void upstream_schedule_netreq(getdns_upstream *upstream, getdns_network_req *netreq); -static void upstream_reschedule_events(getdns_upstream *upstream, - uint64_t idle_timeout); +static void upstream_reschedule_events(getdns_upstream *upstream); static int upstream_working_ok(getdns_upstream *upstream); static int upstream_auth_status_ok(getdns_upstream *upstream, getdns_network_req *netreq); @@ -438,13 +415,10 @@ tcp_connect(getdns_upstream *upstream, getdns_transport_list_t transport) #endif if (connect(fd, (struct sockaddr *)&upstream->addr, upstream->addr_len) == -1) { - if (_getdns_EINPROGRESS || _getdns_EWOULDBLOCK) + if (_getdns_socketerror() == _getdns_EINPROGRESS || + _getdns_socketerror() == _getdns_EWOULDBLOCK) return fd; -#ifdef USE_WINSOCK - closesocket(fd); -#else - close(fd); -#endif + _getdns_closesocket(fd); return -1; } return fd; @@ -455,22 +429,13 @@ tcp_connected(getdns_upstream *upstream) { int error = 0; socklen_t len = (socklen_t)sizeof(error); getsockopt(upstream->fd, SOL_SOCKET, SO_ERROR, (void*)&error, &len); -#ifdef USE_WINSOCK - if (error == WSAEINPROGRESS) + if (error == _getdns_EINPROGRESS) return STUB_TCP_AGAIN; - else if (error == WSAEWOULDBLOCK) - return STUB_TCP_WOULDBLOCK; - else if (error != 0) - return STUB_SETUP_ERROR; -#else - if (error == EINPROGRESS) - return STUB_TCP_AGAIN; - else if (error == EWOULDBLOCK || error == EAGAIN) + else if (error == _getdns_EWOULDBLOCK || error == _getdns_EAGAIN) return STUB_TCP_WOULDBLOCK; else if (error != 0) { return STUB_SETUP_ERROR; } -#endif if (upstream->transport == GETDNS_TRANSPORT_TCP && upstream->queries_sent == 0) { upstream->conn_state = GETDNS_CONN_OPEN; @@ -496,42 +461,56 @@ stub_next_upstream(getdns_network_req *netreq) dnsreq->upstreams->current_udp = 0; } +static void +remove_from_write_queue(getdns_upstream *upstream, getdns_network_req * netreq) +{ + getdns_network_req *r, *prev_r; + + for ( r = upstream->write_queue, prev_r = NULL + ; r + ; prev_r = r, r = r->write_queue_tail) { + + if (r != netreq) + continue; + + if (prev_r) + prev_r->write_queue_tail = r->write_queue_tail; + else + upstream->write_queue = r->write_queue_tail; + + if (r == upstream->write_queue_last) { + /* If r was the last netreq, + * its write_queue tail MUST be NULL + */ + assert(r->write_queue_tail == NULL); + upstream->write_queue_last = prev_r ? prev_r : NULL; + } + + netreq->write_queue_tail = NULL; + break; /* netreq found and removed */ + } +} + static void stub_cleanup(getdns_network_req *netreq) { DEBUG_STUB("%s %-35s: MSG: %p\n", STUB_DEBUG_CLEANUP, __FUNC__, (void*)netreq); getdns_dns_req *dnsreq = netreq->owner; - getdns_network_req *r, *prev_r; - getdns_upstream *upstream; GETDNS_CLEAR_EVENT(dnsreq->loop, &netreq->event); - /* Nothing globally scheduled? Then nothing queued */ - if (!netreq->upstream || !(upstream = netreq->upstream)->event.ev) - return; - - /* Delete from upstream->netreq_by_query_id (if present) */ - (void) _getdns_rbtree_delete(&upstream->netreq_by_query_id, - (void *)(intptr_t)GLDNS_ID_WIRE(netreq->query)); - - /* Delete from upstream->write_queue (if present) */ - for (prev_r = NULL, r = upstream->write_queue; r; - prev_r = r, r = r->write_queue_tail) - - if (r == netreq) { - if (prev_r) - prev_r->write_queue_tail = r->write_queue_tail; - else - upstream->write_queue = r->write_queue_tail; - - if (r == upstream->write_queue_last) - upstream->write_queue_last = - prev_r ? prev_r : NULL; - netreq->write_queue_tail = NULL; - break; - } - upstream_reschedule_events(upstream, upstream->keepalive_timeout); + if (netreq->query_id_registered) { + (void) _getdns_rbtree_delete( + netreq->query_id_registered, netreq->node.key); + netreq->query_id_registered = NULL; + netreq->node.key = NULL; + } + if (netreq->upstream) { + remove_from_write_queue(netreq->upstream, netreq); + if (netreq->upstream->event.ev) + upstream_reschedule_events(netreq->upstream); + } } static void @@ -545,17 +524,8 @@ upstream_failed(getdns_upstream *upstream, int during_setup) when idle.*/ /* [TLS1]TODO: Work out how to re-open the connection and re-try the queries if there is only one upstream.*/ + GETDNS_CLEAR_EVENT(upstream->loop, &upstream->event); if (during_setup) { - /* Reset timeout on setup failure to trigger fallback handling.*/ - GETDNS_CLEAR_EVENT(upstream->loop, &upstream->event); - /* Need this check because if the setup failed because the interface is - not up we get -1 and then a seg fault. Found when using IPv6 address - but IPv6 interface not enabled.*/ - if (upstream->fd != -1) { - GETDNS_SCHEDULE_EVENT(upstream->loop, upstream->fd, TIMEOUT_FOREVER, - getdns_eventloop_event_init(&upstream->event, upstream, - NULL, upstream_write_cb, NULL)); - } /* Special case if failure was due to authentication issues since this upstream could be used oppotunistically with no problem.*/ if (!(upstream->transport == GETDNS_TRANSPORT_TLS && @@ -565,6 +535,11 @@ upstream_failed(getdns_upstream *upstream, int during_setup) upstream->conn_shutdowns++; /* [TLS1]TODO: Re-try these queries if possible.*/ } + upstream->conn_state = GETDNS_CONN_TEARDOWN; + + while (upstream->write_queue) + upstream_write_cb(upstream); + while (upstream->netreq_by_query_id.count) { netreq = (getdns_network_req *) _getdns_rbtree_first(&upstream->netreq_by_query_id); @@ -572,7 +547,7 @@ upstream_failed(getdns_upstream *upstream, int during_setup) _getdns_netreq_change_state(netreq, NET_REQ_ERRORED); _getdns_check_dns_req_complete(netreq->owner); } - upstream->conn_state = GETDNS_CONN_TEARDOWN; + _getdns_upstream_shutdown(upstream); } void @@ -582,11 +557,7 @@ _getdns_cancel_stub_request(getdns_network_req *netreq) STUB_DEBUG_CLEANUP, __FUNC__, (void*)netreq); stub_cleanup(netreq); if (netreq->fd >= 0) { -#ifdef USE_WINSOCK - closesocket(netreq->fd); -#else - close(netreq->fd); -#endif + _getdns_closesocket(netreq->fd); netreq->fd = -1; } } @@ -601,15 +572,11 @@ stub_timeout_cb(void *userarg) _getdns_netreq_change_state(netreq, NET_REQ_TIMED_OUT); /* Handle upstream*/ if (netreq->fd >= 0) { -#ifdef USE_WINSOCK - closesocket(netreq->fd); -#else - close(netreq->fd); -#endif + _getdns_closesocket(netreq->fd); netreq->fd = -1; netreq->upstream->udp_timeouts++; if (netreq->upstream->udp_timeouts % 100 == 0) - _getdns_upstream_log(netreq->upstream, GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_DEBUG, + _getdns_upstream_log(netreq->upstream, GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_INFO, "%-40s : Upstream stats: Transport=UDP - Resp=%d,Timeouts=%d\n", netreq->upstream->addr_str, (int)netreq->upstream->udp_responses, (int)netreq->upstream->udp_timeouts); @@ -641,41 +608,12 @@ upstream_idle_timeout_cb(void *userarg) static void upstream_setup_timeout_cb(void *userarg) { - int ret; getdns_upstream *upstream = (getdns_upstream *)userarg; -#ifdef USE_POLL_DEFAULT_EVENTLOOP - struct pollfd fds; -#else - fd_set fds; - struct timeval tval; -#endif DEBUG_STUB("%s %-35s: FD: %d\n", STUB_DEBUG_CLEANUP, __FUNC__, upstream->fd); - /* Clean up and trigger a write to let the fallback code to its job */ - upstream_failed(upstream, 1); - /* Need to handle the case where the far end doesn't respond to a - * TCP SYN and doesn't do a reset (as is the case with e.g. 8.8.8.8@853). - * For that case the socket never becomes writable so doesn't trigger any - * callbacks. If so then clear out the queue in one go.*/ -#ifdef USE_POLL_DEFAULT_EVENTLOOP - fds.fd = upstream->fd; - fds.events = POLLOUT; - ret = poll(&fds, 1, 0); -#else - FD_ZERO(&fds); - FD_SET((int)(upstream->fd), &fds); - tval.tv_sec = 0; - tval.tv_usec = 0; - ret = select(upstream->fd+1, NULL, &fds, NULL, &tval); -#endif - if (ret == 0) { - DEBUG_STUB("%s %-35s: FD: %d Cleaning up dangling queue\n", - STUB_DEBUG_CLEANUP, __FUNC__, upstream->fd); - while (upstream->write_queue) - upstream_write_cb(upstream); - } + upstream_failed(upstream, 1); } @@ -701,7 +639,7 @@ stub_tcp_read(int fd, getdns_tcp_state *tcp, struct mem_funcs *mf) } read = recv(fd, (void *)tcp->read_pos, tcp->to_read, 0); if (read < 0) { - if (_getdns_EWOULDBLOCK) + if (_getdns_socketerror() == _getdns_EWOULDBLOCK) return STUB_TCP_WOULDBLOCK; else return STUB_TCP_ERROR; @@ -775,6 +713,7 @@ stub_tcp_write(int fd, getdns_tcp_state *tcp, getdns_network_req *netreq) } while (!_getdns_rbtree_insert( &netreq->upstream->netreq_by_query_id, &netreq->node)); + netreq->query_id_registered = &netreq->upstream->netreq_by_query_id; GLDNS_ID_SET(netreq->query, query_id); @@ -818,11 +757,11 @@ stub_tcp_write(int fd, getdns_tcp_state *tcp, getdns_network_req *netreq) (struct sockaddr *)&(netreq->upstream->addr), netreq->upstream->addr_len); #endif - if ((written < 0 && (_getdns_EWOULDBLOCK || + if ((written < 0 && (_getdns_socketerror() == _getdns_EWOULDBLOCK || /* Add the error case where the connection is in progress which is when a cookie is not available (e.g. when doing the first request to an upstream). We must let the handshake complete since non-blocking. */ - _getdns_EINPROGRESS)) || + _getdns_socketerror() == _getdns_EINPROGRESS)) || (size_t)written < pkt_len + 2) { /* We couldn't write the whole packet. @@ -850,15 +789,10 @@ stub_tcp_write(int fd, getdns_tcp_state *tcp, getdns_network_req *netreq) /* Coming back from an earlier unfinished write or handshake. * Try to send remaining data */ -#ifdef USE_WINSOCK - written = send(fd, tcp->write_buf + tcp->written, + written = send(fd, (void *)(tcp->write_buf + tcp->written), tcp->write_buf_len - tcp->written, 0); -#else - written = write(fd, tcp->write_buf + tcp->written, - tcp->write_buf_len - tcp->written); -#endif if (written == -1) { - if (_getdns_EWOULDBLOCK) + if (_getdns_socketerror() == _getdns_EWOULDBLOCK) return STUB_TCP_WOULDBLOCK; else { DEBUG_STUB("%s %-35s: MSG: %p error while writing to TCP socket:" @@ -909,7 +843,7 @@ tls_verify_callback(int preverify_ok, X509_STORE_CTX *ctx) X509_verify_cert_error_string(err)); #endif if (!preverify_ok && !upstream->tls_fallback_ok) - _getdns_upstream_log(upstream, GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_DEBUG, + _getdns_upstream_log(upstream, GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_ERR, "%-40s : Verify failed : Transport=TLS - *Failure* - (%d) \"%s\"\n", upstream->addr_str, err, X509_verify_cert_error_string(err)); @@ -945,7 +879,7 @@ tls_verify_callback(int preverify_ok, X509_STORE_CTX *ctx) DEBUG_STUB("%s %-35s: FD: %d, WARNING: Proceeding even though pinset validation failed!\n", STUB_DEBUG_SETUP_TLS, __FUNC__, upstream->fd); else - _getdns_upstream_log(upstream, GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_DEBUG, + _getdns_upstream_log(upstream, GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_ERR, "%-40s : Conn failed : Transport=TLS - *Failure* - Pinset validation failure\n", upstream->addr_str); } else { @@ -1016,8 +950,11 @@ tls_create_object(getdns_dns_req *dnsreq, int fd, getdns_upstream *upstream) X509_VERIFY_PARAM_set1_host(param, upstream->tls_auth_name, 0); #else if (dnsreq->netreqs[0]->tls_auth_min == GETDNS_AUTHENTICATION_REQUIRED) { - DEBUG_STUB("%s %-35s: ERROR: TLS Authentication functionality not available\n", + DEBUG_STUB("%s %-35s: ERROR: Hostname Authentication not available from TLS library (check library version)\n", STUB_DEBUG_SETUP_TLS, __FUNC__); + _getdns_upstream_log(upstream, GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_ERR, + "%-40s : ERROR: Hostname Authentication not available from TLS library (check library version)\n", + upstream->addr_str); upstream->tls_hs_state = GETDNS_HS_FAILED; return NULL; } @@ -1273,6 +1210,7 @@ stub_tls_write(getdns_upstream *upstream, getdns_tcp_state *tcp, } while (!_getdns_rbtree_insert( &netreq->upstream->netreq_by_query_id, &netreq->node)); + netreq->query_id_registered = &netreq->upstream->netreq_by_query_id; GLDNS_ID_SET(netreq->query, query_id); @@ -1397,7 +1335,8 @@ stub_udp_read_cb(void *userarg) * i.e. overflow */ 0, NULL, NULL); - if (read == -1 && _getdns_EWOULDBLOCK) + if (read == -1 && (_getdns_socketerror() == _getdns_EWOULDBLOCK || + _getdns_socketerror() == _getdns_ECONNRESET)) return; /* Try again later */ if (read == -1) { @@ -1409,11 +1348,7 @@ stub_udp_read_cb(void *userarg) _getdns_netreq_change_state(netreq, NET_REQ_ERRORED); /* Handle upstream*/ if (netreq->fd >= 0) { -#ifdef USE_WINSOCK - closesocket(netreq->fd); -#else - close(netreq->fd); -#endif + _getdns_closesocket(netreq->fd); netreq->fd = -1; stub_next_upstream(netreq); } @@ -1433,11 +1368,7 @@ stub_udp_read_cb(void *userarg) GETDNS_CLEAR_EVENT(dnsreq->loop, &netreq->event); -#ifdef USE_WINSOCK - closesocket(netreq->fd); -#else - close(netreq->fd); -#endif + _getdns_closesocket(netreq->fd); netreq->fd = -1; while (GLDNS_TC_WIRE(netreq->response)) { DEBUG_STUB("%s %-35s: MSG: %p TC bit set in response \n", STUB_DEBUG_READ, @@ -1474,7 +1405,7 @@ stub_udp_read_cb(void *userarg) upstream->udp_responses++; if (upstream->udp_responses == 1 || upstream->udp_responses % 100 == 0) - _getdns_upstream_log(upstream, GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_DEBUG, + _getdns_upstream_log(upstream, GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_INFO, "%-40s : Upstream stats: Transport=UDP - Resp=%d,Timeouts=%d\n", upstream->addr_str, (int)upstream->udp_responses, (int)upstream->udp_timeouts); @@ -1530,11 +1461,7 @@ stub_udp_write_cb(void *userarg) _getdns_netreq_change_state(netreq, NET_REQ_ERRORED); /* Handle upstream*/ if (netreq->fd >= 0) { -#ifdef USE_WINSOCK - closesocket(netreq->fd); -#else - close(netreq->fd); -#endif + _getdns_closesocket(netreq->fd); netreq->fd = -1; stub_next_upstream(netreq); } @@ -1601,7 +1528,6 @@ upstream_read_cb(void *userarg) return; default: - /* Lookup netreq */ query_id = (uint16_t) q; query_id_intptr = (intptr_t) query_id; @@ -1613,7 +1539,16 @@ upstream_read_cb(void *userarg) upstream->tcp.to_read = 2; return; } + if (netreq->query_id_registered == &upstream->netreq_by_query_id) { + netreq->query_id_registered = NULL; + netreq->node.key = NULL; + } else if (netreq->query_id_registered) { + (void) _getdns_rbtree_delete( + netreq->query_id_registered, netreq->node.key); + netreq->query_id_registered = NULL; + netreq->node.key = NULL; + } DEBUG_STUB("%s %-35s: MSG: %p (read)\n", STUB_DEBUG_READ, __FUNC__, (void*)netreq); _getdns_netreq_change_state(netreq, NET_REQ_FINISHED); @@ -1700,7 +1635,9 @@ upstream_write_cb(void *userarg) __FUNC__, (void*)netreq); /* Health checks on current connection */ - if (upstream->conn_state == GETDNS_CONN_TEARDOWN) + if (upstream->conn_state == GETDNS_CONN_TEARDOWN || + upstream->conn_state == GETDNS_CONN_CLOSED || + upstream->fd == -1) q = STUB_CONN_GONE; else if (!upstream_working_ok(upstream)) q = STUB_TCP_ERROR; @@ -1724,10 +1661,8 @@ upstream_write_cb(void *userarg) /* Could not complete the set up. Need to fallback.*/ DEBUG_STUB("%s %-35s: Upstream: %p ERROR = %d\n", STUB_DEBUG_WRITE, __FUNC__, (void*)userarg, q); - (void) _getdns_rbtree_delete(&upstream->netreq_by_query_id, - (void *)(intptr_t)GLDNS_ID_WIRE(netreq->query)); upstream_failed(upstream, (q == STUB_TCP_ERROR ? 0:1)); - /* Fall through */ + return; case STUB_CONN_GONE: case STUB_NO_AUTH: /* Cleaning up after connection or auth check failure. Need to fallback. */ @@ -1744,6 +1679,9 @@ upstream_write_cb(void *userarg) return; default: + /* Unqueue the netreq from the write_queue */ + remove_from_write_queue(upstream, netreq); + if (netreq->owner->return_call_reporting && netreq->upstream->tls_obj && netreq->debug_tls_peer_cert.data == NULL && @@ -1756,9 +1694,9 @@ upstream_write_cb(void *userarg) netreq->debug_tls_auth_status = netreq->upstream->tls_auth_state; upstream->queries_sent++; - /* Unqueue the netreq from the write_queue */ - if (!(upstream->write_queue = netreq->write_queue_tail)) { - upstream->write_queue_last = NULL; + /* Empty write_queue?, then deschedule upstream write_cb */ + if (upstream->write_queue == NULL) { + assert(upstream->write_queue_last == NULL); GETDNS_CLEAR_EVENT(upstream->loop, &upstream->event); upstream->event.write_cb = NULL; /* Reschedule (if already reading) to clear writable */ @@ -1806,13 +1744,20 @@ upstream_active(getdns_upstream *upstream) } static int -upstream_usable(getdns_upstream *upstream) +upstream_usable(getdns_upstream *upstream, int backoff_ok) { + /* If backoff_ok is not true then only use upstreams that are in a healthy + state. */ if ((upstream->conn_state == GETDNS_CONN_CLOSED || upstream->conn_state == GETDNS_CONN_SETUP || upstream->conn_state == GETDNS_CONN_OPEN) && upstream->keepalive_shutdown == 0) return 1; + /* Otherwise, allow upstreams that are backed off to be used because that + is better that having no upstream at all. */ + if (backoff_ok == 1 && + upstream->conn_state == GETDNS_CONN_BACKOFF) + return 1; return 0; } @@ -1828,15 +1773,22 @@ upstream_stats(getdns_upstream *upstream) { /* [TLS1]TODO: This arbitrary logic at the moment - review and improve!*/ return (upstream->total_responses - upstream->total_timeouts - - upstream->conn_shutdowns*GETDNS_TRANSPORT_FAIL_MULT); + - upstream->conn_shutdowns*GETDNS_TRANSPORT_FAIL_MULT + - upstream->conn_setup_failed); } static int upstream_valid(getdns_upstream *upstream, getdns_transport_list_t transport, - getdns_network_req *netreq) + getdns_network_req *netreq, + int backoff_ok) { - if (!(upstream->transport == transport && upstream_usable(upstream))) + /* Checking upstreams with backoff_ok true will aslo return upstreams + that are in a backoff state. Otherwise only use upstreams that have + a 'good' connection state. backoff_ok is usefull when no upstreams at all + are valid, for example when the network connection is down and need to + keep trying to connect before failing completely. */ + if (!(upstream->transport == transport && upstream_usable(upstream, backoff_ok))) return 0; if (transport == GETDNS_TRANSPORT_TCP) return 1; @@ -1887,7 +1839,7 @@ upstream_select_stateful(getdns_network_req *netreq, getdns_transport_list_t tra if (upstreams->upstreams[i].conn_state == GETDNS_CONN_BACKOFF && upstreams->upstreams[i].conn_retry_time < now) { upstreams->upstreams[i].conn_state = GETDNS_CONN_CLOSED; - _getdns_upstream_log(upstream, GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_DEBUG, + _getdns_upstream_log(upstream, GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_NOTICE, "%-40s : Re-instating upstream\n", upstreams->upstreams[i].addr_str); } @@ -1902,14 +1854,13 @@ upstream_select_stateful(getdns_network_req *netreq, getdns_transport_list_t tra } /* OK - Find the next one to use. First check we have at least one valid - upstream because we completely back off failed - upstreams we may have no valid upstream at all (in contrast to UDP). This - will be better communicated to the user when we have better error codes*/ + upstream (not backed-off) because we completely back off failed + upstreams we may have no valid upstream at all (in contrast to UDP).*/ i = upstreams->current_stateful; do { DEBUG_STUB("%s %-35s: Testing upstreams %d %d\n", STUB_DEBUG_SETUP, __FUNC__, (int)i, (int)upstreams->upstreams[i].conn_state); - if (upstream_valid(&upstreams->upstreams[i], transport, netreq)) { + if (upstream_valid(&upstreams->upstreams[i], transport, netreq, 0)) { upstream = &upstreams->upstreams[i]; break; } @@ -1917,14 +1868,48 @@ upstream_select_stateful(getdns_network_req *netreq, getdns_transport_list_t tra if (i >= upstreams->count) i = 0; } while (i != upstreams->current_stateful); - if (!upstream) - return NULL; + if (!upstream) { + /* Oh, oh. We have no valid upstreams. Try to find one that might work so + allow backed off upstreams to be considered valid. + Don't worry about the policy, just use the one with the least bad + stats that still fits the bill (right transport, right authentication) + to try to avoid total failure due to network outages. */ + do { + if (upstream_valid(&upstreams->upstreams[i], transport, netreq, 1)) { + upstream = &upstreams->upstreams[i]; + break; + } + i++; + if (i >= upstreams->count) + i = 0; + } while (i != upstreams->current_stateful); + if (!upstream) { + /* We _really_ have nothing that authenticates well enough right now... + leave to regular backoff logic. */ + return NULL; + } + do { + i++; + if (i >= upstreams->count) + i = 0; + if (upstream_valid(&upstreams->upstreams[i], transport, netreq, 1) && + upstream_stats(&upstreams->upstreams[i]) > upstream_stats(upstream)) + upstream = &upstreams->upstreams[i]; + } while (i != upstreams->current_stateful); + upstream->conn_state = GETDNS_CONN_CLOSED; + upstream->conn_backoff_interval = 1; + _getdns_upstream_log(upstream, GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_NOTICE, + "%-40s : No valid upstreams... promoting this backed-off upstream for re-try...\n", + upstream->addr_str); + return upstream; + } /* Now select the specific upstream */ if (netreq->owner->context->round_robin_upstreams == 0) { - /* Base the decision on the stats, noting we will have started from 0*/ + /* Base the decision on the stats and being not backed-off, + noting we will have started from 0*/ for (i++; i < upstreams->count; i++) { - if (upstream_valid(&upstreams->upstreams[i], transport, netreq) && + if (upstream_valid(&upstreams->upstreams[i], transport, netreq, 0) && upstream_stats(&upstreams->upstreams[i]) > upstream_stats(upstream)) upstream = &upstreams->upstreams[i]; } @@ -2010,18 +1995,14 @@ upstream_connect(getdns_upstream *upstream, getdns_transport_list_t transport, upstream->tls_obj = tls_create_object(dnsreq, fd, upstream); if (upstream->tls_obj == NULL) { upstream_failed(upstream, 1); -#ifdef USE_WINSOCK - closesocket(fd); -#else - close(fd); -#endif + _getdns_closesocket(fd); return -1; } upstream->tls_hs_state = GETDNS_HS_WRITE; } upstream->conn_state = GETDNS_CONN_SETUP; _getdns_upstream_log(upstream, GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_DEBUG, - "%-40s : Conn init : Transport=%s - Profile=%s\n", + "%-40s : Conn opened: %s - %s Profile\n", upstream->addr_str, transport == GETDNS_TRANSPORT_TLS ? "TLS":"TCP", dnsreq->context->tls_auth_min == GETDNS_AUTHENTICATION_NONE ? "Opportunistic":"Strict"); break; @@ -2048,12 +2029,17 @@ upstream_find_for_transport(getdns_network_req *netreq, } else { /* For stateful transport we should keep trying until all our transports - are exhausted/backed-off (no upstream)*/ + are exhausted/backed-off (no upstream) and until we have tried each + upstream at least once for this netreq in a total backoff scenario */ + size_t i = 0; do { upstream = upstream_select_stateful(netreq, transport); if (!upstream) return NULL; *fd = upstream_connect(upstream, transport, netreq->owner); + if (i >= upstream->upstreams->count) + return NULL; + i++; } while (*fd == -1); DEBUG_STUB("%s %-35s: FD: %d Connecting to upstream: %p No: %d\n", STUB_DEBUG_SETUP, __FUNC__, *fd, (void*)upstream, @@ -2078,7 +2064,7 @@ upstream_find_for_netreq(getdns_network_req *netreq) continue; if (fd == -1) { - if (_getdns_EMFILE) + if (_getdns_socketerror() == _getdns_EMFILE) return STUB_TRY_AGAIN_LATER; return -1; } @@ -2091,7 +2077,7 @@ upstream_find_for_netreq(getdns_network_req *netreq) } /* Handle better, will give generic error*/ DEBUG_STUB("%s %-35s: MSG: %p No valid upstream! \n", STUB_DEBUG_SCHEDULE, __FUNC__, (void*)netreq); - _getdns_context_log(netreq->owner->context, GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_DEBUG, + _getdns_context_log(netreq->owner->context, GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_ERR, "*FAILURE* no valid transports or upstreams available!\n"); return -1; } @@ -2120,11 +2106,17 @@ fallback_on_write(getdns_network_req *netreq) } static void -upstream_reschedule_events(getdns_upstream *upstream, uint64_t idle_timeout) { +upstream_reschedule_events(getdns_upstream *upstream) { DEBUG_STUB("%s %-35s: FD: %d \n", STUB_DEBUG_SCHEDULE, __FUNC__, upstream->fd); - GETDNS_CLEAR_EVENT(upstream->loop, &upstream->event); + if (upstream->event.ev) + GETDNS_CLEAR_EVENT(upstream->loop, &upstream->event); + + if (upstream->fd == -1 || !( upstream->conn_state == GETDNS_CONN_SETUP + || upstream->conn_state == GETDNS_CONN_OPEN )) + return; + if (!upstream->write_queue && upstream->event.write_cb) { upstream->event.write_cb = NULL; } @@ -2142,18 +2134,13 @@ upstream_reschedule_events(getdns_upstream *upstream, uint64_t idle_timeout) { upstream->fd, TIMEOUT_FOREVER, &upstream->event); else { DEBUG_STUB("%s %-35s: FD: %d Connection idle - timeout is %d\n", - STUB_DEBUG_SCHEDULE, __FUNC__, upstream->fd, (int)idle_timeout); - /* TODO: Schedule a read also anyway, - * to digest timed out answers. - * Dont forget to schedule with upstream->fd then! - * - * upstream->event.read_cb = upstream_read_cb; - */ + STUB_DEBUG_SCHEDULE, __FUNC__, upstream->fd, + (int)upstream->keepalive_timeout); + + upstream->event.read_cb = upstream_read_cb; upstream->event.timeout_cb = upstream_idle_timeout_cb; - if (upstream->conn_state != GETDNS_CONN_OPEN) - idle_timeout = 0; - GETDNS_SCHEDULE_EVENT(upstream->loop, -1, - idle_timeout, &upstream->event); + GETDNS_SCHEDULE_EVENT(upstream->loop, upstream->fd, + upstream->keepalive_timeout, &upstream->event); } } diff --git a/src/sync.c b/src/sync.c index 75f8cca3..debf904b 100644 --- a/src/sync.c +++ b/src/sync.c @@ -76,7 +76,7 @@ getdns_sync_data_init(getdns_context *context, getdns_sync_data *data) # ifdef HAVE_UNBOUND_EVENT_API if (_getdns_ub_loop_enabled(&context->ub_loop)) { context->ub_loop.extension = ext; - } else + } else # endif # ifndef USE_WINSOCK return ext->vmt->schedule(ext, ub_fd(context->unbound_ctx), @@ -85,7 +85,9 @@ getdns_sync_data_init(getdns_context *context, getdns_sync_data *data) /* No sync full recursion requests on windows without * UNBOUND_EVENT_API because ub_fd() doesn't work on windows. */ - ; /* pass */ +# ifdef HAVE_UNBOUND_EVENT_API + { ; } /* pass */ +# endif # endif #endif return GETDNS_RETURN_GOOD; @@ -114,30 +116,24 @@ getdns_sync_data_cleanup(getdns_sync_data *data) upstream = &ctxt->upstreams->upstreams[i]; if (upstream->loop != &data->context->sync_eventloop.loop) continue; - if (upstream->event.read_cb || upstream->event.write_cb) { - GETDNS_CLEAR_EVENT(upstream->loop, &upstream->event); - - } else if (upstream->event.timeout_cb) { - /* Timeout's at upstream are idle-timeouts only. - * They should be fired on completion of the - * synchronous request. - */ - GETDNS_CLEAR_EVENT(upstream->loop, &upstream->event); - if (upstream->conn_state != GETDNS_CONN_OPEN || - upstream->keepalive_timeout == 0) - (*upstream->event.timeout_cb)(upstream->event.userarg); + GETDNS_CLEAR_EVENT(upstream->loop, &upstream->event); + if (upstream->event.timeout_cb && + ( upstream->conn_state != GETDNS_CONN_OPEN + || upstream->keepalive_timeout == 0)) { + (*upstream->event.timeout_cb)(upstream->event.userarg); + upstream->event.timeout_cb = NULL; } upstream->loop = data->context->extension; upstream->is_sync_loop = 0; - if (upstream->event.read_cb || upstream->event.write_cb) - GETDNS_SCHEDULE_EVENT(upstream->loop, upstream->fd, - TIMEOUT_FOREVER, &upstream->event); - else if (upstream->event.timeout_cb && - upstream->conn_state == GETDNS_CONN_OPEN && - upstream->keepalive_timeout != 0) { - GETDNS_SCHEDULE_EVENT(upstream->loop, upstream->fd, - upstream->keepalive_timeout, &upstream->event); + if ( upstream->event.read_cb || upstream->event.write_cb + || upstream->event.timeout_cb) { + GETDNS_SCHEDULE_EVENT(upstream->loop, + ( upstream->event.read_cb + || upstream->event.write_cb ? upstream->fd : -1), + ( upstream->event.timeout_cb + ? upstream->keepalive_timeout : TIMEOUT_FOREVER ), + &upstream->event); } } } diff --git a/src/test/check_getdns_context_set_upstream_recursive_servers.h b/src/test/check_getdns_context_set_upstream_recursive_servers.h index e1bb359f..85089e3c 100644 --- a/src/test/check_getdns_context_set_upstream_recursive_servers.h +++ b/src/test/check_getdns_context_set_upstream_recursive_servers.h @@ -65,7 +65,7 @@ CONTEXT_CREATE(TRUE); ASSERT_RC(getdns_context_set_upstream_recursive_servers(context, NULL), - GETDNS_RETURN_INVALID_PARAMETER, "Return code from getdns_context_set_upstream_recursive_servers()"); + GETDNS_RETURN_GOOD, "Return code from getdns_context_set_upstream_recursive_servers()"); CONTEXT_DESTROY; 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/255-yaml-config.tpkg/255-yaml-config.Makefile b/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.Makefile new file mode 100644 index 00000000..c297df6d --- /dev/null +++ b/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.Makefile @@ -0,0 +1,15 @@ +builddir = @BUILDDIR@ +testname = @TPKG_NAME@ +LIBTOOL = $(builddir)/libtool + +CFLAGS=-I$(builddir)/src +LDLIBS=$(builddir)/src/libgetdns.la + +.SUFFIXES: .c .o .a .lo .h + +.c.lo: + $(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) -c $< -o $@ + +$(testname): $(testname).lo + $(LIBTOOL) --tag=CC --mode=link $(CC) $(LDLIBS) $(LDFLAGS) -o $(testname) $(testname).lo + diff --git a/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.c b/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.c new file mode 100644 index 00000000..4dd335fc --- /dev/null +++ b/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.c @@ -0,0 +1,111 @@ +#include +#include +#include +#include + +int main(int ac, char *av[]) +{ + FILE *f; + char *buf = NULL; + size_t len; + ssize_t bytes_read; + + f = fopen(av[1], "r"); + if (!f) { + fprintf(stderr, "Could not open %s", av[1]); + exit(EXIT_FAILURE); + } + + bytes_read = getdelim(&buf, &len, '\0', f); + fclose(f); + + if (bytes_read == -1) { + fprintf(stderr, "Could not read %s", av[1]); + exit(EXIT_FAILURE); + } + + buf = realloc(buf, bytes_read + 1); + if (!buf) { + fprintf(stderr, "Could not grow buffer"); + exit(EXIT_FAILURE); + } + buf[bytes_read] = '\0'; + + getdns_dict *dict = NULL; + getdns_list *list = NULL; + getdns_bindata *bindata = NULL; + getdns_return_t r; + + if (!(dict = getdns_dict_create())) { + fprintf(stderr, "Could not create dict"); + goto fail; + } + + r = getdns_yaml2dict(buf, &dict); + if (r) { + fprintf(stderr, "Error setting dict data: %s", getdns_get_errorstr_by_id(r)); + goto fail; + } + + /* + * Now add a list, bindata and int to the dict by hand to check + * the other yaml2* functions work. + */ + if (!(list = getdns_list_create())) { + fprintf(stderr, "Could not create list"); + goto fail; + } + + r = getdns_str2list("[\"One\", \"two\", \"three\"]", &list); + if (r) { + fprintf(stderr, "Error setting list data: %s", getdns_get_errorstr_by_id(r)); + goto fail; + } + + r = getdns_dict_set_list(dict, "List entry", list); + if (r) { + fprintf(stderr, "Error adding list to dict: %s", getdns_get_errorstr_by_id(r)); + goto fail; + } + + r = getdns_str2bindata("2001:7fd::1", &bindata); + if (r) { + fprintf(stderr, "Error setting bindata: %s", getdns_get_errorstr_by_id(r)); + goto fail; + } + + r = getdns_dict_set_bindata(dict, "Bindata entry", bindata); + if (r) { + fprintf(stderr, "Error adding list to dict: %s", getdns_get_errorstr_by_id(r)); + goto fail; + } + + uint32_t intval; + r = getdns_str2int("32767", &intval); + if (r) { + fprintf(stderr, "Error setting int: %s", getdns_get_errorstr_by_id(r)); + goto fail; + } + if (intval != 32767) { + fprintf(stderr, "Error reading int: wrong value"); + goto fail; + } + + char *dict_str = getdns_pretty_print_dict(dict); + if (!dict_str) { + fprintf(stderr, "Could not convert dict to string"); + goto fail; + } + + printf("%s\n", dict_str); + free(dict_str); + getdns_dict_destroy(dict); + exit(EXIT_SUCCESS); + +fail: + if (dict) + getdns_dict_destroy(dict); + if (list) + getdns_list_destroy(list); + exit(EXIT_FAILURE); +} diff --git a/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.dsc b/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.dsc new file mode 100644 index 00000000..e00f4468 --- /dev/null +++ b/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.dsc @@ -0,0 +1,16 @@ +BaseName: 255-yaml-config +Version: 1.0 +Description: Test YAML configuration +CreationDate: Wed 13 Sep 2017 12:42:32 BST +Maintainer: Jim Hague +Category: +Component: +CmdDepends: +Depends: 200-stub-only-compile.tpkg +Help: +Pre: 255-yaml-config.pre +Post: +Test: 255-yaml-config.test +AuxFiles: +Passed: +Failure: diff --git a/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.good b/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.good new file mode 100644 index 00000000..2703569c --- /dev/null +++ b/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.good @@ -0,0 +1,87 @@ +{ + "Bindata entry": , + "List entry": + [ + , + , + + ], + "dnssec_return_status": 1000, + "upstream_recursive_servers": + [ + { + "address_data": , + "tls_auth_name": , + "tls_pubkey_pinset": + [ + { + "digest": , + "value": + } + ] + }, + { + "address_data": , + "tls_auth_name": , + "tls_pubkey_pinset": + [ + { + "digest": , + "value": + } + ] + }, + { + "address_data": , + "tls_auth_name": , + "tls_pubkey_pinset": + [ + { + "digest": , + "value": + } + ] + }, + { + "address_data": , + "tls_auth_name": + }, + { + "address_data": , + "tls_auth_name": , + "tls_pubkey_pinset": + [ + { + "digest": , + "value": + } + ] + }, + { + "address_data": , + "tls_auth_name": , + "tls_pubkey_pinset": + [ + { + "digest": , + "value": + } + ] + }, + { + "address_data": , + "tls_auth_name": , + "tls_pubkey_pinset": + [ + { + "digest": , + "value": + } + ] + }, + { + "address_data": , + "tls_auth_name": + } + ] +} diff --git a/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.input b/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.input new file mode 100644 index 00000000..68e4460f --- /dev/null +++ b/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.input @@ -0,0 +1,47 @@ +upstream_recursive_servers: +# IPv4 addresses +# The Surfnet/Sinodun servers + - address_data: 145.100.185.15 + tls_auth_name: "dnsovertls.sinodun.com" + tls_pubkey_pinset: + - digest: "sha256" + value: 62lKu9HsDVbyiPenApnc4sfmSYTHOVfFgL3pyB+cBL4= + - address_data: 145.100.185.16 + tls_auth_name: "dnsovertls1.sinodun.com" + tls_pubkey_pinset: + - digest: "sha256" + value: cE2ecALeE5B+urJhDrJlVFmf38cJLAvqekONvjvpqUA= +# The getdnsapi.net server + - address_data: 185.49.141.37 + tls_auth_name: "getdnsapi.net" + tls_pubkey_pinset: + - digest: "sha256" + value: foxZRnIh9gZpWnl+zEiKa0EJ2rdCGroMWm02gaxSc9Q= +# The uncensored DNS servers (no SPKI available) + - address_data: 184.105.193.78 + tls_auth_name: "unicast.censurfridns.dk" +# IPv6 addresses +# The Surfnet/Sinodun servers + - address_data: 2001:610:1:40ba:145:100:185:15 + tls_auth_name: "dnsovertls.sinodun.com" + tls_pubkey_pinset: + - digest: "sha256" + value: 62lKu9HsDVbyiPenApnc4sfmSYTHOVfFgL3pyB+cBL4= + - address_data: 2001:610:1:40ba:145:100:185:16 + tls_auth_name: "dnsovertls1.sinodun.com" + tls_pubkey_pinset: + - digest: "sha256" + value: cE2ecALeE5B+urJhDrJlVFmf38cJLAvqekONvjvpqUA= +# The getdnsapi.net server + - address_data: 2a04:b900:0:100::38 + tls_auth_name: "getdnsapi.net" + tls_pubkey_pinset: + - digest: "sha256" + value: foxZRnIh9gZpWnl+zEiKa0EJ2rdCGroMWm02gaxSc9Q= +# The uncensored DNS server (no SPKI available) + - address_data: 2620:ff:c000:0:1::64:25 + tls_auth_name: "unicast.censurfridns.dk" + +# Require DNSSEC validation. For releases earlier than 1.2 a trust anchor must +# be configured configured manually. This can be done with unbound-anchor. +dnssec_return_status: GETDNS_EXTENSION_TRUE diff --git a/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.pre b/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.pre new file mode 100644 index 00000000..802443c2 --- /dev/null +++ b/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.pre @@ -0,0 +1,14 @@ +# #-- 255-yaml-config.test --# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# use .tpkg.var.test for in test variable passing +[ -f .tpkg.var.test ] && source .tpkg.var.test + +( + grep '^CC=' "${BUILDDIR}/build-stub-only/src/Makefile" + grep '^LDFLAGS=' "${BUILDDIR}/build-stub-only/src/Makefile" + + BUILDDIR4SED=`echo "${BUILDDIR}/build-stub-only" | sed 's/\//\\\\\//g'` + sed -e "s/@BUILDDIR@/${BUILDDIR4SED}/g" \ + -e "s/@TPKG_NAME@/${TPKG_NAME}/g" "${TPKG_NAME}.Makefile" +) > Makefile diff --git a/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.test b/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.test new file mode 100644 index 00000000..b3735fb4 --- /dev/null +++ b/src/test/tpkg/255-yaml-config.tpkg/255-yaml-config.test @@ -0,0 +1,7 @@ +# #-- 255-yaml-config.test --# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# use .tpkg.var.test for in test variable passing +[ -f .tpkg.var.test ] && source .tpkg.var.test + +make && "./${TPKG_NAME}" 255-yaml-config.input | tee out && diff out "${TPKG_NAME}.good" diff --git a/src/test/tpkg/290-transports.tpkg/290-transports.dsc b/src/test/tpkg/290-transports.tpkg/290-transports.dsc index 8508f07a..acc5f398 100644 --- a/src/test/tpkg/290-transports.tpkg/290-transports.dsc +++ b/src/test/tpkg/290-transports.tpkg/290-transports.dsc @@ -6,7 +6,7 @@ Maintainer: Hoda Rohani Category: Component: CmdDepends: -Depends: 110-link.tpkg +Depends: 210-stub-only-link.tpkg Help: Pre: Post: diff --git a/src/test/tpkg/290-transports.tpkg/290-transports.test b/src/test/tpkg/290-transports.tpkg/290-transports.test index 54f1639e..ce3730e2 100644 --- a/src/test/tpkg/290-transports.tpkg/290-transports.test +++ b/src/test/tpkg/290-transports.tpkg/290-transports.test @@ -4,6 +4,12 @@ # use .tpkg.var.test for in test variable passing [ -f .tpkg.var.test ] && source .tpkg.var.test +if grep -q '^#define HAVE_SSL_HN_AUTH 1' "${BUILDDIR}/build-stub-only/src/config.h" +then + HAVE_SSL_HN_AUTH=1 +else + HAVE_SSL_HN_AUTH=0 +fi SERVER_IP="8.8.8.8" SERVER_IPv6="2001:4860:4860::8888" @@ -126,17 +132,30 @@ for (( ii = 0; ii < 1; ii++)); do SERVER_IP_TSIG_WRONG_NAME=`echo ${SERVER_IP_TSIG}${TSIG_ALG}":"${TSIG_NAME::${#TSIG_NAME}-1}":"${TSIG_SECRET}` SERVER_IP_TSIG_WRONG_SECRET=`echo ${SERVER_IP_TSIG}${TSIG_ALG}":"${TSIG_NAME}":"${TSIG_SECRET::${#TSIG_SECRET}-1}` - NUM_GOOD_QUERIES=9 - GOOD_QUERIES=( - "-s -A getdnsapi.net -l U @${SERVER_IP} +edns_cookies" "U" "-" - "-s -A getdnsapi.net -l T @${SERVER_IP}" "T" "-" - "-s -A getdnsapi.net -l U @${SERVER_IP_TSIG}${TSIG_ALG}:${TSIG_NAME}:${TSIG_SECRET}" "U" "-" - "-s -A getdnsapi.net -l U @${SERVER_IP_TSIG}${TSIG_NAME}:${TSIG_SECRET}" "U" "-" - "-s -A getdnsapi.net -l L @${TLS_SERVER_IP_NO_NAME}" "L" "N" - "-s -A getdnsapi.net -l L -m @${TLS_SERVER_IP}" "L" "S" - "-s -A getdnsapi.net -l L -m @${TLS_SERVER_IP_NO_NAME} -K pin-sha256=\"${TLS_SERVER_KEY}\"" "L" "S" - "-s -A getdnsapi.net -l L -m @${TLS_SERVER_IP} -K pin-sha256=\"${TLS_SERVER_KEY}\"" "L" "S" - "-s -G DNSKEY getdnsapi.net -l U @${SERVER_IP} -b 512 -D" "U" "-") + if [[ $HAVE_SSL_HN_AUTH = 1 ]] + then + NUM_GOOD_QUERIES=9 + GOOD_QUERIES=( + "-s -A getdnsapi.net -l U @${SERVER_IP} +edns_cookies" "U" "-" + "-s -A getdnsapi.net -l T @${SERVER_IP}" "T" "-" + "-s -A getdnsapi.net -l U @${SERVER_IP_TSIG}${TSIG_ALG}:${TSIG_NAME}:${TSIG_SECRET}" "U" "-" + "-s -A getdnsapi.net -l U @${SERVER_IP_TSIG}${TSIG_NAME}:${TSIG_SECRET}" "U" "-" + "-s -A getdnsapi.net -l L @${TLS_SERVER_IP_NO_NAME}" "L" "N" + "-s -A getdnsapi.net -l L -m @${TLS_SERVER_IP}" "L" "S" + "-s -A getdnsapi.net -l L -m @${TLS_SERVER_IP_NO_NAME} -K pin-sha256=\"${TLS_SERVER_KEY}\"" "L" "S" + "-s -A getdnsapi.net -l L -m @${TLS_SERVER_IP} -K pin-sha256=\"${TLS_SERVER_KEY}\"" "L" "S" + "-s -G DNSKEY getdnsapi.net -l U @${SERVER_IP} -b 512 -D" "U" "-") + else + NUM_GOOD_QUERIES=7 + GOOD_QUERIES=( + "-s -A getdnsapi.net -l U @${SERVER_IP} +edns_cookies" "U" "-" + "-s -A getdnsapi.net -l T @${SERVER_IP}" "T" "-" + "-s -A getdnsapi.net -l U @${SERVER_IP_TSIG}${TSIG_ALG}:${TSIG_NAME}:${TSIG_SECRET}" "U" "-" + "-s -A getdnsapi.net -l U @${SERVER_IP_TSIG}${TSIG_NAME}:${TSIG_SECRET}" "U" "-" + "-s -A getdnsapi.net -l L @${TLS_SERVER_IP_NO_NAME}" "L" "N" + "-s -A getdnsapi.net -l L -m @${TLS_SERVER_IP_NO_NAME} -K pin-sha256=\"${TLS_SERVER_KEY}\"" "L" "S" + "-s -G DNSKEY getdnsapi.net -l U @${SERVER_IP} -b 512 -D" "U" "-") + fi #"-s -A getdnsapi.net -l L -m @${TLS_SERVER_SS_IP_NO_NAME} -K pin-sha256=\"${TLS_SERVER_SS_KEY}\"" "L" "S" NUM_GOOD_FB_QUERIES=6 @@ -173,21 +192,21 @@ for (( ii = 0; ii < 1; ii++)); do fi echo "*Success cases:" for (( j = 0; j < $NUM_GOOD_QUERIES; j+=1 )); do - check_good "`"${GETDNS_QUERY}" -V +return_call_reporting $SYNC_MODE ${GOOD_QUERIES[$j*$NUM_ARGS]} `" ${GOOD_QUERIES[$((j*NUM_ARGS))+1]} ${GOOD_QUERIES[$((j*NUM_ARGS))+2]} + check_good "`"${GETDNS_STUB_QUERY}" -V +return_call_reporting $SYNC_MODE ${GOOD_QUERIES[$j*$NUM_ARGS]} `" ${GOOD_QUERIES[$((j*NUM_ARGS))+1]} ${GOOD_QUERIES[$((j*NUM_ARGS))+2]} echo "getdns_query $SYNC_MODE ${GOOD_QUERIES[$j*$NUM_ARGS]}" (( COUNT++ )) done echo "*Success fallback cases:" for (( j = 0; j < $NUM_GOOD_FB_QUERIES; j+=1 )); do - check_good "`"${GETDNS_QUERY}" -V +return_call_reporting $SYNC_MODE ${GOOD_FALLBACK_QUERIES[$j*$NUM_ARGS]} 2>/dev/null`" ${GOOD_FALLBACK_QUERIES[$((j*NUM_ARGS))+1]} ${GOOD_FALLBACK_QUERIES[$((j*NUM_ARGS))+2]} + check_good "`"${GETDNS_STUB_QUERY}" -V +return_call_reporting $SYNC_MODE ${GOOD_FALLBACK_QUERIES[$j*$NUM_ARGS]} 2>/dev/null`" ${GOOD_FALLBACK_QUERIES[$((j*NUM_ARGS))+1]} ${GOOD_FALLBACK_QUERIES[$((j*NUM_ARGS))+2]} echo "getdns_query $SYNC_MODE ${GOOD_FALLBACK_QUERIES[$j*$NUM_ARGS]} TESTS: ${GOOD_FALLBACK_QUERIES[$((j*NUM_ARGS))+1]} ${GOOD_FALLBACK_QUERIES[$((j*NUM_ARGS))+2]}" (( COUNT++ )) done echo "*Transport not available cases:" for (( j = 0; j < ${#NOT_AVAILABLE_QUERIES[@]}; j+=1 )); do - check_bad "`"${GETDNS_QUERY}" -V $SYNC_MODE ${NOT_AVAILABLE_QUERIES[${j}]} 2>&1`" + check_bad "`"${GETDNS_STUB_QUERY}" -V $SYNC_MODE ${NOT_AVAILABLE_QUERIES[${j}]} 2>&1`" echo "getdns_query $SYNC_MODE ${NOT_AVAILABLE_QUERIES[${j}]}" (( COUNT++ )) done @@ -198,3 +217,8 @@ done echo echo "Finished transport test: did $COUNT queries, $GOOD_COUNT passes, $FAIL_COUNT failures" echo +if [[ $FAIL_COUNT -gt 0 ]] +then + exit 1 +fi + 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/clean.sh b/src/test/tpkg/clean.sh index 60e4ef42..a405d5b0 100755 --- a/src/test/tpkg/clean.sh +++ b/src/test/tpkg/clean.sh @@ -3,5 +3,5 @@ export SRCDIR=`dirname $0` ( cd $SRCDIR ./tpkg clean - rm -fr build build-stub-only build-event-loops build-static-analysis install scan-build-reports .tpkg.var.master *.info + rm -fr build build-stub-only build-event-loops build-static-analysis install scan-build-reports .tpkg.var.master *.info Makefile ) 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/setup-env.sh b/src/test/tpkg/setup-env.sh index 18cdfbeb..d3269d64 100755 --- a/src/test/tpkg/setup-env.sh +++ b/src/test/tpkg/setup-env.sh @@ -36,3 +36,4 @@ export TPKG="${TPKG}" export LIBTOOL="${LIBTOOL}" END_OF_TPKG_VAR_MASTER +${TPKG} f 255-yaml-config.tpkg diff --git a/src/test/tpkg/tpkg b/src/test/tpkg/tpkg index 713adcfb..3701b723 100755 --- a/src/test/tpkg/tpkg +++ b/src/test/tpkg/tpkg @@ -193,10 +193,13 @@ 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" out " -d\t\tUse directories instead of tar.gz for tpkg archive format" + out " -f\t\tForce test to be re-run if already executed" out out " (C) NLnetLabs, Miek Gieben. Licensed under the GPL version 2." } @@ -329,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 @@ -858,9 +862,10 @@ echo "--------------- Test Output ------------------" | write_result result.$dsc pre out "[log] Executing test" - -( ${SHELL} $dsc_test ${TPKG_ARGS} 2>&1 ) | write_result result.$dsc_basename +( ${SHELL} $dsc_test ${TPKG_ARGS} 2>&1 ) > result.$dsc_basename.tmp test_result=$? +write_result result.$dsc_basename < result.$dsc_basename.tmp +rm -f result.$dsc_basename.tmp epoch # would like to run after post, but that is not possible :-( if [ $test_result -ne 0 ]; then err "[warning] Test executed with errors: $test_result." diff --git a/src/tools/getdns_query.c b/src/tools/getdns_query.c index c98eba86..0e94c5e7 100644 --- a/src/tools/getdns_query.c +++ b/src/tools/getdns_query.c @@ -46,6 +46,10 @@ typedef unsigned short in_port_t; #include #endif +#ifdef HAVE_GETDNS_YAML2DICT +getdns_return_t getdns_yaml2dict(const char *, getdns_dict **dict); +#endif + #define EXAMPLE_PIN "pin-sha256=\"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\"" static int verbosity = 0; @@ -215,7 +219,8 @@ print_usage(FILE *out, const char *progname) fprintf(out, "\t-C\t\n"); fprintf(out, "\t\tRead settings from config file \n"); fprintf(out, "\t\tThe getdns context will be configured with these settings\n"); - fprintf(out, "\t\tThe file must be in json dict format.\n"); + fprintf(out, "\t\tThe file must be in YAML format (with extension of '.yml')\n"); + fprintf(out, "\t\tor JSON dict format (with extension '.conf')\n"); if (i_am_stubby) { fprintf(out, "\t\tBy default, configuration is first read from"); fprintf(out, "\n\t\t\"/etc/stubby.conf\" and then from \"$HOME/.stubby.conf\"\n"); @@ -346,7 +351,7 @@ static getdns_return_t validate_chain(getdns_dict *response) if ((r = getdns_list_set_dict(to_validate, 0, reply))) goto error; - if (verbosity) printf("reply "PRIsz", dnssec_status: ", i); + if (verbosity) printf("reply %d, dnssec_status: ", (int)i); switch ((s = getdns_validate_dnssec( to_validate, validation_chain, trust_anchor))) { @@ -478,16 +483,25 @@ done: return r; } -static void parse_config(const char *config_str) +static void parse_config(const char *config_str, int yaml_config) { getdns_dict *config_dict; getdns_list *list; getdns_return_t r; - if ((r = getdns_str2dict(config_str, &config_dict))) + if (yaml_config) { +#ifdef USE_YAML_CONFIG + r = getdns_yaml2dict(config_str, &config_dict); +#else + fprintf(stderr, "Support for YAML configuration files not available.\n"); + return; +#endif + } else { + r = getdns_str2dict(config_str, &config_dict); + } + if (r) fprintf(stderr, "Could not parse config file: %s\n", getdns_get_errorstr_by_id(r)); - else { if (!(r = getdns_dict_get_list( config_dict, "listen_addresses", &list))) { @@ -535,6 +549,7 @@ int parse_config_file(const char *fn, int report_open_failure) FILE *fh; char *config_file = NULL; long config_file_sz; + size_t read_sz; if (!(fh = fopen(fn, "r"))) { if (report_open_failure) @@ -559,15 +574,16 @@ int parse_config_file(const char *fn, int report_open_failure) return GETDNS_RETURN_MEMORY_ERROR; } rewind(fh); - if (fread(config_file, 1, config_file_sz, fh) != (size_t)config_file_sz) { + read_sz = fread(config_file, 1, config_file_sz + 1, fh); + if (read_sz > (size_t)config_file_sz || ferror(fh) || !feof(fh)) { fprintf( stderr, "An error occurred while reading \"%s\": %s\n" , fn, strerror(errno)); fclose(fh); return GETDNS_RETURN_MEMORY_ERROR; } - config_file[config_file_sz] = 0; + config_file[read_sz] = 0; fclose(fh); - parse_config(config_file); + parse_config(config_file, strstr(fn, ".yml") != NULL); free(config_file); return GETDNS_RETURN_GOOD; } @@ -656,7 +672,7 @@ getdns_return_t parse_args(int argc, char **argv) } continue; } else if (arg[0] == '{') { - parse_config(arg); + parse_config(arg, 0); continue; } else if (arg[0] != '-') { @@ -1071,11 +1087,11 @@ next: ; /* apply the accumulated pubkey pinset to all upstreams: */ for (j = 0; j < upstream_count; j++) { if (r = getdns_list_get_dict(upstream_list, j, &upstream), r) { - fprintf(stderr, "Failed to get upstream "PRIsz" when adding pinset\n", j); + fprintf(stderr, "Failed to get upstream %d when adding pinset\n", (int)j); return r; } if (r = getdns_dict_set_list(upstream, "tls_pubkey_pinset", pubkey_pinset), r) { - fprintf(stderr, "Failed to set pubkey pinset on upstream "PRIsz"\n", j); + fprintf(stderr, "Failed to set pubkey pinset on upstream %d\n", (int)j); return r; } } @@ -1269,7 +1285,9 @@ void read_line_cb(void *userarg) if (listen_count) (void) getdns_context_set_listen_addresses( context, NULL, NULL, NULL); - (void) getdns_context_set_idle_timeout(context, 0); + if (interactive && !query_file) + (void) getdns_context_set_upstream_recursive_servers( + context, NULL); return; } if (query_file && verbosity) @@ -1668,16 +1686,22 @@ static void stubby_log(void *userarg, uint64_t system, #ifdef GETDNS_ON_WINDOWS time_t tsec; + if (!verbosity) + return; + gettimeofday(&tv, NULL); tsec = (time_t) tv.tv_sec; gmtime_s(&tm, (const time_t *) &tsec); #else + if (!verbosity) + return; + gettimeofday(&tv, NULL); gmtime_r(&tv.tv_sec, &tm); #endif strftime(buf, 10, "%H:%M:%S", &tm); (void)userarg; (void)system; (void)level; - (void) fprintf(stderr, "[%s.%.6d] STUBBY: ", buf, (int)tv.tv_usec); + (void) fprintf(stderr, "[%s.%.6d] UPSTREAM ", buf, (int)tv.tv_usec); (void) vfprintf(stderr, fmt, ap); } @@ -1725,16 +1749,16 @@ main(int argc, char **argv) , "%s/.stubby.conf" , getenv("HOME") ); - (void) parse_config(default_stubby_config); + (void) parse_config(default_stubby_config, 0); (void) parse_config_file("/etc/stubby.conf", 0); if (n_chars > 0 && n_chars < (int)sizeof(home_stubby_conf_fn)){ (void) parse_config_file(home_stubby_conf_fn, 0); } clear_listen_list_on_arg = 1; - - (void) getdns_context_set_logfunc(context, NULL, - GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_DEBUG, stubby_log); } + (void) getdns_context_set_logfunc(context, NULL, + GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_DEBUG, stubby_log); + if ((r = parse_args(argc, argv))) goto done_destroy_context; clear_listen_list_on_arg = 0; diff --git a/src/types-internal.h b/src/types-internal.h index 7834fc32..05589f4a 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; @@ -188,7 +188,9 @@ typedef struct getdns_tcp_state { typedef struct getdns_network_req { /* For storage in upstream->netreq_by_query_id */ - _getdns_rbnode_t node; + _getdns_rbnode_t node; + /* The netreq_by_query_id tree in which this netreq was registered */ + _getdns_rbtree_t *query_id_registered; #ifdef HAVE_MDNS_SUPPORT /* * for storage of continuous query context in hash table of cached results. @@ -300,6 +302,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 +327,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 +369,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 c518e398..8c756e70 100644 --- a/src/util-internal.h +++ b/src/util-internal.h @@ -203,7 +203,7 @@ INLINE uint64_t _getdns_get_now_ms() struct timeval tv; (void) gettimeofday(&tv, NULL); - return tv.tv_sec * 1000 + tv.tv_usec / 1000; + return (uint64_t)tv.tv_sec * 1000 + tv.tv_usec / 1000; } INLINE uint64_t _getdns_ms_until_expiry(uint64_t expires) 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 61142309..0cbd46ea 160000 --- a/stubby +++ b/stubby @@ -1 +1 @@ -Subproject commit 6114230904d9413514d521358ca7956aac99e822 +Subproject commit 0cbd46ea6d0da727c42b0890d498366cd2b1ec2c