diff --git a/.gitignore b/.gitignore index fce9d682..7bbbdebd 100644 --- a/.gitignore +++ b/.gitignore @@ -36,9 +36,10 @@ src/test/check_getdns src/test/check_getdns_event src/test/check_getdns_uv src/test/check_getdns_ev -src/test/getdns_query src/test/scratchpad src/test/scratchpad.c +src/tools/getdns_query +src/tools/stubby doc/*.3 src/getdns/getdns.h *.log diff --git a/.gitmodules b/.gitmodules index 06b16b93..a79d3846 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "src/test/jsmn"] - path = src/test/jsmn + path = src/jsmn url = https://github.com/getdnsapi/jsmn.git branch = getdns diff --git a/ChangeLog b/ChangeLog index 46484397..16a6428f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -14,6 +14,26 @@ getdns_get_suffix(). Thanks Jim Hague * Better README.md. Thanks Andrew Sullivan +* 2016-10-19: Version 1.1.0-a2 + * Improved TLS connection management + * OpenSSL 1.1 support + * Stubby, Server version of getdns_query that by default listens + on 127.0.0.1 and ::1 and reads config from /etc/stubby.conf + and $HOME/.stubby.conf + +* 2016-07-14: Version 1.1.0a1 + * Conversion functions from text strings to getdns native types: + getdns_str2dict(), getdns_str2list(), getdns_str2bindata() and + getdns_str2int() + * A getdns_context_config() function that configures a context + with settings given in a getdns_dict + * A a getdns_context_set_listen_addresses() function and companion + getdns_reply() function to construct simple name servers. + * Relocate getdns_query to src/tools and build by default + * Enhancements to the logic used to select connection based upstream + transports (TCP, TLS) to improve robustness and re-use of + connections/upstreams. + * 2016-07-14: Version 1.0.0b2 * Collect coverage information from the unit tests Thanks Shane Kerr diff --git a/Makefile.in b/Makefile.in index 1bf01cfb..a98b0654 100644 --- a/Makefile.in +++ b/Makefile.in @@ -44,7 +44,7 @@ libdir = @libdir@ srcdir = @srcdir@ INSTALL = @INSTALL@ -all : default @GETDNS_QUERY@ +all : default @GETDNS_QUERY@ @STUBBY@ everything: default cd src/test && $(MAKE) @@ -52,7 +52,7 @@ everything: default default: cd src && $(MAKE) $@ -install: all getdns.pc getdns_ext_event.pc @INSTALL_GETDNS_QUERY@ +install: all getdns.pc getdns_ext_event.pc @INSTALL_GETDNS_QUERY@ @INSTALL_STUBBY@ $(INSTALL) -m 755 -d $(DESTDIR)$(docdir) $(INSTALL) -m 644 $(srcdir)/AUTHORS $(DESTDIR)$(docdir) $(INSTALL) -m 644 $(srcdir)/ChangeLog $(DESTDIR)$(docdir) @@ -87,7 +87,7 @@ install: all getdns.pc getdns_ext_event.pc @INSTALL_GETDNS_QUERY@ @echo "*** at package installation time from the post-install script." @echo "***" -uninstall: @UNINSTALL_GETDNS_QUERY@ +uninstall: @UNINSTALL_GETDNS_QUERY@ @UNINSTALL_STUBBY@ rm -rf $(DESTDIR)$(docdir) cd doc && $(MAKE) $@ cd src && $(MAKE) $@ @@ -104,6 +104,9 @@ test: getdns_query: cd src && $(MAKE) $@ +stubby: + cd src && $(MAKE) $@ + scratchpad: cd src && $(MAKE) $@ @@ -111,10 +114,16 @@ pad: scratchpad src/test/scratchpad || ./libtool exec gdb src/test/scratchpad install-getdns_query: - cd src/test && $(MAKE) install + cd src/tools && $(MAKE) $@ uninstall-getdns_query: - cd src/test && $(MAKE) uninstall + cd src/tools && $(MAKE) $@ + +install-stubby: + cd src/tools && $(MAKE) $@ + +uninstall-stubby: + cd src/tools && $(MAKE) $@ clean: cd src && $(MAKE) $@ @@ -187,6 +196,8 @@ $(distdir): mkdir -p $(distdir)/src/compat mkdir -p $(distdir)/src/util mkdir -p $(distdir)/src/gldns + mkdir -p $(distdir)/src/tools + mkdir -p $(distdir)/src/jsmn mkdir -p $(distdir)/doc mkdir -p $(distdir)/spec mkdir -p $(distdir)/spec/example @@ -229,9 +240,11 @@ $(distdir): cp $(srcdir)/spec/*.tgz $(distdir)/spec || true cp $(srcdir)/spec/example/Makefile.in $(distdir)/spec/example cp $(srcdir)/spec/example/*.[ch] $(distdir)/spec/example - cp $(srcdir)/src/test/jsmn/*.[ch] $(distdir)/src/test/jsmn - cp $(srcdir)/src/test/jsmn/LICENSE $(distdir)/src/test/jsmn - cp $(srcdir)/src/test/jsmn/README.md $(distdir)/src/test/jsmn + cp $(srcdir)/src/tools/Makefile.in $(distdir)/src/tools + cp $(srcdir)/src/tools/*.[ch] $(distdir)/src/tools + 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 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 2f59f6de..4f802fca 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,8 @@ Traditional access to DNS data from applications has several limitations: * Sophisticated uses of the DNS (things like IDNA and DNSSEC validation) require considerable application work, possibly by application developers with little experience with the vagaries of DNS. +getdns also provides a experimental DNS Privacy enabled client called 'stubby' - see below for more details. + ## Motivation for providing the API The developers are of the opinion that DNSSEC offers a unique global infrastructure for establishing and enhancing cryptographic trust relations. With the development of this API we intend to offer application developers a modern and flexible interface that enables end-to-end trust in the DNS architecture, and which will inspire application developers to implement innovative security solutions in their applications. @@ -73,9 +75,13 @@ If you want to make use of the configuration files that utilise a JSON-like form before building. -If you want to use the getdns_query command line wrapper script for testing or to enable getdns as a daemon then you must build it using +As well as building the getdns library 2 other tools are installed by default by the above process: + +* getdns_query: a command line test script wrapper for getdns +* stubby: a experimental DNS Privacy enabled client + +Note: If you only want to build stubby, then use the `--enable-stub-only` and `--without-libidn` options when running 'configure'. - # make getdns_query ## Minimizing dependencies @@ -91,6 +97,18 @@ The implementation works with a variety of event loops, each built as a separate * [libuv](https://github.com/joyent/libuv) * [libev](http://software.schmorp.de/pkg/libev.html) +## Stubby + +* Stubby is an experimental implementation of a DNS Privacy enabled stub resolver. It is currently suitable for advanced/technical users - all feedback is welcome! Also see [dnsprivacy.org](https://dnsprivacy.org) for more information on DNS Privacy and stubby. +* By default stubby will attempt to use 'Opportunistic' Privacy for DNS queries. +* A sample configuration file is available in the source code (src/tools/stubby.conf) which uses 'Strict' Privacy and some of the available test DNS Privacy servers to resolve queries. Note these servers are test servers that offer no service guarantees. The location of a configuration file can be specified with the '-C' flag +* RECOMMENDED: Minimal logging output from Stubby is available (e.g. which servers are used and connection level statistics) by also using the '--enable-debug-daemon' flag when running 'configure'. + +To use stubby +* Start stubby from the command line +* Test it by doing, for example, 'dig @127.0.0.1 www.example.com' +* Alter the default DNS resolvers on your system to point at localhost (127.0.0.1, ::1) + ## Regression Tests A suite of regression tests are included with the library, if you make changes or just diff --git a/configure.ac b/configure.ac index 6b3b3710..f3b35c98 100644 --- a/configure.ac +++ b/configure.ac @@ -36,8 +36,8 @@ sinclude(./m4/acx_getaddrinfo.m4) sinclude(./m4/ax_check_compile_flag.m4) sinclude(./m4/pkg.m4) -AC_INIT([getdns], [1.0.0], [users@getdnsapi.net], [], [https://getdnsapi.net]) -AC_SUBST(RELEASE_CANDIDATE, []) +AC_INIT([getdns], [1.1.0], [users@getdnsapi.net], [], [https://getdnsapi.net]) +AC_SUBST(RELEASE_CANDIDATE, [-alpha3]) # Set current date from system if not set AC_ARG_WITH([current-date], @@ -47,7 +47,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, [0x01000000]) +AC_SUBST(GETDNS_NUMERIC_VERSION, [0x0100A300]) 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" @@ -78,8 +78,9 @@ GETDNS_COMPILATION_COMMENT="AC_PACKAGE_NAME $GETDNS_VERSION configured on $CURRE # getdns-0.5.1 had libversion 4:1:3 (but should have been getdns-0.6.0) # getdns-0.9.0 had libversion 5:0:4 # getdns-1.0.0 will have libversion 5:1:4 +# getdns-1.1.0 will have libversion 6:0:0 # -GETDNS_LIBVERSION=5:1:4 +GETDNS_LIBVERSION=6:0:0 AC_SUBST(GETDNS_COMPILATION_COMMENT) AC_SUBST(GETDNS_LIBVERSION) @@ -150,6 +151,7 @@ ACX_ARG_RPATH AC_ARG_ENABLE(debug-sched, AC_HELP_STRING([--enable-debug-sched], [Enable scheduling debugging messages])) AC_ARG_ENABLE(debug-stub, AC_HELP_STRING([--enable-debug-stub], [Enable stub debugging messages])) +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 scheduling, stub and dnssec debugging])) @@ -157,6 +159,7 @@ case "$enable_all_debugging" in yes) enable_debug_sched=yes enable_debug_stub=yes + enable_debug_daemon=yes enable_debug_sec=yes enable_debug_server=yes ;; @@ -177,6 +180,13 @@ case "$enable_debug_stub" in no|*) ;; esac +case "$enable_debug_daemon" in + yes) + AC_DEFINE_UNQUOTED([DAEMON_DEBUG], [1], [Define this to enable printing of daemon debugging messages.]) + ;; + no|*) + ;; +esac case "$enable_debug_sec" in yes) AC_DEFINE_UNQUOTED([SEC_DEBUG], [1], [Define this to enable printing of dnssec debugging messages.]) @@ -907,9 +917,9 @@ AC_DEFINE_UNQUOTED([TRUST_ANCHOR_FILE], ["$TRUST_ANCHOR_FILE"], [Default trust a AC_SUBST(TRUST_ANCHOR_FILE) AC_MSG_NOTICE([Default trust anchor: $TRUST_ANCHOR_FILE]) -AC_ARG_WITH(getdns_query, AS_HELP_STRING([--with-getdns_query], - [Also compile and install the getdns_query tool]), - [], [withval="no"]) +AC_ARG_WITH(getdns_query, AS_HELP_STRING([--without-getdns_query], + [Do not compile and install the getdns_query tool]), + [], [withval="yes"]) if test x_$withval = x_no; then GETDNS_QUERY="" INSTALL_GETDNS_QUERY="" @@ -919,6 +929,26 @@ else INSTALL_GETDNS_QUERY="install-getdns_query" UNINSTALL_GETDNS_QUERY="uninstall-getdns_query" fi +AC_SUBST(GETDNS_QUERY) +AC_SUBST(INSTALL_GETDNS_QUERY) +AC_SUBST(UNINSTALL_GETDNS_QUERY) + +AC_ARG_WITH(stubby, AS_HELP_STRING([--without-stubby], + [Do not compile and install stubby, the (stub) resolver daemon]), + [], [withval="yes"]) +if test x_$withval = x_no; then + STUBBY="" + INSTALL_STUBBY="" + UNINSTALL_STUBBY="" +else + STUBBY="stubby" + INSTALL_STUBBY="install-stubby" + UNINSTALL_STUBBY="uninstall-stubby" +fi +AC_SUBST(STUBBY) +AC_SUBST(INSTALL_STUBBY) +AC_SUBST(UNINSTALL_STUBBY) + 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"]) @@ -931,11 +961,7 @@ case "$withval" in ;; esac -AC_SUBST(GETDNS_QUERY) -AC_SUBST(INSTALL_GETDNS_QUERY) -AC_SUBST(UNINSTALL_GETDNS_QUERY) - -AC_CONFIG_FILES([Makefile src/Makefile src/version.c src/getdns/getdns.h src/getdns/getdns_extra.h spec/example/Makefile src/test/Makefile doc/Makefile getdns.pc getdns_ext_event.pc]) +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 diff --git a/src/Makefile.in b/src/Makefile.in index 02e8a0b7..2523aab8 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -67,7 +67,7 @@ C99COMPATFLAGS=@C99COMPATFLAGS@ GETDNS_OBJ=const-info.lo convert.lo dict.lo dnssec.lo general.lo \ list.lo request-internal.lo pubkey-pinning.lo rr-dict.lo \ - rr-iter.lo stub.lo sync.lo ub_loop.lo util-internal.lo + rr-iter.lo server.lo stub.lo sync.lo ub_loop.lo util-internal.lo GLDNS_OBJ=keyraw.lo gbuffer.lo wire2str.lo parse.lo parseutil.lo rrdef.lo \ str2wire.lo @@ -78,6 +78,8 @@ COMPAT_OBJ=$(LIBOBJS:.o=.lo) UTIL_OBJ=rbtree.lo val_secalgo.lo +JSMN_OBJ=jsmn.lo + EXTENSION_OBJ=default_eventloop.lo libevent.lo libev.lo NON_C99_OBJS=context.lo libuv.lo @@ -106,6 +108,9 @@ $(COMPAT_OBJ): $(UTIL_OBJ): $(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) $(WNOERRORFLAG) -c $(srcdir)/util/$(@:.lo=.c) -o $@ +$(JSMN_OBJ): + $(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) -DJSMN_GETDNS -c $(srcdir)/jsmn/$(@:.lo=.c) -o $@ + $(EXTENSION_OBJ): $(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) $(WPEDANTICFLAG) -c $(srcdir)/extension/$(@:.lo=.c) -o $@ @@ -146,15 +151,17 @@ 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.lo $(GLDNS_OBJ) $(COMPAT_OBJ) $(UTIL_OBJ) - $(LIBTOOL) --tag=CC --mode=link $(CC) -o $@ $(GETDNS_OBJ) version.lo context.lo default_eventloop.lo $(GLDNS_OBJ) $(COMPAT_OBJ) $(UTIL_OBJ) $(LDFLAGS) -rpath $(libdir) -version-info $(libversion) -no-undefined -export-symbols $(srcdir)/libgetdns.symbols - +libgetdns.la: $(GETDNS_OBJ) version.lo context.lo default_eventloop.lo $(GLDNS_OBJ) $(COMPAT_OBJ) $(UTIL_OBJ) $(JSMN_OBJ) + $(LIBTOOL) --tag=CC --mode=link $(CC) -o $@ $(GETDNS_OBJ) version.lo context.lo default_eventloop.lo $(GLDNS_OBJ) $(COMPAT_OBJ) $(UTIL_OBJ) $(JSMN_OBJ) $(LDFLAGS) -rpath $(libdir) -version-info $(libversion) -no-undefined -export-symbols $(srcdir)/libgetdns.symbols test: all cd test && $(MAKE) $@ getdns_query: all - cd test && $(MAKE) $@ + cd tools && $(MAKE) $@ + +stubby: all + cd tools && $(MAKE) $@ scratchpad: all cd test && $(MAKE) $@ @@ -162,11 +169,13 @@ scratchpad: all pad: scratchpad clean: + cd tools && $(MAKE) $@ cd test && $(MAKE) $@ rm -f *.o *.lo extension/*.lo extension/*.o $(PROGRAMS) libgetdns.la libgetdns_ext_*.la rm -rf .libs extension/.libs distclean : clean + cd tools && $(MAKE) $@ cd test && $(MAKE) $@ rmdir test 2>/dev/null || true rm -f Makefile config.status config.log Doxyfile config.h version.c getdns/Makefile getdns/getdns.h getdns/getdns_extra.h @@ -174,42 +183,17 @@ distclean : clean rmdir extension 2>/dev/null || true rm -Rf autom4te.cache -$(distdir): FORCE - mkdir -p $(distdir)/src - cp configure.ac $(distdir) - cp configure $(distdir) - cp Makefile.in $(distdir) - cp src/Makefile.in $(distdir)/src - -distcheck: $(distdir).tar.gz - gzip -cd $(distdir).tar.gz | tar xvf - - cd $(distdir) && ./configure - cd $(distdir) && $(MAKE) all - cd $(distdir) && $(MAKE) check - cd $(distdir) && $(MAKE) DESTDIR=$${PWD}/_inst install - cd $(distdir) && $(MAKE) DESTDIR=$${PWD}/_inst uninstall - @remaining="`find $${PWD}/$(distdir)/_inst -type f | wc -l`"; \ - if test "$${remaining}" -ne 0; then - echo "@@@ $${remaining} file(s) remaining in stage directory!"; \ - exit 1; \ - fi - cd $(distdir) && $(MAKE) clean - rm -rf $(distdir) - @echo "*** Package $(distdir).tar.gz is ready for distribution" - Makefile: $(srcdir)/Makefile.in ../config.status cd .. && ./config.status src/Makefile -configure.status: configure - cd .. && ./config.status --recheck - 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" *.c gldns/*.c compat/*.c util/*.c extension/*.c| \ + (blddir=`pwd`; cd $(srcdir) ; gcc -MM -I. -I"$$blddir" *.c gldns/*.c compat/*.c util/*.c jsmn/*.c extension/*.c| \ sed -e "s? $$blddir/? ?g" \ -e 's?gldns/?$$(srcdir)/gldns/?g' \ -e 's?compat/?$$(srcdir)/compat/?g' \ -e 's?util/?$$(srcdir)/util/?g' \ + -e 's?jsmn/?$$(srcdir)/jsmn/?g' \ -e 's?extension/?$$(srcdir)/extension/?g' \ -e 's? \([a-z_-]*\)\.\([ch]\)? $$(srcdir)/\1.\2?g' \ -e 's? \$$(srcdir)/config\.h? config.h?g' \ @@ -221,6 +205,7 @@ depend: -e 's!\(.*\)\.o[ :]*!\1.lo \1.o: !g' >> Makefile.in.new ) (cd $(srcdir) ; diff Makefile.in.new Makefile.in && rm Makefile.in.new \ || mv Makefile.in.new Makefile.in ) + cd tools && $(MAKE) $@ cd test && $(MAKE) $@ .PHONY: clean test @@ -233,69 +218,78 @@ context.lo context.o: $(srcdir)/context.c config.h $(srcdir)/debug.h $(srcdir)/g $(srcdir)/gldns/wire2str.h $(srcdir)/context.h getdns/getdns.h getdns/getdns_extra.h \ getdns/getdns.h $(srcdir)/types-internal.h $(srcdir)/util/rbtree.h \ $(srcdir)/extension/default_eventloop.h config.h getdns/getdns_extra.h $(srcdir)/ub_loop.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)/server.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 convert.lo convert.o: $(srcdir)/convert.c config.h getdns/getdns.h getdns/getdns_extra.h \ getdns/getdns.h $(srcdir)/util-internal.h $(srcdir)/context.h $(srcdir)/types-internal.h $(srcdir)/util/rbtree.h \ $(srcdir)/extension/default_eventloop.h config.h getdns/getdns_extra.h $(srcdir)/ub_loop.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)/gldns/str2wire.h $(srcdir)/gldns/rrdef.h $(srcdir)/dict.h $(srcdir)/list.h $(srcdir)/convert.h + $(srcdir)/debug.h $(srcdir)/server.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h $(srcdir)/gldns/pkthdr.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 dict.lo dict.o: $(srcdir)/dict.c config.h $(srcdir)/types-internal.h getdns/getdns.h \ getdns/getdns_extra.h getdns/getdns.h $(srcdir)/util/rbtree.h $(srcdir)/util-internal.h \ $(srcdir)/context.h $(srcdir)/extension/default_eventloop.h config.h getdns/getdns_extra.h \ - $(srcdir)/ub_loop.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)/ub_loop.h $(srcdir)/debug.h $(srcdir)/server.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 dnssec.lo dnssec.o: $(srcdir)/dnssec.c config.h $(srcdir)/debug.h getdns/getdns.h $(srcdir)/context.h \ getdns/getdns_extra.h getdns/getdns.h $(srcdir)/types-internal.h $(srcdir)/util/rbtree.h \ $(srcdir)/extension/default_eventloop.h config.h getdns/getdns_extra.h $(srcdir)/ub_loop.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)/server.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 general.lo general.o: $(srcdir)/general.c config.h $(srcdir)/general.h getdns/getdns.h $(srcdir)/types-internal.h \ getdns/getdns_extra.h getdns/getdns.h $(srcdir)/util/rbtree.h $(srcdir)/ub_loop.h $(srcdir)/debug.h \ $(srcdir)/gldns/wire2str.h $(srcdir)/context.h $(srcdir)/extension/default_eventloop.h config.h \ - getdns/getdns_extra.h $(srcdir)/util-internal.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h \ + getdns/getdns_extra.h $(srcdir)/server.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 list.lo list.o: $(srcdir)/list.c $(srcdir)/types-internal.h getdns/getdns.h getdns/getdns_extra.h \ getdns/getdns.h $(srcdir)/util/rbtree.h $(srcdir)/util-internal.h config.h $(srcdir)/context.h \ $(srcdir)/extension/default_eventloop.h config.h getdns/getdns_extra.h $(srcdir)/ub_loop.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)/debug.h $(srcdir)/server.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h $(srcdir)/gldns/pkthdr.h \ + $(srcdir)/list.h $(srcdir)/dict.h pubkey-pinning.lo pubkey-pinning.o: $(srcdir)/pubkey-pinning.c config.h $(srcdir)/debug.h getdns/getdns.h \ $(srcdir)/context.h getdns/getdns.h getdns/getdns_extra.h $(srcdir)/types-internal.h \ $(srcdir)/util/rbtree.h $(srcdir)/extension/default_eventloop.h config.h \ - getdns/getdns_extra.h $(srcdir)/ub_loop.h $(srcdir)/util-internal.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h \ - $(srcdir)/gldns/gbuffer.h $(srcdir)/gldns/pkthdr.h + getdns/getdns_extra.h $(srcdir)/ub_loop.h $(srcdir)/server.h $(srcdir)/util-internal.h $(srcdir)/rr-iter.h \ + $(srcdir)/rr-dict.h $(srcdir)/gldns/gbuffer.h $(srcdir)/gldns/pkthdr.h request-internal.lo request-internal.o: $(srcdir)/request-internal.c config.h $(srcdir)/types-internal.h \ getdns/getdns.h getdns/getdns_extra.h getdns/getdns.h $(srcdir)/util/rbtree.h \ $(srcdir)/util-internal.h $(srcdir)/context.h $(srcdir)/extension/default_eventloop.h config.h \ - getdns/getdns_extra.h $(srcdir)/ub_loop.h $(srcdir)/debug.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h \ + getdns/getdns_extra.h $(srcdir)/ub_loop.h $(srcdir)/debug.h $(srcdir)/server.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 rr-dict.lo rr-dict.o: $(srcdir)/rr-dict.c $(srcdir)/rr-dict.h config.h getdns/getdns.h $(srcdir)/gldns/gbuffer.h \ $(srcdir)/util-internal.h $(srcdir)/context.h getdns/getdns_extra.h getdns/getdns.h \ $(srcdir)/types-internal.h $(srcdir)/util/rbtree.h $(srcdir)/extension/default_eventloop.h config.h \ - getdns/getdns_extra.h $(srcdir)/ub_loop.h $(srcdir)/debug.h $(srcdir)/rr-iter.h $(srcdir)/gldns/pkthdr.h $(srcdir)/dict.h + getdns/getdns_extra.h $(srcdir)/ub_loop.h $(srcdir)/debug.h $(srcdir)/server.h $(srcdir)/rr-iter.h \ + $(srcdir)/gldns/pkthdr.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 \ $(srcdir)/gldns/gbuffer.h $(srcdir)/gldns/pkthdr.h $(srcdir)/gldns/rrdef.h +server.lo server.o: $(srcdir)/server.c config.h getdns/getdns_extra.h getdns/getdns.h \ + $(srcdir)/context.h getdns/getdns.h $(srcdir)/types-internal.h $(srcdir)/util/rbtree.h \ + $(srcdir)/extension/default_eventloop.h config.h getdns/getdns_extra.h $(srcdir)/ub_loop.h \ + $(srcdir)/debug.h $(srcdir)/server.h stub.lo stub.o: $(srcdir)/stub.c config.h $(srcdir)/debug.h $(srcdir)/stub.h getdns/getdns.h $(srcdir)/types-internal.h \ getdns/getdns_extra.h getdns/getdns.h $(srcdir)/util/rbtree.h $(srcdir)/gldns/gbuffer.h \ $(srcdir)/gldns/pkthdr.h $(srcdir)/gldns/rrdef.h $(srcdir)/gldns/str2wire.h $(srcdir)/gldns/rrdef.h \ $(srcdir)/gldns/wire2str.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h $(srcdir)/context.h \ $(srcdir)/extension/default_eventloop.h config.h getdns/getdns_extra.h $(srcdir)/ub_loop.h \ - $(srcdir)/util-internal.h $(srcdir)/general.h $(srcdir)/pubkey-pinning.h + $(srcdir)/server.h $(srcdir)/util-internal.h $(srcdir)/general.h $(srcdir)/pubkey-pinning.h sync.lo sync.o: $(srcdir)/sync.c getdns/getdns.h config.h $(srcdir)/context.h getdns/getdns_extra.h \ getdns/getdns.h $(srcdir)/types-internal.h $(srcdir)/util/rbtree.h \ $(srcdir)/extension/default_eventloop.h config.h getdns/getdns_extra.h $(srcdir)/ub_loop.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)/debug.h $(srcdir)/server.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 ub_loop.lo ub_loop.o: $(srcdir)/ub_loop.c $(srcdir)/ub_loop.h config.h getdns/getdns.h \ getdns/getdns_extra.h getdns/getdns.h $(srcdir)/types-internal.h $(srcdir)/util/rbtree.h \ $(srcdir)/debug.h util-internal.lo util-internal.o: $(srcdir)/util-internal.c config.h getdns/getdns.h $(srcdir)/dict.h \ $(srcdir)/util/rbtree.h $(srcdir)/types-internal.h getdns/getdns_extra.h getdns/getdns.h \ $(srcdir)/list.h $(srcdir)/util-internal.h $(srcdir)/context.h $(srcdir)/extension/default_eventloop.h config.h \ - getdns/getdns_extra.h $(srcdir)/ub_loop.h $(srcdir)/debug.h $(srcdir)/rr-iter.h $(srcdir)/rr-dict.h \ + getdns/getdns_extra.h $(srcdir)/ub_loop.h $(srcdir)/debug.h $(srcdir)/server.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 version.lo version.o: version.c @@ -327,6 +321,7 @@ rbtree.lo rbtree.o: $(srcdir)/util/rbtree.c config.h $(srcdir)/util/log.h $(srcd $(srcdir)/util/fptr_wlist.h $(srcdir)/util/rbtree.h val_secalgo.lo val_secalgo.o: $(srcdir)/util/val_secalgo.c config.h $(srcdir)/util/val_secalgo.h $(srcdir)/util/log.h \ $(srcdir)/debug.h config.h $(srcdir)/gldns/rrdef.h $(srcdir)/gldns/keyraw.h $(srcdir)/gldns/gbuffer.h +jsmn.lo jsmn.o: $(srcdir)/jsmn/jsmn.c $(srcdir)/jsmn/jsmn.h default_eventloop.lo default_eventloop.o: $(srcdir)/extension/default_eventloop.c config.h \ $(srcdir)/extension/default_eventloop.h getdns/getdns.h getdns/getdns_extra.h \ $(srcdir)/debug.h config.h $(srcdir)/types-internal.h getdns/getdns.h getdns/getdns_extra.h \ diff --git a/src/const-info.c b/src/const-info.c index 200100c2..0294e281 100644 --- a/src/const-info.c +++ b/src/const-info.c @@ -123,3 +123,212 @@ getdns_get_errorstr_by_id(uint16_t err) else return NULL; } + +static struct const_name_info consts_name_info[] = { + { "GETDNS_APPEND_NAME_ALWAYS", 550 }, + { "GETDNS_APPEND_NAME_NEVER", 553 }, + { "GETDNS_APPEND_NAME_ONLY_TO_MULTIPLE_LABEL_NAME_AFTER_FAILURE", 552 }, + { "GETDNS_APPEND_NAME_ONLY_TO_SINGLE_LABEL_AFTER_FAILURE", 551 }, + { "GETDNS_APPEND_NAME_TO_SINGLE_LABEL_FIRST", 554 }, + { "GETDNS_AUTHENTICATION_NONE", 1300 }, + { "GETDNS_AUTHENTICATION_REQUIRED", 1301 }, + { "GETDNS_BAD_DNS_ALL_NUMERIC_LABEL", 1101 }, + { "GETDNS_BAD_DNS_CNAME_IN_TARGET", 1100 }, + { "GETDNS_BAD_DNS_CNAME_RETURNED_FOR_OTHER_TYPE", 1102 }, + { "GETDNS_CALLBACK_CANCEL", 701 }, + { "GETDNS_CALLBACK_COMPLETE", 700 }, + { "GETDNS_CALLBACK_ERROR", 703 }, + { "GETDNS_CALLBACK_TIMEOUT", 702 }, + { "GETDNS_CONTEXT_CODE_APPEND_NAME", 607 }, + { "GETDNS_CONTEXT_CODE_DNSSEC_ALLOWED_SKEW", 614 }, + { "GETDNS_CONTEXT_CODE_DNSSEC_TRUST_ANCHORS", 609 }, + { "GETDNS_CONTEXT_CODE_DNS_ROOT_SERVERS", 604 }, + { "GETDNS_CONTEXT_CODE_DNS_TRANSPORT", 605 }, + { "GETDNS_CONTEXT_CODE_EDNS_CLIENT_SUBNET_PRIVATE", 619 }, + { "GETDNS_CONTEXT_CODE_EDNS_DO_BIT", 613 }, + { "GETDNS_CONTEXT_CODE_EDNS_EXTENDED_RCODE", 611 }, + { "GETDNS_CONTEXT_CODE_EDNS_MAXIMUM_UDP_PAYLOAD_SIZE", 610 }, + { "GETDNS_CONTEXT_CODE_EDNS_VERSION", 612 }, + { "GETDNS_CONTEXT_CODE_FOLLOW_REDIRECTS", 602 }, + { "GETDNS_CONTEXT_CODE_IDLE_TIMEOUT", 617 }, + { "GETDNS_CONTEXT_CODE_LIMIT_OUTSTANDING_QUERIES", 606 }, + { "GETDNS_CONTEXT_CODE_MEMORY_FUNCTIONS", 615 }, + { "GETDNS_CONTEXT_CODE_NAMESPACES", 600 }, + { "GETDNS_CONTEXT_CODE_PUBKEY_PINSET", 621 }, + { "GETDNS_CONTEXT_CODE_RESOLUTION_TYPE", 601 }, + { "GETDNS_CONTEXT_CODE_SUFFIX", 608 }, + { "GETDNS_CONTEXT_CODE_TIMEOUT", 616 }, + { "GETDNS_CONTEXT_CODE_TLS_AUTHENTICATION", 618 }, + { "GETDNS_CONTEXT_CODE_TLS_QUERY_PADDING_BLOCKSIZE", 620 }, + { "GETDNS_CONTEXT_CODE_UPSTREAM_RECURSIVE_SERVERS", 603 }, + { "GETDNS_DNSSEC_BOGUS", 401 }, + { "GETDNS_DNSSEC_INDETERMINATE", 402 }, + { "GETDNS_DNSSEC_INSECURE", 403 }, + { "GETDNS_DNSSEC_NOT_PERFORMED", 404 }, + { "GETDNS_DNSSEC_SECURE", 400 }, + { "GETDNS_EXTENSION_FALSE", 1001 }, + { "GETDNS_EXTENSION_TRUE", 1000 }, + { "GETDNS_NAMESPACE_DNS", 500 }, + { "GETDNS_NAMESPACE_LOCALNAMES", 501 }, + { "GETDNS_NAMESPACE_MDNS", 503 }, + { "GETDNS_NAMESPACE_NETBIOS", 502 }, + { "GETDNS_NAMESPACE_NIS", 504 }, + { "GETDNS_NAMETYPE_DNS", 800 }, + { "GETDNS_NAMETYPE_WINS", 801 }, + { "GETDNS_OPCODE_IQUERY", 1 }, + { "GETDNS_OPCODE_NOTIFY", 4 }, + { "GETDNS_OPCODE_QUERY", 0 }, + { "GETDNS_OPCODE_STATUS", 2 }, + { "GETDNS_OPCODE_UPDATE", 5 }, + { "GETDNS_RCODE_BADALG", 21 }, + { "GETDNS_RCODE_BADKEY", 17 }, + { "GETDNS_RCODE_BADMODE", 19 }, + { "GETDNS_RCODE_BADNAME", 20 }, + { "GETDNS_RCODE_BADSIG", 16 }, + { "GETDNS_RCODE_BADTIME", 18 }, + { "GETDNS_RCODE_BADTRUNC", 22 }, + { "GETDNS_RCODE_BADVERS", 16 }, + { "GETDNS_RCODE_FORMERR", 1 }, + { "GETDNS_RCODE_NOERROR", 0 }, + { "GETDNS_RCODE_NOTAUTH", 9 }, + { "GETDNS_RCODE_NOTIMP", 4 }, + { "GETDNS_RCODE_NOTZONE", 10 }, + { "GETDNS_RCODE_NXDOMAIN", 3 }, + { "GETDNS_RCODE_NXRRSET", 8 }, + { "GETDNS_RCODE_REFUSED", 5 }, + { "GETDNS_RCODE_SERVFAIL", 2 }, + { "GETDNS_RCODE_YXDOMAIN", 6 }, + { "GETDNS_RCODE_YXRRSET", 7 }, + { "GETDNS_REDIRECTS_DO_NOT_FOLLOW", 531 }, + { "GETDNS_REDIRECTS_FOLLOW", 530 }, + { "GETDNS_RESOLUTION_RECURSING", 521 }, + { "GETDNS_RESOLUTION_STUB", 520 }, + { "GETDNS_RESPSTATUS_ALL_BOGUS_ANSWERS", 904 }, + { "GETDNS_RESPSTATUS_ALL_TIMEOUT", 902 }, + { "GETDNS_RESPSTATUS_GOOD", 900 }, + { "GETDNS_RESPSTATUS_NO_NAME", 901 }, + { "GETDNS_RESPSTATUS_NO_SECURE_ANSWERS", 903 }, + { "GETDNS_RETURN_BAD_CONTEXT", 301 }, + { "GETDNS_RETURN_BAD_DOMAIN_NAME", 300 }, + { "GETDNS_RETURN_CONTEXT_UPDATE_FAIL", 302 }, + { "GETDNS_RETURN_DNSSEC_WITH_STUB_DISALLOWED", 309 }, + { "GETDNS_RETURN_EXTENSION_MISFORMAT", 308 }, + { "GETDNS_RETURN_GENERIC_ERROR", 1 }, + { "GETDNS_RETURN_GOOD", 0 }, + { "GETDNS_RETURN_INVALID_PARAMETER", 311 }, + { "GETDNS_RETURN_MEMORY_ERROR", 310 }, + { "GETDNS_RETURN_NEED_MORE_SPACE", 399 }, + { "GETDNS_RETURN_NOT_IMPLEMENTED", 312 }, + { "GETDNS_RETURN_NO_SUCH_DICT_NAME", 305 }, + { "GETDNS_RETURN_NO_SUCH_EXTENSION", 307 }, + { "GETDNS_RETURN_NO_SUCH_LIST_ITEM", 304 }, + { "GETDNS_RETURN_UNKNOWN_TRANSACTION", 303 }, + { "GETDNS_RETURN_WRONG_TYPE_REQUESTED", 306 }, + { "GETDNS_RRCLASS_ANY", 255 }, + { "GETDNS_RRCLASS_CH", 3 }, + { "GETDNS_RRCLASS_HS", 4 }, + { "GETDNS_RRCLASS_IN", 1 }, + { "GETDNS_RRCLASS_NONE", 254 }, + { "GETDNS_RRTYPE_A", 1 }, + { "GETDNS_RRTYPE_AAAA", 28 }, + { "GETDNS_RRTYPE_AFSDB", 18 }, + { "GETDNS_RRTYPE_ANY", 255 }, + { "GETDNS_RRTYPE_APL", 42 }, + { "GETDNS_RRTYPE_ATMA", 34 }, + { "GETDNS_RRTYPE_AXFR", 252 }, + { "GETDNS_RRTYPE_CAA", 257 }, + { "GETDNS_RRTYPE_CDNSKEY", 60 }, + { "GETDNS_RRTYPE_CDS", 59 }, + { "GETDNS_RRTYPE_CERT", 37 }, + { "GETDNS_RRTYPE_CNAME", 5 }, + { "GETDNS_RRTYPE_CSYNC", 62 }, + { "GETDNS_RRTYPE_DHCID", 49 }, + { "GETDNS_RRTYPE_DLV", 32769 }, + { "GETDNS_RRTYPE_DNAME", 39 }, + { "GETDNS_RRTYPE_DNSKEY", 48 }, + { "GETDNS_RRTYPE_DS", 43 }, + { "GETDNS_RRTYPE_EID", 31 }, + { "GETDNS_RRTYPE_GID", 102 }, + { "GETDNS_RRTYPE_GPOS", 27 }, + { "GETDNS_RRTYPE_HINFO", 13 }, + { "GETDNS_RRTYPE_HIP", 55 }, + { "GETDNS_RRTYPE_IPSECKEY", 45 }, + { "GETDNS_RRTYPE_ISDN", 20 }, + { "GETDNS_RRTYPE_IXFR", 251 }, + { "GETDNS_RRTYPE_KEY", 25 }, + { "GETDNS_RRTYPE_KX", 36 }, + { "GETDNS_RRTYPE_LOC", 29 }, + { "GETDNS_RRTYPE_LP", 107 }, + { "GETDNS_RRTYPE_MAILA", 254 }, + { "GETDNS_RRTYPE_MAILB", 253 }, + { "GETDNS_RRTYPE_MB", 7 }, + { "GETDNS_RRTYPE_MD", 3 }, + { "GETDNS_RRTYPE_MF", 4 }, + { "GETDNS_RRTYPE_MG", 8 }, + { "GETDNS_RRTYPE_MINFO", 14 }, + { "GETDNS_RRTYPE_MR", 9 }, + { "GETDNS_RRTYPE_MX", 15 }, + { "GETDNS_RRTYPE_NAPTR", 35 }, + { "GETDNS_RRTYPE_NID", 104 }, + { "GETDNS_RRTYPE_NIMLOC", 32 }, + { "GETDNS_RRTYPE_NINFO", 56 }, + { "GETDNS_RRTYPE_NS", 2 }, + { "GETDNS_RRTYPE_NSAP", 22 }, + { "GETDNS_RRTYPE_NSEC", 47 }, + { "GETDNS_RRTYPE_NULL", 10 }, + { "GETDNS_RRTYPE_NXT", 30 }, + { "GETDNS_RRTYPE_OPENPGPKEY", 61 }, + { "GETDNS_RRTYPE_OPT", 41 }, + { "GETDNS_RRTYPE_PTR", 12 }, + { "GETDNS_RRTYPE_PX", 26 }, + { "GETDNS_RRTYPE_RKEY", 57 }, + { "GETDNS_RRTYPE_RP", 17 }, + { "GETDNS_RRTYPE_RRSIG", 46 }, + { "GETDNS_RRTYPE_RT", 21 }, + { "GETDNS_RRTYPE_SIG", 24 }, + { "GETDNS_RRTYPE_SINK", 40 }, + { "GETDNS_RRTYPE_SOA", 6 }, + { "GETDNS_RRTYPE_SPF", 99 }, + { "GETDNS_RRTYPE_SRV", 33 }, + { "GETDNS_RRTYPE_SSHFP", 44 }, + { "GETDNS_RRTYPE_TA", 32768 }, + { "GETDNS_RRTYPE_TALINK", 58 }, + { "GETDNS_RRTYPE_TKEY", 249 }, + { "GETDNS_RRTYPE_TLSA", 52 }, + { "GETDNS_RRTYPE_TSIG", 250 }, + { "GETDNS_RRTYPE_TXT", 16 }, + { "GETDNS_RRTYPE_UID", 101 }, + { "GETDNS_RRTYPE_UINFO", 100 }, + { "GETDNS_RRTYPE_UNSPEC", 103 }, + { "GETDNS_RRTYPE_URI", 256 }, + { "GETDNS_RRTYPE_WKS", 11 }, + { "GETDNS_TRANSPORT_TCP", 1201 }, + { "GETDNS_TRANSPORT_TCP_ONLY", 542 }, + { "GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN", 543 }, + { "GETDNS_TRANSPORT_TLS", 1202 }, + { "GETDNS_TRANSPORT_TLS_FIRST_AND_FALL_BACK_TO_TCP_KEEP_CONNECTIONS_OPEN", 545 }, + { "GETDNS_TRANSPORT_TLS_ONLY_KEEP_CONNECTIONS_OPEN", 544 }, + { "GETDNS_TRANSPORT_UDP", 1200 }, + { "GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP", 540 }, + { "GETDNS_TRANSPORT_UDP_ONLY", 541 }, +}; + +static int const_name_info_cmp(const void *a, const void *b) +{ + return strcmp( ((struct const_name_info *) a)->name + , ((struct const_name_info *) b)->name ); +} + +int +_getdns_get_const_name_info(const char *name, uint32_t *code) +{ + struct const_name_info key = { name, 0 }; + struct const_name_info *i = bsearch(&key, consts_name_info, + sizeof(consts_name_info) / sizeof(struct const_name_info), + sizeof(struct const_name_info), const_name_info_cmp); + if (!i) + return 0; + if (code) + *code = i->code; + return 1; +} diff --git a/src/const-info.h b/src/const-info.h index 39d7c98e..379e2512 100644 --- a/src/const-info.h +++ b/src/const-info.h @@ -47,6 +47,13 @@ struct const_info { struct const_info *_getdns_get_const_info(int value); +struct const_name_info { + const char *name; + uint32_t code; +}; + +int _getdns_get_const_name_info(const char *name, uint32_t *code); + #endif /* const-info.h */ diff --git a/src/context.c b/src/context.c index 20a7c9f1..8e2ee9e5 100644 --- a/src/context.c +++ b/src/context.c @@ -89,6 +89,9 @@ typedef unsigned short in_port_t; #define GETDNS_STR_PORT_ZERO "0" #define GETDNS_STR_PORT_DNS "53" #define GETDNS_STR_PORT_DNS_OVER_TLS "853" +/* How long to wait in seconds before re-trying a connection based backed-off + upstream. Using 1 hour for all transports - based on RFC7858 value for for TLS.*/ +#define BACKOFF_RETRY 3600 #ifdef HAVE_PTHREADS static pthread_mutex_t ssl_init_lock = PTHREAD_MUTEX_INITIALIZER; @@ -234,6 +237,25 @@ add_WIN_cacerts_to_openssl_store(SSL_CTX* tls_ctx) } #endif +#if !defined(STUB_NATIVE_DNSSEC) || (defined(DAEMON_DEBUG) && DAEMON_DEBUG) +static uint8_t* +upstream_addr(getdns_upstream *upstream) +{ + return upstream->addr.ss_family == AF_INET + ? (void *)&((struct sockaddr_in*)&upstream->addr)->sin_addr + : (void *)&((struct sockaddr_in6*)&upstream->addr)->sin6_addr; +} +#endif + + +static in_port_t +upstream_port(getdns_upstream *upstream) +{ + return ntohs(upstream->addr.ss_family == AF_INET + ? ((struct sockaddr_in *)&upstream->addr)->sin_port + : ((struct sockaddr_in6*)&upstream->addr)->sin6_port); +} + static void destroy_local_host(_getdns_rbnode_t * node, void *arg) { getdns_context *context = (getdns_context *)arg; @@ -274,7 +296,6 @@ create_default_dns_transports(struct getdns_context *context) context->dns_transports[0] = GETDNS_TRANSPORT_UDP; context->dns_transports[1] = GETDNS_TRANSPORT_TCP; context->dns_transport_count = 2; - context->dns_transport_current = 0; return GETDNS_RETURN_GOOD; } @@ -626,7 +647,7 @@ upstreams_create(getdns_context *context, size_t size) r->mf = context->mf; r->referenced = 1; r->count = 0; - r->current = 0; + r->current_udp = 0; return r; } @@ -688,48 +709,89 @@ _getdns_upstreams_dereference(getdns_upstreams *upstreams) GETDNS_FREE(upstreams->mf, upstreams); } -#if defined(DAEMON_DEBUG) && DAEMON_DEBUG -static char* -getdns_auth_str_array[] = { - GETDNS_STR_AUTH_NONE, - GETDNS_STR_AUTH_FAILED, - GETDNS_STR_AUTH_OK -}; -#endif - void _getdns_upstream_shutdown(getdns_upstream *upstream) { - /*There is a race condition with a new request being scheduled - while this happens so take ownership of the fd asap*/ - int fd = upstream->fd; - upstream->fd = -1; - /* If the connection had a problem, but had worked this time, - * then allow re-use in the future*/ - if (upstream->tcp.write_error == 1 && - upstream->responses_received > 0) - upstream->tcp.write_error = 0; - upstream->writes_done = 0; - upstream->responses_received = 0; - upstream->keepalive_timeout = 0; - if (upstream->tls_hs_state != GETDNS_HS_FAILED) { - upstream->tls_hs_state = GETDNS_HS_NONE; - upstream->tls_auth_failed = 0; + /*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; +#if defined(DAEMON_DEBUG) && DAEMON_DEBUG + DEBUG_DAEMON("%s %s : Conn closed : Transport=%s - Resp=%d,Timeouts=%d,Auth=%s,Keepalive(ms)=%d\n", + STUB_DEBUG_DAEMON, 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); + DEBUG_DAEMON("%s %s : Upstream stats: Transport=%s - Resp=%d,Timeouts=%d,Best_auth=%s\n", + STUB_DEBUG_DAEMON, 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)); + DEBUG_DAEMON("%s %s : Upstream stats: Transport=%s - Conns=%d,Conn_fails=%d,Conn_shutdowns=%d,Backoffs=%d\n", + STUB_DEBUG_DAEMON, 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); +#endif + + /* 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.*/ + + /* [TLS1]TODO: This arbitrary logic at the moment - review and improve!*/ + if (upstream->conn_setup_failed >= GETDNS_CONN_ATTEMPTS || + (upstream->conn_shutdowns >= GETDNS_CONN_ATTEMPTS*GETDNS_TRANSPORT_FAIL_MULT + && upstream->total_responses == 0) || + (upstream->conn_completed >= GETDNS_CONN_ATTEMPTS && + upstream->total_responses == 0 && + upstream->total_timeouts > GETDNS_TRANSPORT_FAIL_MULT)) { + upstream->conn_state = GETDNS_CONN_BACKOFF; + upstream->conn_retry_time = time(NULL) + BACKOFF_RETRY; + upstream->total_responses = 0; + upstream->total_timeouts = 0; + upstream->conn_completed = 0; + upstream->conn_setup_failed = 0; + upstream->conn_shutdowns = 0; + upstream->conn_backoffs++; +#if defined(DAEMON_DEBUG) && DAEMON_DEBUG + DEBUG_DAEMON("%s %s : !Backing off this upstream - Will retry as new upstream at %s", + STUB_DEBUG_DAEMON, upstream->addr_str, + asctime(gmtime(&upstream->conn_retry_time))); +#endif } + // Reset per connection counters + upstream->queries_sent = 0; + upstream->responses_received = 0; + upstream->responses_timeouts = 0; + upstream->keepalive_timeout = 0; + upstream->keepalive_shutdown = 0; + /* Now TLS stuff*/ + upstream->tls_auth_state = GETDNS_AUTH_NONE; if (upstream->tls_obj != NULL) { SSL_shutdown(upstream->tls_obj); SSL_free(upstream->tls_obj); upstream->tls_obj = NULL; } - if (fd != -1) - { + if (upstream->fd != -1) { #ifdef USE_WINSOCK - closesocket(fd); + closesocket(upstream->fd); #else - close(fd); + close(upstream->fd); #endif + upstream->fd = -1; } + /* Set connection ready for use again*/ + if (upstream->conn_state != GETDNS_CONN_BACKOFF) + upstream->conn_state = GETDNS_CONN_CLOSED; } static int @@ -835,13 +897,30 @@ upstream_init(getdns_upstream *upstream, upstream->addr_len = ai->ai_addrlen; (void) memcpy(&upstream->addr, ai->ai_addr, ai->ai_addrlen); +#if defined(DAEMON_DEBUG) && DAEMON_DEBUG + inet_ntop(upstream->addr.ss_family, upstream_addr(upstream), + upstream->addr_str, INET6_ADDRSTRLEN); +#endif - /* How is this upstream doing? */ - upstream->writes_done = 0; + /* How is this upstream doing on connections? */ + upstream->conn_completed = 0; + upstream->conn_shutdowns = 0; + upstream->conn_setup_failed = 0; + upstream->conn_retry_time = 0; + upstream->conn_backoffs = 0; + upstream->total_responses = 0; + upstream->total_timeouts = 0; + upstream->conn_state = GETDNS_CONN_CLOSED; + upstream->queries_sent = 0; upstream->responses_received = 0; + upstream->responses_timeouts = 0; + upstream->keepalive_shutdown = 0; upstream->keepalive_timeout = 0; + /* How is this upstream doing on UDP? */ upstream->to_retry = 2; upstream->back_off = 1; + upstream->udp_responses = 0; + upstream->udp_timeouts = 0; /* For sharing a socket to this upstream with TCP */ upstream->fd = -1; @@ -849,10 +928,11 @@ upstream_init(getdns_upstream *upstream, upstream->tls_session = NULL; upstream->transport = GETDNS_TRANSPORT_TCP; upstream->tls_hs_state = GETDNS_HS_NONE; - upstream->tls_auth_failed = 0; upstream->tls_auth_name[0] = '\0'; + upstream->tls_auth_state = GETDNS_AUTH_NONE; + upstream->last_tls_auth_state = GETDNS_AUTH_NONE; + upstream->best_tls_auth_state = GETDNS_AUTH_NONE; upstream->tls_pubkey_pinset = NULL; - upstream->tcp.write_error = 0; upstream->loop = NULL; (void) getdns_eventloop_event_init( &upstream->event, upstream, NULL, NULL, NULL); @@ -1253,6 +1333,8 @@ getdns_context_create_with_extended_memory_functions( _getdns_rbtree_init(&result->outbound_requests, transaction_id_cmp); _getdns_rbtree_init(&result->local_hosts, local_host_cmp); + result->server = NULL; + #ifdef HAVE_LIBUNBOUND result->resolution_type = GETDNS_RESOLUTION_RECURSING; #else @@ -1309,6 +1391,27 @@ getdns_context_create_with_extended_memory_functions( _getdns_default_eventloop_init(&result->default_eventloop); _getdns_default_eventloop_init(&result->sync_eventloop); + /* request extension defaults + */ + result->header = NULL; + result->add_opt_parameters = NULL; + result->add_warning_for_bad_dns = 0; + result->dnssec_return_all_statuses = 0; + result->dnssec_return_full_validation_chain = 0; + result->dnssec_return_only_secure = 0; + result->dnssec_return_status = 0; + result->dnssec_return_validation_chain = 0; +#ifdef DNSSEC_ROADBLOCK_AVOIDANCE + result->dnssec_roadblock_avoidance = 0; +#endif + result->edns_cookies = 0; + result->return_api_information = 0; + result->return_both_v4_and_v6 = 0; + result->return_call_reporting = 0; + result->specify_class = GETDNS_RRCLASS_IN; + + /* state data used to detect changes to the system config files + */ result->fchg_resolvconf = NULL; result->fchg_hosts = NULL; @@ -1328,7 +1431,6 @@ getdns_context_create_with_extended_memory_functions( result->tls_auth = GETDNS_AUTHENTICATION_NONE; result->tls_auth_min = GETDNS_AUTHENTICATION_NONE; result->limit_outstanding_queries = 0; - result->return_dnssec_status = GETDNS_EXTENSION_FALSE; /* unbound context is initialized here */ /* Unbound needs SSL to be init'ed this early when TLS is used. However we @@ -1421,6 +1523,9 @@ getdns_context_destroy(struct getdns_context *context) /* cancel all outstanding requests */ cancel_outstanding_requests(context, 1); + /* Destroy listening addresses */ + (void) getdns_context_set_listen_addresses(context, NULL, NULL, NULL); + /* This needs to be done before cleaning the extension, because there * might be an idle_timeout schedules, which will not get unscheduled * with cancel_outstanding_requests. @@ -1453,8 +1558,7 @@ getdns_context_destroy(struct getdns_context *context) if (context->tls_ctx) SSL_CTX_free(context->tls_ctx); - if (context->dns_root_servers) - getdns_list_destroy(context->dns_root_servers); + getdns_list_destroy(context->dns_root_servers); #if defined(HAVE_LIBUNBOUND) && !defined(HAVE_UB_CTX_SET_STUB) if (context->root_servers_fn[0]) @@ -1471,6 +1575,10 @@ 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); + #ifdef USE_WINSOCK WSACleanup(); #endif @@ -2833,22 +2941,8 @@ getdns_cancel_callback(getdns_context *context, return r; } /* getdns_cancel_callback */ -#ifndef STUB_NATIVE_DNSSEC -static uint8_t* -upstream_addr(getdns_upstream *upstream) -{ - return upstream->addr.ss_family == AF_INET - ? (void *)&((struct sockaddr_in*)&upstream->addr)->sin_addr - : (void *)&((struct sockaddr_in6*)&upstream->addr)->sin6_addr; -} -static in_port_t -upstream_port(getdns_upstream *upstream) -{ - return ntohs(upstream->addr.ss_family == AF_INET - ? ((struct sockaddr_in *)&upstream->addr)->sin_port - : ((struct sockaddr_in6*)&upstream->addr)->sin6_port); -} +#ifndef STUB_NATIVE_DNSSEC static uint32_t * upstream_scope_id(getdns_upstream *upstream) @@ -3344,14 +3438,6 @@ getdns_context_get_eventloop(getdns_context *context, getdns_eventloop **loop) return GETDNS_RETURN_GOOD; } -static in_port_t -upstream_port(getdns_upstream *upstream) -{ - return ntohs(upstream->addr.ss_family == AF_INET - ? ((struct sockaddr_in *)&upstream->addr)->sin_port - : ((struct sockaddr_in6*)&upstream->addr)->sin6_port); -} - static getdns_dict* _get_context_settings(getdns_context* context) { @@ -3480,7 +3566,7 @@ getdns_context_set_return_dnssec_status(getdns_context* context, int enabled) { enabled != GETDNS_EXTENSION_FALSE) { return GETDNS_RETURN_INVALID_PARAMETER; } - context->return_dnssec_status = enabled; + context->dnssec_return_status = enabled == GETDNS_EXTENSION_TRUE; return GETDNS_RETURN_GOOD; } @@ -3954,4 +4040,209 @@ getdns_context_get_tls_query_padding_blocksize(getdns_context *context, uint16_t return GETDNS_RETURN_GOOD; } +static int _streq(const getdns_bindata *name, const char *str) +{ + if (strlen(str) != name->size) + return 0; + else return strncmp((const char *)name->data, str, name->size) == 0; +} + +static getdns_return_t _get_list_or_read_file(const getdns_dict *config_dict, + const char *setting, getdns_list **r_list, int *destroy_list) +{ + getdns_bindata *fn_bd; + char fn[FILENAME_MAX]; + FILE *fh; + getdns_return_t r; + + assert(r_list); + assert(destroy_list); + + *destroy_list = 0; + if (!(r = getdns_dict_get_list(config_dict, setting, r_list))) + return GETDNS_RETURN_GOOD; + + else if ((r = getdns_dict_get_bindata(config_dict, setting, &fn_bd))) + return r; + + else if (fn_bd->size >= FILENAME_MAX) + return GETDNS_RETURN_INVALID_PARAMETER; + + (void)memcpy(fn, fn_bd->data, fn_bd->size); + fn[fn_bd->size] = 0; + + if (!(fh = fopen(fn, "r"))) + return GETDNS_RETURN_GENERIC_ERROR; + + if (!(r = getdns_fp2rr_list(fh, r_list, NULL, 3600))) + *destroy_list = 1; + + fclose(fh); + return r; +} + +#define CONTEXT_SETTING_INT(X) \ + } else if (_streq(setting, #X)) { \ + if (!(r = getdns_dict_get_int(config_dict, #X , &n))) \ + r = getdns_context_set_ ## X (context, n); + +#define CONTEXT_SETTING_LIST(X) \ + } else if (_streq(setting, #X)) { \ + if (!(r = getdns_dict_get_list(config_dict, #X , &list))) \ + r = getdns_context_set_ ## X (context, list); + +#define CONTEXT_SETTING_LIST_OR_ZONEFILE(X) \ + } else if (_streq(setting, #X)) { \ + if (!(r = _get_list_or_read_file( \ + config_dict, #X , &list, &destroy_list))) \ + r = getdns_context_set_ ## X(context, list); \ + if (destroy_list) getdns_list_destroy(list); + +#define CONTEXT_SETTING_ARRAY(X, T) \ + } else if (_streq(setting, #X )) { \ + if (!(r = getdns_dict_get_list(config_dict, #X , &list)) && \ + !(r = getdns_list_get_length(list, &count))) { \ + for (i=0; iX = 1; \ + else if (n == GETDNS_EXTENSION_FALSE) context->X = 0; \ + else r = GETDNS_RETURN_INVALID_PARAMETER; \ + } + +static getdns_return_t +_getdns_context_config_setting(getdns_context *context, + const getdns_dict *config_dict, const getdns_bindata *setting) +{ + getdns_return_t r = GETDNS_RETURN_GOOD; + getdns_dict *dict; + getdns_list *list; + getdns_namespace_t namespaces[100]; + getdns_transport_list_t dns_transport_list[100]; + size_t count, i; + uint32_t n; + int destroy_list = 0; + + if (_streq(setting, "all_context")) { + if (!(r = getdns_dict_get_dict(config_dict, "all_context", &dict))) + r = getdns_context_config(context, dict); + + CONTEXT_SETTING_INT(resolution_type) + CONTEXT_SETTING_ARRAY(namespaces, namespace) + CONTEXT_SETTING_INT(dns_transport) + CONTEXT_SETTING_ARRAY(dns_transport_list, transport_list) + CONTEXT_SETTING_INT(idle_timeout) + CONTEXT_SETTING_INT(limit_outstanding_queries) + CONTEXT_SETTING_INT(timeout) + CONTEXT_SETTING_INT(follow_redirects) + CONTEXT_SETTING_LIST_OR_ZONEFILE(dns_root_servers) + CONTEXT_SETTING_INT(append_name) + CONTEXT_SETTING_LIST(suffix) + CONTEXT_SETTING_LIST_OR_ZONEFILE(dnssec_trust_anchors) + CONTEXT_SETTING_INT(dnssec_allowed_skew) + CONTEXT_SETTING_LIST(upstream_recursive_servers) + CONTEXT_SETTING_INT(edns_maximum_udp_payload_size) + CONTEXT_SETTING_INT(edns_extended_rcode) + CONTEXT_SETTING_INT(edns_version) + CONTEXT_SETTING_INT(edns_do_bit) + + /***************************************/ + /**** ****/ + /**** Unofficial context settings ****/ + /**** ****/ + /***************************************/ + + CONTEXT_SETTING_INT(edns_client_subnet_private) + CONTEXT_SETTING_INT(tls_authentication) + CONTEXT_SETTING_INT(tls_query_padding_blocksize) + + /**************************************/ + /**** ****/ + /**** Default extensions setting ****/ + /**** ****/ + /**************************************/ + EXTENSION_SETTING_BOOL(add_warning_for_bad_dns) + EXTENSION_SETTING_BOOL(dnssec_return_all_statuses) + EXTENSION_SETTING_BOOL(dnssec_return_full_validation_chain) + EXTENSION_SETTING_BOOL(dnssec_return_only_secure) + EXTENSION_SETTING_BOOL(dnssec_return_status) + EXTENSION_SETTING_BOOL(dnssec_return_validation_chain) +#if defined(DNSSEC_ROADBLOCK_AVOIDANCE) && defined(HAVE_LIBUNBOUND) + EXTENSION_SETTING_BOOL(dnssec_roadblock_avoidance) +#endif +#ifdef EDNS_COOKIES + EXTENSION_SETTING_BOOL(edns_cookies) +#endif + EXTENSION_SETTING_BOOL(return_api_information) + EXTENSION_SETTING_BOOL(return_both_v4_and_v6) + EXTENSION_SETTING_BOOL(return_call_reporting) + + } else if (_streq(setting, "add_opt_parameters")) { + if (!(r = getdns_dict_get_dict(config_dict, "add_opt_parameters" , &dict))) { + if (context->add_opt_parameters) + getdns_dict_destroy(context->add_opt_parameters); + context->add_opt_parameters = NULL; + r = _getdns_dict_copy(dict, &context->add_opt_parameters); + } + + } else if (_streq(setting, "header")) { + if (!(r = getdns_dict_get_dict(config_dict, "header" , &dict))) { + if (context->header) + getdns_dict_destroy(context->header); + if (!(context->header = + getdns_dict_create_with_context(context))) + r = GETDNS_RETURN_MEMORY_ERROR; + else r = getdns_dict_set_dict( + context->header, "header", dict); + } + + } else if (_streq(setting, "specify_class")) { + if (!(r = getdns_dict_get_int( + config_dict, "specify_class" , &n))) + context->specify_class = (uint16_t)n; + + + /************************************/ + /**** ****/ + /**** Ignored context settings ****/ + /**** ****/ + /************************************/ + } else if (!_streq(setting, "implementation_string") && + !_streq(setting, "version_string")) { + r = GETDNS_RETURN_NOT_IMPLEMENTED; + } + return r; +} + +getdns_return_t +getdns_context_config(getdns_context *context, const getdns_dict *config_dict) +{ + getdns_list *settings; + getdns_return_t r; + getdns_bindata *setting; + size_t i; + + if ((r = getdns_dict_get_names(config_dict, &settings))) + return r; + + for (i = 0; !(r = getdns_list_get_bindata(settings,i,&setting)); i++) { + if ((r = _getdns_context_config_setting( + context, config_dict, setting))) + break; + } + if (r == GETDNS_RETURN_NO_SUCH_LIST_ITEM) + r = GETDNS_RETURN_GOOD; + + getdns_list_destroy(settings); + return r; +} + /* context.c */ diff --git a/src/context.h b/src/context.h index 86bfb817..b89a5876 100644 --- a/src/context.h +++ b/src/context.h @@ -44,6 +44,7 @@ #include "extension/default_eventloop.h" #include "util/rbtree.h" #include "ub_loop.h" +#include "server.h" struct getdns_dns_req; struct ub_ctx; @@ -80,6 +81,14 @@ typedef enum getdns_tls_hs_state { GETDNS_HS_FAILED } getdns_tls_hs_state_t; +typedef enum getdns_conn_state { + GETDNS_CONN_CLOSED, + GETDNS_CONN_SETUP, + GETDNS_CONN_OPEN, + GETDNS_CONN_TEARDOWN, + GETDNS_CONN_BACKOFF +} getdns_conn_state_t; + typedef enum getdns_tsig_algo { GETDNS_NO_TSIG = 0, /* Do not use tsig */ GETDNS_HMAC_MD5 = 1, /* 128 bits */ @@ -115,31 +124,55 @@ typedef struct getdns_upstream { socklen_t addr_len; struct sockaddr_storage addr; +#if defined(DAEMON_DEBUG) && DAEMON_DEBUG + char addr_str[INET6_ADDRSTRLEN]; +#endif - /* How is this upstream doing? */ - size_t writes_done; - size_t responses_received; - uint64_t keepalive_timeout; + /* How is this upstream doing over UDP? */ int to_retry; int back_off; + size_t udp_responses; + size_t udp_timeouts; - /* For sharing a TCP socket to this upstream */ + /* For stateful upstreams, need to share the connection and track the + activity on the connection */ int fd; getdns_transport_list_t transport; - SSL* tls_obj; - SSL_SESSION* tls_session; - getdns_tls_hs_state_t tls_hs_state; getdns_eventloop_event event; getdns_eventloop *loop; getdns_tcp_state tcp; - char tls_auth_name[256]; - size_t tls_auth_failed; - sha256_pin_t *tls_pubkey_pinset; + /* These are running totals or historical info */ + size_t conn_completed; + size_t conn_shutdowns; + size_t conn_setup_failed; + time_t conn_retry_time; + size_t conn_backoffs; + size_t total_responses; + size_t total_timeouts; + getdns_auth_state_t best_tls_auth_state; + getdns_auth_state_t last_tls_auth_state; + /* These are per connection. */ + getdns_conn_state_t conn_state; + size_t queries_sent; + size_t responses_received; + size_t responses_timeouts; + size_t keepalive_shutdown; + uint64_t keepalive_timeout; - /* Pipelining of TCP network requests */ + /* Management of outstanding requests on stateful transports */ getdns_network_req *write_queue; getdns_network_req *write_queue_last; - _getdns_rbtree_t netreq_by_query_id; + _getdns_rbtree_t netreq_by_query_id; + + /* TLS specific connection handling*/ + SSL* tls_obj; + SSL_SESSION* tls_session; + getdns_tls_hs_state_t tls_hs_state; + getdns_auth_state_t tls_auth_state; + unsigned tls_fallback_ok : 1; + /* Auth credentials*/ + char tls_auth_name[256]; + sha256_pin_t *tls_pubkey_pinset; /* When requests have been scheduled asynchronously on an upstream * that is kept open, and a synchronous call is then done with the @@ -157,6 +190,7 @@ typedef struct getdns_upstream { */ getdns_dns_req *finished_dnsreqs; getdns_eventloop_event finished_event; + unsigned is_sync_loop : 1; /* EDNS cookies */ uint32_t secret; @@ -168,8 +202,6 @@ typedef struct getdns_upstream { unsigned has_prev_client_cookie : 1; unsigned has_server_cookie : 1; unsigned server_cookie_len : 5; - unsigned tls_fallback_ok : 1; - unsigned is_sync_loop : 1; /* TSIG */ uint8_t tsig_dname[256]; @@ -184,7 +216,7 @@ typedef struct getdns_upstreams { struct mem_funcs mf; size_t referenced; size_t count; - size_t current; + size_t current_udp; getdns_upstream upstreams[]; } getdns_upstreams; @@ -219,7 +251,6 @@ struct getdns_context { getdns_transport_list_t *dns_transports; size_t dns_transport_count; - size_t dns_transport_current; uint8_t edns_extended_rcode; uint8_t edns_version; @@ -250,8 +281,6 @@ struct getdns_context { /* A tree to hold local host information*/ _getdns_rbtree_t local_hosts; - int return_dnssec_status; - /* which resolution type the contexts are configured for * 0 means nothing set */ @@ -262,6 +291,8 @@ struct getdns_context { */ _getdns_rbtree_t outbound_requests; + struct listen_set *server; + /* Event loop extension. */ getdns_eventloop *extension; @@ -275,6 +306,24 @@ struct getdns_context { _getdns_default_eventloop default_eventloop; _getdns_default_eventloop sync_eventloop; + /* request extension defaults */ + getdns_dict *header; + getdns_dict *add_opt_parameters; + unsigned add_warning_for_bad_dns : 1; + unsigned dnssec_return_all_statuses : 1; + unsigned dnssec_return_full_validation_chain : 1; + unsigned dnssec_return_only_secure : 1; + unsigned dnssec_return_status : 1; + unsigned dnssec_return_validation_chain : 1; +#ifdef DNSSEC_ROADBLOCK_AVOIDANCE + unsigned dnssec_roadblock_avoidance : 1; +#endif + unsigned edns_cookies : 1; + unsigned return_api_information : 1; /* Not used */ + unsigned return_both_v4_and_v6 : 1; + unsigned return_call_reporting : 1; + uint16_t specify_class; + /* * state data used to detect changes to the system config files */ diff --git a/src/convert.c b/src/convert.c index 9b3a28af..5e2c2684 100644 --- a/src/convert.c +++ b/src/convert.c @@ -48,8 +48,12 @@ #include "util-internal.h" #include "gldns/wire2str.h" #include "gldns/str2wire.h" +#include "gldns/parseutil.h" +#include "const-info.h" +#include "types-internal.h" /* For getdns_item */ #include "dict.h" #include "list.h" +#include "jsmn/jsmn.h" #include "convert.h" #include "debug.h" @@ -834,7 +838,6 @@ _getdns_reply_dict2wire( } } remove_dnssec = !getdns_dict_get_int(reply, "/header/do", &n) && n == 0; - DEBUG_SERVER("remove_dnssec: %d\n", remove_dnssec); if (!getdns_dict_get_list(reply, "answer", §ion)) { for ( n = 0, i = 0 @@ -1069,5 +1072,673 @@ getdns_msg_dict2str_scan( return r; } +static getdns_dict * +_getdns_ipaddr_dict_mf(struct mem_funcs *mf, const char *ipstr) +{ + getdns_dict *r = _getdns_dict_create_with_mf(mf); + char *s = strchr(ipstr, '%'), *scope_id_str = ""; + char *p = strchr(ipstr, '@'), *portstr = ""; + char *t = strchr(ipstr, '#'), *tls_portstr = ""; + char *n = strchr(ipstr, '~'), *tls_namestr = ""; + /* ^[alg:]name:key */ + char *T = strchr(ipstr, '^'), *tsig_name_str = "" + , *tsig_secret_str = "" + , *tsig_algorithm_str = ""; + char *br, *c; + int tsig_secret_size; + uint8_t tsig_secret_buf[256]; /* 4 times SHA512 */ + getdns_bindata tsig_secret; + uint8_t buf[sizeof(struct in6_addr)]; + getdns_bindata addr; + + addr.data = buf; + + if (!r) return NULL; + + if (*ipstr == '[') { + char *br = strchr(ipstr, ']'); + if (br) { + ipstr += 1; + *br = 0; + if ((c = strchr(br + 1, ':'))) { + p = c; + } + } + } else if ((br = strchr(ipstr, '.')) && (c = strchr(br + 1, ':')) + && (T == NULL || c < T)) + p = c; + + else if ((*ipstr == '*') && (c = strchr(ipstr+1, ':'))) + p = c; + + if (s) { + *s = 0; + scope_id_str = s + 1; + } + if (p) { + *p = 0; + portstr = p + 1; + } + if (t) { + *t = 0; + tls_portstr = t + 1; + } + if (n) { + *n = 0; + tls_namestr = n + 1; + } + if (T) { + *T = 0; + tsig_name_str = T + 1; + if ((T = strchr(tsig_name_str, ':'))) { + *T = 0; + tsig_secret_str = T + 1; + if ((T = strchr(tsig_secret_str, ':'))) { + *T = 0; + tsig_algorithm_str = tsig_name_str; + tsig_name_str = tsig_secret_str; + tsig_secret_str = T + 1; + } + } else { + tsig_name_str = ""; + } + } + if (*ipstr == '*') { + getdns_dict_util_set_string(r, "address_type", "IPv6"); + addr.size = 16; + (void) memset(buf, 0, 16); + } else if (strchr(ipstr, ':')) { + getdns_dict_util_set_string(r, "address_type", "IPv6"); + addr.size = 16; + if (inet_pton(AF_INET6, ipstr, buf) <= 0) { + getdns_dict_destroy(r); + return NULL; + } + } else { + getdns_dict_util_set_string(r, "address_type", "IPv4"); + addr.size = 4; + if (inet_pton(AF_INET, ipstr, buf) <= 0) { + getdns_dict_destroy(r); + return NULL; + } + } + getdns_dict_set_bindata(r, "address_data", &addr); + if (*portstr) + getdns_dict_set_int(r, "port", (int32_t)atoi(portstr)); + if (*tls_portstr) + getdns_dict_set_int(r, "tls_port", (int32_t)atoi(tls_portstr)); + if (*tls_namestr) { + getdns_dict_util_set_string(r, "tls_auth_name", tls_namestr); + } + if (*scope_id_str) + getdns_dict_util_set_string(r, "scope_id", scope_id_str); + if (*tsig_name_str) + getdns_dict_util_set_string(r, "tsig_name", tsig_name_str); + if (*tsig_algorithm_str) + getdns_dict_util_set_string(r, "tsig_algorithm", tsig_algorithm_str); + if (*tsig_secret_str) { + tsig_secret_size = gldns_b64_pton( + tsig_secret_str, tsig_secret_buf, sizeof(tsig_secret_buf)); + if (tsig_secret_size > 0) { + tsig_secret.size = tsig_secret_size; + tsig_secret.data = tsig_secret_buf; + getdns_dict_set_bindata(r, "tsig_secret", &tsig_secret); + } + } + return r; +} + +static int _jsmn_get_ipdict(struct mem_funcs *mf, const char *js, jsmntok_t *t, + getdns_dict **value) +{ + char value_str[3072]; + int size = t->end - t->start; + + if (size <= 0 || size >= (int)sizeof(value_str)) + return 0; + + (void) memcpy(value_str, js + t->start, size); + value_str[size] = '\0'; + + *value = _getdns_ipaddr_dict_mf(mf, value_str); + return *value != NULL; +} + +static int _jsmn_get_data(struct mem_funcs *mf, const char *js, jsmntok_t *t, + getdns_bindata **value) +{ + int i; + size_t j; + uint8_t h, l; + + if ((t->end - t->start) < 4 || (t->end - t->start) % 2 == 1 || + js[t->start] != '0' || js[t->start + 1] != 'x') + return 0; + + for (i = t->start + 2; i < t->end; i++) + if (!((js[i] >= '0' && js[i] <= '9') + ||(js[i] >= 'a' && js[i] <= 'f') + ||(js[i] >= 'A' && js[i] <= 'F'))) + return 0; + + if (!(*value = GETDNS_MALLOC(*mf, getdns_bindata))) + return 0; + + else if (!((*value)->data = GETDNS_XMALLOC( + *mf, uint8_t, (t->end - t->start) / 2 - 1))) { + GETDNS_FREE(*mf, *value); + return 0; + } + for (i = t->start + 2, j = 0; i < t->end; i++, j++) { + h = js[i] >= '0' && js[i] <= '9' ? js[i] - '0' + : js[i] >= 'A' && js[i] <= 'F' ? js[i] + 10 - 'A' + : js[i] + 10 - 'a'; + h <<= 4; + i++; + l = js[i] >= '0' && js[i] <= '9' ? js[i] - '0' + : js[i] >= 'A' && js[i] <= 'F' ? js[i] + 10 - 'A' + : js[i] + 10 - 'a'; + (*value)->data[j] = h | l; + } + (*value)->size = j; + return 1; +} + +static int _jsmn_get_dname(struct mem_funcs *mf, const char *js, jsmntok_t *t, + getdns_bindata **value) +{ + char value_str[1025]; + int size = t->end - t->start; + (void)mf; /* TODO: Fix to use mf */ + + if (size <= 0 || size >= (int)sizeof(value_str) || js[t->end - 1] != '.') + return 0; + + (void) memcpy(value_str, js + t->start, size); + value_str[size] = '\0'; + + return !getdns_convert_fqdn_to_dns_name(value_str, value); +} + +static int _jsmn_get_ipv4(struct mem_funcs *mf, const char *js, jsmntok_t *t, + getdns_bindata **value) +{ + char value_str[16]; + int size = t->end - t->start; + uint8_t buf[4]; + + if (size <= 0 || size >= (int)sizeof(value_str)) + return 0; + + (void) memcpy(value_str, js + t->start, size); + value_str[size] = '\0'; + + if (inet_pton(AF_INET, value_str, buf) <= 0) + ; /* pass */ + + else if (!(*value = GETDNS_MALLOC(*mf, getdns_bindata))) + ; /* pass */ + + else if (!((*value)->data = GETDNS_XMALLOC(*mf, uint8_t, 4))) + GETDNS_FREE(*mf, *value); + + else { + (*value)->size = 4; + (void) memcpy((*value)->data, buf, 4); + return 1; + } + return 0; +} + +static int _jsmn_get_ipv6(struct mem_funcs *mf, const char *js, jsmntok_t *t, + getdns_bindata **value) +{ + char value_str[40]; + int size = t->end - t->start; + uint8_t buf[16]; + + if (size <= 0 || size >= (int)sizeof(value_str)) + return 0; + + (void) memcpy(value_str, js + t->start, size); + value_str[size] = '\0'; + + if (inet_pton(AF_INET6, value_str, buf) <= 0) + ; /* pass */ + + else if (!(*value = GETDNS_MALLOC(*mf, getdns_bindata))) + ; /* pass */ + + else if (!((*value)->data = GETDNS_XMALLOC(*mf, uint8_t, 16))) + GETDNS_FREE(*mf, *value); + + else { + (*value)->size = 16; + (void) memcpy((*value)->data, buf, 16); + return 1; + } + return 0; +} + +static int _jsmn_get_int(const char *js, jsmntok_t *t, uint32_t *value) +{ + char value_str[11]; + int size = t->end - t->start; + char *endptr; + + if (size <= 0 || size >= (int)sizeof(value_str)) + return 0; + + (void) memcpy(value_str, js + t->start, size); + value_str[size] = '\0'; + + *value = (uint32_t)strtoul(value_str, &endptr, 10); + return *value_str != '\0' && *endptr == '\0'; +} + +static int _jsmn_get_const(const char *js, jsmntok_t *t, uint32_t *value) +{ + char value_str[80]; + int size = t->end - t->start; + + if (size <= 0 || size >= (int)sizeof(value_str)) + return 0; + + (void) memcpy(value_str, js + t->start, size); + value_str[size] = '\0'; + + return _getdns_get_const_name_info(value_str, value); +} + +static void +_getdns_destroy_item_data(struct mem_funcs *mf, getdns_item *item) +{ + switch (item->dtype) { + case t_dict: + getdns_dict_destroy(item->data.dict); + break; + + case t_list: + getdns_list_destroy(item->data.list); + break; + + case t_bindata: + GETDNS_FREE(*mf, item->data.bindata->data); + GETDNS_FREE(*mf, item->data.bindata); + default: + break; + } +} +static int _jsmn_get_item(struct mem_funcs *mf, const char *js, jsmntok_t *t, + size_t count, getdns_item *item, getdns_return_t *r); + +static int _jsmn_get_dict(struct mem_funcs *mf, const char *js, jsmntok_t *t, + size_t count, getdns_dict *dict, getdns_return_t *r) +{ + int i; + size_t j = 1; + char key_spc[1024], *key = NULL; + getdns_item child_item; + + if (t->size <= 0) + *r = GETDNS_RETURN_GOOD; + + else for (i = 0; i < t->size; i++) { + if (t[j].type != JSMN_STRING && + t[j].type != JSMN_PRIMITIVE) { + + /* Key must be string or primitive */ + *r = GETDNS_RETURN_WRONG_TYPE_REQUESTED; + break; + } + if (t[j].end <= t[j].start) { + /* Key must be at least 1 character */ + *r = GETDNS_RETURN_GENERIC_ERROR; /* range error */ + break; + } + if (t[j].end - t[j].start < (int)sizeof(key_spc)) + key = key_spc; + + else if (!(key = GETDNS_XMALLOC( + *mf, char, t[j].end - t[j].start + 1))) { + + *r = GETDNS_RETURN_MEMORY_ERROR; + break; + } + (void) memcpy(key, js + t[j].start, t[j].end - t[j].start); + key[t[j].end - t[j].start] = '\0'; + j += 1; + + j += _jsmn_get_item(mf, js, t + j, count - j, &child_item, r); + if (*r) break; + + switch (child_item.dtype) { + case t_int: + *r = getdns_dict_set_int(dict, key, + child_item.data.n); + break; + case t_bindata: + *r = getdns_dict_set_bindata(dict, key, + child_item.data.bindata); + break; + case t_list: + *r = getdns_dict_set_list(dict, key, + child_item.data.list); + break; + case t_dict: + *r = getdns_dict_set_dict(dict, key, + child_item.data.dict); + break; + default: + *r = GETDNS_RETURN_WRONG_TYPE_REQUESTED; + break; + + } + _getdns_destroy_item_data(mf, &child_item); + if (*r) break; + if (key && key != key_spc) { + GETDNS_FREE(*mf, key); + key = NULL; + } + } + if (key && key != key_spc) + GETDNS_FREE(*mf, key); + + if (*r) { + getdns_dict_destroy(dict); + return 0; + } + return j; +} + +static int _jsmn_get_list(struct mem_funcs *mf, const char *js, jsmntok_t *t, + size_t count, getdns_list *list, getdns_return_t *r) +{ + int i; + size_t j = 1, index = 0; + getdns_item child_item; + + if (t->size <= 0) + *r = GETDNS_RETURN_GOOD; + + else for (i = 0; i < t->size; i++) { + j += _jsmn_get_item(mf, js, t + j, count - j, &child_item, r); + if (*r) break; + + switch (child_item.dtype) { + case t_int: + *r = getdns_list_set_int(list, index++, + child_item.data.n); + break; + case t_bindata: + *r = getdns_list_set_bindata(list, index++, + child_item.data.bindata); + break; + case t_list: + *r = getdns_list_set_list(list, index++, + child_item.data.list); + break; + case t_dict: + *r = getdns_list_set_dict(list, index++, + child_item.data.dict); + break; + default: + *r = GETDNS_RETURN_WRONG_TYPE_REQUESTED; + break; + + } + _getdns_destroy_item_data(mf, &child_item); + if (*r) break; + } + if (*r) { + getdns_list_destroy(list); + return 0; + } + return j; +} + +static int _jsmn_get_item(struct mem_funcs *mf, const char *js, jsmntok_t *t, + size_t count, getdns_item *item, getdns_return_t *r) +{ + assert(item); + + switch (t->type) { + case JSMN_STRING: + if (t->end < t->start) + *r = GETDNS_RETURN_GENERIC_ERROR; + + else if (!(item->data.bindata = + GETDNS_MALLOC(*mf, getdns_bindata))) + *r = GETDNS_RETURN_MEMORY_ERROR; + + else if (!(item->data.bindata->data = GETDNS_XMALLOC( + *mf, uint8_t, t->end - t->start + 1))) { + GETDNS_FREE(*mf, item->data.bindata); + *r = GETDNS_RETURN_MEMORY_ERROR; + } else { + item->dtype = t_bindata; + if (t->end - t->start) { + (void) memcpy(item->data.bindata->data, + js + t->start, t->end - t->start); + } + item->data.bindata->data[t->end - t->start] = '\0'; + item->data.bindata->size = t->end - t->start; + *r = GETDNS_RETURN_GOOD; + return 1; + } + break; + + case JSMN_PRIMITIVE: + /* There is no such thing as an empty primitive */ + if (t->end <= t->start) { + *r = GETDNS_RETURN_GENERIC_ERROR; + break; + + } else if (_jsmn_get_int(js, t, &item->data.n) + || _jsmn_get_const(js, t, &item->data.n)) { + + item->dtype = t_int; + } + else if (_jsmn_get_data(mf, js, t, &item->data.bindata) + || _jsmn_get_dname(mf, js, t, &item->data.bindata) + || _jsmn_get_ipv4(mf, js, t, &item->data.bindata) + || _jsmn_get_ipv6(mf, js, t, &item->data.bindata)) + + item->dtype = t_bindata; + + else if (_jsmn_get_ipdict(mf, js, t, &item->data.dict)) + + item->dtype = t_dict; + else { + *r = GETDNS_RETURN_GENERIC_ERROR; + break; + } + *r = GETDNS_RETURN_GOOD; + return 1; + + case JSMN_OBJECT: + if (!(item->data.dict = _getdns_dict_create_with_mf(mf))) { + *r = GETDNS_RETURN_MEMORY_ERROR; + break; + } + item->dtype = t_dict; + return _jsmn_get_dict(mf, js, t, count, item->data.dict, r); + + case JSMN_ARRAY: + if (!(item->data.list = _getdns_list_create_with_mf(mf))) { + *r = GETDNS_RETURN_MEMORY_ERROR; + break; + } + item->dtype = t_list; + return _jsmn_get_list(mf, js, t, count, item->data.list, r); + + default: + *r = GETDNS_RETURN_WRONG_TYPE_REQUESTED; + break; + } + return 0; +} + +static getdns_return_t +_getdns_str2item_mf(struct mem_funcs *mf, const char *str, getdns_item *item) +{ + jsmn_parser p; + jsmntok_t *tok = NULL, *new_tok; + size_t tokcount = 100; + int r; + getdns_return_t gr; + + jsmn_init(&p); + tok = GETDNS_XMALLOC(*mf, jsmntok_t, tokcount); + do { + r = jsmn_parse(&p, str, strlen(str), tok, tokcount); + if (r == JSMN_ERROR_NOMEM) { + tokcount *= 2; + if (!(new_tok = GETDNS_XREALLOC( + *mf, tok, jsmntok_t, tokcount))) { + GETDNS_FREE(*mf, tok); + return GETDNS_RETURN_MEMORY_ERROR; + } + tok = new_tok; + } + } while (r == JSMN_ERROR_NOMEM); + if (r < 0) + gr = GETDNS_RETURN_GENERIC_ERROR; + else + (void) _jsmn_get_item(mf, str, tok, p.toknext, item, &gr); + GETDNS_FREE(*mf, tok); + return gr; +} + +getdns_return_t +getdns_str2dict(const char *str, getdns_dict **dict) +{ + getdns_item item; + getdns_return_t r; + + if (!str || !dict) + return GETDNS_RETURN_INVALID_PARAMETER; + + while (*str && isspace(*str)) + str++; + + if (*str != '{') { + getdns_dict *dict_r = _getdns_ipaddr_dict_mf( + &_getdns_plain_mem_funcs, str); + + if (dict_r) { + *dict = dict_r; + return GETDNS_RETURN_GOOD; + } + } + if ((r = _getdns_str2item_mf(&_getdns_plain_mem_funcs, str, &item))) + return r; + + else if (item.dtype != t_dict) { + uint8_t buf[16]; + getdns_dict *dict_r; + + if (item.dtype != t_bindata) + r = GETDNS_RETURN_WRONG_TYPE_REQUESTED; + + else if (item.data.bindata->size == 4 && + inet_pton(AF_INET, str, buf) == 1) { + + if (!(dict_r = getdns_dict_create())) + r = GETDNS_RETURN_MEMORY_ERROR; + + else if ((r = getdns_dict_util_set_string( + dict_r, "address_type", "IPv4"))) + getdns_dict_destroy(dict_r); + + else if ((r = getdns_dict_set_bindata( + dict_r, "address_data", item.data.bindata))) + getdns_dict_destroy(dict_r); + else + *dict = dict_r; + + } else if (item.data.bindata->size == 16 && + inet_pton(AF_INET6, str, buf) == 1) { + + if (!(dict_r = getdns_dict_create())) + r = GETDNS_RETURN_MEMORY_ERROR; + + else if ((r = getdns_dict_util_set_string( + dict_r, "address_type", "IPv6"))) + getdns_dict_destroy(dict_r); + + else if ((r = getdns_dict_set_bindata( + dict_r, "address_data", item.data.bindata))) + getdns_dict_destroy(dict_r); + else + *dict = dict_r; + } else + r = GETDNS_RETURN_WRONG_TYPE_REQUESTED; + + _getdns_destroy_item_data(&_getdns_plain_mem_funcs, &item); + return r; + } + *dict = item.data.dict; + return GETDNS_RETURN_GOOD; +} + +getdns_return_t +getdns_str2list(const char *str, getdns_list **list) +{ + getdns_item item; + getdns_return_t r; + + if (!str || !list) + return GETDNS_RETURN_INVALID_PARAMETER; + + if ((r = _getdns_str2item_mf(&_getdns_plain_mem_funcs, str, &item))) + return r; + + else if (item.dtype != t_list) { + _getdns_destroy_item_data(&_getdns_plain_mem_funcs, &item); + return GETDNS_RETURN_WRONG_TYPE_REQUESTED; + } + *list = item.data.list; + return GETDNS_RETURN_GOOD; +} + +getdns_return_t +getdns_str2bindata(const char *str, getdns_bindata **bindata) +{ + getdns_item item; + getdns_return_t r; + + if (!str || !bindata) + return GETDNS_RETURN_INVALID_PARAMETER; + + if ((r = _getdns_str2item_mf(&_getdns_plain_mem_funcs, str, &item))) + return r; + + else if (item.dtype != t_bindata) { + _getdns_destroy_item_data(&_getdns_plain_mem_funcs, &item); + return GETDNS_RETURN_WRONG_TYPE_REQUESTED; + } + *bindata = item.data.bindata; + return GETDNS_RETURN_GOOD; +} + +getdns_return_t +getdns_str2int(const char *str, uint32_t *value) +{ + getdns_item item; + getdns_return_t r; + + if (!str || !value) + return GETDNS_RETURN_INVALID_PARAMETER; + + if ((r = _getdns_str2item_mf(&_getdns_plain_mem_funcs, str, &item))) + return r; + + else if (item.dtype != t_int) { + _getdns_destroy_item_data(&_getdns_plain_mem_funcs, &item); + return GETDNS_RETURN_WRONG_TYPE_REQUESTED; + } + *value = item.data.n; + return GETDNS_RETURN_GOOD; +} -/* convert.c */ diff --git a/src/debug.h b/src/debug.h index e106cf67..643b198d 100644 --- a/src/debug.h +++ b/src/debug.h @@ -37,7 +37,7 @@ #include "config.h" -#define STUB_DEBUG_ENTRY "-> ENTRY: " +#define STUB_DEBUG_ENTRY "=> ENTRY: " #define STUB_DEBUG_SETUP "--- SETUP: " #define STUB_DEBUG_SETUP_TLS "--- SETUP(TLS): " #define STUB_DEBUG_TSIG "--- TSIG: " @@ -45,6 +45,7 @@ #define STUB_DEBUG_READ "------- READ: " #define STUB_DEBUG_WRITE "------- WRITE: " #define STUB_DEBUG_CLEANUP "--- CLEANUP: " +#define STUB_DEBUG_DAEMON "GETDNS_DAEMON: " #define DEBUG_ON(...) do { \ struct timeval tv; \ @@ -88,6 +89,13 @@ #define DEBUG_STUB(...) DEBUG_OFF(__VA_ARGS__) #endif +#if defined(DAEMON_DEBUG) && DAEMON_DEBUG +#include +#define DEBUG_DAEMON(...) DEBUG_ON(__VA_ARGS__) +#else +#define DEBUG_DAEMON(...) DEBUG_OFF(__VA_ARGS__) +#endif + #if defined(SEC_DEBUG) && SEC_DEBUG #include #define DEBUG_SEC(...) DEBUG_ON(__VA_ARGS__) diff --git a/src/dnssec.c b/src/dnssec.c index 87e2943b..0269fe3d 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -2696,6 +2696,7 @@ static int chain_head_validate(struct mem_funcs *mf, time_t now, uint32_t skew, * evaluated by processing each head in turn. The worst outcome per network request * is the dnssec status for that network request. */ +#ifdef STUB_NATIVE_DNSSEC static void chain_set_netreq_dnssec_status(chain_head *chain, _getdns_rrset_iter *tas) { chain_head *head; @@ -2732,6 +2733,7 @@ static void chain_set_netreq_dnssec_status(chain_head *chain, _getdns_rrset_iter } } } +#endif /* The DNSSEC status of all heads for a chain structure is evaluated by * processing each head in turn. The worst outcome is the dnssec status for diff --git a/src/getdns/getdns_extra.h.in b/src/getdns/getdns_extra.h.in index 7e2a16e3..5abf39e7 100644 --- a/src/getdns/getdns_extra.h.in +++ b/src/getdns/getdns_extra.h.in @@ -900,6 +900,196 @@ getdns_msg_dict2str_buf( getdns_return_t getdns_msg_dict2str_scan( const getdns_dict *msg_dict, char **str, int *str_len); + +/** + * Convert string text to a getdns_dict. + * + * @param str A textual representation of a getdns_dict. + * The format is similar, but not precisely JSON. + * - dict keys may be given without quotes. + * For example: `{ timeout: 2000 }` is the same as { "timeout": 2000 } + * - When str contains an IP or IPv6 address, it is converted + * to an getdns dict representation of that address. This may contain + * a port, tls_port, tsig spec or tls authentication name in the same + * way as may be given with the `getdns_query` tool. For example: + * `185.49.140.67:80#443` will result in the following getdns_dict: + * + * { address_type: "IPv4" + * , address_data: "185.49.140.67" + * , port: 80 + * , tls_port: 443 + * } + * + * @param dict The returned getdns_dict. + * @return GETDNS_RETURN_GOOD on success or an error code on failure. + */ +getdns_return_t +getdns_str2dict(const char *str, getdns_dict **dict); + +/** + * Convert string text to a getdns_list. + * + * @param str A textual representation of a getdns_list. + * The format is similar, but not precisely JSON. + * @param list The returned getdns_list. + * @return GETDNS_RETURN_GOOD on success or an error code on failure. + */ +getdns_return_t +getdns_str2list(const char *str, getdns_list **list); + +/** + * Convert string text to a getdns_bindata. + * + * @param str A textual representation of a getdns_bindata + * The format is similar, but not precisely JSON. + * - Strings between double-quotes will be converted to bindata + * containers, but *without the trailing null byte*. + * For example: `{ suffix: [ "nlnetlabs.nl.", "nlnet.nl." ] }` + * - bindata representation of IP or IPv6 addresses may be + * given in their presentation format. For example: + * `{ dns_root_servers: [ 2001:7fd::1, 193.0.14.129 ] }` + * - Arbitrary binary data may be given with a `0x` prefix. + * For example: + * + * { add_opt_parameters: + * { options: [ { option_code: 10 + * , option_data: 0xA9E4EC50C03F5D65 + * } ] + * } + * } + * + * - Wireformat domain name bindatas can be given with a trailing dot. + * For example: + * + * { upstream_recursive_servers: + * [ { address_data : 2a04:b900:0:100::37 + * , tsig_name : hmac-md5.tsigs.getdnsapi.net. + * , tsig_algorithm: hmac-md5.sig-alg.reg.int. + * , tsig_secret : 0xD7A1BAF4E4DE5D6EB149 + * } ] + * } + * + * @param bindata The returned getdns_bindata. + * @return GETDNS_RETURN_GOOD on success or an error code on failure. + */ +getdns_return_t +getdns_str2bindata(const char *str, getdns_bindata **bindata); + +/** + * Convert string text to a getdns 32 bits unsigned integer. + * + * @param str A textual representation of the integer. + * The format is similar, but not precisely JSON. + * - integer values may be given by the constant name. + * For example: `{ resolution_type: GETDNS_RESOLUTION_STUB }` + * or `{ specify_class: GETDNS_RRCLASS_CH }` + * @param value The returned integer. + * @return GETDNS_RETURN_GOOD on success or an error code on failure. + */ +getdns_return_t +getdns_str2int(const char *str, uint32_t *value); + +/** + * Configure a context with settings given in a getdns_dict. + * + * @param context The context to be configured. + * @param config_dict The getdns_dict containing the settings. + * The settings have the same name as returned by the + * getdns_context_get_api_information() function, or as + * used in the names of the getdns_context_get_*() and + * getdns_context_set_*() functions. + * - The dict returned by + * getdns_context_get_api_information() can be used + * as the config_dict directly, but context settings + * do *not* have to be below a `"all_context"` key. + * - It is possible to set default values for extensions + * that could otherwise only be given on a per query + * basis. For example: + * `{ dnssec_return_status: GETDNS_EXTENSION_TRUE }` is + * equivalent to using the + * getdns_context_set_return_dnssec_status() function + * with that value, but default values for the other + * extensions can be set by this method now too. + * For example + * `{ return_call_reporting: GETDNS_EXTENSION_TRUE}` + * - Trust anchor files and root hints content can also be + * given by file, for example: + * + * { dns_root_servers : "named.root" + * , dnssec_trust_anchors: "/etc/unbound/getdns-root.key" + * } + * @return GETDNS_RETURN_GOOD on success or an error code on failure. + * **Beware** that context might be partially configured on error. For retry + * strategies it is advised to recreate a new config. + */ +getdns_return_t +getdns_context_config(getdns_context *context, const getdns_dict *config_dict); + + +/** + * The user defined request handler that will be called on incoming requests. + */ +typedef void (*getdns_request_handler_t)( + getdns_context *context, + getdns_callback_type_t callback_type, + getdns_dict *request, + void *userarg, + getdns_transaction_t request_id +); + +/** + * Create a name server by registering a list of addresses to listen on and + * a user defined function that will handle the requests. + * + * @param context The context managing the eventloop that needs to be run to + * start serving. + * @param listen_addresses A list of address dicts or bindatas that will be + * listened on for DNS requests. Both UDP and TCP + * transports will be used. + * @param userarg A user defined argument that will be passed to the handler + * untouched. + * @param handler The user defined request handler that will be called with the + * request received in reply dict format. To reply to this request + * the function has to construct a response (or modify the request) + * and call getdns_reply() with the response and the with the request + * associated request_id. The user is responsible of destroying + * both the replies and the response. **Beware** that if requests are + * not answered by the function, by not calling getdns_reply() this + * will cause a memory leak. The user most use getdns_reply() + * with NULL as the response to not answer/cancel a request. + * @return GETDNS_RETURN_GOOD on success or an error code on failure. + * On failure, the current set of listening addresses is left in place. + * Also, if there is overlap in listening_addresses between the active set + * and the newly given set, the ones in the active set will remain in their + * current condition and will not be closed and reopened, also all assoicated + * DNS transactions will remain. + */ +getdns_return_t +getdns_context_set_listen_addresses( + getdns_context *context, const getdns_list *listen_addresses, + void *userarg, getdns_request_handler_t handler); + +/** + * Answer the request associated with a request_id that is received by a + * request handler + * + * @param context The context managing the eventloop that needs to be run to + * listen for and answer requests. + * @param reply The answer in getdns reply dict or response dict format. + * When NULL is given as reply, the request is not answered + * but all associated state is deleted. + * @param request_id The identifier that links this response with the + * received request. + * @return GETDNS_RETURN_GOOD on success or an error code on failure. + * On fatal failure (no retry strategy possible) the user still needs to + * cancel the request by recalling getdns_reply() but with NULL as response, + * to clean up state. + */ +getdns_return_t +getdns_reply(getdns_context *context, + getdns_dict *reply, getdns_transaction_t request_id); + + /** @} */ @@ -918,7 +1108,6 @@ getdns_return_t getdns_strerror(getdns_return_t err, char *buf, size_t buflen); */ /** @} */ - /** @} */ diff --git a/src/test/jsmn b/src/jsmn similarity index 100% rename from src/test/jsmn rename to src/jsmn diff --git a/src/libgetdns.symbols b/src/libgetdns.symbols index 2eb8e381..6e3cb128 100644 --- a/src/libgetdns.symbols +++ b/src/libgetdns.symbols @@ -1,6 +1,7 @@ getdns_address getdns_address_sync getdns_cancel_callback +getdns_context_config getdns_context_create getdns_context_create_with_extended_memory_functions getdns_context_create_with_memory_functions @@ -50,6 +51,7 @@ getdns_context_set_extended_memory_functions getdns_context_set_follow_redirects getdns_context_set_idle_timeout getdns_context_set_limit_outstanding_queries +getdns_context_set_listen_addresses getdns_context_set_memory_functions getdns_context_set_namespaces getdns_context_set_resolution_type @@ -123,6 +125,7 @@ getdns_print_json_dict getdns_print_json_list getdns_pubkey_pin_create_from_string getdns_pubkey_pinset_sanity_check +getdns_reply getdns_root_trust_anchor getdns_rr_dict2str getdns_rr_dict2str_buf @@ -134,6 +137,10 @@ getdns_service getdns_service_sync getdns_snprint_json_dict getdns_snprint_json_list +getdns_str2bindata +getdns_str2dict +getdns_str2int +getdns_str2list getdns_str2rr_dict getdns_strerror getdns_validate_dnssec diff --git a/src/mk-const-info.c.sh b/src/mk-const-info.c.sh index 9e8b2826..f9c54323 100755 --- a/src/mk-const-info.c.sh +++ b/src/mk-const-info.c.sh @@ -46,18 +46,20 @@ getdns_get_errorstr_by_id(uint16_t err) else return NULL; } -END_OF_TAIL -gawk 'BEGIN{p=1}{if(p)print}/^static struct const_name_info consts_name_info\[\] = {$/{p=0}' test/getdns_str2dict.c > test/new-getdns_str2dict.c -gawk '/^[ ]+GETDNS_[A-Z_]+[ ]+=[ ]+[0-9]+/{ key = sprintf("%d", $3); consts[$1] = key; }/^#define GETDNS_[A-Z_]+[ ]+[0-9]+/ && !/_TEXT/{ key = sprintf("%d", $3); consts[$2] = key; }/^#define GETDNS_[A-Z_]+[ ]+\(\(getdns_(return|append_name)_t) [0-9]+ \)/{ key = sprintf("%d", $4); consts[$2] = key; }END{ n = asorti(consts, const_vals); for ( i = 1; i <= n; i++) { val = const_vals[i]; name = consts[val]; print "\t{ \""val"\", "name" },"}}' getdns/getdns.h.in getdns/getdns_extra.h.in | sed 's/,,/,/g' >> test/new-getdns_str2dict.c -cat >> test/new-getdns_str2dict.c << END_OF_TAIL +static struct const_name_info consts_name_info[] = { +END_OF_TAIL +gawk '/^[ ]+GETDNS_[A-Z_]+[ ]+=[ ]+[0-9]+/{ key = sprintf("%d", $3); consts[$1] = key; }/^#define GETDNS_[A-Z_]+[ ]+[0-9]+/ && !/_TEXT/{ key = sprintf("%d", $3); consts[$2] = key; }/^#define GETDNS_[A-Z_]+[ ]+\(\(getdns_(return|append_name)_t) [0-9]+ \)/{ key = sprintf("%d", $4); consts[$2] = key; }END{ n = asorti(consts, const_vals); for ( i = 1; i <= n; i++) { val = const_vals[i]; name = consts[val]; print "\t{ \""val"\", "name" },"}}' getdns/getdns.h.in getdns/getdns_extra.h.in | sed 's/,,/,/g' >> const-info.c +cat >> const-info.c << END_OF_TAIL }; + static int const_name_info_cmp(const void *a, const void *b) { return strcmp( ((struct const_name_info *) a)->name , ((struct const_name_info *) b)->name ); } -static int + +int _getdns_get_const_name_info(const char *name, uint32_t *code) { struct const_name_info key = { name, 0 }; @@ -71,5 +73,4 @@ _getdns_get_const_name_info(const char *name, uint32_t *code) return 1; } END_OF_TAIL -mv test/new-getdns_str2dict.c test/getdns_str2dict.c diff --git a/src/pubkey-pinning.c b/src/pubkey-pinning.c index 1dd67080..89ba5d86 100644 --- a/src/pubkey-pinning.c +++ b/src/pubkey-pinning.c @@ -382,10 +382,10 @@ _getdns_verify_pinset_match(const sha256_pin_t *pinset, X509_STORE_CTX *store) { getdns_return_t ret = GETDNS_RETURN_GENERIC_ERROR; - X509 *x; + X509 *x, *prev; int i, len; unsigned char raw[4096]; - unsigned char *next = raw; + unsigned char *next; unsigned char buf[sizeof(pinset->pin)]; const sha256_pin_t *p; @@ -407,14 +407,7 @@ _getdns_verify_pinset_match(const sha256_pin_t *pinset, /* TODO: how do we handle raw public keys? */ - for (i = 0; i < sk_X509_num(X509_STORE_CTX_get0_untrusted(store)); i++) { - if (i > 0) { - /* TODO: how do we ensure that the certificates in - * each stage appropriately sign the previous one? - * for now, to be safe, we only examine the end-entity - * cert: */ - return GETDNS_RETURN_GENERIC_ERROR; - } + for (i = 0; i < sk_X509_num(X509_STORE_CTX_get0_untrusted(store)); i++, prev = x) { x = sk_X509_value(X509_STORE_CTX_get0_untrusted(store), i); #if defined(STUB_DEBUG) && STUB_DEBUG @@ -423,6 +416,24 @@ _getdns_verify_pinset_match(const sha256_pin_t *pinset, X509_NAME_print_ex_fp(stderr, X509_get_subject_name(x), 1, XN_FLAG_ONELINE); fprintf(stderr, "\n"); #endif + if (i > 0) { + /* we ensure that "prev" is signed by "x" */ + EVP_PKEY *pkey = X509_get_pubkey(x); + int verified; + if (!pkey) { + DEBUG_STUB("%s %-35s: Could not get pubkey from cert %d (%p)\n", + STUB_DEBUG_SETUP_TLS, __FUNC__, i, (void*)x); + return GETDNS_RETURN_GENERIC_ERROR; + } + verified = X509_verify(prev, pkey); + EVP_PKEY_free(pkey); + if (!verified) { + DEBUG_STUB("%s %-35s: cert %d (%p) was not signed by cert %d\n", + STUB_DEBUG_SETUP_TLS, __FUNC__, i-1, (void*)prev, i); + return GETDNS_RETURN_GENERIC_ERROR; + } + } + /* digest the cert with sha256 */ len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(x), NULL); if (len > (int)sizeof(raw)) { @@ -430,6 +441,7 @@ _getdns_verify_pinset_match(const sha256_pin_t *pinset, 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", @@ -442,11 +454,11 @@ _getdns_verify_pinset_match(const sha256_pin_t *pinset, 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", - STUB_DEBUG_SETUP_TLS, __FUNC__, i, (void *)p, sizeof(p->pin)); + STUB_DEBUG_SETUP_TLS, __FUNC__, i, (void*)p, sizeof(p->pin)); return GETDNS_RETURN_GOOD; } else DEBUG_STUB("%s %-35s: Pubkey %d did not match pin %p\n", - STUB_DEBUG_SETUP_TLS, __FUNC__, i, (void *)p); + STUB_DEBUG_SETUP_TLS, __FUNC__, i, (void*)p); } return ret; diff --git a/src/request-internal.c b/src/request-internal.c index ad33b4b4..e1071a9a 100644 --- a/src/request-internal.c +++ b/src/request-internal.c @@ -85,20 +85,20 @@ getdns_dict *dnssec_ok_checking_disabled_avoid_roadblocks static int -is_extension_set(getdns_dict *extensions, const char *extension) +is_extension_set(getdns_dict *extensions, const char *name, int default_value) { getdns_return_t r; uint32_t value; - if (! extensions) - return 0; - else if (extensions == dnssec_ok_checking_disabled + if ( ! extensions + || extensions == dnssec_ok_checking_disabled || extensions == dnssec_ok_checking_disabled_roadblock_avoidance || extensions == dnssec_ok_checking_disabled_avoid_roadblocks) return 0; - r = getdns_dict_get_int(extensions, extension, &value); - return r == GETDNS_RETURN_GOOD && value == GETDNS_EXTENSION_TRUE; + r = getdns_dict_get_int(extensions, name, &value); + return r == GETDNS_RETURN_GOOD ? ( value == GETDNS_EXTENSION_TRUE ) + : default_value; } static void @@ -177,11 +177,10 @@ network_req_init(getdns_network_req *net_req, getdns_dns_req *owner, net_req->fd = -1; net_req->transport_current = 0; memset(&net_req->event, 0, sizeof(net_req->event)); - memset(&net_req->tcp, 0, sizeof(net_req->tcp)); net_req->keepalive_sent = 0; net_req->write_queue_tail = NULL; /* Some fields to record info for return_call_reporting */ - net_req->debug_tls_auth_status = 0; + net_req->debug_tls_auth_status = GETDNS_AUTH_NONE; net_req->debug_udp = 0; if (max_query_sz == 0) { @@ -206,6 +205,9 @@ network_req_init(getdns_network_req *net_req, getdns_dns_req *owner, buf = netreq_reset(net_req); gldns_buffer_init_frm_data( &gbuf, net_req->query, net_req->wire_data_sz - 2); + if (owner->context->header) + _getdns_reply_dict2wire(owner->context->header, &gbuf, 1); + gldns_buffer_rewind(&gbuf); _getdns_reply_dict2wire(extensions, &gbuf, 1); if (dnssec_extension_set) /* We will do validation ourselves */ GLDNS_CD_SET(net_req->query); @@ -658,28 +660,32 @@ getdns_dns_req * _getdns_dns_req_new(getdns_context *context, getdns_eventloop *loop, const char *name, uint16_t request_type, getdns_dict *extensions) { - int dnssec_return_status - = context->return_dnssec_status == GETDNS_EXTENSION_TRUE - || is_extension_set(extensions, "dnssec_return_status"); - int dnssec_return_only_secure - = is_extension_set(extensions, "dnssec_return_only_secure"); - int dnssec_return_all_statuses - = is_extension_set(extensions, "dnssec_return_all_statuses"); - int dnssec_return_full_validation_chain - = is_extension_set(extensions, "dnssec_return_full_validation_chain"); - int dnssec_return_validation_chain - = is_extension_set(extensions, "dnssec_return_validation_chain"); - int edns_cookies - = is_extension_set(extensions, "edns_cookies"); + int dnssec_return_status = is_extension_set( + extensions, "dnssec_return_status", + context->dnssec_return_status); + int dnssec_return_only_secure = is_extension_set( + extensions, "dnssec_return_only_secure", + context->dnssec_return_only_secure); + int dnssec_return_all_statuses = is_extension_set( + extensions, "dnssec_return_all_statuses", + context->dnssec_return_all_statuses); + int dnssec_return_full_validation_chain = is_extension_set( + extensions, "dnssec_return_full_validation_chain", + context->dnssec_return_full_validation_chain); + int dnssec_return_validation_chain = is_extension_set( + extensions, "dnssec_return_validation_chain", + context->dnssec_return_validation_chain); + int edns_cookies = is_extension_set( + extensions, "edns_cookies", + context->edns_cookies); #ifdef DNSSEC_ROADBLOCK_AVOIDANCE int avoid_dnssec_roadblocks = (extensions == dnssec_ok_checking_disabled_avoid_roadblocks); - int dnssec_roadblock_avoidance - = is_extension_set(extensions, "dnssec_roadblock_avoidance") + int dnssec_roadblock_avoidance = avoid_dnssec_roadblocks || (extensions == dnssec_ok_checking_disabled_roadblock_avoidance) - || avoid_dnssec_roadblocks; + || is_extension_set(extensions, "dnssec_roadblock_avoidance", + context->dnssec_roadblock_avoidance); #endif - int dnssec_extension_set = dnssec_return_status || dnssec_return_only_secure || dnssec_return_all_statuses || dnssec_return_validation_chain @@ -713,9 +719,9 @@ _getdns_dns_req_new(getdns_context *context, getdns_eventloop *loop, int with_opt; getdns_dns_req *result = NULL; - uint32_t klass = GLDNS_RR_CLASS_IN; - int a_aaaa_query = - is_extension_set(extensions, "return_both_v4_and_v6") && + uint32_t klass = context->specify_class; + int a_aaaa_query = is_extension_set(extensions, + "return_both_v4_and_v6", context->return_both_v4_and_v6) && ( request_type == GETDNS_RRTYPE_A || request_type == GETDNS_RRTYPE_AAAA ); /* Reserve for the buffer at least one more byte @@ -732,7 +738,10 @@ _getdns_dns_req_new(getdns_context *context, getdns_eventloop *loop, have_add_opt_parameters = getdns_dict_get_dict(extensions, "add_opt_parameters", &add_opt_parameters) == GETDNS_RETURN_GOOD; - + if (!have_add_opt_parameters && context->add_opt_parameters) { + add_opt_parameters = context->add_opt_parameters; + have_add_opt_parameters = 1; + } if (dnssec_extension_set) { edns_maximum_udp_payload_size = -1; edns_extended_rcode = 0; @@ -895,10 +904,10 @@ _getdns_dns_req_new(getdns_context *context, getdns_eventloop *loop, #endif result->edns_client_subnet_private = context->edns_client_subnet_private; result->tls_query_padding_blocksize = context->tls_query_padding_blocksize; - result->return_call_reporting = - is_extension_set(extensions, "return_call_reporting"); - result->add_warning_for_bad_dns = - is_extension_set(extensions, "add_warning_for_bad_dns"); + result->return_call_reporting = is_extension_set(extensions, + "return_call_reporting" , context->return_call_reporting); + result->add_warning_for_bad_dns = is_extension_set(extensions, + "add_warning_for_bad_dns", context->add_warning_for_bad_dns); /* will be set by caller */ result->user_pointer = NULL; diff --git a/src/test/getdns_context_set_listen_addresses.c b/src/server.c similarity index 82% rename from src/test/getdns_context_set_listen_addresses.c rename to src/server.c index 0f432dd7..968564e8 100644 --- a/src/test/getdns_context_set_listen_addresses.c +++ b/src/server.c @@ -26,11 +26,20 @@ */ #include "config.h" -#include "getdns_context_set_listen_addresses.h" + +#ifndef USE_WINSOCK +#include +#else +#include +#include +#endif + #include "getdns/getdns_extra.h" +#include "context.h" #include "types-internal.h" #include "debug.h" -#include +#include "util/rbtree.h" +#include "server.h" #define DNS_REQUEST_SZ 4096 #define DOWNSTREAM_IDLE_TIMEOUT 5000 @@ -63,9 +72,10 @@ struct listener { */ struct listen_set { getdns_context *context; - listen_set *next; + void *userarg; getdns_request_handler_t handler; + _getdns_rbtree_t connections_set; size_t count; listener items[]; }; @@ -79,6 +89,9 @@ struct tcp_to_write { }; struct connection { + /* struct connection is a sub struct of _getdns_rbnode_t */ + _getdns_rbnode_t super; + listener *l; struct sockaddr_storage remote_in; socklen_t addrlen; @@ -88,14 +101,8 @@ struct connection { }; typedef struct tcp_connection { - /* A TCP connection is a connection */ - listener *l; - struct sockaddr_storage remote_in; - socklen_t addrlen; - - connection *next; - connection **prev_next; - /************************************/ + /* struct tcp_connection is a sub struct of connection */ + connection super; int fd; getdns_eventloop_event event; @@ -118,10 +125,10 @@ static void tcp_connection_destroy(tcp_connection *conn) tcp_to_write *cur, *next; - if (!(mf = priv_getdns_context_mf(conn->l->set->context))) + if (!(mf = &conn->super.l->set->context->mf)) return; - if (getdns_context_get_eventloop(conn->l->set->context, &loop)) + if (getdns_context_get_eventloop(conn->super.l->set->context, &loop)) return; if (conn->event.read_cb||conn->event.write_cb||conn->event.timeout_cb) @@ -139,10 +146,14 @@ static void tcp_connection_destroy(tcp_connection *conn) return; /* Unlink this connection */ - if ((*conn->prev_next = conn->next)) - conn->next->prev_next = conn->prev_next; + (void) _getdns_rbtree_delete( + &conn->super.l->set->connections_set, conn); + DEBUG_SERVER("[connection del] count: %d\n", + (int)conn->super.l->set->connections_set.count); + if ((*conn->super.prev_next = conn->super.next)) + conn->super.next->prev_next = conn->super.prev_next; - free_listen_set_when_done(conn->l->set); + free_listen_set_when_done(conn->super.l->set); GETDNS_FREE(*mf, conn); } @@ -157,10 +168,10 @@ static void tcp_write_cb(void *userarg) assert(userarg); - if (!(mf = priv_getdns_context_mf(conn->l->set->context))) + if (!(mf = &conn->super.l->set->context->mf)) return; - if (getdns_context_get_eventloop(conn->l->set->context, &loop)) + if (getdns_context_get_eventloop(conn->super.l->set->context, &loop)) return; /* Reset tcp_connection idle timeout */ @@ -195,28 +206,30 @@ static void tcp_write_cb(void *userarg) DOWNSTREAM_IDLE_TIMEOUT, &conn->event); } -void -_getdns_cancel_reply(getdns_context *context, getdns_transaction_t request_id) +static void +_getdns_cancel_reply(getdns_context *context, connection *conn) { - /* TODO: Check request_id at context->outbound_requests */ - connection *conn = (connection *)(intptr_t)request_id; struct mem_funcs *mf; if (!context || !conn) return; if (conn->l->transport == GETDNS_TRANSPORT_TCP) { - tcp_connection *conn = (tcp_connection *)(intptr_t)request_id; + tcp_connection *tcp_conn = (tcp_connection *)conn; - if (conn->to_answer > 0 && --conn->to_answer == 0 && - conn->fd == -1) - tcp_connection_destroy(conn); + if (tcp_conn->to_answer > 0 && --tcp_conn->to_answer == 0 && + tcp_conn->fd == -1) + tcp_connection_destroy(tcp_conn); } else if (conn->l->transport == GETDNS_TRANSPORT_UDP && - (mf = priv_getdns_context_mf(conn->l->set->context))) { + (mf = &conn->l->set->context->mf)) { listen_set *set = conn->l->set; /* Unlink this connection */ + (void) _getdns_rbtree_delete( + &set->connections_set, conn); + DEBUG_SERVER("[connection del] count: %d\n", + (int)set->connections_set.count); if ((*conn->prev_next = conn->next)) conn->next->prev_next = conn->prev_next; GETDNS_FREE(*mf, conn); @@ -226,7 +239,7 @@ _getdns_cancel_reply(getdns_context *context, getdns_transaction_t request_id) getdns_return_t getdns_reply( - getdns_context *context, getdns_transaction_t request_id, getdns_dict *reply) + getdns_context *context, getdns_dict *reply, getdns_transaction_t request_id) { /* TODO: Check request_id at context->outbound_requests */ connection *conn = (connection *)(intptr_t)request_id; @@ -236,10 +249,21 @@ getdns_reply( size_t len; getdns_return_t r; - if (!context || !reply || !conn) + if (!context || !conn) return GETDNS_RETURN_INVALID_PARAMETER; - if (!(mf = priv_getdns_context_mf(conn->l->set->context))) + if (!context->server) + return GETDNS_RETURN_GENERIC_ERROR;; + + if (_getdns_rbtree_search(&context->server->connections_set, conn) + != &conn->super) + return GETDNS_RETURN_NO_SUCH_LIST_ITEM; + + if (!reply) { + _getdns_cancel_reply(context, conn); + return GETDNS_RETURN_GOOD; + } + if (!(mf = &conn->l->set->context->mf)) return GETDNS_RETURN_GENERIC_ERROR;; if ((r = getdns_context_get_eventloop(conn->l->set->context, &loop))) @@ -252,7 +276,7 @@ getdns_reply( else if (conn->l->transport == GETDNS_TRANSPORT_UDP) { listener *l = conn->l; - if (conn->l->fd >= 0 && sendto(conn->l->fd, buf, len, 0, + if (conn->l->fd >= 0 && sendto(conn->l->fd, (void *)buf, len, 0, (struct sockaddr *)&conn->remote_in, conn->addrlen) == -1) { /* IO error, cleanup this listener */ loop->vmt->clear(loop, &conn->l->event); @@ -260,6 +284,10 @@ getdns_reply( conn->l->fd = -1; } /* Unlink this connection */ + (void) _getdns_rbtree_delete( + &l->set->connections_set, conn); + DEBUG_SERVER("[connection del] count: %d\n", + (int)l->set->connections_set.count); if ((*conn->prev_next = conn->next)) conn->next->prev_next = conn->prev_next; @@ -320,10 +348,10 @@ static void tcp_read_cb(void *userarg) assert(userarg); - if (!(mf = priv_getdns_context_mf(conn->l->set->context))) + if (!(mf = &conn->super.l->set->context->mf)) return; - if ((r = getdns_context_get_eventloop(conn->l->set->context, &loop))) + if ((r = getdns_context_get_eventloop(conn->super.l->set->context, &loop))) return; /* Reset tcp_connection idle timeout */ @@ -380,9 +408,14 @@ static void tcp_read_cb(void *userarg) else { conn->to_answer++; + /* TODO: wish list item: + * (void) getdns_dict_set_int64( + * request_dict, "request_id", intptr_t)conn); + */ /* Call request handler */ - conn->l->set->handler( - conn->l->set->context, request_dict, (intptr_t)conn); + conn->super.l->set->handler( + conn->super.l->set->context, GETDNS_CALLBACK_COMPLETE, + request_dict, conn->super.l->set->userarg, (intptr_t)conn); conn->read_pos = conn->read_buf; conn->to_read = 2; @@ -402,7 +435,8 @@ static void tcp_timeout_cb(void *userarg) if (conn->to_answer) { getdns_eventloop *loop; - if (getdns_context_get_eventloop(conn->l->set->context, &loop)) + if (getdns_context_get_eventloop( + conn->super.l->set->context, &loop)) return; loop->vmt->clear(loop, &conn->event); @@ -423,7 +457,7 @@ static void tcp_accept_cb(void *userarg) assert(userarg); - if (!(mf = priv_getdns_context_mf(l->set->context))) + if (!(mf = &l->set->context->mf)) return; if ((r = getdns_context_get_eventloop(l->set->context, &loop))) @@ -433,11 +467,10 @@ static void tcp_accept_cb(void *userarg) return; (void) memset(conn, 0, sizeof(tcp_connection)); - - conn->l = l; - conn->addrlen = sizeof(conn->remote_in); - if ((conn->fd = accept(l->fd, - (struct sockaddr *)&conn->remote_in, &conn->addrlen)) == -1) { + conn->super.l = l; + conn->super.addrlen = sizeof(conn->super.remote_in); + if ((conn->fd = accept(l->fd, (struct sockaddr *) + &conn->super.remote_in, &conn->super.addrlen)) == -1) { /* IO error, cleanup this listener */ loop->vmt->clear(loop, &l->event); close(l->fd); @@ -458,10 +491,20 @@ static void tcp_accept_cb(void *userarg) conn->event.timeout_cb = tcp_timeout_cb; /* Insert connection */ - if ((conn->next = l->connections)) - conn->next->prev_next = &conn->next; - conn->prev_next = &l->connections; + conn->super.super.key = conn; + if (!_getdns_rbtree_insert( + &l->set->connections_set, &conn->super.super)) { + /* Memory error */ + GETDNS_FREE(*mf, conn); + return; + } + DEBUG_SERVER("[connection add] count: %d\n", + (int)l->set->connections_set.count); + if ((conn->super.next = l->connections)) + conn->super.next->prev_next = &conn->super.next; + conn->super.prev_next = &l->connections; l->connections = (connection *)conn; + (void) loop->vmt->schedule(loop, conn->fd, DOWNSTREAM_IDLE_TIMEOUT, &conn->event); @@ -485,7 +528,7 @@ static void udp_read_cb(void *userarg) if (l->fd == -1) return; - if (!(mf = priv_getdns_context_mf(l->set->context))) + if (!(mf = &l->set->context->mf)) return; if ((r = getdns_context_get_eventloop(l->set->context, &loop))) @@ -496,7 +539,7 @@ static void udp_read_cb(void *userarg) conn->l = l; conn->addrlen = sizeof(conn->remote_in); - if ((len = recvfrom(l->fd, buf, sizeof(buf), 0, + if ((len = recvfrom(l->fd, (void *)buf, sizeof(buf), 0, (struct sockaddr *)&conn->remote_in, &conn->addrlen)) == -1) { /* IO error, cleanup this listener. */ loop->vmt->clear(loop, &l->event); @@ -574,37 +617,33 @@ static void udp_read_cb(void *userarg) else { /* Insert connection */ + conn->super.key = conn; + if (!_getdns_rbtree_insert( + &l->set->connections_set, &conn->super)) { + /* Memory error */ + GETDNS_FREE(*mf, conn); + return; + } + DEBUG_SERVER("[connection add] count: %d\n", + (int)l->set->connections_set.count); if ((conn->next = l->connections)) conn->next->prev_next = &conn->next; conn->prev_next = &l->connections; l->connections = conn; + /* TODO: wish list item: + * (void) getdns_dict_set_int64( + * request_dict, "request_id", (intptr_t)conn); + */ /* Call request handler */ - l->set->handler(l->set->context, request_dict, (intptr_t)conn); + l->set->handler(l->set->context, GETDNS_CALLBACK_COMPLETE, + request_dict, l->set->userarg, (intptr_t)conn); + return; } GETDNS_FREE(*mf, conn); } -static void rm_listen_set(listen_set **root, listen_set *set) -{ - assert(root); - - while (*root && *root != set) - root = &(*root)->next; - - *root = set->next; - set->next = NULL; -} - -static listen_set *lookup_listen_set(listen_set *root, getdns_context *key) -{ - while (root && root->context != key) - root = root->next; - - return root; -} - static void free_listen_set_when_done(listen_set *set) { struct mem_funcs *mf; @@ -613,7 +652,7 @@ static void free_listen_set_when_done(listen_set *set) assert(set); assert(set->context); - if (!(mf = priv_getdns_context_mf(set->context))) + if (!(mf = &set->context->mf)) return; DEBUG_SERVER("To free listen set: %p\n", (void *)set); @@ -639,7 +678,7 @@ static void remove_listeners(listen_set *set) assert(set); assert(set->context); - if (!(mf = priv_getdns_context_mf(set->context))) + if (!(mf = &set->context->mf)) return; if (getdns_context_get_eventloop(set->context, &loop)) @@ -663,7 +702,8 @@ static void remove_listeners(listen_set *set) while (*conn_p) { tcp_connection_destroy(*conn_p); if (*conn_p && (*conn_p)->to_answer > 0) - conn_p = (tcp_connection **)&(*conn_p)->next; + conn_p = (tcp_connection **) + &(*conn_p)->super.next; } } free_listen_set_when_done(set); @@ -671,7 +711,11 @@ static void remove_listeners(listen_set *set) static getdns_return_t add_listeners(listen_set *set) { +#ifdef USE_WINSOCK + static const char enable = 1; +#else static const int enable = 1; +#endif struct mem_funcs *mf; getdns_eventloop *loop; @@ -681,7 +725,7 @@ static getdns_return_t add_listeners(listen_set *set) assert(set); assert(set->context); - if (!(mf = priv_getdns_context_mf(set->context))) + if (!(mf = &set->context->mf)) return GETDNS_RETURN_GENERIC_ERROR; if ((r = getdns_context_get_eventloop(set->context, &loop))) @@ -738,17 +782,21 @@ static getdns_return_t add_listeners(listen_set *set) return GETDNS_RETURN_GOOD; } -getdns_return_t getdns_context_set_listen_addresses(getdns_context *context, - getdns_request_handler_t request_handler, - const getdns_list *listen_addresses) +static int +ptr_cmp(const void *a, const void *b) +{ + return a == b ? 0 : (a < b ? -1 : 1); +} + +getdns_return_t getdns_context_set_listen_addresses( + getdns_context *context, const getdns_list *listen_addresses, + void *userarg, getdns_request_handler_t request_handler) { static const getdns_transport_list_t listen_transports[] = { GETDNS_TRANSPORT_UDP, GETDNS_TRANSPORT_TCP }; static const uint32_t transport_ports[] = { 53, 53 }; static const size_t n_transports = sizeof( listen_transports) / sizeof(*listen_transports); - static listen_set *root = NULL; - listen_set *current_set; listen_set *new_set; size_t new_set_count; @@ -763,7 +811,8 @@ getdns_return_t getdns_context_set_listen_addresses(getdns_context *context, DEBUG_SERVER("getdns_context_set_listen_addresses(%p, , %p)\n", (void *)context, (void *)listen_addresses); - if (!(mf = priv_getdns_context_mf(context))) + + if (!(mf = &context->mf)) return GETDNS_RETURN_GENERIC_ERROR; if ((r = getdns_context_get_eventloop(context, &loop))) @@ -775,7 +824,7 @@ getdns_return_t getdns_context_set_listen_addresses(getdns_context *context, else if ((r = getdns_list_get_length(listen_addresses, &new_set_count))) return r; - if ((current_set = lookup_listen_set(root, context))) { + if ((current_set = context->server)) { for (i = 0; i < current_set->count; i++) current_set->items[i].action = to_remove; } @@ -783,7 +832,7 @@ getdns_return_t getdns_context_set_listen_addresses(getdns_context *context, if (!current_set) return GETDNS_RETURN_GOOD; - rm_listen_set(&root, current_set); + context->server = NULL; /* action is already to_remove */ remove_listeners(current_set); return GETDNS_RETURN_GOOD; @@ -796,12 +845,14 @@ getdns_return_t getdns_context_set_listen_addresses(getdns_context *context, sizeof(listener) * new_set_count * n_transports))) return GETDNS_RETURN_MEMORY_ERROR; + _getdns_rbtree_init(&new_set->connections_set, ptr_cmp); + DEBUG_SERVER("New listen set: %p, current_set: %p\n", (void *)new_set, (void *)current_set); new_set->context = context; - new_set->next = root; new_set->handler = request_handler; + new_set->userarg = userarg; new_set->count = new_set_count * n_transports; (void) memset(new_set->items, 0, sizeof(listener) * new_set_count * n_transports); @@ -941,10 +992,10 @@ getdns_return_t getdns_context_set_listen_addresses(getdns_context *context, } } if (current_set) { - rm_listen_set(&root, current_set); + context->server = NULL; remove_listeners(current_set); /* Is already remove */ } - root = new_set; + context->server = new_set; return GETDNS_RETURN_GOOD; } diff --git a/src/test/getdns_context_config.h b/src/server.h similarity index 82% rename from src/test/getdns_context_config.h rename to src/server.h index 7252f367..f60f267e 100644 --- a/src/test/getdns_context_config.h +++ b/src/server.h @@ -1,3 +1,11 @@ +/** + * \file server.h + * @brief Functions for serving requests + * + * getdns_context_set_listen_addresses() and getdns_reply() are implemented + * here. + */ + /* * Copyright (c) 2013, NLNet Labs, Verisign, Inc. * All rights reserved. @@ -24,18 +32,9 @@ * (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 _GETDNS_SERVER_H_ +#define _GETDNS_SERVER_H_ -#include "getdns/getdns.h" - -#ifndef GETDNS_CONTEXT_CONFIG_H_ -#define GETDNS_CONTEXT_CONFIG_H_ - -/* -getdns_return_t getdns_context_config( - getdns_context *context, const getdns_dict *config_dict); - */ -getdns_return_t _getdns_context_config_(getdns_context *context, - getdns_dict *extensions, const getdns_dict *config_dict); - -#endif +struct listen_set; +#endif /* _GETDNS_SERVER_H_ */ diff --git a/src/stub.c b/src/stub.c index d4c24e93..14ff33b4 100644 --- a/src/stub.c +++ b/src/stub.c @@ -63,9 +63,11 @@ typedef u_short sa_family_t; * STUB_TCP_WOULDBLOCK added to deal with edge triggered event loops (versus * level triggered). See also lines containing WSA TODO below... */ +#define STUB_NO_AUTH -8 /* Existing TLS connection is not authenticated */ +#define STUB_CONN_GONE -7 /* Connection has failed, clear queue*/ #define STUB_TCP_WOULDBLOCK -6 #define STUB_OUT_OF_OPTIONS -5 /* upstream options exceeded MAXIMUM_UPSTREAM_OPTION_SPACE */ -#define STUB_TLS_SETUP_ERROR -4 +#define STUB_SETUP_ERROR -4 #define STUB_TCP_AGAIN -3 #define STUB_TCP_ERROR -2 @@ -85,6 +87,9 @@ 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 int upstream_working_ok(getdns_upstream *upstream); +static int upstream_auth_status_ok(getdns_upstream *upstream, + getdns_network_req *netreq); static int upstream_connect(getdns_upstream *upstream, getdns_transport_list_t transport, getdns_dns_req *dnsreq); @@ -246,8 +251,8 @@ match_edns_opt_rr(uint16_t code, uint8_t *response, size_t response_len, size_t data_len = rr_iter->nxt - rr_iter->pos; (void) gldns_wire2str_rr_scan( &data, &data_len, &str, &str_len, (uint8_t *)rr_iter->pkt, rr_iter->pkt_end - rr_iter->pkt); - DEBUG_STUB("%s %-35s: OPT RR: %s\n", - STUB_DEBUG_CLEANUP, __FUNC__, str_spc); + DEBUG_STUB("%s %-35s: OPT RR: %s", + STUB_DEBUG_READ, __FUNC__, str_spc); #endif /* OPT found, now search for the specified option */ @@ -321,25 +326,36 @@ process_keepalive( int found = match_edns_opt_rr(GLDNS_EDNS_KEEPALIVE, response, response_len, &position, &option_len); if (found != 2 || option_len != 2) { - if (netreq->keepalive_sent == 1) - /* If no keepalive sent back, then we must use 0 idle timeout - as server does not support it.*/ -#if defined(KEEP_CONNECTIONS_OPEN_DEBUG) && KEEP_CONNECTIONS_OPEN_DEBUG - upstream->keepalive_timeout = netreq->owner->context->idle_timeout; -#else - upstream->keepalive_timeout = 0; + if (netreq->keepalive_sent == 1) { + /* For TCP if no keepalive sent back, then we must use 0 idle timeout + as server does not support it. TLS allows idle connections without + keepalive, according to RFC7858. */ +#if !defined(KEEP_CONNECTIONS_OPEN_DEBUG) || !KEEP_CONNECTIONS_OPEN_DEBUG + if (upstream->transport != GETDNS_TRANSPORT_TLS) + upstream->keepalive_timeout = 0; + else #endif + upstream->keepalive_timeout = netreq->owner->context->idle_timeout; + } return; } /* Use server sent value unless the client specified a shorter one. Convert to ms first (wire value has units of 100ms) */ uint64_t server_keepalive = ((uint64_t)gldns_read_uint16(position))*100; + DEBUG_STUB("%s %-35s: FD: %d Server Keepalive recieved: %d ms\n", + STUB_DEBUG_READ, __FUNC__, upstream->fd, + (int)server_keepalive); if (netreq->owner->context->idle_timeout < server_keepalive) upstream->keepalive_timeout = netreq->owner->context->idle_timeout; else { + if (server_keepalive == 0) { + /* This means the server wants us to shut the connection (sending no + more queries). */ + upstream->keepalive_shutdown = 1; + } upstream->keepalive_timeout = server_keepalive; DEBUG_STUB("%s %-35s: FD: %d Server Keepalive used: %d ms\n", - STUB_DEBUG_CLEANUP, __FUNC__, upstream->fd, + STUB_DEBUG_READ, __FUNC__, upstream->fd, (int)server_keepalive); } } @@ -368,12 +384,15 @@ static int tcp_connect(getdns_upstream *upstream, getdns_transport_list_t transport) { int fd = -1; - DEBUG_STUB("%s %-35s: Creating TCP connection: %p\n", STUB_DEBUG_SETUP, - __FUNC__, (void *)upstream); + DEBUG_STUB("%s %-35s: Creating TCP connection: %p\n", STUB_DEBUG_SETUP, + __FUNC__, (void*)upstream); if ((fd = socket(upstream->addr.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) return -1; getdns_sock_nonblock(fd); + /* Note that error detection is different with TFO. Since the handshake + doesn't start till the sendto() lack of connection is often delayed until + then or even the subsequent event depending on the error and platform.*/ #ifdef USE_TCP_FASTOPEN /* Leave the connect to the later call to sendto() if using TCP*/ if (transport == GETDNS_TRANSPORT_TCP) @@ -414,29 +433,30 @@ tcp_connect(getdns_upstream *upstream, getdns_transport_list_t transport) static int tcp_connected(getdns_upstream *upstream) { - /* Already tried and failed, so let the fallback code take care of things */ - /* TODO: We _should_ use a timeout on the TCP handshake*/ - if (upstream->fd == -1 || upstream->tcp.write_error != 0) - return STUB_TCP_ERROR; - 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) - return STUB_TCP_WOULDBLOCK; + return STUB_TCP_AGAIN; else if (error == WSAEWOULDBLOCK) return STUB_TCP_WOULDBLOCK; else if (error != 0) - return STUB_TCP_ERROR; + return STUB_SETUP_ERROR; #else if (error == EINPROGRESS) - return STUB_TCP_WOULDBLOCK; + return STUB_TCP_AGAIN; else if (error == EWOULDBLOCK || error == EAGAIN) return STUB_TCP_WOULDBLOCK; - else if (error != 0) - return STUB_TCP_ERROR; + 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; + upstream->conn_completed++; + } return 0; } @@ -452,19 +472,16 @@ stub_next_upstream(getdns_network_req *netreq) if (! --netreq->upstream->to_retry) netreq->upstream->to_retry = -(netreq->upstream->back_off *= 2); - /*[TLS]:TODO - This works because the next message won't try the exact - * same upstream (and the next message may not use the same transport), - * but the next message will find the next matching one thanks to logic in - * upstream_select, but this could be better */ - if (++dnsreq->upstreams->current >= dnsreq->upstreams->count) - dnsreq->upstreams->current = 0; + dnsreq->upstreams->current_udp+=GETDNS_UPSTREAM_TRANSPORTS; + if (dnsreq->upstreams->current_udp >= dnsreq->upstreams->count) + dnsreq->upstreams->current_udp = 0; } static void stub_cleanup(getdns_network_req *netreq) { DEBUG_STUB("%s %-35s: MSG: %p\n", - STUB_DEBUG_CLEANUP, __FUNC__, (void *)netreq); + STUB_DEBUG_CLEANUP, __FUNC__, (void*)netreq); getdns_dns_req *dnsreq = netreq->owner; getdns_network_req *r, *prev_r; getdns_upstream *upstream; @@ -472,8 +489,6 @@ stub_cleanup(getdns_network_req *netreq) GETDNS_CLEAR_EVENT(dnsreq->loop, &netreq->event); - GETDNS_NULL_FREE(dnsreq->context->mf, netreq->tcp.read_buf); - /* Nothing globally scheduled? Then nothing queued */ if (!(upstream = netreq->upstream)->event.ev) return; @@ -502,51 +517,52 @@ stub_cleanup(getdns_network_req *netreq) upstream_reschedule_events(upstream, upstream->keepalive_timeout); } -static int -tls_cleanup(getdns_upstream *upstream, int handshake_fail) -{ - DEBUG_STUB("%s %-35s: FD: %d\n", - STUB_DEBUG_CLEANUP, __FUNC__, upstream->fd); - if (upstream->tls_obj != NULL) - SSL_free(upstream->tls_obj); - upstream->tls_obj = NULL; - /* This will prevent the connection from being tried again for the cases - where we know it didn't work. Otherwise leave it to try again.*/ - if (handshake_fail) - upstream->tls_hs_state = GETDNS_HS_FAILED; - /* Reset timeout on failure*/ - GETDNS_CLEAR_EVENT(upstream->loop, &upstream->event); - GETDNS_SCHEDULE_EVENT(upstream->loop, upstream->fd, TIMEOUT_FOREVER, - getdns_eventloop_event_init(&upstream->event, upstream, - NULL, upstream_write_cb, NULL)); - return STUB_TLS_SETUP_ERROR; -} - static void -upstream_erred(getdns_upstream *upstream) +upstream_failed(getdns_upstream *upstream, int during_setup) { - DEBUG_STUB("%s %-35s: FD: %d\n", - STUB_DEBUG_CLEANUP, __FUNC__, upstream->fd); - getdns_network_req *netreq; - - while ((netreq = upstream->write_queue)) { - stub_cleanup(netreq); - netreq->state = NET_REQ_FINISHED; - _getdns_check_dns_req_complete(netreq->owner); + DEBUG_STUB("%s %-35s: FD: %d During setup = %d\n", + STUB_DEBUG_CLEANUP, __FUNC__, upstream->fd, during_setup); + /* Fallback code should take care of queue queries and then close conn + when idle.*/ + /* [TLS1]TODO: Work out how to re-open the connection and re-try + the queries if there is only one upstream.*/ + 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 && + upstream->tls_auth_state == GETDNS_AUTH_FAILED)) + upstream->conn_setup_failed++; + } else { + upstream->conn_shutdowns++; + /* [TLS1]TODO: Re-try these queries if possible.*/ + getdns_network_req *netreq; + while (upstream->netreq_by_query_id.count) { + netreq = (getdns_network_req *) + _getdns_rbtree_first(&upstream->netreq_by_query_id); + stub_cleanup(netreq); + netreq->state = NET_REQ_FINISHED; + _getdns_check_dns_req_complete(netreq->owner); + } } - while (upstream->netreq_by_query_id.count) { - netreq = (getdns_network_req *) - _getdns_rbtree_first(&upstream->netreq_by_query_id); - stub_cleanup(netreq); - netreq->state = NET_REQ_FINISHED; - _getdns_check_dns_req_complete(netreq->owner); - } - _getdns_upstream_shutdown(upstream); + + upstream->conn_state = GETDNS_CONN_TEARDOWN; } void _getdns_cancel_stub_request(getdns_network_req *netreq) { + DEBUG_STUB("%s %-35s: MSG: %p\n", + STUB_DEBUG_CLEANUP, __FUNC__, (void*)netreq); stub_cleanup(netreq); if (netreq->fd >= 0) { #ifdef USE_WINSOCK @@ -557,41 +573,40 @@ _getdns_cancel_stub_request(getdns_network_req *netreq) } } -/* May be needed in future for better UDP error handling?*/ -/*static void -stub_erred(getdns_network_req *netreq) -{ - DEBUG_STUB("*** %s\n", __FUNC__); - stub_next_upstream(netreq); - stub_cleanup(netreq); - if (netreq->fd >= 0) close(netreq->fd); - netreq->state = NET_REQ_FINISHED; - _getdns_check_dns_req_complete(netreq->owner); -}*/ - static void stub_timeout_cb(void *userarg) { getdns_network_req *netreq = (getdns_network_req *)userarg; DEBUG_STUB("%s %-35s: MSG: %p\n", - STUB_DEBUG_CLEANUP, __FUNC__, (void *)netreq); - stub_next_upstream(netreq); + STUB_DEBUG_CLEANUP, __FUNC__, (void*)netreq); stub_cleanup(netreq); - if (netreq->fd >= 0) + netreq->state = NET_REQ_TIMED_OUT; + /* Handle upstream*/ + if (netreq->fd >= 0) { #ifdef USE_WINSOCK closesocket(netreq->fd); #else close(netreq->fd); #endif - netreq->state = NET_REQ_TIMED_OUT; - if (netreq->owner->user_callback) { + netreq->upstream->udp_timeouts++; +#if defined(DAEMON_DEBUG) && DAEMON_DEBUG + if (netreq->upstream->udp_timeouts % 100 == 0) + DEBUG_DAEMON("%s %s : Upstream stats: Transport=UDP - Resp=%d,Timeouts=%d\n", + STUB_DEBUG_DAEMON, netreq->upstream->addr_str, + (int)netreq->upstream->udp_responses, (int)netreq->upstream->udp_timeouts); +#endif + stub_next_upstream(netreq); + } else { + netreq->upstream->responses_timeouts++; + } + if (netreq->owner->user_callback) { netreq->debug_end_time = _getdns_get_time_as_uintt64(); + /* Note this calls cancel_request which calls stub_cleanup again....!*/ (void) _getdns_context_request_timed_out(netreq->owner); } else _getdns_check_dns_req_complete(netreq->owner); } - static void upstream_idle_timeout_cb(void *userarg) { @@ -606,13 +621,13 @@ upstream_idle_timeout_cb(void *userarg) } static void -upstream_tls_timeout_cb(void *userarg) +upstream_setup_timeout_cb(void *userarg) { getdns_upstream *upstream = (getdns_upstream *)userarg; 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 */ - tls_cleanup(upstream, 1); + 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). @@ -627,38 +642,13 @@ upstream_tls_timeout_cb(void *userarg) tval.tv_usec = 0; ret = select(upstream->fd+1, NULL, &fds, NULL, &tval); 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); } } -static void -stub_tls_timeout_cb(void *userarg) -{ - getdns_network_req *netreq = (getdns_network_req *)userarg; - getdns_upstream *upstream = netreq->upstream; - DEBUG_STUB("%s %-35s: MSG: %p\n", - STUB_DEBUG_CLEANUP, __FUNC__, (void *)netreq); - /* Clean up and trigger a write to let the fallback code to its job */ - tls_cleanup(upstream, 0); - - /* 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.*/ - int ret; - fd_set fds; - FD_ZERO(&fds); - FD_SET(FD_SET_T upstream->fd, &fds); - struct timeval tval; - tval.tv_sec = 0; - tval.tv_usec = 0; - ret = select(upstream->fd+1, NULL, &fds, NULL, &tval); - if (ret == 0) { - while (upstream->write_queue) - upstream_write_cb(upstream); - } -} /****************************/ /* TCP read/write functions */ @@ -769,7 +759,7 @@ stub_tcp_write(int fd, getdns_tcp_state *tcp, getdns_network_req *netreq) if (netreq->owner->edns_client_subnet_private) if (attach_edns_client_subnet_private(netreq)) return STUB_OUT_OF_OPTIONS; - if (netreq->upstream->writes_done == 0 && + if (netreq->upstream->queries_sent == 0 && netreq->owner->context->idle_timeout != 0) { /* Add the keepalive option to the first query on this connection*/ DEBUG_STUB("%s %-35s: FD: %d Requesting keepalive \n", @@ -863,72 +853,71 @@ tls_requested(getdns_network_req *netreq) 1 : 0; } -static int -tls_should_write(getdns_upstream *upstream) -{ - /* Should messages be written on TLS upstream. */ - return ((upstream->transport == GETDNS_TRANSPORT_TLS) && - upstream->tls_hs_state != GETDNS_HS_NONE) ? 1 : 0; -} - -static int -tls_should_read(getdns_upstream *upstream) -{ - return ((upstream->transport == GETDNS_TRANSPORT_TLS) && - !(upstream->tls_hs_state == GETDNS_HS_FAILED || - upstream->tls_hs_state == GETDNS_HS_NONE)) ? 1 : 0; -} - -static int -tls_failed(getdns_upstream *upstream) -{ - /* No messages should be scheduled onto an upstream in this state */ - return ((upstream->transport == GETDNS_TRANSPORT_TLS) && - upstream->tls_hs_state == GETDNS_HS_FAILED) ? 1 : 0; -} - -static int -tls_auth_status_ok(getdns_upstream *upstream, getdns_network_req *netreq) { - return (netreq->tls_auth_min == GETDNS_AUTHENTICATION_REQUIRED && - upstream->tls_auth_failed) ? 0 : 1; -} - int tls_verify_callback(int preverify_ok, X509_STORE_CTX *ctx) { getdns_upstream *upstream; getdns_return_t pinset_ret = GETDNS_RETURN_GOOD; upstream = _getdns_upstream_from_x509_store(ctx); + if (!upstream) + return 0; -#if defined(STUB_DEBUG) && STUB_DEBUG || defined(X509_V_ERR_HOSTNAME_MISMATCH) - int err = X509_STORE_CTX_get_error(ctx); - + int err = X509_STORE_CTX_get_error(ctx); +#if defined(STUB_DEBUG) && STUB_DEBUG DEBUG_STUB("%s %-35s: FD: %d Verify result: (%d) \"%s\"\n", STUB_DEBUG_SETUP_TLS, __FUNC__, upstream->fd, err, X509_verify_cert_error_string(err)); #endif + /* First deal with the hostname authentication done by OpenSSL. */ #ifdef X509_V_ERR_HOSTNAME_MISMATCH /*Report if error is hostname mismatch*/ - if (upstream && upstream->tls_fallback_ok && err == X509_V_ERR_HOSTNAME_MISMATCH) + if (err == X509_V_ERR_HOSTNAME_MISMATCH && upstream->tls_fallback_ok) DEBUG_STUB("%s %-35s: FD: %d WARNING: Proceeding even though hostname validation failed!\n", STUB_DEBUG_SETUP_TLS, __FUNC__, upstream->fd); +#else + /* if we weren't built against OpenSSL with hostname matching we + * could not have matched the hostname, so this would be an automatic + * tls_auth_fail if there is a hostname provided*/ + if (upstream->tls_auth_name[0]) { + upstream->tls_auth_state = GETDNS_AUTH_FAILED; + preverify_ok = 0; + } #endif - if (upstream && upstream->tls_pubkey_pinset) + + /* Now deal with the pinset validation*/ + if (upstream->tls_pubkey_pinset) pinset_ret = _getdns_verify_pinset_match(upstream->tls_pubkey_pinset, ctx); if (pinset_ret != GETDNS_RETURN_GOOD) { DEBUG_STUB("%s %-35s: FD: %d, WARNING: Pinset validation failure!\n", STUB_DEBUG_SETUP_TLS, __FUNC__, upstream->fd); preverify_ok = 0; - upstream->tls_auth_failed = 1; + upstream->tls_auth_state = GETDNS_AUTH_FAILED; if (upstream->tls_fallback_ok) DEBUG_STUB("%s %-35s: FD: %d, WARNING: Proceeding even though pinset validation failed!\n", STUB_DEBUG_SETUP_TLS, __FUNC__, upstream->fd); + } else { + /* If we _only_ had a pinset and it is good then force succesful + authentication when the cert self-signed */ + if ((upstream->tls_pubkey_pinset && upstream->tls_auth_name[0] == '\0') && + (err == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN || + err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT)) { + preverify_ok = 1; + DEBUG_STUB("%s %-35s: FD: %d, Allowing self-signed (%d) cert since pins match\n", + STUB_DEBUG_SETUP_TLS, __FUNC__, upstream->fd, err); + } } + + /* If nothing has failed yet and we had credentials, we have succesfully authenticated*/ + if (preverify_ok == 0) + upstream->tls_auth_state = GETDNS_AUTH_FAILED; + else if (upstream->tls_auth_state == GETDNS_AUTH_NONE && + (upstream->tls_pubkey_pinset || upstream->tls_auth_name[0])) + upstream->tls_auth_state = GETDNS_AUTH_OK; /* If fallback is allowed, proceed regardless of what the auth error is (might not be hostname or pinset related) */ - return (upstream && upstream->tls_fallback_ok) ? 1 : preverify_ok; + return (upstream->tls_fallback_ok) ? 1 : preverify_ok; } static SSL* @@ -971,11 +960,9 @@ 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) { - /* TODO: Trigger post-handshake custom validation*/ DEBUG_STUB("%s %-35s: ERROR: TLS Authentication functionality not available\n", STUB_DEBUG_SETUP_TLS, __FUNC__); upstream->tls_hs_state = GETDNS_HS_FAILED; - upstream->tls_auth_failed = 1; return NULL; } #endif @@ -993,14 +980,12 @@ tls_create_object(getdns_dns_req *dnsreq, int fd, getdns_upstream *upstream) DEBUG_STUB("%s %-35s: ERROR: No host name or pubkey pinset provided for TLS authentication\n", STUB_DEBUG_SETUP_TLS, __FUNC__); upstream->tls_hs_state = GETDNS_HS_FAILED; - upstream->tls_auth_failed = 1; return NULL; } } else { /* no hostname verification, so we will make opportunistic connections */ DEBUG_STUB("%s %-35s: Proceeding even though no hostname provided!\n", STUB_DEBUG_SETUP_TLS, __FUNC__); - upstream->tls_auth_failed = 1; upstream->tls_fallback_ok = 1; } } @@ -1015,6 +1000,20 @@ tls_create_object(getdns_dns_req *dnsreq, int fd, getdns_upstream *upstream) SSL_set_connect_state(ssl); (void) SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); + + /* Session resumption. There are trade-offs here. Want to do it when + possible only if we have the right type of connection. Note a change + to the upstream auth info creates a new upstream so never re-uses.*/ + if (upstream->tls_session != NULL) { + if ((upstream->tls_fallback_ok == 0 && + upstream->last_tls_auth_state == GETDNS_AUTH_OK) || + upstream->tls_fallback_ok == 1) { + SSL_set_session(ssl, upstream->tls_session); + DEBUG_STUB("%s %-35s: Attempting session re-use\n", STUB_DEBUG_SETUP_TLS, + __FUNC__); + } + } + return ssl; } @@ -1050,24 +1049,18 @@ tls_do_handshake(getdns_upstream *upstream) DEBUG_STUB("%s %-35s: FD: %d Handshake failed %d\n", STUB_DEBUG_SETUP_TLS, __FUNC__, upstream->fd, want); - return tls_cleanup(upstream, 1); + return STUB_SETUP_ERROR; } } upstream->tls_hs_state = GETDNS_HS_DONE; - DEBUG_STUB("%s %-35s: FD: %d Handshake succeeded\n", - STUB_DEBUG_SETUP_TLS, __FUNC__, upstream->fd); - r = SSL_get_verify_result(upstream->tls_obj); - if (upstream->tls_auth_name[0]) -#ifdef X509_V_ERR_HOSTNAME_MISMATCH - if (r == X509_V_ERR_HOSTNAME_MISMATCH) -#else - /* if we weren't built against OpenSSL with hostname matching we - * could not have matched the hostname, so this would be an automatic - * tls_auth_fail. */ -#endif - upstream->tls_auth_failed = 1; - DEBUG_STUB("%s %-35s: FD: %d Session is %s\n", - STUB_DEBUG_SETUP_TLS, __FUNC__, upstream->fd, + upstream->conn_state = GETDNS_CONN_OPEN; + upstream->conn_completed++; + /* A re-used session is not verified so need to fix up state in that case */ + if (SSL_session_reused(upstream->tls_obj)) + upstream->tls_auth_state = upstream->last_tls_auth_state; + DEBUG_STUB("%s %-35s: FD: %d Handshake succeeded with auth state %s. Session is %s.\n", + STUB_DEBUG_SETUP_TLS, __FUNC__, upstream->fd, + _getdns_auth_str(upstream->tls_auth_state), SSL_session_reused(upstream->tls_obj) ?"re-used":"new"); if (upstream->tls_session != NULL) SSL_SESSION_free(upstream->tls_session); @@ -1086,21 +1079,17 @@ static int tls_connected(getdns_upstream* upstream) { /* Already have a TLS connection*/ - if (upstream->tls_hs_state == GETDNS_HS_DONE && - (upstream->tls_obj != NULL)) + if (upstream->tls_hs_state == GETDNS_HS_DONE) return 0; /* Already tried and failed, so let the fallback code take care of things */ if (upstream->tls_hs_state == GETDNS_HS_FAILED) - return STUB_TLS_SETUP_ERROR; + return STUB_SETUP_ERROR; - /* Lets make sure the connection is up before we try a handshake*/ + /* Lets make sure the TCP connection is up before we try a handshake*/ int q = tcp_connected(upstream); - if (q != 0) { - if (q == STUB_TCP_ERROR) - tls_cleanup(upstream, 0); + if (q != 0) return q; - } return tls_do_handshake(upstream); } @@ -1205,8 +1194,12 @@ stub_tls_write(getdns_upstream *upstream, getdns_tcp_state *tcp, int q = tls_connected(upstream); if (q != 0) return q; - if (!tls_auth_status_ok(upstream, netreq)) - return STUB_TLS_SETUP_ERROR; + /* This is the case where the upstream is connected but it isn't an authenticated + connection, but the request needs an authenticated connection. For now, we + fail the write as a special case, since other oppotunistic requests can still use + this upstream. but this needs more thought: Should we open a second connection? */ + if (!upstream_auth_status_ok(upstream, netreq)) + return STUB_NO_AUTH; /* Do we have remaining data that we could not write before? */ if (! tcp->write_buf) { @@ -1237,7 +1230,7 @@ stub_tls_write(getdns_upstream *upstream, getdns_tcp_state *tcp, if (netreq->owner->edns_client_subnet_private) if (attach_edns_client_subnet_private(netreq)) return STUB_OUT_OF_OPTIONS; - if (netreq->upstream->writes_done % EDNS_KEEPALIVE_RESEND == 0 && + if (netreq->upstream->queries_sent % EDNS_KEEPALIVE_RESEND == 0 && netreq->owner->context->idle_timeout != 0) { /* Add the keepalive option to every nth query on this connection */ @@ -1304,7 +1297,7 @@ stub_udp_read_cb(void *userarg) getdns_upstream *upstream = netreq->upstream; ssize_t read; DEBUG_STUB("%s %-35s: MSG: %p \n", STUB_DEBUG_READ, - __FUNC__, (void *)netreq); + __FUNC__, (void*)netreq); GETDNS_CLEAR_EVENT(dnsreq->loop, &netreq->event); @@ -1333,10 +1326,11 @@ stub_udp_read_cb(void *userarg) closesocket(netreq->fd); #else close(netreq->fd); + netreq->fd = -1; #endif while (GLDNS_TC_WIRE(netreq->response)) { DEBUG_STUB("%s %-35s: MSG: %p TC bit set in response \n", STUB_DEBUG_READ, - __FUNC__, (void *)netreq); + __FUNC__, (void*)netreq); if (!(netreq->transport_current < netreq->transport_count)) break; getdns_transport_list_t next_transport = @@ -1357,9 +1351,17 @@ stub_udp_read_cb(void *userarg) return; } netreq->response_len = read; - dnsreq->upstreams->current = 0; + dnsreq->upstreams->current_udp = 0; netreq->debug_end_time = _getdns_get_time_as_uintt64(); netreq->state = NET_REQ_FINISHED; + upstream->udp_responses++; +#if defined(DAEMON_DEBUG) && DAEMON_DEBUG + if (upstream->udp_responses == 1 || + upstream->udp_responses % 100 == 0) + DEBUG_DAEMON("%s %s : Upstream stats: Transport=UDP - Resp=%d,Timeouts=%d\n", + STUB_DEBUG_DAEMON, upstream->addr_str, + (int)upstream->udp_responses, (int)upstream->udp_timeouts); +#endif _getdns_check_dns_req_complete(dnsreq); } @@ -1444,7 +1446,7 @@ upstream_read_cb(void *userarg) intptr_t query_id_intptr; getdns_dns_req *dnsreq; - if (tls_should_read(upstream)) + if (upstream->transport == GETDNS_TRANSPORT_TLS) q = stub_tls_read(upstream, &upstream->tcp, &upstream->upstreams->mf); else @@ -1457,9 +1459,9 @@ upstream_read_cb(void *userarg) */ case STUB_TCP_WOULDBLOCK: return; - + case STUB_SETUP_ERROR: /* Can happen for TLS HS*/ case STUB_TCP_ERROR: - upstream_erred(upstream); + upstream_failed(upstream, (q == STUB_TCP_ERROR ? 0:1) ); return; default: @@ -1477,22 +1479,19 @@ upstream_read_cb(void *userarg) } DEBUG_STUB("%s %-35s: MSG: %p (read)\n", - STUB_DEBUG_READ, __FUNC__, (void *)netreq); + STUB_DEBUG_READ, __FUNC__, (void*)netreq); netreq->state = NET_REQ_FINISHED; netreq->response = upstream->tcp.read_buf; netreq->response_len = upstream->tcp.read_pos - upstream->tcp.read_buf; upstream->tcp.read_buf = NULL; upstream->responses_received++; - /* TODO[TLS]: I don't think we should do this for TCP. We should stay - * on a working connection until we hit a problem.*/ - upstream->upstreams->current = 0; /* !THIS CODE NEEDS TESTING! */ if (netreq->owner->edns_cookies && match_and_process_server_cookie( - netreq->upstream, netreq->tcp.read_buf, - netreq->tcp.read_pos - netreq->tcp.read_buf)) + netreq->upstream, upstream->tcp.read_buf, + upstream->tcp.read_pos - upstream->tcp.read_buf)) return; /* Client cookie didn't match (or FORMERR) */ if (netreq->owner->context->idle_timeout != 0) @@ -1552,18 +1551,24 @@ upstream_write_cb(void *userarg) getdns_upstream *upstream = (getdns_upstream *)userarg; getdns_network_req *netreq = upstream->write_queue; int q; - + if (!netreq) { GETDNS_CLEAR_EVENT(upstream->loop, &upstream->event); upstream->event.write_cb = NULL; return; } - /* TODO: think about TCP AGAIN */ + netreq->debug_start_time = _getdns_get_time_as_uintt64(); DEBUG_STUB("%s %-35s: MSG: %p (writing)\n", STUB_DEBUG_WRITE, - __FUNC__, (void *)netreq); + __FUNC__, (void*)netreq); - if (tls_requested(netreq) && tls_should_write(upstream)) + /* Health checks on current connection */ + if (upstream->conn_state == GETDNS_CONN_TEARDOWN) + q = STUB_CONN_GONE; + else if (!upstream_working_ok(upstream)) + q = STUB_TCP_ERROR; + /* Seems ok, now try to write */ + else if (tls_requested(netreq)) q = stub_tls_write(upstream, &upstream->tcp, netreq); else q = stub_tcp_write(upstream->fd, &upstream->tcp, netreq); @@ -1572,32 +1577,37 @@ upstream_write_cb(void *userarg) case STUB_TCP_AGAIN: /* WSA TODO: if callback is still upstream_write_cb, do it again */ - case STUB_TCP_WOULDBLOCK: return; - case STUB_TCP_ERROR: - /* Problem with the TCP connection itself. Need to fallback.*/ - DEBUG_STUB("%s %-35s: MSG: %p ERROR!\n", STUB_DEBUG_WRITE, - __FUNC__, userarg); - upstream->tcp.write_error = 1; - /* Use policy of trying next upstream in this case. Need more work on - * TCP connection re-use.*/ - stub_next_upstream(netreq); + /* New problem with the TCP connection itself. Need to fallback.*/ /* Fall through */ - case STUB_TLS_SETUP_ERROR: - /* Could not complete the TLS set up. Need to fallback.*/ + case STUB_SETUP_ERROR: + /* 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); + upstream_failed(upstream, (q == STUB_TCP_ERROR ? 0:1)); + /* Fall through */ + case STUB_CONN_GONE: + case STUB_NO_AUTH: + /* Cleaning up after connection or auth check failure. Need to fallback. */ stub_cleanup(netreq); +#if defined(DAEMON_DEBUG) && DAEMON_DEBUG + DEBUG_DAEMON("%s %s : Conn closed : Transport=%s - *Failure*\n", + STUB_DEBUG_DAEMON, upstream->addr_str, + (upstream->transport == GETDNS_TRANSPORT_TLS ? "TLS" : "TCP")); +#endif if (fallback_on_write(netreq) == STUB_TCP_ERROR) { + /* TODO: Need new state to report transport unavailable*/ netreq->state = NET_REQ_FINISHED; _getdns_check_dns_req_complete(netreq->owner); } return; default: - /* Need this because auth status is reset on connection clode */ - netreq->debug_tls_auth_status = netreq->upstream->tls_auth_failed; - upstream->writes_done++; + /* Need this because auth status is reset on connection close */ + netreq->debug_tls_auth_status = netreq->upstream->tls_auth_state; + upstream->queries_sent++; netreq->query_id = (uint16_t) q; /* Unqueue the netreq from the write_queue */ if (!(upstream->write_queue = netreq->write_queue_tail)) { @@ -1630,87 +1640,167 @@ upstream_write_cb(void *userarg) /*****************************/ static int -upstream_transport_valid(getdns_upstream *upstream, +upstream_working_ok(getdns_upstream *upstream) +{ + /* [TLS1]TODO: This arbitrary logic at the moment - review and improve!*/ + return (upstream->responses_timeouts > + upstream->responses_received*GETDNS_CONN_ATTEMPTS ? 0 : 1); +} + +static int +upstream_active(getdns_upstream *upstream) +{ + if ((upstream->conn_state == GETDNS_CONN_SETUP || + upstream->conn_state == GETDNS_CONN_OPEN) && + upstream->keepalive_shutdown == 0) + return 1; + return 0; +} + +static int +upstream_auth_status_ok(getdns_upstream *upstream, getdns_network_req *netreq) { + if (netreq->tls_auth_min != GETDNS_AUTHENTICATION_REQUIRED) + return 1; + return (upstream->tls_auth_state == GETDNS_AUTH_OK ? 1 : 0); +} + +static int +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); +} + +static int +upstream_valid(getdns_upstream *upstream, + getdns_transport_list_t transport, + getdns_network_req *netreq) +{ + if (upstream->transport != transport || upstream->conn_state != GETDNS_CONN_CLOSED) + return 0; + if (transport == GETDNS_TRANSPORT_TCP) + return 1; + /* We need to check past authentication history to see if this is usable for TLS.*/ + if (netreq->tls_auth_min != GETDNS_AUTHENTICATION_REQUIRED) + return 1; + return ((upstream->best_tls_auth_state == GETDNS_AUTH_OK || + upstream->best_tls_auth_state == GETDNS_AUTH_NONE) ? 1 : 0); +} + +static int +upstream_valid_and_open(getdns_upstream *upstream, getdns_transport_list_t transport, getdns_network_req *netreq) { - /* Single shot UDP, uses same upstream as plain TCP. */ - if (transport == GETDNS_TRANSPORT_UDP) - return (upstream->transport == GETDNS_TRANSPORT_TCP ? 1:0); - /* If we got an error and have never managed to write to this TCP then - treat it as a hard failure */ - if (transport == GETDNS_TRANSPORT_TCP && - upstream->transport == GETDNS_TRANSPORT_TCP && - upstream->tcp.write_error != 0) { + if (!(upstream->transport == transport && upstream_active(upstream))) return 0; - } - /* Otherwise, transport must match, and not have failed */ - if (upstream->transport != transport) + if (transport == GETDNS_TRANSPORT_TCP) + return 1; + /* Connection is complete, we know the auth status so check*/ + if (upstream->conn_state == GETDNS_CONN_OPEN && + !upstream_auth_status_ok(upstream, netreq)) return 0; - if (tls_failed(upstream) || !tls_auth_status_ok(upstream, netreq)) - return 0; - return 1; + /* We must have a TLS connection still setting up so schedule and the + write code will check again once the connection is complete*/ + return 1; } static getdns_upstream * -upstream_select(getdns_network_req *netreq, getdns_transport_list_t transport) +upstream_select_stateful(getdns_network_req *netreq, getdns_transport_list_t transport) +{ + getdns_upstream *upstream = NULL; + getdns_upstreams *upstreams = netreq->owner->upstreams; + size_t i; + time_t now = time(NULL); + + if (!upstreams->count) + return NULL; + + /* A check to re-instate backed-off upstreams after X amount of time*/ + for (i = 0; i < upstreams->count; i++) { + if (upstreams->upstreams[i].conn_state == GETDNS_CONN_BACKOFF && + upstreams->upstreams[i].conn_retry_time < now) { + upstreams->upstreams[i].conn_state = GETDNS_CONN_CLOSED; +#if defined(DAEMON_DEBUG) && DAEMON_DEBUG + DEBUG_DAEMON("%s %s : Re-instating upstream\n", + STUB_DEBUG_DAEMON, upstreams->upstreams[i].addr_str); +#endif + } + } + + /* First find if an open upstream has the correct properties and use that*/ + for (i = 0; i < upstreams->count; i++) { + if (upstream_valid_and_open(&upstreams->upstreams[i], transport, netreq)) + return &upstreams->upstreams[i]; + } + + /* OK - we will have to open one. Choose the first one that has the best stats + and the right properties, but 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*/ + for (i = 0; i < upstreams->count; i++) { + 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)) { + upstream = &upstreams->upstreams[i]; + break; + } + } + if (!upstream) + return NULL; + for (i++; i < upstreams->count; i++) { + if (upstream_valid(&upstreams->upstreams[i], transport, netreq) && + upstream_stats(&upstreams->upstreams[i]) > upstream_stats(upstream)) + upstream = &upstreams->upstreams[i]; + } + return upstream; +} + +static getdns_upstream * +upstream_select(getdns_network_req *netreq) { getdns_upstream *upstream; getdns_upstreams *upstreams = netreq->owner->upstreams; size_t i; - + if (!upstreams->count) return NULL; - - - /* Only do this when a new message is scheduled?*/ - for (i = 0; i < upstreams->count; i++) + /* First UPD/TCP upstream is always at i=0 and then start of each upstream block*/ + /* TODO: Have direct access to sets of upstreams for different transports*/ + for (i = 0; i < upstreams->count; i+=GETDNS_UPSTREAM_TRANSPORTS) if (upstreams->upstreams[i].to_retry <= 0) upstreams->upstreams[i].to_retry++; - /* TODO[TLS]: Should we create a tmp array of upstreams with correct*/ - /* transport type and/or maintain separate current for transports?*/ - i = upstreams->current; - DEBUG_STUB("%s %-35s: Starting from upstream: %d of %d available \n", STUB_DEBUG_SETUP, - __FUNC__, (int)i, (int)upstreams->count); + i = upstreams->current_udp; do { - if (upstreams->upstreams[i].to_retry > 0 && - upstream_transport_valid(&upstreams->upstreams[i], transport, netreq)) { - upstreams->current = i; - DEBUG_STUB("%s %-35s: Selected upstream: %d %p transport: %d\n", - STUB_DEBUG_SETUP, __FUNC__, (int)i, - (void *)&upstreams->upstreams[i], transport); + if (upstreams->upstreams[i].to_retry > 0) { + upstreams->current_udp = i; return &upstreams->upstreams[i]; } - if (++i >= upstreams->count) + i+=GETDNS_UPSTREAM_TRANSPORTS; + if (i > upstreams->count) i = 0; - } while (i != upstreams->current); + } while (i != upstreams->current_udp); upstream = upstreams->upstreams; - for (i = 0; i < upstreams->count; i++) - if (upstreams->upstreams[i].back_off < upstream->back_off && - upstream_transport_valid(&upstreams->upstreams[i], transport, netreq)) + for (i = 0; i < upstreams->count; i+=GETDNS_UPSTREAM_TRANSPORTS) + if (upstreams->upstreams[i].back_off < + upstream->back_off) upstream = &upstreams->upstreams[i]; - /* Need to check again that the transport is valid */ - if (!upstream_transport_valid(upstream, transport, netreq)) { - DEBUG_STUB("%s %-35s: No valid upstream available for transport %d!\n", - STUB_DEBUG_SETUP, __FUNC__, transport); - return NULL; - } upstream->back_off++; upstream->to_retry = 1; - upstreams->current = upstream - upstreams->upstreams; + upstreams->current_udp = (upstream - upstreams->upstreams) / GETDNS_UPSTREAM_TRANSPORTS; return upstream; } - int upstream_connect(getdns_upstream *upstream, getdns_transport_list_t transport, getdns_dns_req *dnsreq) { - DEBUG_STUB("%s %-35s: Checking upstream connection: %p\n", STUB_DEBUG_SETUP, - __FUNC__, (void *)upstream); + DEBUG_STUB("%s %-35s: Getting upstream connection: %p\n", STUB_DEBUG_SETUP, + __FUNC__, (void*)upstream); int fd = -1; switch(transport) { case GETDNS_TRANSPORT_UDP: @@ -1718,40 +1808,40 @@ upstream_connect(getdns_upstream *upstream, getdns_transport_list_t transport, upstream->addr.ss_family, SOCK_DGRAM, IPPROTO_UDP)) == -1) return -1; getdns_sock_nonblock(fd); - return fd; + break; case GETDNS_TRANSPORT_TCP: + case GETDNS_TRANSPORT_TLS: /* Use existing if available*/ if (upstream->fd != -1) return upstream->fd; fd = tcp_connect(upstream, transport); - upstream->loop = dnsreq->loop; - upstream->is_sync_loop = dnsreq->is_sync_request; - upstream->fd = fd; - break; - - case GETDNS_TRANSPORT_TLS: - /* Use existing if available*/ - if (upstream->fd != -1 && !tls_failed(upstream)) - return upstream->fd; - fd = tcp_connect(upstream, transport); - if (fd == -1) return -1; - upstream->tls_obj = tls_create_object(dnsreq, fd, upstream); - if (upstream->tls_obj == NULL) { -#ifdef USE_WINSOCK - closesocket(fd); -#else - close(fd); -#endif + if (fd == -1) { + upstream_failed(upstream, 1); return -1; } - - if (upstream->tls_session != NULL) - SSL_set_session(upstream->tls_obj, upstream->tls_session); - upstream->tls_hs_state = GETDNS_HS_WRITE; upstream->loop = dnsreq->loop; upstream->is_sync_loop = dnsreq->is_sync_request; upstream->fd = fd; + if (transport == GETDNS_TRANSPORT_TLS) { + 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 + return -1; + } + upstream->tls_hs_state = GETDNS_HS_WRITE; + } + upstream->conn_state = GETDNS_CONN_SETUP; +#if defined(DAEMON_DEBUG) && DAEMON_DEBUG + DEBUG_DAEMON("%s %s : Conn init : Transport=%s - Profile=%s\n", STUB_DEBUG_DAEMON, + upstream->addr_str, transport == GETDNS_TRANSPORT_TLS ? "TLS":"TCP", + dnsreq->context->tls_auth_min == GETDNS_AUTHENTICATION_NONE ? "Opportunistic":"Strict"); +#endif break; default: return -1; @@ -1762,16 +1852,31 @@ upstream_connect(getdns_upstream *upstream, getdns_transport_list_t transport, static getdns_upstream* upstream_find_for_transport(getdns_network_req *netreq, - getdns_transport_list_t transport, - int *fd) + getdns_transport_list_t transport, + int *fd) { - // TODO[TLS]: Need to loop over upstreams here!! - getdns_upstream *upstream = upstream_select(netreq, transport); - if (!upstream) - return NULL; - *fd = upstream_connect(upstream, transport, netreq->owner); - DEBUG_STUB("%s %-35s: FD: %d Connected for upstream: %p\n", - STUB_DEBUG_SETUP, __FUNC__, *fd, (void *)upstream); + getdns_upstream *upstream = NULL; + + /* UDP always returns an upstream, the only reason this will fail is if + no socket is available, in which case that is an error.*/ + if (transport == GETDNS_TRANSPORT_UDP) { + upstream = upstream_select(netreq); + *fd = upstream_connect(upstream, transport, netreq->owner); + return upstream; + } + else { + /* For stateful transport we should keep trying until all our transports + are exhausted/backed-off (no upstream)*/ + do { + upstream = upstream_select_stateful(netreq, transport); + if (!upstream) + return NULL; + *fd = upstream_connect(upstream, transport, netreq->owner); + } while (*fd == -1); + DEBUG_STUB("%s %-35s: FD: %d Connecting to upstream: %p No: %d\n", + STUB_DEBUG_SETUP, __FUNC__, *fd, (void*)upstream, + (int)(upstream - netreq->owner->context->upstreams->upstreams)); + } return upstream; } @@ -1792,6 +1897,12 @@ upstream_find_for_netreq(getdns_network_req *netreq) netreq->keepalive_sent = 0; return fd; } + /* Handle better, will give generic error*/ + DEBUG_STUB("%s %-35s: MSG: %p No valid upstream! \n", STUB_DEBUG_SCHEDULE, __FUNC__, (void*)netreq); +#if defined(DAEMON_DEBUG) && DAEMON_DEBUG + DEBUG_DAEMON("%s *FAILURE* no valid transports or upstreams available!\n", + STUB_DEBUG_DAEMON); +#endif return -1; } @@ -1803,9 +1914,8 @@ static int fallback_on_write(getdns_network_req *netreq) { - /* Deal with UDP and change error code*/ - - DEBUG_STUB("%s %-35s: MSG: %p FALLING BACK \n", STUB_DEBUG_SCHEDULE, __FUNC__, (void *)netreq); + /* Deal with UDP one day*/ + DEBUG_STUB("%s %-35s: MSG: %p FALLING BACK \n", STUB_DEBUG_SCHEDULE, __FUNC__, (void*)netreq); /* Try to find a fallback transport*/ getdns_return_t result = _getdns_submit_stub_request(netreq); @@ -1843,7 +1953,7 @@ upstream_reschedule_events(getdns_upstream *upstream, uint64_t idle_timeout) { DEBUG_STUB("%s %-35s: FD: %d Connection idle - timeout is %d\n", STUB_DEBUG_SCHEDULE, __FUNC__, upstream->fd, (int)idle_timeout); upstream->event.timeout_cb = upstream_idle_timeout_cb; - if (upstream->tcp.write_error != 0) + if (upstream->conn_state != GETDNS_CONN_OPEN) idle_timeout = 0; GETDNS_SCHEDULE_EVENT(upstream->loop, -1, idle_timeout, &upstream->event); @@ -1853,7 +1963,7 @@ upstream_reschedule_events(getdns_upstream *upstream, uint64_t idle_timeout) { static void upstream_schedule_netreq(getdns_upstream *upstream, getdns_network_req *netreq) { - DEBUG_STUB("%s %-35s: MSG: %p (schedule event)\n", STUB_DEBUG_SCHEDULE, __FUNC__, (void *)netreq); + DEBUG_STUB("%s %-35s: MSG: %p (schedule event)\n", STUB_DEBUG_SCHEDULE, __FUNC__, (void*)netreq); /* We have a connected socket and a global event loop */ assert(upstream->fd >= 0); assert(upstream->loop); @@ -1869,11 +1979,9 @@ upstream_schedule_netreq(getdns_upstream *upstream, getdns_network_req *netreq) } upstream->event.timeout_cb = NULL; upstream->event.write_cb = upstream_write_cb; - if (upstream->tls_hs_state == GETDNS_HS_WRITE) { + if (upstream->queries_sent == 0) { /* Set a timeout on the upstream so we can catch failed setup*/ - /* TODO[TLS]: When generic fallback supported, we should decide how - * to split the timeout between transports. */ - upstream->event.timeout_cb = upstream_tls_timeout_cb; + upstream->event.timeout_cb = upstream_setup_timeout_cb; GETDNS_SCHEDULE_EVENT(upstream->loop, upstream->fd, netreq->owner->context->timeout / 2, &upstream->event); @@ -1907,7 +2015,7 @@ getdns_return_t _getdns_submit_stub_request(getdns_network_req *netreq) { DEBUG_STUB("%s %-35s: MSG: %p TYPE: %d\n", STUB_DEBUG_ENTRY, __FUNC__, - (void *)netreq, netreq->request_type); + (void*)netreq, netreq->request_type); int fd = -1; getdns_dns_req *dnsreq = netreq->owner; @@ -1915,6 +2023,7 @@ _getdns_submit_stub_request(getdns_network_req *netreq) * All other set up is done async*/ fd = upstream_find_for_netreq(netreq); if (fd == -1) + /* Handle better, will give unhelpful error is some cases */ return GETDNS_RETURN_GENERIC_ERROR; getdns_transport_list_t transport = @@ -1998,14 +2107,10 @@ _getdns_submit_stub_request(getdns_network_req *netreq) */ GETDNS_SCHEDULE_EVENT( dnsreq->loop, -1, - dnsreq->context->timeout, - getdns_eventloop_event_init( &netreq->event, netreq, NULL, NULL, - - ( transport == GETDNS_TRANSPORT_TLS - ? stub_tls_timeout_cb : stub_timeout_cb))); + stub_timeout_cb)); return GETDNS_RETURN_GOOD; default: diff --git a/src/test/Makefile.in b/src/test/Makefile.in index afb807c5..c481fdab 100644 --- a/src/test/Makefile.in +++ b/src/test/Makefile.in @@ -67,20 +67,14 @@ CHECK_CFLAGS=@CHECK_CFLAGS@ CHECK_OBJS=check_getdns_common.lo check_getdns_context_set_timeout.lo \ check_getdns.lo check_getdns_transport.lo -DECOMPOSED_OBJS_WITHOUT_JSMN=getdns_str2dict.lo getdns_context_config.lo \ - getdns_context_set_listen_addresses.lo - -DECOMPOSED_OBJS=$(DECOMPOSED_OBJS_WITHOUT_JSMN) jsmn.lo - ALL_OBJS=$(CHECK_OBJS) check_getdns_libevent.lo check_getdns_libev.lo \ - check_getdns_selectloop.lo getdns_query.lo scratchpad.lo \ + check_getdns_selectloop.lo scratchpad.lo \ testmessages.lo tests_dict.lo tests_list.lo tests_namespaces.lo \ - tests_stub_async.lo tests_stub_sync.lo \ - $(DECOMPOSED_OBJS_WITHOUT_JSMN) + tests_stub_async.lo tests_stub_sync.lo NON_C99_OBJS=check_getdns_libuv.lo -PROGRAMS=tests_dict tests_list tests_namespaces tests_stub_async tests_stub_sync getdns_query $(CHECK_GETDNS) $(CHECK_EV_PROG) $(CHECK_EVENT_PROG) $(CHECK_UV_PROG) +PROGRAMS=tests_dict tests_list tests_namespaces tests_stub_async tests_stub_sync $(CHECK_GETDNS) $(CHECK_EV_PROG) $(CHECK_EVENT_PROG) $(CHECK_UV_PROG) .SUFFIXES: .c .o .a .lo .h @@ -95,9 +89,6 @@ default: all all: $(PROGRAMS) -jsmn.lo: - $(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) $(WPEDANTICFLAG) -DJSMN_GETDNS -c $(srcdir)/jsmn/jsmn.c -o $@ - $(ALL_OBJS): $(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) $(WPEDANTICFLAG) -c $(srcdir)/$(@:.lo=.c) -o $@ @@ -134,9 +125,6 @@ check_getdns_uv: check_getdns.lo check_getdns_common.lo check_getdns_context_set check_getdns_ev: check_getdns.lo check_getdns_common.lo check_getdns_context_set_timeout.lo check_getdns_transport.lo check_getdns_libev.lo ../libgetdns_ext_ev.la $(LIBTOOL) --tag=CC --mode=link $(CC) -o $@ check_getdns.lo check_getdns_common.lo check_getdns_context_set_timeout.lo check_getdns_transport.lo check_getdns_libev.lo $(LDFLAGS) $(LDLIBS) $(CHECK_LIBS) ../libgetdns_ext_ev.la $(EXTENSION_LIBEV_LDFLAGS) $(EXTENSION_LIBEV_EXT_LIBS) -getdns_query: getdns_query.lo $(DECOMPOSED_OBJS) - $(LIBTOOL) --tag=CC --mode=link $(CC) -o $@ getdns_query.lo $(DECOMPOSED_OBJS) $(LDFLAGS) $(LDLIBS) - scratchpad: scratchpad.lo $(LIBTOOL) --tag=CC --mode=link $(CC) -o $@ scratchpad.lo $(LDFLAGS) $(LDLIBS) @@ -145,12 +133,11 @@ scratchpad.lo: scratchpad.c $(srcdir)/scratchpad.c: scratchpad.template.c [ ! -f $(srcdir)/scratchpad.c ] && cp -p $(srcdir)/scratchpad.template.c $(srcdir)/scratchpad.c || true -install: getdns_query - $(INSTALL) -m 755 -d $(DESTDIR)$(bindir) - $(LIBTOOL) --mode=install cp getdns_query $(DESTDIR)$(bindir) +install: + echo nothing to install uninstall: - $(LIBTOOL) --mode=uninstall rm -f $(DESTDIR)$(bindir)/getdns_query + echo nothing to uninstall nolibcheck: @echo "***" @@ -222,7 +209,6 @@ depend: -e 's? \$$(srcdir)/\.\./config\.h? ../config.h?g' \ -e 's? \$$(srcdir)/\.\./getdns/getdns\.h? ../getdns/getdns.h?g' \ -e 's? \$$(srcdir)/\.\./getdns/getdns_extra\.h? ../getdns/getdns_extra.h?g' \ - -e 's? jsmn/jsmn\.h? $$(srcdir)/jsmn/jsmn.h?g' \ -e 's!\(.*\)\.o[ :]*!\1.lo \1.o: !g' >> Makefile.in.new ) (cd $(srcdir) ; diff Makefile.in.new Makefile.in && rm Makefile.in.new \ || mv Makefile.in.new Makefile.in ) @@ -274,20 +260,6 @@ check_getdns_selectloop.lo check_getdns_selectloop.o: $(srcdir)/check_getdns_sel check_getdns_transport.lo check_getdns_transport.o: $(srcdir)/check_getdns_transport.c \ $(srcdir)/check_getdns_transport.h $(srcdir)/check_getdns_common.h ../getdns/getdns.h \ ../getdns/getdns_extra.h -getdns_context_config.lo getdns_context_config.o: $(srcdir)/getdns_context_config.c $(srcdir)/getdns_context_config.h \ - ../getdns/getdns.h ../getdns/getdns_extra.h -getdns_context_set_listen_addresses.lo getdns_context_set_listen_addresses.o: \ - $(srcdir)/getdns_context_set_listen_addresses.c ../config.h \ - $(srcdir)/getdns_context_set_listen_addresses.h ../getdns/getdns.h \ - ../getdns/getdns_extra.h $(srcdir)/../types-internal.h ../getdns/getdns.h \ - ../getdns/getdns_extra.h $(srcdir)/../util/rbtree.h $(srcdir)/../debug.h ../config.h -getdns_query.lo getdns_query.o: $(srcdir)/getdns_query.c ../config.h $(srcdir)/../debug.h ../config.h \ - $(srcdir)/getdns_str2dict.h ../getdns/getdns.h $(srcdir)/getdns_context_config.h \ - $(srcdir)/getdns_context_set_listen_addresses.h ../getdns/getdns_extra.h -getdns_str2dict.lo getdns_str2dict.o: $(srcdir)/getdns_str2dict.c ../config.h $(srcdir)/../const-info.h \ - $(srcdir)/jsmn/jsmn.h $(srcdir)/getdns_str2dict.h ../getdns/getdns.h $(srcdir)/../types-internal.h \ - ../getdns/getdns.h ../getdns/getdns_extra.h $(srcdir)/../util/rbtree.h $(srcdir)/../list.h \ - $(srcdir)/../types-internal.h $(srcdir)/../dict.h scratchpad.template.lo scratchpad.template.o: scratchpad.template.c ../getdns/getdns.h \ ../getdns/getdns_extra.h testmessages.lo testmessages.o: $(srcdir)/testmessages.c $(srcdir)/testmessages.h diff --git a/src/test/check_getdns_context_set_dns_transport.h b/src/test/check_getdns_context_set_dns_transport.h index 9b4aac23..fba6c195 100644 --- a/src/test/check_getdns_context_set_dns_transport.h +++ b/src/test/check_getdns_context_set_dns_transport.h @@ -300,6 +300,7 @@ } CONTEXT_DESTROY; + getdns_dict_destroy(extensions); getdns_list_destroy(root_servers); diff --git a/src/test/getdns_context_config.c b/src/test/getdns_context_config.c deleted file mode 100644 index 3cb7e272..00000000 --- a/src/test/getdns_context_config.c +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright (c) 2013, NLNet Labs, Verisign, 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 "getdns_context_config.h" -#include "getdns/getdns_extra.h" -#include -#include -#include - -static int _streq(const getdns_bindata *name, const char *str) -{ - if (strlen(str) != name->size) - return 0; - else return strncmp((const char *)name->data, str, name->size) == 0; -} - -static getdns_return_t _get_list_or_read_file(const getdns_dict *config_dict, - const char *setting, getdns_list **r_list, int *destroy_list) -{ - getdns_bindata *fn_bd; - char fn[FILENAME_MAX]; - FILE *fh; - getdns_return_t r; - - assert(r_list); - assert(destroy_list); - - *destroy_list = 0; - if (!(r = getdns_dict_get_list(config_dict, setting, r_list))) - return GETDNS_RETURN_GOOD; - - else if ((r = getdns_dict_get_bindata(config_dict, setting, &fn_bd))) - return r; - - else if (fn_bd->size >= FILENAME_MAX) - return GETDNS_RETURN_INVALID_PARAMETER; - - (void)memcpy(fn, fn_bd->data, fn_bd->size); - fn[fn_bd->size] = 0; - - if (!(fh = fopen(fn, "r"))) - return GETDNS_RETURN_GENERIC_ERROR; - - if (!(r = getdns_fp2rr_list(fh, r_list, NULL, 3600))) - *destroy_list = 1; - - fclose(fh); - return r; -} - -#define CONTEXT_SETTING_INT(X) \ - } else if (_streq(setting, #X)) { \ - if (!(r = getdns_dict_get_int(config_dict, #X , &n))) \ - r = getdns_context_set_ ## X (context, n); - -#define CONTEXT_SETTING_LIST(X) \ - } else if (_streq(setting, #X)) { \ - if (!(r = getdns_dict_get_list(config_dict, #X , &list))) \ - r = getdns_context_set_ ## X (context, list); - -#define CONTEXT_SETTING_LIST_OR_ZONEFILE(X) \ - } else if (_streq(setting, #X)) { \ - if (!(r = _get_list_or_read_file( \ - config_dict, #X , &list, &destroy_list))) \ - r = getdns_context_set_ ## X(context, list); \ - if (destroy_list) getdns_list_destroy(list); - -#define CONTEXT_SETTING_ARRAY(X, T) \ - } else if (_streq(setting, #X )) { \ - if (!(r = getdns_dict_get_list(config_dict, #X , &list)) && \ - !(r = getdns_list_get_length(list, &count))) { \ - for (i=0; i /* For bsearch */ -#include /* For isspace */ - -static struct mem_funcs _getdns_plain_mem_funcs = { - MF_PLAIN, .mf.pln = { malloc, realloc, free } -}; - -/* TODO: Replace with gldns_b64_pton - * once getdns_ipaddr_dict becomes part of the library - */ -static int _gldns_b64_pton(char const *src, uint8_t *target, size_t targsize) -{ - const uint8_t pad64 = 64; /* is 64th in the b64 array */ - const char* s = src; - uint8_t in[4]; - size_t o = 0, incount = 0; - - while(*s) { - /* skip any character that is not base64 */ - /* conceptually we do: - const char* b64 = pad'=' is appended to array - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; - const char* d = strchr(b64, *s++); - and use d-b64; - */ - char d = *s++; - if(d <= 'Z' && d >= 'A') - d -= 'A'; - else if(d <= 'z' && d >= 'a') - d = d - 'a' + 26; - else if(d <= '9' && d >= '0') - d = d - '0' + 52; - else if(d == '+') - d = 62; - else if(d == '/') - d = 63; - else if(d == '=') - d = 64; - else continue; - in[incount++] = (uint8_t)d; - if(incount != 4) - continue; - /* process whole block of 4 characters into 3 output bytes */ - if(in[3] == pad64 && in[2] == pad64) { /* A B = = */ - if(o+1 > targsize) - return -1; - target[o] = (in[0]<<2) | ((in[1]&0x30)>>4); - o += 1; - break; /* we are done */ - } else if(in[3] == pad64) { /* A B C = */ - if(o+2 > targsize) - return -1; - target[o] = (in[0]<<2) | ((in[1]&0x30)>>4); - target[o+1]= ((in[1]&0x0f)<<4) | ((in[2]&0x3c)>>2); - o += 2; - break; /* we are done */ - } else { - if(o+3 > targsize) - return -1; - /* write xxxxxxyy yyyyzzzz zzwwwwww */ - target[o] = (in[0]<<2) | ((in[1]&0x30)>>4); - target[o+1]= ((in[1]&0x0f)<<4) | ((in[2]&0x3c)>>2); - target[o+2]= ((in[2]&0x03)<<6) | in[3]; - o += 3; - } - incount = 0; - } - return (int)o; -} - -static getdns_dict * -_getdns_ipaddr_dict_mf(struct mem_funcs *mf, const char *ipstr) -{ - getdns_dict *r = _getdns_dict_create_with_mf(mf); - char *s = strchr(ipstr, '%'), *scope_id_str = ""; - char *p = strchr(ipstr, '@'), *portstr = ""; - char *t = strchr(ipstr, '#'), *tls_portstr = ""; - char *n = strchr(ipstr, '~'), *tls_namestr = ""; - /* ^[alg:]name:key */ - char *T = strchr(ipstr, '^'), *tsig_name_str = "" - , *tsig_secret_str = "" - , *tsig_algorithm_str = ""; - char *br, *c; - int tsig_secret_size; - uint8_t tsig_secret_buf[256]; /* 4 times SHA512 */ - getdns_bindata tsig_secret; - uint8_t buf[sizeof(struct in6_addr)]; - getdns_bindata addr; - - addr.data = buf; - - if (!r) return NULL; - - if (*ipstr == '[') { - char *br = strchr(ipstr, ']'); - if (br) { - ipstr += 1; - *br = 0; - if ((c = strchr(br + 1, ':'))) { - p = c; - } - } - } else if ((br = strchr(ipstr, '.')) && (c = strchr(br + 1, ':')) - && (T == NULL || c < T)) - p = c; - - else if ((*ipstr == '*') && (c = strchr(ipstr+1, ':'))) - p = c; - - if (s) { - *s = 0; - scope_id_str = s + 1; - } - if (p) { - *p = 0; - portstr = p + 1; - } - if (t) { - *t = 0; - tls_portstr = t + 1; - } - if (n) { - *n = 0; - tls_namestr = n + 1; - } - if (T) { - *T = 0; - tsig_name_str = T + 1; - if ((T = strchr(tsig_name_str, ':'))) { - *T = 0; - tsig_secret_str = T + 1; - if ((T = strchr(tsig_secret_str, ':'))) { - *T = 0; - tsig_algorithm_str = tsig_name_str; - tsig_name_str = tsig_secret_str; - tsig_secret_str = T + 1; - } - } else { - tsig_name_str = ""; - } - } - if (*ipstr == '*') { - getdns_dict_util_set_string(r, "address_type", "IPv6"); - addr.size = 16; - (void) memset(buf, 0, 16); - } else if (strchr(ipstr, ':')) { - getdns_dict_util_set_string(r, "address_type", "IPv6"); - addr.size = 16; - if (inet_pton(AF_INET6, ipstr, buf) <= 0) { - getdns_dict_destroy(r); - return NULL; - } - } else { - getdns_dict_util_set_string(r, "address_type", "IPv4"); - addr.size = 4; - if (inet_pton(AF_INET, ipstr, buf) <= 0) { - getdns_dict_destroy(r); - return NULL; - } - } - getdns_dict_set_bindata(r, "address_data", &addr); - if (*portstr) - getdns_dict_set_int(r, "port", (int32_t)atoi(portstr)); - if (*tls_portstr) - getdns_dict_set_int(r, "tls_port", (int32_t)atoi(tls_portstr)); - if (*tls_namestr) { - getdns_dict_util_set_string(r, "tls_auth_name", tls_namestr); - } - if (*scope_id_str) - getdns_dict_util_set_string(r, "scope_id", scope_id_str); - if (*tsig_name_str) - getdns_dict_util_set_string(r, "tsig_name", tsig_name_str); - if (*tsig_algorithm_str) - getdns_dict_util_set_string(r, "tsig_algorithm", tsig_algorithm_str); - if (*tsig_secret_str) { - tsig_secret_size = _gldns_b64_pton( - tsig_secret_str, tsig_secret_buf, sizeof(tsig_secret_buf)); - if (tsig_secret_size > 0) { - tsig_secret.size = tsig_secret_size; - tsig_secret.data = tsig_secret_buf; - getdns_dict_set_bindata(r, "tsig_secret", &tsig_secret); - } - } - return r; -} - -static int _jsmn_get_ipdict(struct mem_funcs *mf, const char *js, jsmntok_t *t, - getdns_dict **value) -{ - char value_str[3072]; - int size = t->end - t->start; - - if (size <= 0 || size >= (int)sizeof(value_str)) - return 0; - - (void) memcpy(value_str, js + t->start, size); - value_str[size] = '\0'; - - *value = _getdns_ipaddr_dict_mf(mf, value_str); - return *value != NULL; -} - -static int _jsmn_get_data(struct mem_funcs *mf, const char *js, jsmntok_t *t, - getdns_bindata **value) -{ - int i; - size_t j; - uint8_t h, l; - - if ((t->end - t->start) < 4 || (t->end - t->start) % 2 == 1 || - js[t->start] != '0' || js[t->start + 1] != 'x') - return 0; - - for (i = t->start + 2; i < t->end; i++) - if (!((js[i] >= '0' && js[i] <= '9') - ||(js[i] >= 'a' && js[i] <= 'f') - ||(js[i] >= 'A' && js[i] <= 'F'))) - return 0; - - if (!(*value = GETDNS_MALLOC(*mf, getdns_bindata))) - return 0; - - else if (!((*value)->data = GETDNS_XMALLOC( - *mf, uint8_t, (t->end - t->start) / 2 - 1))) { - GETDNS_FREE(*mf, *value); - return 0; - } - for (i = t->start + 2, j = 0; i < t->end; i++, j++) { - h = js[i] >= '0' && js[i] <= '9' ? js[i] - '0' - : js[i] >= 'A' && js[i] <= 'F' ? js[i] + 10 - 'A' - : js[i] + 10 - 'a'; - h <<= 4; - i++; - l = js[i] >= '0' && js[i] <= '9' ? js[i] - '0' - : js[i] >= 'A' && js[i] <= 'F' ? js[i] + 10 - 'A' - : js[i] + 10 - 'a'; - (*value)->data[j] = h | l; - } - (*value)->size = j; - return 1; -} - -static int _jsmn_get_dname(const char *js, jsmntok_t *t, - getdns_bindata **value) -{ - char value_str[1025]; - int size = t->end - t->start; - - if (size <= 0 || size >= (int)sizeof(value_str) || js[t->end - 1] != '.') - return 0; - - (void) memcpy(value_str, js + t->start, size); - value_str[size] = '\0'; - - return !getdns_convert_fqdn_to_dns_name(value_str, value); -} - -static int _jsmn_get_ipv4(struct mem_funcs *mf, const char *js, jsmntok_t *t, - getdns_bindata **value) -{ - char value_str[16]; - int size = t->end - t->start; - uint8_t buf[4]; - - if (size <= 0 || size >= (int)sizeof(value_str)) - return 0; - - (void) memcpy(value_str, js + t->start, size); - value_str[size] = '\0'; - - if (inet_pton(AF_INET, value_str, buf) <= 0) - ; /* pass */ - - else if (!(*value = GETDNS_MALLOC(*mf, getdns_bindata))) - ; /* pass */ - - else if (!((*value)->data = GETDNS_XMALLOC(*mf, uint8_t, 4))) - GETDNS_FREE(*mf, *value); - - else { - (*value)->size = 4; - (void) memcpy((*value)->data, buf, 4); - return 1; - } - return 0; -} - -static int _jsmn_get_ipv6(struct mem_funcs *mf, const char *js, jsmntok_t *t, - getdns_bindata **value) -{ - char value_str[40]; - int size = t->end - t->start; - uint8_t buf[16]; - - if (size <= 0 || size >= (int)sizeof(value_str)) - return 0; - - (void) memcpy(value_str, js + t->start, size); - value_str[size] = '\0'; - - if (inet_pton(AF_INET6, value_str, buf) <= 0) - ; /* pass */ - - else if (!(*value = GETDNS_MALLOC(*mf, getdns_bindata))) - ; /* pass */ - - else if (!((*value)->data = GETDNS_XMALLOC(*mf, uint8_t, 16))) - GETDNS_FREE(*mf, *value); - - else { - (*value)->size = 16; - (void) memcpy((*value)->data, buf, 16); - return 1; - } - return 0; -} - -static int _jsmn_get_int(const char *js, jsmntok_t *t, uint32_t *value) -{ - char value_str[11]; - int size = t->end - t->start; - char *endptr; - - if (size <= 0 || size >= (int)sizeof(value_str)) - return 0; - - (void) memcpy(value_str, js + t->start, size); - value_str[size] = '\0'; - - *value = (uint32_t)strtoul(value_str, &endptr, 10); - return *value_str != '\0' && *endptr == '\0'; -} - -static int _getdns_get_const_name_info(const char *name, uint32_t *code); - -static int _jsmn_get_const(const char *js, jsmntok_t *t, uint32_t *value) -{ - char value_str[80]; - int size = t->end - t->start; - - if (size <= 0 || size >= (int)sizeof(value_str)) - return 0; - - (void) memcpy(value_str, js + t->start, size); - value_str[size] = '\0'; - - return _getdns_get_const_name_info(value_str, value); -} - -static void -_getdns_destroy_item_data(struct mem_funcs *mf, getdns_item *item) -{ - switch (item->dtype) { - case t_dict: - getdns_dict_destroy(item->data.dict); - break; - - case t_list: - getdns_list_destroy(item->data.list); - break; - - case t_bindata: - GETDNS_FREE(*mf, item->data.bindata->data); - GETDNS_FREE(*mf, item->data.bindata); - default: - break; - } -} -static int _jsmn_get_item(struct mem_funcs *mf, const char *js, jsmntok_t *t, - size_t count, getdns_item *item, getdns_return_t *r); - -static int _jsmn_get_dict(struct mem_funcs *mf, const char *js, jsmntok_t *t, - size_t count, getdns_dict *dict, getdns_return_t *r) -{ - int i; - size_t j = 1; - char key_spc[1024], *key = NULL; - getdns_item child_item; - - if (t->size <= 0) - *r = GETDNS_RETURN_GOOD; - - else for (i = 0; i < t->size; i++) { - if (t[j].type != JSMN_STRING && - t[j].type != JSMN_PRIMITIVE) { - - /* Key must be string or primitive */ - *r = GETDNS_RETURN_WRONG_TYPE_REQUESTED; - break; - } - if (t[j].end <= t[j].start) { - /* Key must be at least 1 character */ - *r = GETDNS_RETURN_GENERIC_ERROR; /* range error */ - break; - } - if (t[j].end - t[j].start < (int)sizeof(key_spc)) - key = key_spc; - - else if (!(key = GETDNS_XMALLOC( - *mf, char, t[j].end - t[j].start + 1))) { - - *r = GETDNS_RETURN_MEMORY_ERROR; - break; - } - (void) memcpy(key, js + t[j].start, t[j].end - t[j].start); - key[t[j].end - t[j].start] = '\0'; - j += 1; - - j += _jsmn_get_item(mf, js, t + j, count - j, &child_item, r); - if (*r) break; - - switch (child_item.dtype) { - case t_int: - *r = getdns_dict_set_int(dict, key, - child_item.data.n); - break; - case t_bindata: - *r = getdns_dict_set_bindata(dict, key, - child_item.data.bindata); - break; - case t_list: - *r = getdns_dict_set_list(dict, key, - child_item.data.list); - break; - case t_dict: - *r = getdns_dict_set_dict(dict, key, - child_item.data.dict); - break; - default: - *r = GETDNS_RETURN_WRONG_TYPE_REQUESTED; - break; - - } - _getdns_destroy_item_data(mf, &child_item); - if (*r) break; - if (key && key != key_spc) { - GETDNS_FREE(*mf, key); - key = NULL; - } - } - if (key && key != key_spc) - GETDNS_FREE(*mf, key); - - if (*r) { - getdns_dict_destroy(dict); - return 0; - } - return j; -} - -static int _jsmn_get_list(struct mem_funcs *mf, const char *js, jsmntok_t *t, - size_t count, getdns_list *list, getdns_return_t *r) -{ - int i; - size_t j = 1, index = 0; - getdns_item child_item; - - if (t->size <= 0) - *r = GETDNS_RETURN_GOOD; - - else for (i = 0; i < t->size; i++) { - j += _jsmn_get_item(mf, js, t + j, count - j, &child_item, r); - if (*r) break; - - switch (child_item.dtype) { - case t_int: - *r = getdns_list_set_int(list, index++, - child_item.data.n); - break; - case t_bindata: - *r = getdns_list_set_bindata(list, index++, - child_item.data.bindata); - break; - case t_list: - *r = getdns_list_set_list(list, index++, - child_item.data.list); - break; - case t_dict: - *r = getdns_list_set_dict(list, index++, - child_item.data.dict); - break; - default: - *r = GETDNS_RETURN_WRONG_TYPE_REQUESTED; - break; - - } - _getdns_destroy_item_data(mf, &child_item); - if (*r) break; - } - if (*r) { - getdns_list_destroy(list); - return 0; - } - return j; -} - -static int _jsmn_get_item(struct mem_funcs *mf, const char *js, jsmntok_t *t, - size_t count, getdns_item *item, getdns_return_t *r) -{ - assert(item); - - switch (t->type) { - case JSMN_STRING: - if (t->end < t->start) - *r = GETDNS_RETURN_GENERIC_ERROR; - - else if (!(item->data.bindata = - GETDNS_MALLOC(*mf, getdns_bindata))) - *r = GETDNS_RETURN_MEMORY_ERROR; - - else if (!(item->data.bindata->data = GETDNS_XMALLOC( - *mf, uint8_t, t->end - t->start + 1))) { - GETDNS_FREE(*mf, item->data.bindata); - *r = GETDNS_RETURN_MEMORY_ERROR; - } else { - item->dtype = t_bindata; - if (t->end - t->start) { - (void) memcpy(item->data.bindata->data, - js + t->start, t->end - t->start); - } - item->data.bindata->data[t->end - t->start] = '\0'; - item->data.bindata->size = t->end - t->start; - *r = GETDNS_RETURN_GOOD; - return 1; - } - break; - - case JSMN_PRIMITIVE: - /* There is no such thing as an empty primitive */ - if (t->end <= t->start) { - *r = GETDNS_RETURN_GENERIC_ERROR; - break; - - } else if (_jsmn_get_int(js, t, &item->data.n) - || _jsmn_get_const(js, t, &item->data.n)) { - - item->dtype = t_int; - } - else if (_jsmn_get_data(mf, js, t, &item->data.bindata) - || _jsmn_get_dname(js, t, &item->data.bindata) - || _jsmn_get_ipv4(mf, js, t, &item->data.bindata) - || _jsmn_get_ipv6(mf, js, t, &item->data.bindata)) - - item->dtype = t_bindata; - - else if (_jsmn_get_ipdict(mf, js, t, &item->data.dict)) - - item->dtype = t_dict; - else { - *r = GETDNS_RETURN_GENERIC_ERROR; - break; - } - *r = GETDNS_RETURN_GOOD; - return 1; - - case JSMN_OBJECT: - if (!(item->data.dict = _getdns_dict_create_with_mf(mf))) { - *r = GETDNS_RETURN_MEMORY_ERROR; - break; - } - item->dtype = t_dict; - return _jsmn_get_dict(mf, js, t, count, item->data.dict, r); - - case JSMN_ARRAY: - if (!(item->data.list = _getdns_list_create_with_mf(mf))) { - *r = GETDNS_RETURN_MEMORY_ERROR; - break; - } - item->dtype = t_list; - return _jsmn_get_list(mf, js, t, count, item->data.list, r); - - default: - *r = GETDNS_RETURN_WRONG_TYPE_REQUESTED; - break; - } - return 0; -} - -static getdns_return_t -_getdns_str2item_mf(struct mem_funcs *mf, const char *str, getdns_item *item) -{ - jsmn_parser p; - jsmntok_t *tok = NULL, *new_tok; - size_t tokcount = 100; - int r; - getdns_return_t gr; - - jsmn_init(&p); - tok = GETDNS_XMALLOC(*mf, jsmntok_t, tokcount); - do { - r = jsmn_parse(&p, str, strlen(str), tok, tokcount); - if (r == JSMN_ERROR_NOMEM) { - tokcount *= 2; - if (!(new_tok = GETDNS_XREALLOC( - *mf, tok, jsmntok_t, tokcount))) { - GETDNS_FREE(*mf, tok); - return GETDNS_RETURN_MEMORY_ERROR; - } - tok = new_tok; - } - } while (r == JSMN_ERROR_NOMEM); - if (r < 0) - gr = GETDNS_RETURN_GENERIC_ERROR; - else - (void) _jsmn_get_item(mf, str, tok, p.toknext, item, &gr); - GETDNS_FREE(*mf, tok); - return gr; -} - -getdns_return_t -getdns_str2dict(const char *str, getdns_dict **dict) -{ - getdns_item item; - getdns_return_t r; - - while (*str && isspace(*str)) - str++; - - if (*str != '{') { - getdns_dict *dict_r = _getdns_ipaddr_dict_mf( - &_getdns_plain_mem_funcs, str); - if (dict_r) { - *dict = dict_r; - return GETDNS_RETURN_GOOD; - } - } - if ((r = _getdns_str2item_mf(&_getdns_plain_mem_funcs, str, &item))) - return r; - - else if (item.dtype != t_dict) { - uint8_t buf[16]; - getdns_dict *dict_r; - - if (item.dtype != t_bindata) - r = GETDNS_RETURN_WRONG_TYPE_REQUESTED; - - else if (item.data.bindata->size == 4 && - inet_pton(AF_INET, str, buf) == 1) { - - if (!(dict_r = getdns_dict_create())) - r = GETDNS_RETURN_MEMORY_ERROR; - - else if ((r = getdns_dict_util_set_string( - dict_r, "address_type", "IPv4"))) - getdns_dict_destroy(dict_r); - - else if ((r = getdns_dict_set_bindata( - dict_r, "address_data", item.data.bindata))) - getdns_dict_destroy(dict_r); - else - *dict = dict_r; - - } else if (item.data.bindata->size == 16 && - inet_pton(AF_INET6, str, buf) == 1) { - - if (!(dict_r = getdns_dict_create())) - r = GETDNS_RETURN_MEMORY_ERROR; - - else if ((r = getdns_dict_util_set_string( - dict_r, "address_type", "IPv6"))) - getdns_dict_destroy(dict_r); - - else if ((r = getdns_dict_set_bindata( - dict_r, "address_data", item.data.bindata))) - getdns_dict_destroy(dict_r); - else - *dict = dict_r; - } else - r = GETDNS_RETURN_WRONG_TYPE_REQUESTED; - - _getdns_destroy_item_data(&_getdns_plain_mem_funcs, &item); - return r; - } - *dict = item.data.dict; - return GETDNS_RETURN_GOOD; -} - -getdns_return_t -getdns_str2list(const char *str, getdns_list **list) -{ - getdns_item item; - getdns_return_t r; - - if ((r = _getdns_str2item_mf(&_getdns_plain_mem_funcs, str, &item))) - return r; - - else if (item.dtype != t_list) { - _getdns_destroy_item_data(&_getdns_plain_mem_funcs, &item); - return GETDNS_RETURN_WRONG_TYPE_REQUESTED; - } - *list = item.data.list; - return GETDNS_RETURN_GOOD; -} - -getdns_return_t -getdns_str2bindata(const char *str, getdns_bindata **bindata) -{ - getdns_item item; - getdns_return_t r; - - if ((r = _getdns_str2item_mf(&_getdns_plain_mem_funcs, str, &item))) - return r; - - else if (item.dtype != t_bindata) { - _getdns_destroy_item_data(&_getdns_plain_mem_funcs, &item); - return GETDNS_RETURN_WRONG_TYPE_REQUESTED; - } - *bindata = item.data.bindata; - return GETDNS_RETURN_GOOD; -} - -getdns_return_t -getdns_str2int(const char *str, uint32_t *value) -{ - getdns_item item; - getdns_return_t r; - - if ((r = _getdns_str2item_mf(&_getdns_plain_mem_funcs, str, &item))) - return r; - - else if (item.dtype != t_int) { - _getdns_destroy_item_data(&_getdns_plain_mem_funcs, &item); - return GETDNS_RETURN_WRONG_TYPE_REQUESTED; - } - *value = item.data.n; - return GETDNS_RETURN_GOOD; -} - - -struct const_name_info { const char *name; uint32_t code; }; -static struct const_name_info consts_name_info[] = { - { "GETDNS_APPEND_NAME_ALWAYS", 550 }, - { "GETDNS_APPEND_NAME_NEVER", 553 }, - { "GETDNS_APPEND_NAME_ONLY_TO_MULTIPLE_LABEL_NAME_AFTER_FAILURE", 552 }, - { "GETDNS_APPEND_NAME_ONLY_TO_SINGLE_LABEL_AFTER_FAILURE", 551 }, - { "GETDNS_APPEND_NAME_TO_SINGLE_LABEL_FIRST", 554 }, - { "GETDNS_AUTHENTICATION_NONE", 1300 }, - { "GETDNS_AUTHENTICATION_REQUIRED", 1301 }, - { "GETDNS_BAD_DNS_ALL_NUMERIC_LABEL", 1101 }, - { "GETDNS_BAD_DNS_CNAME_IN_TARGET", 1100 }, - { "GETDNS_BAD_DNS_CNAME_RETURNED_FOR_OTHER_TYPE", 1102 }, - { "GETDNS_CALLBACK_CANCEL", 701 }, - { "GETDNS_CALLBACK_COMPLETE", 700 }, - { "GETDNS_CALLBACK_ERROR", 703 }, - { "GETDNS_CALLBACK_TIMEOUT", 702 }, - { "GETDNS_CONTEXT_CODE_APPEND_NAME", 607 }, - { "GETDNS_CONTEXT_CODE_DNSSEC_ALLOWED_SKEW", 614 }, - { "GETDNS_CONTEXT_CODE_DNSSEC_TRUST_ANCHORS", 609 }, - { "GETDNS_CONTEXT_CODE_DNS_ROOT_SERVERS", 604 }, - { "GETDNS_CONTEXT_CODE_DNS_TRANSPORT", 605 }, - { "GETDNS_CONTEXT_CODE_EDNS_CLIENT_SUBNET_PRIVATE", 619 }, - { "GETDNS_CONTEXT_CODE_EDNS_DO_BIT", 613 }, - { "GETDNS_CONTEXT_CODE_EDNS_EXTENDED_RCODE", 611 }, - { "GETDNS_CONTEXT_CODE_EDNS_MAXIMUM_UDP_PAYLOAD_SIZE", 610 }, - { "GETDNS_CONTEXT_CODE_EDNS_VERSION", 612 }, - { "GETDNS_CONTEXT_CODE_FOLLOW_REDIRECTS", 602 }, - { "GETDNS_CONTEXT_CODE_IDLE_TIMEOUT", 617 }, - { "GETDNS_CONTEXT_CODE_LIMIT_OUTSTANDING_QUERIES", 606 }, - { "GETDNS_CONTEXT_CODE_MEMORY_FUNCTIONS", 615 }, - { "GETDNS_CONTEXT_CODE_NAMESPACES", 600 }, - { "GETDNS_CONTEXT_CODE_PUBKEY_PINSET", 621 }, - { "GETDNS_CONTEXT_CODE_RESOLUTION_TYPE", 601 }, - { "GETDNS_CONTEXT_CODE_SUFFIX", 608 }, - { "GETDNS_CONTEXT_CODE_TIMEOUT", 616 }, - { "GETDNS_CONTEXT_CODE_TLS_AUTHENTICATION", 618 }, - { "GETDNS_CONTEXT_CODE_TLS_QUERY_PADDING_BLOCKSIZE", 620 }, - { "GETDNS_CONTEXT_CODE_UPSTREAM_RECURSIVE_SERVERS", 603 }, - { "GETDNS_DNSSEC_BOGUS", 401 }, - { "GETDNS_DNSSEC_INDETERMINATE", 402 }, - { "GETDNS_DNSSEC_INSECURE", 403 }, - { "GETDNS_DNSSEC_NOT_PERFORMED", 404 }, - { "GETDNS_DNSSEC_SECURE", 400 }, - { "GETDNS_EXTENSION_FALSE", 1001 }, - { "GETDNS_EXTENSION_TRUE", 1000 }, - { "GETDNS_NAMESPACE_DNS", 500 }, - { "GETDNS_NAMESPACE_LOCALNAMES", 501 }, - { "GETDNS_NAMESPACE_MDNS", 503 }, - { "GETDNS_NAMESPACE_NETBIOS", 502 }, - { "GETDNS_NAMESPACE_NIS", 504 }, - { "GETDNS_NAMETYPE_DNS", 800 }, - { "GETDNS_NAMETYPE_WINS", 801 }, - { "GETDNS_OPCODE_IQUERY", 1 }, - { "GETDNS_OPCODE_NOTIFY", 4 }, - { "GETDNS_OPCODE_QUERY", 0 }, - { "GETDNS_OPCODE_STATUS", 2 }, - { "GETDNS_OPCODE_UPDATE", 5 }, - { "GETDNS_RCODE_BADALG", 21 }, - { "GETDNS_RCODE_BADKEY", 17 }, - { "GETDNS_RCODE_BADMODE", 19 }, - { "GETDNS_RCODE_BADNAME", 20 }, - { "GETDNS_RCODE_BADSIG", 16 }, - { "GETDNS_RCODE_BADTIME", 18 }, - { "GETDNS_RCODE_BADTRUNC", 22 }, - { "GETDNS_RCODE_BADVERS", 16 }, - { "GETDNS_RCODE_COOKIE", 23 }, - { "GETDNS_RCODE_FORMERR", 1 }, - { "GETDNS_RCODE_NOERROR", 0 }, - { "GETDNS_RCODE_NOTAUTH", 9 }, - { "GETDNS_RCODE_NOTIMP", 4 }, - { "GETDNS_RCODE_NOTZONE", 10 }, - { "GETDNS_RCODE_NXDOMAIN", 3 }, - { "GETDNS_RCODE_NXRRSET", 8 }, - { "GETDNS_RCODE_REFUSED", 5 }, - { "GETDNS_RCODE_SERVFAIL", 2 }, - { "GETDNS_RCODE_YXDOMAIN", 6 }, - { "GETDNS_RCODE_YXRRSET", 7 }, - { "GETDNS_REDIRECTS_DO_NOT_FOLLOW", 531 }, - { "GETDNS_REDIRECTS_FOLLOW", 530 }, - { "GETDNS_RESOLUTION_RECURSING", 521 }, - { "GETDNS_RESOLUTION_STUB", 520 }, - { "GETDNS_RESPSTATUS_ALL_BOGUS_ANSWERS", 904 }, - { "GETDNS_RESPSTATUS_ALL_TIMEOUT", 902 }, - { "GETDNS_RESPSTATUS_GOOD", 900 }, - { "GETDNS_RESPSTATUS_NO_NAME", 901 }, - { "GETDNS_RESPSTATUS_NO_SECURE_ANSWERS", 903 }, - { "GETDNS_RETURN_BAD_CONTEXT", 301 }, - { "GETDNS_RETURN_BAD_DOMAIN_NAME", 300 }, - { "GETDNS_RETURN_CONTEXT_UPDATE_FAIL", 302 }, - { "GETDNS_RETURN_DNSSEC_WITH_STUB_DISALLOWED", 309 }, - { "GETDNS_RETURN_EXTENSION_MISFORMAT", 308 }, - { "GETDNS_RETURN_GENERIC_ERROR", 1 }, - { "GETDNS_RETURN_GOOD", 0 }, - { "GETDNS_RETURN_INVALID_PARAMETER", 311 }, - { "GETDNS_RETURN_MEMORY_ERROR", 310 }, - { "GETDNS_RETURN_NEED_MORE_SPACE", 399 }, - { "GETDNS_RETURN_NOT_IMPLEMENTED", 312 }, - { "GETDNS_RETURN_NO_SUCH_DICT_NAME", 305 }, - { "GETDNS_RETURN_NO_SUCH_EXTENSION", 307 }, - { "GETDNS_RETURN_NO_SUCH_LIST_ITEM", 304 }, - { "GETDNS_RETURN_UNKNOWN_TRANSACTION", 303 }, - { "GETDNS_RETURN_WRONG_TYPE_REQUESTED", 306 }, - { "GETDNS_RRCLASS_ANY", 255 }, - { "GETDNS_RRCLASS_CH", 3 }, - { "GETDNS_RRCLASS_HS", 4 }, - { "GETDNS_RRCLASS_IN", 1 }, - { "GETDNS_RRCLASS_NONE", 254 }, - { "GETDNS_RRTYPE_A", 1 }, - { "GETDNS_RRTYPE_AAAA", 28 }, - { "GETDNS_RRTYPE_AFSDB", 18 }, - { "GETDNS_RRTYPE_ANY", 255 }, - { "GETDNS_RRTYPE_APL", 42 }, - { "GETDNS_RRTYPE_ATMA", 34 }, - { "GETDNS_RRTYPE_AXFR", 252 }, - { "GETDNS_RRTYPE_CAA", 257 }, - { "GETDNS_RRTYPE_CDNSKEY", 60 }, - { "GETDNS_RRTYPE_CDS", 59 }, - { "GETDNS_RRTYPE_CERT", 37 }, - { "GETDNS_RRTYPE_CNAME", 5 }, - { "GETDNS_RRTYPE_CSYNC", 62 }, - { "GETDNS_RRTYPE_DHCID", 49 }, - { "GETDNS_RRTYPE_DLV", 32769 }, - { "GETDNS_RRTYPE_DNAME", 39 }, - { "GETDNS_RRTYPE_DNSKEY", 48 }, - { "GETDNS_RRTYPE_DS", 43 }, - { "GETDNS_RRTYPE_EID", 31 }, - { "GETDNS_RRTYPE_GID", 102 }, - { "GETDNS_RRTYPE_GPOS", 27 }, - { "GETDNS_RRTYPE_HINFO", 13 }, - { "GETDNS_RRTYPE_HIP", 55 }, - { "GETDNS_RRTYPE_IPSECKEY", 45 }, - { "GETDNS_RRTYPE_ISDN", 20 }, - { "GETDNS_RRTYPE_IXFR", 251 }, - { "GETDNS_RRTYPE_KEY", 25 }, - { "GETDNS_RRTYPE_KX", 36 }, - { "GETDNS_RRTYPE_LOC", 29 }, - { "GETDNS_RRTYPE_LP", 107 }, - { "GETDNS_RRTYPE_MAILA", 254 }, - { "GETDNS_RRTYPE_MAILB", 253 }, - { "GETDNS_RRTYPE_MB", 7 }, - { "GETDNS_RRTYPE_MD", 3 }, - { "GETDNS_RRTYPE_MF", 4 }, - { "GETDNS_RRTYPE_MG", 8 }, - { "GETDNS_RRTYPE_MINFO", 14 }, - { "GETDNS_RRTYPE_MR", 9 }, - { "GETDNS_RRTYPE_MX", 15 }, - { "GETDNS_RRTYPE_NAPTR", 35 }, - { "GETDNS_RRTYPE_NID", 104 }, - { "GETDNS_RRTYPE_NIMLOC", 32 }, - { "GETDNS_RRTYPE_NINFO", 56 }, - { "GETDNS_RRTYPE_NS", 2 }, - { "GETDNS_RRTYPE_NSAP", 22 }, - { "GETDNS_RRTYPE_NSEC", 47 }, - { "GETDNS_RRTYPE_NULL", 10 }, - { "GETDNS_RRTYPE_NXT", 30 }, - { "GETDNS_RRTYPE_OPENPGPKEY", 61 }, - { "GETDNS_RRTYPE_OPT", 41 }, - { "GETDNS_RRTYPE_PTR", 12 }, - { "GETDNS_RRTYPE_PX", 26 }, - { "GETDNS_RRTYPE_RKEY", 57 }, - { "GETDNS_RRTYPE_RP", 17 }, - { "GETDNS_RRTYPE_RRSIG", 46 }, - { "GETDNS_RRTYPE_RT", 21 }, - { "GETDNS_RRTYPE_SIG", 24 }, - { "GETDNS_RRTYPE_SINK", 40 }, - { "GETDNS_RRTYPE_SOA", 6 }, - { "GETDNS_RRTYPE_SPF", 99 }, - { "GETDNS_RRTYPE_SRV", 33 }, - { "GETDNS_RRTYPE_SSHFP", 44 }, - { "GETDNS_RRTYPE_TA", 32768 }, - { "GETDNS_RRTYPE_TALINK", 58 }, - { "GETDNS_RRTYPE_TKEY", 249 }, - { "GETDNS_RRTYPE_TLSA", 52 }, - { "GETDNS_RRTYPE_TSIG", 250 }, - { "GETDNS_RRTYPE_TXT", 16 }, - { "GETDNS_RRTYPE_UID", 101 }, - { "GETDNS_RRTYPE_UINFO", 100 }, - { "GETDNS_RRTYPE_UNSPEC", 103 }, - { "GETDNS_RRTYPE_URI", 256 }, - { "GETDNS_RRTYPE_WKS", 11 }, - { "GETDNS_TRANSPORT_TCP", 1201 }, - { "GETDNS_TRANSPORT_TCP_ONLY", 542 }, - { "GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN", 543 }, - { "GETDNS_TRANSPORT_TLS", 1202 }, - { "GETDNS_TRANSPORT_TLS_FIRST_AND_FALL_BACK_TO_TCP_KEEP_CONNECTIONS_OPEN", 545 }, - { "GETDNS_TRANSPORT_TLS_ONLY_KEEP_CONNECTIONS_OPEN", 544 }, - { "GETDNS_TRANSPORT_UDP", 1200 }, - { "GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP", 540 }, - { "GETDNS_TRANSPORT_UDP_ONLY", 541 }, -}; -static int const_name_info_cmp(const void *a, const void *b) -{ - return strcmp( ((struct const_name_info *) a)->name - , ((struct const_name_info *) b)->name ); -} -static int -_getdns_get_const_name_info(const char *name, uint32_t *code) -{ - struct const_name_info key = { name, 0 }; - struct const_name_info *i = bsearch(&key, consts_name_info, - sizeof(consts_name_info) / sizeof(struct const_name_info), - sizeof(struct const_name_info), const_name_info_cmp); - if (!i) - return 0; - if (code) - *code = i->code; - return 1; -} diff --git a/src/test/getdns_str2dict.h b/src/test/getdns_str2dict.h deleted file mode 100644 index 71cb26c8..00000000 --- a/src/test/getdns_str2dict.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2013, NLNet Labs, Verisign, 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. - */ - -#ifndef GETDNS_STR2DICT_H_ -#define GETDNS_STR2DICT_H_ -#include "getdns/getdns.h" - -getdns_return_t getdns_str2dict(const char *str, getdns_dict **dict); -getdns_return_t getdns_str2list(const char *str, getdns_list **list); -getdns_return_t getdns_str2bindata(const char *str, getdns_bindata **bindata); -getdns_return_t getdns_str2int(const char *str, uint32_t *value); - -#endif diff --git a/src/test/tests_transports.sh b/src/test/tests_transports.sh index 13e5b6b6..0cb0947b 100755 --- a/src/test/tests_transports.sh +++ b/src/test/tests_transports.sh @@ -5,25 +5,72 @@ SERVER_IP="8.8.8.8" SERVER_IPv6="2001:4860:4860::8888" TLS_SERVER_IP="185.49.141.38~getdnsapi.net" TLS_SERVER_IPv6="2a04:b900:0:100::38~getdnsapi.net" +TLS_SERVER_SS_IP="184.105.193.78~tls-dns-u.odvr.dns-oarc.net" #Self signed cert TLS_SERVER_KEY="foxZRnIh9gZpWnl+zEiKa0EJ2rdCGroMWm02gaxSc9S=" +TLS_SERVER_SS_KEY="pOXrpUt9kgPgbWxBFFcBTbRH2heo2wHwXp1fd4AEVXI=" TLS_SERVER_WRONG_KEY="foxZRnIh9gZpWnl+zEiKa0EJ2rdCGroMWm02gaxSc1S=" GOOD_RESULT_SYNC="Status was: At least one response was returned" GOOD_RESULT_ASYNC="successfull" BAD_RESULT_SYNC="1 'Generic error'" BAD_RESULT_ASYNC="callback_type of 703" +NUM_ARGS=3 GOOD_COUNT=0 FAIL_COUNT=0 + +check_auth () { + local my_auth_ok=0; + auth_result=`echo $1 | sed 's/.*tls_auth_status\": .*//'` + if [[ $2 == "-" ]] ; then + my_auth_ok=1; + fi + if [[ $2 == "N" ]] && [[ $auth_result == "None" ]]; then + my_auth_ok=1; + fi + if [[ $2 == "F" ]] && [[ $auth_result == "Failed" ]]; then + my_auth_ok=1; + fi + if [[ $2 == "S" ]] && [[ $auth_result == "Success" ]]; then + my_auth_ok=1; + fi + echo $my_auth_ok; +} + +check_trans () { + local my_trans_ok=0; + trans_result=`echo $1 | sed "s/.*\"transport\": GETDNS_TRANSPORT_//" | sed 's/ }.*//' | sed 's/,.*//'` + if [[ $2 == "U" ]] && [[ $trans_result == "UDP" ]]; then + my_trans_ok=1; + fi + if [[ $2 == "T" ]] && [[ $trans_result == "TCP" ]]; then + my_trans_ok=1; + fi + if [[ $2 == "L" ]] && [[ $trans_result == "TLS" ]]; then + my_trans_ok=1; + fi + echo $my_trans_ok; +} + check_good () { - result=`echo $1 | grep "Response code was: GOOD." | tail -1 | sed 's/ All done.'// | sed 's/Response code was: GOOD. '//` + auth_ok=0; + result_ok=0; + trans_ok=0; + result=`echo $1 | sed 's/ All done.'// | sed 's/.*Response code was: GOOD. '//` async_success=`echo $result | grep -c "$GOOD_RESULT_ASYNC"` if [[ $result =~ $GOOD_RESULT_SYNC ]] || [[ $async_success =~ 1 ]]; then - (( GOOD_COUNT++ )) - echo -n "PASS: " - else - (( FAIL_COUNT++ )) - echo "FAIL (RESULT): " $1 - echo -n "FAIL: " + result_ok=1; + fi + if [[ $result_ok == 1 ]] ; then + trans_ok=$(check_trans "$1" "$2") + auth_ok=$(check_auth "$1" "$3") + fi + if [[ $result_ok == 1 ]] && [[ $auth_ok == 1 ]] && [[ $trans_ok == 1 ]]; then + (( GOOD_COUNT++ )) + echo -n "PASS: " + else + (( FAIL_COUNT++ )) + echo "FAIL (RESULT): Result: $result Auth: $auth_ok Trans: $trans_ok" + echo -n "FAIL: " fi } @@ -80,30 +127,38 @@ while getopts ":p:s:t:k:idh" opt; do done TLS_SERVER_IP_NO_NAME=`echo ${TLS_SERVER_IP%~*}` +TLS_SERVER_SS_IP_NO_NAME=`echo ${TLS_SERVER_SS_IP%~*}` TLS_SERVER_IP_WRONG_NAME=`echo ${TLS_SERVER_IP::${#TLS_SERVER_IP}-1}` +NUM_GOOD_QUERIES=7 GOOD_QUERIES=( -"-s -A -q getdnsapi.net -l U @${SERVER_IP} " -"-s -A -q getdnsapi.net -l T @${SERVER_IP} " -"-s -A -q getdnsapi.net -l L @${TLS_SERVER_IP_NO_NAME}" -"-s -A -q getdnsapi.net -l L -m @${TLS_SERVER_IP}" -"-s -A -q getdnsapi.net -l L -m @${TLS_SERVER_IP_NO_NAME} -K pin-sha256=\"${TLS_SERVER_KEY}\"" -"-s -A -q getdnsapi.net -l L -m @${TLS_SERVER_IP} -K pin-sha256=\"${TLS_SERVER_KEY}\"" -"-s -G -q DNSKEY getdnsapi.net -l U @${SERVER_IP} -b 512 -D") +"-s -A getdnsapi.net -l U @${SERVER_IP}" "U" "-" +"-s -A getdnsapi.net -l T @${SERVER_IP}" "T" "-" +"-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 -A getdnsapi.net -l L -m @${TLS_SERVER_SS_IP_NO_NAME} -K pin-sha256=\"${TLS_SERVER_SS_KEY}\"" "L" "S" +"-s -G DNSKEY getdnsapi.net -l U @${SERVER_IP} -b 512 -D" "U" "-") +NUM_GOOD_FB_QUERIES=6 GOOD_FALLBACK_QUERIES=( -"-s -A -q getdnsapi.net -l LT @${SERVER_IP}" -"-s -A -q getdnsapi.net -l LT @${SERVER_IP}" -"-s -A -q getdnsapi.net -l LT @${TLS_SERVER_IP_NO_NAME}" -"-s -A -q getdnsapi.net -l LT -m @${TLS_SERVER_IP_NO_NAME}" -"-s -A -q getdnsapi.net -l L @${SERVER_IP} @${TLS_SERVER_IP_NO_NAME}" -"-s -G -q DNSKEY getdnsapi.net -l UT @${SERVER_IP} -b 512 -D") +"-s -A getdnsapi.net -l LU @${SERVER_IP}" "U" "-" +"-s -A getdnsapi.net -l LT @${SERVER_IP}" "T" "-" +"-s -A getdnsapi.net -l LT @${TLS_SERVER_IP_NO_NAME}" "L" "N" +"-s -A getdnsapi.net -l LT -m @${TLS_SERVER_IP_NO_NAME}" "L" "N" +"-s -A getdnsapi.net -l L @${SERVER_IP} @${TLS_SERVER_IP_NO_NAME}" "L" "-" +"-s -G DNSKEY getdnsapi.net -l UT @${SERVER_IP} -b 512 -D" "T" "-") NOT_AVAILABLE_QUERIES=( -"-s -A -q getdnsapi.net -l L @${SERVER_IP}" -"-s -A -q getdnsapi.net -l L -m @${TLS_SERVER_IP_WRONG_NAME}" -"-s -A -q getdnsapi.net -l L -m @${TLS_SERVER_IP_NO_NAME}" -"-s -A -q getdnsapi.net -l L -m @${TLS_SERVER_IP_NO_NAME} -K pin-sha256=\"${TLS_SERVER_WRONG_KEY}\"") +"-s -A getdnsapi.net -l L @${SERVER_IP}" +"-s -A getdnsapi.net -l L -m @${TLS_SERVER_IP_WRONG_NAME}" +"-s -A getdnsapi.net -l L -m @${TLS_SERVER_IP_NO_NAME}" +"-s -A getdnsapi.net -l L -m @${TLS_SERVER_IP_NO_NAME} -K pin-sha256=\"${TLS_SERVER_WRONG_KEY}\"" +"-s -A getdnsapi.net -l L -m @${TLS_SERVER_IP} -K pin-sha256=\"${TLS_SERVER_WRONG_KEY}\"" +"-s -A getdnsapi.net -l L -m @${TLS_SERVER_IP_WRONG_NAME} -K pin-sha256=\"${TLS_SERVER_KEY}\"" +"-s -A getdnsapi.net -l L -m @${TLS_SERVER_IP_WRONG_NAME} -K pin-sha256=\"${TLS_SERVER_WRONG_KEY}\"" +"-s -A getdnsapi.net -l L -m @${TLS_SERVER_SS_IP} -K pin-sha256=\"${TLS_SERVER_SS_KEY}\"") echo "Starting transport test" @@ -118,19 +173,19 @@ for (( i = 0; i < 2; i+=1 )); do fi echo "*Success cases:" - for (( j = 0; j < ${#GOOD_QUERIES[@]}; j+=1 )); do - check_good "`$DIR/getdns_query $SYNC_MODE ${GOOD_QUERIES[${j}]} 2>/dev/null`" - echo "getdns_query $SYNC_MODE ${GOOD_QUERIES[${j}]}" - (( COUNT++ )) + for (( j = 0; j < $NUM_GOOD_QUERIES; j+=1 )); do + check_good "`$DIR/getdns_query +return_call_reporting $SYNC_MODE ${GOOD_QUERIES[$j*$NUM_ARGS]} 2>/dev/null`" ${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 < ${#GOOD_FALLBACK_QUERIES[@]}; j+=1 )); do - check_good "`$DIR/getdns_query $SYNC_MODE ${GOOD_FALLBACK_QUERIES[${j}]} 2>/dev/null`" - echo "getdns_query $SYNC_MODE ${GOOD_FALLBACK_QUERIES[${j}]}" - (( COUNT++ )) + for (( j = 0; j < $NUM_GOOD_FB_QUERIES; j+=1 )); do + check_good "`$DIR/getdns_query +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 "`$DIR/getdns_query $SYNC_MODE ${NOT_AVAILABLE_QUERIES[${j}]} 2>&1`" diff --git a/src/test/tpkg/110-link.tpkg/110-link.test b/src/test/tpkg/110-link.tpkg/110-link.test index c0d77db0..424c9074 100644 --- a/src/test/tpkg/110-link.tpkg/110-link.test +++ b/src/test/tpkg/110-link.tpkg/110-link.test @@ -6,5 +6,5 @@ cd "${BUILDDIR}/build" make getdns_query \ - && echo "export GETDNS_QUERY=\"${BUILDDIR}/build/src/test/getdns_query\"" \ + && echo "export GETDNS_QUERY=\"${BUILDDIR}/build/src/tools/getdns_query\"" \ >> ../.tpkg.var.master diff --git a/src/test/tpkg/210-stub-only-link.tpkg/210-stub-only-link.test b/src/test/tpkg/210-stub-only-link.tpkg/210-stub-only-link.test index 33379762..94aaf626 100644 --- a/src/test/tpkg/210-stub-only-link.tpkg/210-stub-only-link.test +++ b/src/test/tpkg/210-stub-only-link.tpkg/210-stub-only-link.test @@ -6,5 +6,5 @@ cd "${BUILDDIR}/build-stub-only" make getdns_query \ - && echo "export GETDNS_STUB_QUERY=\"${BUILDDIR}/build-stub-only/src/test/getdns_query\"" \ + && echo "export GETDNS_STUB_QUERY=\"${BUILDDIR}/build-stub-only/src/tools/getdns_query\"" \ >> ../.tpkg.var.master diff --git a/src/tools/Makefile.in b/src/tools/Makefile.in new file mode 100644 index 00000000..d98cf437 --- /dev/null +++ b/src/tools/Makefile.in @@ -0,0 +1,117 @@ +# +# @configure_input@ +# +# Copyright (c) 2013, Verisign, Inc., 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. + +package = @PACKAGE_NAME@ +version = @PACKAGE_VERSION@ +tarname = @PACKAGE_TARNAME@ +distdir = $(tarname)-$(version) + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +INSTALL = @INSTALL@ +LIBTOOL = ../../libtool + +srcdir = @srcdir@ + +CC=@CC@ +WPEDANTICFLAG=@WPEDANTICFLAG@ +CFLAGS=-I$(srcdir)/.. -I$(srcdir) -I.. $(cflags) @CFLAGS@ @CPPFLAGS@ $(WPEDANTICFLAG) $(XTRA_CFLAGS) +LDFLAGS=-L.. @LDFLAGS@ +LDLIBS=../libgetdns.la @LIBS@ + +ALL_OBJS=getdns_query.lo + +PROGRAMS=getdns_query + + +.SUFFIXES: .c .o .a .lo .h + +.c.o: + $(CC) $(CFLAGS) -c $< -o $@ + +.c.lo: + $(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) -c $< -o $@ + +default: all + +all: $(PROGRAMS) + +$(ALL_OBJS): + $(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) -c $(srcdir)/$(@:.lo=.c) -o $@ + +getdns_query: getdns_query.lo + $(LIBTOOL) --tag=CC --mode=link $(CC) $(CFLAGS) -o $@ getdns_query.lo $(LDFLAGS) $(LDLIBS) + +stubby: getdns_query.lo + $(LIBTOOL) --tag=CC --mode=link $(CC) $(CFLAGS) -o $@ getdns_query.lo $(LDFLAGS) $(LDLIBS) + +install-getdns_query: getdns_query + $(INSTALL) -m 755 -d $(DESTDIR)$(bindir) + $(LIBTOOL) --mode=install cp getdns_query $(DESTDIR)$(bindir) + +uninstall-getdns_query: + $(LIBTOOL) --mode=uninstall rm -f $(DESTDIR)$(bindir)/getdns_query + +install-stubby: getdns_query + $(INSTALL) -m 755 -d $(DESTDIR)$(bindir) + $(LIBTOOL) --mode=install cp getdns_query $(DESTDIR)$(bindir)/stubby + +uninstall-stubby: + $(LIBTOOL) --mode=uninstall rm -f $(DESTDIR)$(bindir)/stubby + +clean: + rm -f *.o *.lo $(PROGRAMS) + rm -rf .libs + +distclean : clean + rm -f Makefile + +Makefile: $(srcdir)/Makefile.in ../../config.status + cd ../.. && ./config.status src/test/Makefile + +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.. -I"$$blddir"/.. *.c | \ + sed -e "s? $$blddir/? ?g" \ + -e 's? \([a-z0-9_-]*\)\.\([ch]\)? $$(srcdir)/\1.\2?g' \ + -e 's? \.\./\([a-z0-9_-]*\)\.h? $$(srcdir)/../\1.h?g' \ + -e 's? \.\./\([a-z0-9_-]*\)/\([a-z0-9_-]*\)\.h? $$(srcdir)/../\1/\2.h?g' \ + -e 's? \$$(srcdir)/config\.h? ../config.h?g' \ + -e 's? \$$(srcdir)/\.\./config\.h? ../config.h?g' \ + -e 's? \$$(srcdir)/\.\./getdns/getdns\.h? ../getdns/getdns.h?g' \ + -e 's? \$$(srcdir)/\.\./getdns/getdns_extra\.h? ../getdns/getdns_extra.h?g' \ + -e 's!\(.*\)\.o[ :]*!\1.lo \1.o: !g' >> Makefile.in.new ) + (cd $(srcdir) ; diff Makefile.in.new Makefile.in && rm Makefile.in.new \ + || mv Makefile.in.new Makefile.in ) + +.PHONY: clean test + +# Dependencies for getdns_query +getdns_query.lo getdns_query.o: $(srcdir)/getdns_query.c ../config.h $(srcdir)/../debug.h ../config.h \ + ../getdns/getdns.h ../getdns/getdns_extra.h diff --git a/src/test/getdns_query.c b/src/tools/getdns_query.c similarity index 86% rename from src/test/getdns_query.c rename to src/tools/getdns_query.c index 86d8f143..61825f1b 100644 --- a/src/test/getdns_query.c +++ b/src/tools/getdns_query.c @@ -27,9 +27,6 @@ #include "config.h" #include "debug.h" -#include "getdns_str2dict.h" -#include "getdns_context_config.h" -#include "getdns_context_set_listen_addresses.h" #include #include #include @@ -51,6 +48,19 @@ typedef unsigned short in_port_t; #define EXAMPLE_PIN "pin-sha256=\"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\"" +static int i_am_stubby = 0; +static const char *default_stubby_config = +"{ resolution_type: GETDNS_RESOLUTION_STUB" +", dns_transport_list: [ GETDNS_TRANSPORT_TLS, GETDNS_TRANSPORT_UDP, GETDNS_TRANSPORT_TCP ]" +", idle_timeout: 10000" +", listen_addresses: [ 127.0.0.1@53, 0::1@53 ]" +", tls_query_padding_blocksize: 256" +", edns_client_subnet_private : 1" +"}"; +static int clear_listen_list_on_arg = 0; +#ifndef GETDNS_ON_WINDOWS +static int run_in_foreground = 1; +#endif static int quiet = 0; static int batch_mode = 0; static char *query_file = NULL; @@ -154,13 +164,19 @@ print_usage(FILE *out, const char *progname) { fprintf(out, "usage: %s [